Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package COSE; import com.upokecenter.cbor.CBORObject; import com.upokecenter.cbor.CBORType; import static java.lang.Integer.min; import java.math.BigInteger; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.math.ec.ECPoint; /** * The Signer class is used to implement the COSE_Signer object. * This provides the information dealing with a single signature for the SignMessage class. * <p> * Create a Signer object for adding a new signature to a message, existing signers will have a Signer object created for them when a SignMessage object is created by Message.DecodeFromBytes. * <p> * Examples of using this class can be found in * <br><a href="https://github.com/cose-wg/COSE-JAVA/wiki/Sign-Message-Example">Single Signer Example</a> an example of signing and verify a message with a single signature. * <br><a href="https://github.com/cose-wg/COSE-JAVA/wiki/Multi-Sign-Example">Multiple Signer Example</a> an example of signing and verifying a message which has multiple signatures. * @author jimsch */ public class Signer extends Attribute { protected byte[] rgbSignature; protected String contextString; OneKey cnKey; /** * Create a new signer object to add to a SignMessage */ public Signer() { contextString = "Signature"; } /** * Create a new signer object for a SignMessage and set the key to be used. * * @param key key to use for signing. */ public Signer(OneKey key) { contextString = "Signature"; cnKey = key; } /** * Remove the key object from the signer * * @since COSE 0.9.1 */ public void clearKey() { cnKey = null; } /** * Provide the key for the signer * * @param keyIn key to be used for signing or verification * @throws CoseException Errors generated by the COSE module * @deprecated As of COSE 0.9.1, use setKey(OneKey) */ @Deprecated public void setKey(CBORObject keyIn) throws CoseException { cnKey = new OneKey(keyIn); setupKey(cnKey); } /** * Set a key object on a signer * * @since COSE 0.9.1 * @param keyIn key to be used for signing or verification */ public void setKey(OneKey keyIn) throws CoseException { setupKey(keyIn); } /** * Set the key on the object, if there is not a signature on this object then set * the algorithm and the key id from the key if they exist on the key and do not exist in the message. * * @param key key to be used */ private void setupKey(OneKey key) throws CoseException { CBORObject cn2; CBORObject cn; cnKey = key; if (rgbSignature != null) return; cn = key.get(KeyKeys.Algorithm); if (cn != null) { cn2 = findAttribute(HeaderKeys.Algorithm); if (cn2 == null) addAttribute(HeaderKeys.Algorithm, cn, Attribute.PROTECTED); } cn = key.get(KeyKeys.KeyId); if (cn != null) { cn2 = findAttribute(HeaderKeys.KID); if (cn2 == null) addAttribute(HeaderKeys.KID, cn, Attribute.UNPROTECTED); } } /** * Internal function used in creating a Sign1Message object from a byte string. * * @param obj COSE_Sign1 encoded object. * @throws CoseException Errors generated by the COSE module */ protected void DecodeFromCBORObject(CBORObject obj) throws CoseException { if (obj.getType() != CBORType.Array) throw new CoseException("Invalid Signer structure"); if (obj.size() != 3) throw new CoseException("Invalid Signer structure"); if (obj.get(0).getType() == CBORType.ByteString) { rgbProtected = obj.get(0).GetByteString(); if (rgbProtected.length == 0) { objProtected = CBORObject.NewMap(); } else { objProtected = CBORObject.DecodeFromBytes(rgbProtected); if (objProtected.size() == 0) rgbProtected = new byte[0]; } } else throw new CoseException("Invalid Signer structure"); if (obj.get(1).getType() == CBORType.Map) { objUnprotected = obj.get(1); } else throw new CoseException("Invalid Signer structure"); if (obj.get(2).getType() == CBORType.ByteString) rgbSignature = obj.get(2).GetByteString(); else if (!obj.get(2).isNull()) throw new CoseException("Invalid Signer structure"); } /** * Internal function used to create a serialization of a COSE_Sign1 message * * @return CBOR object which can be encoded. * @throws CoseException Errors generated by the COSE module */ protected CBORObject EncodeToCBORObject() throws CoseException { if (rgbSignature == null) throw new CoseException("Message not yet signed"); if (rgbProtected == null) throw new CoseException("Internal Error"); CBORObject obj = CBORObject.NewArray(); obj.Add(rgbProtected); obj.Add(objUnprotected); obj.Add(rgbSignature); return obj; } public void sign(byte[] rgbBodyProtected, byte[] rgbContent) throws CoseException { if (rgbProtected == null) { if (objProtected.size() == 0) rgbProtected = new byte[0]; else rgbProtected = objProtected.EncodeToBytes(); } CBORObject obj = CBORObject.NewArray(); obj.Add(contextString); obj.Add(rgbBodyProtected); obj.Add(rgbProtected); obj.Add(externalData); obj.Add(rgbContent); AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm)); rgbSignature = Signer.computeSignature(alg, obj.EncodeToBytes(), cnKey); } public boolean validate(byte[] rgbBodyProtected, byte[] rgbContent) throws CoseException { CBORObject obj = CBORObject.NewArray(); obj.Add(contextString); obj.Add(rgbBodyProtected); obj.Add(rgbProtected); obj.Add(externalData); obj.Add(rgbContent); AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm)); return Signer.validateSignature(alg, obj.EncodeToBytes(), rgbSignature, cnKey); } static byte[] computeSignature(AlgorithmID alg, byte[] rgbToBeSigned, OneKey cnKey) throws CoseException { Digest digest; CBORObject cn; switch (alg) { case ECDSA_256: digest = new SHA256Digest(); break; case ECDSA_384: digest = new SHA384Digest(); break; case ECDSA_512: digest = new SHA512Digest(); break; default: throw new CoseException("Unsupported Algorithm Specified"); } switch (alg) { case ECDSA_256: case ECDSA_384: case ECDSA_512: { digest.update(rgbToBeSigned, 0, rgbToBeSigned.length); byte[] rgbDigest = new byte[digest.getDigestSize()]; digest.doFinal(rgbDigest, 0); cn = cnKey.get(KeyKeys.KeyType); if ((cn == null) || (cn != KeyKeys.KeyType_EC2)) throw new CoseException("Must use key with key type EC2"); cn = cnKey.get(KeyKeys.EC2_D); if (cn == null) throw new CoseException("Private key required to sign"); X9ECParameters p = cnKey.GetCurve(); ECDomainParameters parameters = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()); ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1, cn.GetByteString()), parameters); ECDSASigner ecdsa = new ECDSASigner(); ecdsa.init(true, privKey); BigInteger[] sig = ecdsa.generateSignature(rgbDigest); int cb = (p.getCurve().getFieldSize() + 7) / 8; byte[] r = sig[0].toByteArray(); byte[] s = sig[1].toByteArray(); byte[] sigs = new byte[cb * 2]; int cbR = min(cb, r.length); System.arraycopy(r, r.length - cbR, sigs, cb - cbR, cbR); cbR = min(cb, s.length); System.arraycopy(s, s.length - cbR, sigs, cb + cb - cbR, cbR); return sigs; } default: throw new CoseException("Internal error"); } } static boolean validateSignature(AlgorithmID alg, byte[] rgbToBeSigned, byte[] rgbSignature, OneKey cnKey) throws CoseException { Digest digest; switch (alg) { case ECDSA_256: digest = new SHA256Digest(); break; case ECDSA_384: digest = new SHA384Digest(); break; case ECDSA_512: digest = new SHA512Digest(); break; default: throw new CoseException("Unsupported algorithm specified"); } switch (alg) { case ECDSA_256: case ECDSA_384: case ECDSA_512: { byte[] rgbR = new byte[rgbSignature.length / 2]; byte[] rgbS = new byte[rgbSignature.length / 2]; System.arraycopy(rgbSignature, 0, rgbR, 0, rgbR.length); System.arraycopy(rgbSignature, rgbR.length, rgbS, 0, rgbR.length); digest.update(rgbToBeSigned, 0, rgbToBeSigned.length); byte[] rgbDigest = new byte[digest.getDigestSize()]; digest.doFinal(rgbDigest, 0); X9ECParameters p = cnKey.GetCurve(); ECDomainParameters parameters = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()); BigInteger bnX = new BigInteger(1, cnKey.get(KeyKeys.EC2_X.AsCBOR()).GetByteString()); ECPoint point = p.getCurve().createPoint(bnX, new BigInteger(1, cnKey.get(KeyKeys.EC2_Y.AsCBOR()).GetByteString())); ECPublicKeyParameters pubKey = new ECPublicKeyParameters(point, parameters); ECDSASigner ecdsa = new ECDSASigner(); ecdsa.init(false, pubKey); return ecdsa.verifySignature(rgbDigest, new BigInteger(1, rgbR), new BigInteger(1, rgbS)); } default: throw new CoseException("Internal error"); } } }