Rud*_*eri 1 java security digital-signature der ecdsa
我必须验证证书。我不是密码学专家,所以可能我做错了什么(或所有:))。当代码到达最后一步 ( boolean b = sig.verify(CertSign);) 时,它会触发一个异常:java.security.SignatureException: errordecodeing signature bytes。有人可以帮我弄清楚我做错了什么吗?
以下是显示问题的测试代码:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我希望 b 等于真(或假),但我得到以下异常:
_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)_
Run Code Online (Sandbox Code Playgroud)
异常原因
正如对问题的评论中已经提到的,签名的长度与预期的长度不同。
会发生异常,因为预计会有 70 个字节的 DER 编码签名,但您的 CertSign 仅包含 64 个字节。
假设
由于它是 64 个字节,因此可以假设它是一个 ECDSA 签名,由两个连接的 BigInteger 数字组成,每个数字有 32 个字节。
ECDSA 签名到 DER 编码签名的转换
要将其转换为 DER 编码的签名,可以使用以下内容:
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);
}
Run Code Online (Sandbox Code Playgroud)
然后需要调整代码中使用签名的行,例如像这样:
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");
}
Run Code Online (Sandbox Code Playgroud)
快速测试
然后异常消失了,调试控制台中的输出如下所示:
May 11, 2019 5:09:48 PM CardManager test
INFO: controllo fallito
Run Code Online (Sandbox Code Playgroud)
所以验证实际上失败了,但你提到你希望 b 是真或假,所以也许这是正确的结果。但是如果没有更多数据,这是不可能验证的。
进一步测试
我也很快用你的代码尝试了不同的签名算法。对于他们中的每一个,都会有一个错误。
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");
...
Run Code Online (Sandbox Code Playgroud)
仍然 - 没有进一步的数据(例如使用的签名算法等),很难说出不匹配的原因是什么。
使用不同的测试数据
为了验证包括新 DEREncodeSignature 在内的例程是否正常工作,我签署了消息“Hello world!” 与ECDSA使用生成brainpoolP256r1的参数规格和SHA256withECDDSA签名算法,则数据如下所示:
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
};
Run Code Online (Sandbox Code Playgroud)
如果该数据被获取并通过例程运行,包括。DEREncodeSignature 然后在调试控制台中显示以下输出:
May 12, 2019 12:13:42 AM CardManager test
INFO: verificato
Run Code Online (Sandbox Code Playgroud)
(sig = Signature.getInstance("SHA256withECDDSA", "BC");当然使用)
这意味着对于ECDSA带有brainpoolP256r1参数规范的生成数据,SHA256withECDDSA它将与替代测试数据一起使用。