问题
I have to verify a certificate. I'm not an expert of cryptography, so probably I did something (or everything :) ) wrong.
When the code reach the last step (boolean b = sig.verify(CertSign);
), it fires an exception: java.security.SignatureException: error decoding signature bytes.
Could someone help me to figure out what I'm doing wrong?
The following is a test code that shows the problem:
private void test() {
byte [] CertBody = new byte[]{(byte)0x7F,(byte)0x4E,(byte)0x81,(byte)0x82,
(byte)0x5F,(byte)0x29,(byte)0x01,(byte)0x00,
(byte)0x42,(byte)0x08,(byte)0xFB,(byte)0x55,
(byte)0x54,(byte)0x4F,(byte)0x02,(byte)0xFF,
(byte)0xFF,(byte)0x01,(byte)0x5F,(byte)0x4C,
(byte)0x07,(byte)0xFF,(byte)0x53,(byte)0x4D,
(byte)0x52,(byte)0x44,(byte)0x54,(byte)0x02,
(byte)0x7F,(byte)0x49,(byte)0x4E,(byte)0x06,
(byte)0x09,(byte)0x2B,(byte)0x24,(byte)0x03,
(byte)0x03,(byte)0x02,(byte)0x08,(byte)0x01,
(byte)0x01,(byte)0x07,(byte)0x86,(byte)0x41,
(byte)0x04,(byte)0x6C,(byte)0x17,(byte)0x5B,
(byte)0xB9,(byte)0xEF,(byte)0x5D,(byte)0x02,
(byte)0x20,(byte)0x51,(byte)0xCC,(byte)0xB2,
(byte)0x89,(byte)0x09,(byte)0x42,(byte)0x15,
(byte)0x85,(byte)0xD3,(byte)0x1A,(byte)0xCD,
(byte)0xA7,(byte)0x1B,(byte)0x08,(byte)0x4A,
(byte)0x48,(byte)0x8F,(byte)0x7B,(byte)0x77,
(byte)0x5B,(byte)0x57,(byte)0x9B,(byte)0xB7,
(byte)0x41,(byte)0x3B,(byte)0xDA,(byte)0x4C,
(byte)0xDE,(byte)0x70,(byte)0x11,(byte)0x17,
(byte)0xE0,(byte)0xD9,(byte)0xD1,(byte)0x0F,
(byte)0x74,(byte)0xD6,(byte)0x25,(byte)0xB2,
(byte)0x86,(byte)0xCA,(byte)0x04,(byte)0x64,
(byte)0x54,(byte)0x4F,(byte)0x1B,(byte)0x53,
(byte)0xF3,(byte)0x24,(byte)0x02,(byte)0xD3,
(byte)0xB3,(byte)0x74,(byte)0xEF,(byte)0xA6,
(byte)0xC7,(byte)0x5F,(byte)0x20,(byte)0x08,
(byte)0xEA,(byte)0xF4,(byte)0xBE,(byte)0xCC,
(byte)0x03,(byte)0x18,(byte)0x02,(byte)0xA3,
(byte)0x5F,(byte)0x25,(byte)0x04,(byte)0x5A,
(byte)0xAF,(byte)0xA9,(byte)0xDF,(byte)0x5F,
(byte)0x24,(byte)0x04,(byte)0x5C,(byte)0x90,
(byte)0xDD,(byte)0x5F};
byte [] CertSign = new byte[]{(byte)0x3D,(byte)0x61,(byte)0x23,
(byte)0xBE,(byte)0x6C,(byte)0x0B,
(byte)0xC1,(byte)0x3E,(byte)0x7A,
(byte)0x2D,(byte)0x60,(byte)0x3D,
(byte)0x28,(byte)0xF0,(byte)0x29,
(byte)0xCC,(byte)0x8C,(byte)0x55,
(byte)0xF1,(byte)0x9A,(byte)0x5C,
(byte)0x7E,(byte)0xBE,(byte)0xFD,
(byte)0x43,(byte)0x27,(byte)0x1A,
(byte)0x9D,(byte)0xA1,(byte)0x7C,
(byte)0x81,(byte)0x09,(byte)0x5C,
(byte)0x1D,(byte)0x26,(byte)0x27,
(byte)0x76,(byte)0x73,(byte)0x74,
(byte)0x87,(byte)0xF9,(byte)0x6C,
(byte)0x2A,(byte)0xC9,(byte)0xA3,
(byte)0x32,(byte)0x3C,(byte)0x60,
(byte)0x51,(byte)0x71,(byte)0x6D,
(byte)0x2F,(byte)0xC0,(byte)0xF6,
(byte)0x89,(byte)0x14,(byte)0x93,
(byte)0xB2,(byte)0xB6,(byte)0x87,
(byte)0x21,(byte)0xAF,(byte)0x01,
(byte)0x42};
String Algorithm = "brainpoolP256r1";
byte [] PublicPoint = new byte[]{(byte)0x04,(byte)0x68,(byte)0xF8,
(byte)0xD6,(byte)0xB3,(byte)0x94,
(byte)0xD2,(byte)0x8E,(byte)0x2B,
(byte)0x4D,(byte)0xFA,(byte)0x36,
(byte)0x52,(byte)0xAE,(byte)0xAC,
(byte)0xDA,(byte)0xD3,(byte)0x19,
(byte)0x21,(byte)0xC2,(byte)0x12,
(byte)0x8D,(byte)0x58,(byte)0x9F,
(byte)0x4E,(byte)0xF1,(byte)0xB1,
(byte)0xC4,(byte)0x10,(byte)0x97,
(byte)0x29,(byte)0xDC,(byte)0xA8,
(byte)0xA1,(byte)0xC9,(byte)0x53,
(byte)0x04,(byte)0xC8,(byte)0x77,
(byte)0x4F,(byte)0x57,(byte)0xFA,
(byte)0xFB,(byte)0x15,(byte)0x59,
(byte)0xB9,(byte)0x2F,(byte)0x72,
(byte)0x6A,(byte)0xE4,(byte)0x22,
(byte)0x64,(byte)0x3E,(byte)0x1B,
(byte)0x11,(byte)0x8D,(byte)0x69,
(byte)0xAC,(byte)0x36,(byte)0x25,
(byte)0xAD,(byte)0x1C,(byte)0x30,
(byte)0x04,(byte)0xA9};
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
ECNamedCurveParameterSpec params1 = ECNamedCurveTable.getParameterSpec(Algorithm);
try {
KeyFactory fact = KeyFactory.getInstance("ECDSA", "BC");
ECCurve curve = params1.getCurve();
java.security.spec.EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, params1.getSeed());
java.security.spec.ECPoint point=ECPointUtil.decodePoint(ellipticCurve, PublicPoint);
java.security.spec.ECParameterSpec params2=EC5Util.convertSpec(ellipticCurve, params1);
java.security.spec.ECPublicKeySpec keySpec = new java.security.spec.ECPublicKeySpec(point,params2);
ECPublicKey PK = (ECPublicKey)fact.generatePublic(keySpec);
Signature sig;
sig = Signature.getInstance("ECDSA", "BC");
sig.initVerify(PK);
sig.update(CertBody);
boolean b = sig.verify(CertSign);
if (b)
{
Logger.getLogger(CardManager.class.getName()).log(Level.INFO, "verificato");
}
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | InvalidKeyException | SignatureException ex) {
Logger.getLogger("test").log(Level.SEVERE, null, ex);
}
}
I Expected to have the b equals true (or false) but instead I get the following exception:
_java.security.SignatureException: error decoding signature bytes.
at org.bouncycastle.jcajce.provider.asymmetric.util.DSABase.engineVerify(DSABase.java:80)
at java.security.Signature$Delegate.engineVerify(Signature.java:1223)
at java.security.Signature.verify(Signature.java:656)_
回答1:
Reason for the Exception
As already mentioned in the comments to the question, the length of the signature differs from the expected length.
The exception happens, since a DER encoded signature with 70 bytes is expected, but your CertSign contains only 64 bytes.
Assumptions
Since it is 64 bytes, one could assume that it is an ECDSA signature consisting of two concatenated BigInteger numbers with 32 bytes each.
Conversion of ECDSA signature to DER encoded signature
To convert that to a DER encoded signature one can use sth like:
private byte[] DEREncodeSignature(byte [] signature) throws IOException {
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signature, 0, 32));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(r));
v.add(new ASN1Integer(s));
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
}
Then the lines in your code where you use the signature needs to be adjusted, e.g. to sth like this:
boolean b = sig.verify(DEREncodeSignature(CertSign));
if (b) {
Logger.getLogger(CardManager.class.getName()).log(Level.INFO, "verificato");
}
else {
Logger.getLogger(CardManager.class.getName()).log(Level.INFO, "controllo fallito");
}
Quick Test
The exception is then gone, the output in the debug console looks like this:
May 11, 2019 5:09:48 PM CardManager test
INFO: controllo fallito
So the verification actually fails, but you mention that you expect b to be either true or false, so maybe it's the right result. But without having more data this is not possible to validate.
Further Tests
I also quickly tried different signature algorithms with your code. For each of them one gets a false.
String[] signatureAlgorithms = new String[] {"RIPEMD160withECDSA", "SHA224withECDDSA", "SHA256withECDDSA", "SHA384withECDDSA", "SHA512withECDDSA", "SHA1withECDSA", "NONEwithECDSA", "SHA224withECDSA", "SHA256withECDSA", "SHA384withECDSA", "SHA512withECDSA", "SHA3-224withECDSA", "SHA3-256withECDSA", "SHA3-384withECDSA", "SHA3-512withECDSA"};
for(String algorithm : signatureAlgorithms) {
Signature sig;
Logger.getLogger(CardManager.class.getName()).log(Level.INFO, "trying aloorithm " + algorithm + ": ");
sig = Signature.getInstance(algorithm, "BC");
...
Still - without further data (e.g. used signature algorithm etc.) it is difficult to say what the reason for the mismatch is.
Use Different Test Data
To verify that the routines including the new DEREncodeSignature is working correctly I signed the message 'Hello world!' with ECDSA
generator using the brainpoolP256r1
parameter spec and SHA256withECDDSA
signature algorithm, then the data looks like this:
byte[] CertBody = new byte[]{(byte) 0x48,
(byte) 0x65, (byte) 0x6C, (byte) 0x6C,
(byte) 0x6F, (byte) 0x20, (byte) 0x77,
(byte) 0x6F, (byte) 0x72, (byte) 0x6C,
(byte) 0x64, (byte) 0x21};
byte[] CertSign = new byte[]{
(byte)0x0C,(byte)0x5B,(byte)0xE9,
(byte)0xA4,(byte)0xF7,(byte)0xC9,
(byte)0x5A,(byte)0x69,(byte)0x8E,
(byte)0x91,(byte)0x50,(byte)0xB4,
(byte)0x4E,(byte)0x33,(byte)0x14,
(byte)0x7C,(byte)0x2F,(byte)0x15,
(byte)0x93,(byte)0x97,(byte)0x8B,
(byte)0xD8,(byte)0x65,(byte)0x20,
(byte)0x1B,(byte)0x1D,(byte)0xF4,
(byte)0x8A,(byte)0xB3,(byte)0x81,
(byte)0x55,(byte)0x6A,
(byte)0x96,
(byte)0xE4,(byte)0x89,(byte)0x05,
(byte)0x80,(byte)0xAA,(byte)0x34,
(byte)0x3A,(byte)0x86,(byte)0x11,
(byte)0x5F,(byte)0x0B,(byte)0x8F,
(byte)0xF7,(byte)0xE7,(byte)0xA6,
(byte)0x7D,(byte)0xCC,(byte)0x9C,
(byte)0xD0,(byte)0xC8,(byte)0x94,
(byte)0xCA,(byte)0x78,(byte)0x9C,
(byte)0xFE,(byte)0x41,(byte)0x2A,
(byte)0xB0,(byte)0xE1,(byte)0x7E,
(byte)0x29,
};
byte[] PublicPoint = new byte[]{
(byte) 0x04,
(byte) 0x1E, (byte) 0xC8, (byte) 0xB7,
(byte) 0x00, (byte) 0xF1, (byte) 0xFD,
(byte) 0x06, (byte) 0x97, (byte) 0x73,
(byte) 0x71, (byte) 0x09, (byte) 0x12,
(byte) 0xF2, (byte) 0xB7, (byte) 0xEF,
(byte) 0xA5, (byte) 0x23, (byte) 0xA1,
(byte) 0xC1, (byte) 0x6C, (byte) 0xA7,
(byte) 0xD1, (byte) 0x0C, (byte) 0x25,
(byte) 0x6E, (byte) 0x04, (byte) 0x09,
(byte) 0x7A, (byte) 0x62, (byte) 0xC3,
(byte) 0x0E, (byte) 0x93, (byte) 0x54,
(byte) 0x7F, (byte) 0x0C, (byte) 0xE4,
(byte) 0x0F, (byte) 0xF8, (byte) 0x63,
(byte) 0x82, (byte) 0x6D, (byte) 0x0B,
(byte) 0x50, (byte) 0xC0, (byte) 0x59,
(byte) 0x1F, (byte) 0xFC, (byte) 0x36,
(byte) 0x1B, (byte) 0x0E, (byte) 0x2A,
(byte) 0xA3, (byte) 0xD4, (byte) 0x29,
(byte) 0x4E, (byte) 0x30, (byte) 0x91,
(byte) 0x44, (byte) 0x28, (byte) 0x2E,
(byte) 0x15, (byte) 0x76, (byte) 0x48,
(byte) 0xE7
};
If that data is taken and run through the routines incl. DEREncodeSignature then the following output is shown in the debug console:
May 12, 2019 12:13:42 AM CardManager test
INFO: verificato
(using sig = Signature.getInstance("SHA256withECDDSA", "BC");
of course)
This means that for ECDSA
generated data with brainpoolP256r1
parameter spec and SHA256withECDDSA
it would work with the alternative test data.
来源:https://stackoverflow.com/questions/55742269/java-exception-during-signature-verification-error-decoding-signature-bytes