【发布时间】:2016-09-27 02:00:28
【问题描述】:
我需要使用 BouncyCastle 加密提供程序通过 Java 验证 ECDSA 签名。到目前为止,BouncyCastle 未能验证签名。
签名是在Atmel AT88CK590 Crypto Authentication模块中创建的,公钥可以从模块中获取。这是 C/C++ 格式的公钥,长度为 64 个八位字节:
uint8_t pubKey[] = {
// X coordinate of the elliptic curve.
0xc1, 0x71, 0xCB, 0xED, 0x65, 0x71, 0x82, 0x2E, 0x8F, 0x8A, 0x43, 0x8D, 0x72, 0x56, 0xD1, 0xC8,
0x86, 0x3C, 0xD0, 0xBC, 0x7F, 0xCC, 0xE3, 0x6D, 0xE7, 0xB7, 0x17, 0xED, 0x29, 0xC8, 0x38, 0xCB,
// Y coordinate of the elliptic curve.
0x80, 0xCD, 0xBE, 0x0F, 0x1D, 0x5C, 0xC5, 0x46, 0x99, 0x24, 0x8F, 0x6E, 0x0A, 0xEA, 0x1F, 0x7A,
0x43, 0xBA, 0x2B, 0x03, 0x80, 0x90, 0xE9, 0x25, 0xB2, 0xD0, 0xE6, 0x48, 0x93, 0x91, 0x64, 0x83
};
Base64编码的原始消息、签名和公钥:
// Raw message to sign
private static final String tokenStr = "12345678901234567890123456789012";
// Base64 encoded
private static final String pubKeyStr = "wXHL7WVxgi6PikONclbRyIY80Lx/zONt57cX7SnIOMuAzb4PHVzFRpkkj24K6h96
Q7orA4CQ6SWy0OZIk5Fkgw==";
// Base64 encoded
private static final String signatureStr = "XF2WossFTA82ndYFGEH0FPqAldkFQLGd/Bv/Qh8UYip7sXUvCUnFgi1YXjN3WxLn
IwSo3OaHLCOzGAtIis0b3A==";
要转换公钥,我使用以下方法:
private static PublicKey getPublicKeyFromBytes(byte[] pubKey, String ecSpec, String provider)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(ecSpec);
KeyFactory kf = KeyFactory.getInstance(ECDSA_CRYPTO, provider);
ECNamedCurveSpec params = new ECNamedCurveSpec(ecSpec, spec.getCurve(), spec.getG(), spec.getN());
ECPoint pubPoint = ECPointUtil.decodePoint(params.getCurve(), pubKey);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(pubPoint, params);
PublicKey publicKey = kf.generatePublic(pubKeySpec);
return publicKey;
}
要将签名转换为 DER 格式,我使用以下方法:
private static byte[] toDERSignature(byte[] tokenSignature) throws IOException {
byte[] r = Arrays.copyOfRange(tokenSignature, 0, tokenSignature.length / 2);
byte[] s = Arrays.copyOfRange(tokenSignature, tokenSignature.length / 2, tokenSignature.length);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(new BigInteger(1, r)));
v.add(new ASN1Integer(new BigInteger(1, s)));
derOutputStream.writeObject(new DERSequence(v));
byte[] derSignature = byteArrayOutputStream.toByteArray();
return derSignature;
}
这是验证签名的代码:
Security.addProvider(new BouncyCastleProvider());
byte[] tokenBytes = tokenStr.getBytes("UTF-8");
String urlDecodePubKeyStr = pubKeyStr.replace(newlineHtml, "");
byte[] pubKeyBytes = DatatypeConverter.parseBase64Binary(urlDecodePubKeyStr);
String urlDecodeSignatureStr = signatureStr.replace(newlineHtml, "");
byte[] signBytes = DatatypeConverter.parseBase64Binary(urlDecodeSignatureStr);
byte[] derSignature = toDERSignature(signBytes);
ByteBuffer bb = ByteBuffer.allocate(pubKeyBytes.length + 1);
bb.put((byte)4);
bb.put(pubKeyBytes);
PublicKey ecPublicKey = getPublicKeyFromBytes(bb.array(), "prime256v1", "BC");
System.out.println("\nSignature: " + Hex.toHexString(signBytes).toUpperCase());
System.out.println("DER Signature: " + Hex.toHexString(derSignature).toUpperCase());
System.out.println(ecPublicKey.toString());
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initVerify(ecPublicKey);
signature.update(tokenBytes);
boolean result = signature.verify(derSignature);
System.out.println("BC Signature Valid: " + result);
输出:
Signature: 5C5D96A2CB054C0F369DD6051841F414FA8095D90540B19DFC1BFF421F14622A7BB1752F0949C5822D585E33775B12E72304A8DCE6872C23B3180B488ACD1BDC
DER Signature: 304402205C5D96A2CB054C0F369DD6051841F414FA8095D90540B19DFC1BFF421F14622A02207BB1752F0949C5822D585E33775B12E72304A8DCE6872C23B3180B488ACD1BDC
EC Public Key
X: c171cbed6571822e8f8a438d7256d1c8863cd0bc7fcce36de7b717ed29c838cb
Y: 80cdbe0f1d5cc54699248f6e0aea1f7a43ba2b038090e925b2d0e64893916483
BC Signature Valid: false
以前有没有人遇到过同样的问题?我在这里错过了什么?
【问题讨论】:
-
newlineHtml 定义如下: private static final String newlineHtml = " ";
-
您是否对输入数据(又名消息或明文)进行了二进制比较?你确定公钥和私钥是一对吗?哈希算法呢?曲线呢?
标签: java cryptography bouncycastle atmel elliptic-curve