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).
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;
}
}