Search code examples
javaopensslcryptographybouncycastleecdsa

How to verify a signature in openssl consisting of ECDSA [r, s] values?


I am using Bouncy Castle/Java to generate signature, as it gives 2 big integers with R, S values. Now requirement is to verify that using OpenSSL commands.

public static String[] generateSignature(String message) {
    final ECDSASigner ecdsaSigner = new ECDSASigner();

    try {

        // Get private key

        BCECPrivateKey ecPrivateKey = (BCECPrivateKey) JKSUtil.getPrivateKey();
        if(ecPrivateKey != null) {
            // Get parameter spec from private key
            ECParameterSpec ecSpec = ecPrivateKey.getParameters();

            // Create ECDomain object
            ECDomainParameters params = new ECDomainParameters(ecSpec.getCurve(), ecSpec.getG(), // G
                    ecSpec.getN()); // n

            // Create private key parameters
            ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(ecPrivateKey.getD(), // d
                    params);

            ecdsaSigner.init(true, priKey);

            // Create digest message (hash) for the actual message. 
            SHA256Digest keyDigest = new SHA256Digest();
            keyDigest.update(message.getBytes(), 0, message.getBytes().length);
            byte[] digestMessage = new byte[keyDigest.getDigestSize()];
            keyDigest.doFinal(digestMessage, 0);
            BigInteger[] sig = ecdsaSigner.generateSignature(digestMessage);

            // Convert signature to base64
            String[] base64Sig = new String[2];
            base64Sig[0] = new String(Base64.encodeBase64(sig[0].toByteArray()));
            base64Sig[1] = new String(Base64.encodeBase64(sig[1].toByteArray()));
            String signature = base64Sig[0]+"|"+base64Sig[1];

            System.out.println(signature);

            return base64Sig;
        }
    } catch (Exception e) {

    }

    return null;
}

For example: my message which I want to sign is "Name is RS", running the above utility returns me the R and S values with delimiter '|' encoded in base64 as

ALk8VQP5XLmCIe0Kh3IE74fS55huvzv0jw==|Hj+UsStEbDYOqX98EszSET2ULE3D9kCV

Now on openssl side:

openssl enc  -base64 -d -in signature.txt -out signatureToVerify.bin -A
openssl dgst -sha256 -verify eckey.pub -signature signatureToVerify.bin message.txt

Its not able to verify and I am stuck - keep getting the error as "Verification Failure" on openssl side.


Solution

  • OpenSSL requires a X9.62 signature. This consists of the DER encoding of the two integer values R and S. If you look at the Bouncy Castle source code for the provider class SignatureSpi you will find:

    ASN1EncodableVector v = new ASN1EncodableVector();
    v.add(new ASN1Integer(r));
    v.add(new ASN1Integer(s));
    byte[] derEncodedSignature = new DERSequence(v).getEncoded(ASN1Encoding.DER);
    

    which is what you should use to encode the signature using Bouncy Castle. Here r and s should be the BigInteger values you get in the two element result array.

    As you might have guessed, you can also use the Java JCA Signature class instead, which will produce the same signature encoding. If required or wanted you could use the BouncyCastle implementation as underlying provider by registering / indicating the provider. Funny enough the Bouncy Castle library now has a pretty fast EC implementation which beats the Oracle provided implementation, even though that is in native code.


    The binary output is directly compatible with the signature input for OpenSSL. To create a text string it is possible to encode the byte array to a base 64 string (preferably without line endings, e.g. Base64.encode(derEncodedsignature)), which then has to be decoded by openssl enc -base64 -A -d -in signature.txt -out signatureToVerify.bin