【发布时间】:2018-04-20 19:24:37
【问题描述】:
我有一个自签名证书(不是流行的 X.509 证书)。证书和 ECDSA 公钥/私钥对由 java 程序使用 bouncy castle 生成。 我需要使用 openssl 用 C 程序验证这个证书。但是,ECDSA 签名可以通过 java 程序正确验证,但通过 openssl 验证失败。如果我使用相同的私钥使用 openssl 重新签署消息,则使用 openssl 的签名验证将通过。
这里是与密钥生成相关的java代码sn-p。(密钥对生成后,我实际上将它们保存在文件中,所以它总是使用相同的密钥,下面的片段中没有显示)。
KeyPair rootCASigningKeys = cryptoManager.generateKeyPair(SignatureChoices.ecdsaNistP256Signature);
BCECPublicKey bcPub = cryptoManager.toBCECPublicKey(PublicKeyAlgorithm.ecies_nistp256, (java.security.interfaces.ECPublicKey) rootCASigningKeys.getPublic());
ECPublicKey pk = (java.security.interfaces.ECPublicKey) rootCASigningKeys.getPublic();
ECPrivateKey priv = (ECPrivateKey) rootCASigningKeys.getPrivate();
System.out.println("pubkeyX="+getHexString(pk.getW().getAffineX().toByteArray()));
System.out.println("pubkeyY="+getHexString(pk.getW().getAffineY().toByteArray()));
System.out.println("privatekey="+getHexString(priv.getS().toByteArray()));
验证签名的C代码如下:
bool v2x_ecdsa_verify(
const uint8_t *digest,
size_t dgstlen,
const void *sig,
const void *pkey)
{
ECDSA_SIG *ecdsa_sig;
EC_KEY *ec_key;
EC_GROUP *grp
int ret;
/* debug */
EC_POINT *ecpt;
/* debug end */
grp = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); /*TODO: */
ecpt = EC_KEY_get0_public_key((const EC_KEY *)pkey);
struct ec_point_st *cpt = (struct ec_point_st *)ecpt;
printf("Pubkey_x:%d=\n", BN_num_bytes(&cpt->X));
BN_print_fp(stdout, &cpt->X);
printf("Pubkey_y:%d=\n", BN_num_bytes(&cpt->Y));
BN_print_fp(stdout, &cpt->Y);
printf("\n");
printf("digest dump\n");
v2x_dump_hex(V2X_LOG_ERR, digest, 32);
printf("\n");
{
/* DEBUG code, Use the private key outputed by Java code to re-sign the digest */
uint8_t priv_buf[] = {
0x00, 0xB2, 0x6C, 0x1D, 0x0C, 0x62, 0x84, 0x45, 0x31, 0x3C, 0xF3, 0x83, 0x1D, 0x4E, 0xA7, 0x4B,
0x2C, 0x07, 0x19, 0xCF, 0x19, 0xCC, 0x3E, 0xA7, 0xE5, 0x4F, 0xA4, 0xF0, 0x91, 0xBF, 0x5B, 0x96, 0xE8};
BIGNUM *priv_bn = BN_bin2bn(priv_buf, sizeof(priv_buf), NULL);
printf("priv_bn=\n");
BN_print_fp(stdout, priv_bn);
printf("\n");
if (!priv_bn) {
V2X_ERR("Failed to conver privkey \n");
return false;
}
EC_KEY *priv_key = EC_KEY_new();
if (priv_key) {
if (!EC_KEY_set_group(priv_key, grp)) {
V2X_ERR("Failed to set group\n");
return false;
}
EC_KEY_set_private_key(priv_key, priv_bn);
}
ecdsa_sig = v2x_ecdsa_sign(digest, dgstlen, NULL, 0, (void *)priv_key);
/* END of DEBUG code */
}
//ret = ECDSA_do_verify(digest, dgstlen, (ECDSA_SIG *)sig, (EC_KEY *)pkey); /* It fail to verify original signature */
ret = ECDSA_do_verify(digest, dgstlen, ecdsa_sig, (EC_KEY *)pkey); /* it pass to verify the re-signed signature */
if (ret < 0)
V2X_ERR("ret=%d\n", ret);
return (bool)ret;
}
java和C打印出来的key如下:
Java 使用充气城堡:
provider=BC
pubkeyX=00 E4 B6 E1 50 C2 7A 85 DD BD 92 F8 14 C9 0E B0 5E 1E 28 A6 3C A3 B6 B1 69 32 39 BF 1B 1B F0 B0 03
pubkeyY=00 ED DC 75 F0 E9 36 05 25 5F 54 08 74 E7 9D 6E BC 1B DF 97 5A E4 D2 A7 04 A7 E0 5F 21 06 54 26 1E
privatekey=00 B2 6C 1D 0C 62 84 45 31 3C F3 83 1D 4E A7 4B 2C 07 19 CF 19 CC 3E A7 E5 4F A4 F0 91 BF 5B 96 E8
而被签名的 sha256 哈希是
F40D983058408C0519D7E238BEBFA5EDCAA7F3B86AD4C83847F5DD66EA1C051B
C 使用 OpenSSL
Pubkey_x:32=
E4B6E150C27A85DDBD92F814C90EB05E1E28A63CA3B6B1693239BF1B1BF0B003
Pubkey_y:32=
EDDC75F0E93605255F540874E79D6EBC1BDF975AE4D2A704A7E05F210654261E
digest dump
f4 0d 98 30 58 40 8c 05 19 d7 e2 38 be bf a5 ed
ca a7 f3 b8 6a d4 c8 38 47 f5 dd 66 ea 1c 05 1b
priv_bn=
B26C1D0C628445313CF3831D4EA74B2C0719CF19CC3EA7E54FA4F091BF5B96E8
我被困在这里,java 程序可以签署和验证给定的摘要,而 C 程序可以使用相同的密钥对签署和验证相同的摘要。但是 C 程序无法验证 java 程序生成的签名。我还从 java 中导出了生成的签名 r/s 部分,并与我在 C 程序中尝试验证的内容进行比较,它们是相同的。
我可以更深入地了解 OpenSSL 以添加调试,但可惜我不是 java 充气城堡方面的专家。非常感谢任何建议!感谢和抱歉发了这么长的帖子。
【问题讨论】:
-
Bouncy Castle 签名是否始终为 64 字节,而 OpenSSL 签名通常为 70 字节左右? stackoverflow.com/a/39575576/6535399
标签: openssl cryptography bouncycastle ecdsa