2

When verifying a signature using Signature.verify I receive an "Invalid encoding for signature" exception. When verifying same signature using Azure service, the signature is verified.

I have a hash-data (SHA-256), a public key, and a signature that I'm trying to verify. The signature was received using com.microsoft.azure.keyvault.KeyVaultClient.sign method, with signing algorithm "ES256".

This works (using ES256 algorithm) :

    com.microsoft.azure.keyvault.KeyVaultClient keyVaultClient;
    String keyPairIdentifier;

    boolean verify(byte[] hashData, byte[] signature, JsonWebKeySignatureAlgorithm signingAlgorithm) {
        com.microsoft.azure.keyvault.models.KeyVerifyResult result = keyVaultClient.verify(keyPairIdentifier, signingAlgorithm, hashData, signature);
        return result.value().booleanValue();
    }

This fails (certificate holds same public key that is stored in Azure keyvault):

    Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
    ecdsaSign.initVerify(certificate.getPublicKey());
    ecdsaSign.update(hashData);
    ecdsaSign.verify(signature)

Expected result - true (signature is verified)

Actual result:

java.security.SignatureException: Could not verify signature
    at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:325)
    at java.security.Signature$Delegate.engineVerify(Signature.java:1222)
    at java.security.Signature.verify(Signature.java:655)
    at TestKV.KeyVault.VerifyDPSignature.verifySignatureUsingCertificate(VerifyDPSignature.java:143)
    at TestKV.KeyVault.VerifyDPSignature.main(VerifyDPSignature.java:104)
Caused by: java.security.SignatureException: Invalid encoding for signature
    at sun.security.ec.ECDSASignature.decodeSignature(ECDSASignature.java:400)
    at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:322)
    ... 4 more 
Caused by: java.io.IOException: Sequence tag error
    at sun.security.util.DerInputStream.getSequence(DerInputStream.java:330)
    at sun.security.ec.ECDSASignature.decodeSignature (ECDSASignature.java:376)
Asaf Artsi
  • 39
  • 1
  • 6

3 Answers3

1

Azure does JWS which simply concatenates fixed-size I2OSP of r and s but Java JCE, like most but not all standards, uses ASN.1 DER encoding e.g. rfc3279 (caveat: there are now OIDs for ECDSA with other hashes).

To convert JWS/plain to DER, see my (cross) https://security.stackexchange.com/questions/174095/convert-ecdsa-signature-from-plain-to-der-format for a C approach, but Java makes it easier because BigInteger does half the work for you:

// byte[64] plain contains the JWS-style r,s (de-base64-ed if necessary)
byte[] r = new BigInteger(1,Arrays.copyOfRange(plain,0,32)).toByteArray();
byte[] s = new BigInteger(1,Arrays.copyOfRange(plain,32,64)).toByteArray();
byte[] der = new byte[6+r.length+s.length]; der[0] = 0x30; der[1] = der.length-2; int o = 2;
der[o++] = 2; der[o++] = (byte)r.length; System.arraycopy (r,0, der,o, r.length); o+=r.length;
der[o++] = 2; der[o++] = (byte)s.length; System.arraycopy (s,0, der,o, s.length); //o+=s.length;

2020-05 corrected and added: also Java 9 up handles this directly using algoirthm names like SHA256withECDSAinP1363format, and on all versions of Java if you add BouncyCastle it does so using names like SHA256withPLAIN-ECDSA or SHA256withCVC-ECDSA. See how to specify signature length for java.security.Signature sign method and Java ECDSAwithSHA256 signature with inconsistent length .

Community
  • 1
  • 1
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
1

dave_thompson_085 - thanks! There were a few mistakes in the code you attached, the tags of the signature parts should be 0x02, not 0x30, and you didn't increase o after copying the first part. This is the code after the changes:

    byte[] r = new BigInteger(1,Arrays.copyOfRange(signature,0,32)).toByteArray();
    byte[] s = new BigInteger(1,Arrays.copyOfRange(signature,32,64)).toByteArray();
    byte[] der = new byte[6+r.length+s.length];
    der[0] = 0x30; // Tag of signature object
    der[1] = (byte)(der.length-2); // Length of signature object
    int o = 2;
    der[o++] = 0x02; // Tag of ASN1 Integer
    der[o++] = (byte)r.length; // Length of first signature part
    System.arraycopy (r,0, der,o, r.length);
    o += r.length;
    der[o++] = 0x02; // Tag of ASN1 Integer
    der[o++] = (byte)s.length; // Length of second signature part
    System.arraycopy (s,0, der,o, s.length);

After the format change I didn't get the "Sequence tag error" exception. But the verification still failed.

Thanks!

Asaf Artsi
  • 39
  • 1
  • 6
0

I just had to first decode the signature's raw bytes to Base64 in my case.

byte[] signatureBytes = Base64.getDecoder().decode(signature.getBytes());    
byte[] r = new BigInteger(1,Arrays.copyOfRange(signatureBytes,0,32)).toByteArray();    
byte[] s = new BigInteger(1,Arrays.copyOfRange(signatureBytes,32,64)).toByteArray();
helvete
  • 2,455
  • 13
  • 33
  • 37
Rajesh.R
  • 1
  • 2