I tested the ECDSA signature verification solution ( How can I get the PublicKey object from EC public key bytes? ), Which works fine with data.
This is the data:
byte[] pubKey = DatatypeConverter.parseHexBinary("049a55ad1e210cd113457ccd3465b930c9e7ade5e760ef64b63142dad43a308ed08e2d85632e8ff0322d3c7fda14409eafdc4c5b8ee0882fe885c92e3789c36a7a"); byte[] message = DatatypeConverter.parseHexBinary("54686973206973206a75737420736f6d6520706f696e746c6573732064756d6d7920737472696e672e205468616e6b7320616e7977617920666f722074616b696e67207468652074696d6520746f206465636f6465206974203b2d29"); byte[] signature = DatatypeConverter.parseHexBinary("304402205fef461a4714a18a5ca6dce6d5ab8604f09f3899313a28ab430eb9860f8be9d602203c8d36446be85383af3f2e8630f40c4172543322b5e8973e03fff2309755e654");
And this is the code (which prints true):
private static boolean isValidSignature(byte[] pubKey, byte[] message,byte[] signature) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, InvalidKeySpecException { Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider()); ecdsaVerify.initVerify(getPublicKeyFromBytes(pubKey)); ecdsaVerify.update(message); return ecdsaVerify.verify(signature); } private static PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException { ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1"); KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider()); ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN()); ECPoint point = ECPointUtil.decodePoint(params.getCurve(), pubKey); ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params); ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec); return pk; } public static void main (String[] args) { System.out.println(isValidSignature(pubKey, message, signature)); }
My problem arises when I change the signature and data for an example of input from an already implemented system:
final static byte[] pubKey = DatatypeConverter.parseHexBinary("0447303876C6FED5550DF3EE1136989FCD87293D54A5D8E2F2F6D7FBE9A81089B889A5917443AF33E696178CEF4C9D6A4288B2745B29AF6C8BCAD1348F78EB9F9B"); final static byte[] message = DatatypeConverter.parseHexBinary("02158001f53611a06e2d1a270000013ed9305dc2780524015110500000002d0100140092569202017aa00c5dd30000000000000000000000000000000007d1000001020001b20788b80059f48d95cdefc8c6000200200030d41e0000012016840310a50733a9870fffd0430100"); final static byte[] signature = DatatypeConverter.parseHexBinary("531F8918FF250132959B01F7F56FDFD9E6CA3EC2144E12A6DA37C281489A3D96"); ( "02158001f53611a06e2d1a270000013ed9305dc2780524015110500000002d0100140092569202017aa00c5dd30000000000000000000000000000000007d1000001020001b20788b80059f48d95cdefc8c6000200200030d41e0000012016840310a50733a9870fffd0430100"); final static byte[] pubKey = DatatypeConverter.parseHexBinary("0447303876C6FED5550DF3EE1136989FCD87293D54A5D8E2F2F6D7FBE9A81089B889A5917443AF33E696178CEF4C9D6A4288B2745B29AF6C8BCAD1348F78EB9F9B"); final static byte[] message = DatatypeConverter.parseHexBinary("02158001f53611a06e2d1a270000013ed9305dc2780524015110500000002d0100140092569202017aa00c5dd30000000000000000000000000000000007d1000001020001b20788b80059f48d95cdefc8c6000200200030d41e0000012016840310a50733a9870fffd0430100"); final static byte[] signature = DatatypeConverter.parseHexBinary("531F8918FF250132959B01F7F56FDFD9E6CA3EC2144E12A6DA37C281489A3D96");
New data displays this error:
java.security.SignatureException: error decoding signature bytes. at org.bouncycastle.jcajce.provider.asymmetric.util.DSABase.engineVerify(Unknown Source) at java.security.Signature$Delegate.engineVerify(Signature.java:1178) at java.security.Signature.verify(Signature.java:612) at its.sec.exec.TestProgram.isValidSignature(TestProgram.java:168) at its.sec.exec.TestProgram.execution(TestProgram.java:101) at its.sec.exec.TestProgram.main(TestProgram.java:55)
I assume the problem is with the signature that comes with the secure message, because:
- The key pair has the same length and format as in the example. And they are true, since they come from a certificate that signs the message.
- The message itself (payload) should not affect the security process.
The last thing worth mentioning is that in my documentation it is indicated that the signature should be preceded by a field called "R", which "contains the x coordinate of the elliptic curve point resulting from the multiplication of the generator element by an ephemeral private key" and its length should be the same as the signature (32 bytes).
Can someone tell me what I'm missing here?
EDIT: solution
As Peter Dettman pointed out in his answer, signature not formatted correctly (also the contents were incorrect) for calculation by the verify() method. Here is a good explanation, basically saying that:
When encoding in DER, this (signature) becomes the following sequence of bytes:
0x30 b1 0x02 b2 (vr) 0x02 b3 (vs)
Where:
- b1 is a one-byte value equal to the length in bytes of the remaining list of bytes (from the first 0x02 to the end of the encoding);
- b2 is a one-byte value equal to the length in bytes (vr);
- b3 - a single-byte value equal to the length in bytes (vs);
- (vr) is the signed coding of a large number of the minimum length “r” character;
- (vs) is a signed big-endian encoding of s with a minimum length.
Applying this change, signature grows to 70 bytes, and execution does not throw an error.