COSE.Signer.java Source code

Java tutorial

Introduction

Here is the source code for COSE.Signer.java

Source

/*
 * 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");
        }
    }
}