我确切地知道需要二进制数据以及DER编码,这里我使用NID_X9_62_prime256v1(secp256r1)
我的python代码按照我的预期工作
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidSignature
import binascii
private_key_hex = '3041020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420ecf1e6a65ac33acbda328d2269ea493d838d9f884cbe19d37b4c7db432d748d8'
private_key_bytes = binascii.unhexlify(private_key_hex)
private_key = serialization.load_der_private_key(private_key_bytes, password=None, backend=default_backend())
data = b'rm=355190&seid=10255&tms=1681095980140'
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(data)
digest_bytes = digest.finalize()
signature = private_key.sign(digest_bytes, ec.ECDSA(utils.Prehashed(hashes.SHA256())))
signature_hex = binascii.hexlify(signature)
# signature = binascii.unhexlify("3046022100e843d75182aff2db9cdd151efb6bbb8bbe61aabc5a5e2d26f0eb75071e8bb08e022100a1ff522bda09d48c264d48b05416f0177ec5dedd6a50200880a60008a733c66d")
# signature = binascii.unhexlify("3046022100c0f12409c66dd42a30ce6dc2f8ded65f44995aaeeee096412877c56c1cd35d31022100e0c5f16588e597a39e5894b09a89849171c99d6d43c2d6e80b97957fc7a83968")
print('signature:', signature_hex)
public_key = private_key.public_key()
public_key_bytes = public_key.public_bytes(encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo)
public_key_hex = binascii.hexlify(public_key_bytes).decode('utf-8')
print('公钥:', public_key_hex)
try:
public_key.verify(signature, digest_bytes, ec.ECDSA(utils.Prehashed(hashes.SHA256())))
print('signature success')
except InvalidSignature:
print('signature failed')
在这个完全成功的代码示例中,如果私钥和椭圆曲线是固定的,那么公钥也应该是固定的
私钥:3041020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420ecf1e6a65ac33acbda328d2269ea493d838d9f884cbe19d37b4c7db432d748d8
公钥:3059301306072a8648ce3d020106082a8648ce3d030107034200042754a2ad56e81284db868f65c38cc0c594c07a49ca3804e0b9565164f3b882c8e6ff98a23e69c4fe798cf263bebb9853f8136a8921c0aa9740b3d01da68d55d7
曲线:NID_X9_62_prime256v1(secp256r1)
当我使用这个私钥时,试图在C++中获得完全相同的公钥总是失败,这是我的代码,我尝试了两种方法第一:
void test_pub()
{
// hex private_key
const char* private_key_hex = "3041020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420ecf1e6a65ac33acbda328d2269ea493d838d9f884cbe19d37b4c7db432d748d8";
int private_key_len = strlen(private_key_hex) / 2;
EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
BIGNUM* bn_private_key = BN_new();
BN_bin2bn((const unsigned char*)private_key_hex, private_key_len, bn_private_key);
EC_KEY_set_private_key(ec_key, bn_private_key);
EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_POINT* public_key_point = EC_POINT_new(group);
EC_POINT_mul(group, public_key_point, bn_private_key, NULL, NULL, NULL);
EC_KEY_set_public_key(ec_key, public_key_point);
// publickey to DER
int public_key_der_len = i2d_EC_PUBKEY(ec_key, NULL);
unsigned char* public_key_der = (unsigned char*)OPENSSL_malloc(public_key_der_len);
unsigned char* p = public_key_der;
i2d_EC_PUBKEY(ec_key, &p);
// der publickey to hex
char* public_key_hex = (char*)OPENSSL_malloc(public_key_der_len * 2 + 1);
for (int i = 0; i < public_key_der_len; ++i)
{
sprintf(&public_key_hex[i * 2], "%02x", public_key_der[i]);
}
printf("publickey: %s\n", public_key_hex);
// free
OPENSSL_free(public_key_hex);
OPENSSL_free(public_key_der);
BN_free(bn_private_key);
EC_POINT_free(public_key_point);
EC_KEY_free(ec_key);
return;
}
i get public key结果如下,很明显,它与python结果不匹配,虽然可以进一步签名和验证public key:3059301306072a8648ce3d020106082a8648ce3d03010703420004c3a79d5a74e86f443be46278de639454e4c01863d687ba8f446cc545f10e3429dabc6c2e7a17ec00710ea86ac5eef711419c0dc62ec86a9085490bdc48169a51
然后我试了另一种方法:
void test_pub()
{
const char* private_key_hex = "3041020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420ecf1e6a65ac33acbda328d2269ea493d838d9f884cbe19d37b4c7db432d748d8";
int private_key_len = strlen(private_key_hex) / 2;
BIGNUM* bn_private_key = BN_new();
BN_bin2bn((const unsigned char*)private_key_hex, private_key_len, bn_private_key);
//use d2i_ECPrivateKey
size_t der_len = strlen(private_key_hex) / 2;
unsigned char* der_buf = (unsigned char*)malloc(der_len);
hex_to_bin(private_key_hex, der_buf, der_len);
EC_KEY* ec_key = d2i_ECPrivateKey(NULL, (const unsigned char**)&der_buf, der_len);
if (ec_key == NULL)
{
unsigned long err = ERR_get_error();
char err_str[256];
ERR_error_string_n(err, err_str, sizeof(err_str));
printf("Error decoding private key: %s\n", err_str);
}
}
d2i_ECPrivateKey运行失败:私钥解码错误:错误:068000A8:asn1编码例程::错误标记
上面的C++代码是否有错误,openssl的官方示例程序很少,谁能提供一个关于d2i_ECPrivateKey的示例?谢谢
正确地将公钥与私钥分开
1条答案
按热度按时间zdwk9cvp1#
你的主要问题是你把private_key_hex当作一个二进制blob而不是一个十六进制值的字符串,而且你没有使用正确的OpenSSL API。
主要是你想使用d2i_PrivateKey从二进制数组中加载键,你可以使用它,例如。使用i2d_PUBKEY提取公钥。
下面是一个加载私钥并提取公钥的示例,并测试它是否等于上面的示例。我没有将私钥/公钥作为字符串,而是作为二进制数组。如果你需要将一个字符串转换成二进制数组,那么你可以自己写,或者使用openssl apis OPENSSL_hexstr2buf和OPENSSL_buf2hexstr。