javaandroidcbor

Error trying to verify signature on CBOR message


I'm having problems decoding EU Digital Certificate ("Covid pass") using COSE-JAVA. Public key appears to load ok but when I try to validate the CBOR message, I get following error:

COSE.CoseException: Signature verification failure
    at COSE.SignCommon.validateSignature(SignCommon.java:205)
    at COSE.Signer.validate(Signer.java:212)
    at COSE.Message.validate(Message.java:288)
Caused by: java.lang.NullPointerException: Attempt to get length of null array
    at COSE.SignCommon.convertConcatToDer(SignCommon.java:212)

Here is code for validation:

public static String DecodeMessage(byte[] data) throws CoseException, CborParseException {
    Message m = Encrypt0Message.DecodeFromBytes(data);

    PublicKey key = getPublicKey("here goes PEM of public key");

    CborMap cborMap = CborMap.createFromCborByteArray(m.GetContent());

    CounterSign signer = new CounterSign();
    signer.setKey(new OneKey(key, null));
    signer.addAttribute(HeaderKeys.Algorithm, AlgorithmID.ECDSA_256.AsCBOR(), Attribute.ProtectedAttributes);

    // error happens here
    m.validate(signer);


    return cborMap.toJsonString();
}

This is how public key is generated from PEM:

    public static PublicKey getPublicKey(String keyData) {
    try
    {
        Security.removeProvider("BC");
        Security.addProvider(new BouncyCastleProvider());
        Reader rdr = new StringReader(
                "-----BEGIN EC PUBLIC KEY-----\n" + keyData + "\n" + "-----END EC PUBLIC KEY-----\n"
        );

        org.bouncycastle.util.io.pem.PemObject spki = new org.bouncycastle.util.io.pem.PemReader(rdr).readPemObject();

        byte[] content = spki.getContent();
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
        return KeyFactory.getInstance("EC", "BC").generatePublic(pubKeySpec);
    }
    catch (Exception ex)
    {
        return  null;
    }

Public key is defined as:

"publicKeyAlgorithm": {
  "hash": {
    "name": "SHA-256"
  },
  "name": "ECDSA",
  "namedCurve": "P-256"
},
"publicKeyPem": "..."

Here is code for doing same thing in Python, might help someone. Code must work on Android 7.1 (embedded device, no way to upgrade to newer Android).


Solution

  • Solved...

    public static boolean VerifySignature(byte[] data, String publicKey) {
        boolean result = false;
        try {
    
            Message m = Encrypt0Message.DecodeFromBytes(data);
            Sign1Message sm = (Sign1Message) m;
    
            PublicKey key = getPublicKey(publicKey);
    
            if (sm.validate(new OneKey(key, null))) {
                result = true;
            }
        } catch (Exception ex) {
            log.error("Error verifying signature", ex);
        }
        return result;
    }
    

    and

        public static PublicKey getPublicKey(String keyData) {
        try {
            Security.removeProvider("BC");
            Security.addProvider(new BouncyCastleProvider());
            Reader rdr = new StringReader(
                    "-----BEGIN EC PUBLIC KEY-----\n" + keyData + "\n" + "-----END EC PUBLIC KEY-----\n"
            );
    
            org.bouncycastle.util.io.pem.PemObject spki = new org.bouncycastle.util.io.pem.PemReader(rdr).readPemObject();
    
            byte[] content = spki.getContent();
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
            return KeyFactory.getInstance("EC", "BC").generatePublic(pubKeySpec);
        } catch (Exception ex) {
            return null;
        }
    }