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.
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