我正在尝试使用 OpenSSL(用于验证签名)在 C++ 中导入ECDSA公钥,但d2i_ECPKParameters返回 NULL。
使用 Web Cryptographi API 生成的密钥;以spki格式导出的公钥(W3 TR 文档在导出密钥时讨论 ASN.1 结构,以及 spki 的 DER 编码)。
我是 OpenSSL 的新手,我做错了什么?
进口:
bool ecdsa_verify(
const std::array<uint8_t, 20>& hash,
const std::experimental::basic_string_view<uint8_t>& signature,
const std::experimental::basic_string_view<uint8_t>& public_key) {
EC_GROUP* ec_group = nullptr;
const unsigned char* public_key_data = public_key.data();
ec_group = d2i_ECPKParameters(nullptr, &public_key_data, public_key.length());
if (ec_group == nullptr) {
return false; // RETURN POINT
}
EC_KEY* ec_key = EC_KEY_new();
if (ec_key == nullptr) {
EC_GROUP_free(ec_group);
return false;
}
if (!EC_KEY_set_group(ec_key, ec_group)) {
EC_GROUP_free(ec_group);
EC_KEY_free(ec_key);
return false;
}
bool is_signature_valid =
ECDSA_verify(0, hash.data(), hash.size(), signature.data(),
signature.length(), ec_key);
EC_GROUP_free(ec_group);
EC_KEY_free(ec_key);
return is_signature_valid;
}
更新: 其他导入尝试(但仍然无效):
const unsigned char* public_key_data = public_key.data();
EC_KEY* ec_key =
o2i_ECPublicKey(nullptr, &public_key_data, public_key.length());
if (ec_key == nullptr) {
return false; // RETURN POINT
}
bool is_signature_valid =
ECDSA_verify(0, hash.data(), hash.size(), signature.data(),
signature.length(), ec_key);
EC_KEY_free(ec_key);
出口:
function ecdsa_export_pub_key(key) {
return window.crypto.subtle.exportKey(
"spki",
key);
}
更新 2:
我生成了 PEM 密钥(从 JS 中的导出密钥),但主要的是,我不使用 PEM 密钥。在 JavaScript 中导出公钥后,我从 ArrayBuffer 中创建一个新的 Uint8Array,通过 WebSocket(二进制帧)将其发送到服务器,并尝试解析它。接收到的 uint8_t 数组总是 158 字节长度。我使用 P-521——secp521r1。
pkcs8中导出的私钥!
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIApK1m/qpIAZ1iENht
XJxng4bdur6YV2SpMs+uFtSiJ/n96HbjVkqSENavv7vblIow+i5QUhaOkqSNWi0B
7x695C6hgYkDgYYABAATsbs5B+ebSwoIXD6RD2NYONzSWOtt0SigPM27pdYEWpld
/6j6S34gvRHQwDSMzs6//1zVE20Mn+izNM0KPWhRewD6SotR8/2QGWB5uo8GiXx1
RLyBp+TOurQLEsYwiWSLkUIUMvPH/6WCxSNO4FzBf617PRqs7Zv3Vo98d9JH/3mI
TA==
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAE7G7OQfnm0sKCFw+kQ9jWDjc0ljr
bdEooDzNu6XWBFqZXf+o+kt+IL0R0MA0jM7Ov/9c1RNtDJ/oszTNCj1oUXsA+kqL
UfP9kBlgebqPBol8dUS8gafkzrq0CxLGMIlki5FCFDLzx/+lgsUjTuBcwX+tez0a
rO2b91aPfHfSR/95iEw=
-----END PUBLIC KEY-----
一些细节:
% openssl asn1parse -inform PEM -in pub.pem
0:d=0 hl=3 l= 155 cons: SEQUENCE
3:d=1 hl=2 l= 16 cons: SEQUENCE
5:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
14:d=2 hl=2 l= 5 prim: OBJECT :secp521r1
21:d=1 hl=3 l= 134 prim: BIT STRING
% openssl asn1parse -inform PEM -in priv.pem
0:d=0 hl=3 l= 238 cons: SEQUENCE
3:d=1 hl=2 l= 1 prim: INTEGER :00
6:d=1 hl=2 l= 16 cons: SEQUENCE
8:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
17:d=2 hl=2 l= 5 prim: OBJECT :secp521r1
24:d=1 hl=3 l= 214 prim: OCTET STRING [HEX DUMP]:3081D3020101044200A4AD66FEAA48019D6210D86D5C9C678386DDBABE985764A932CFAE16D4A227F9FDE876E3564A9210D6AFBFBBDB948A30FA2E5052168E92A48D5A2D01EF1EBDE42EA1818903818600040013B1BB3907E79B4B0A085C3E910F635838DCD258EB6DD128A03CCDBBA5D6045A995DFFA8FA4B7E20BD11D0C0348CCECEBFFF5CD5136D0C9FE8B334CD0A3D68517B00FA4A8B51F3FD90196079BA8F06897C7544BC81A7E4CEBAB40B12C63089648B91421432F3C7FFA582C5234EE05CC17FAD7B3D1AACED9BF7568F7C77D247FF79884C
调用 o2i_ECPublicKey 时的错误代码:
(使用相同的数据调用,但每次错误都不同——无论如何都会重复。)
error:10067066:elliptic curve routines:ec_GFp_simple_oct2point:invalid
error:10098010:elliptic curve routines:o2i_ECPublicKey:EC lib
error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long
error:10067066:elliptic curve routines:ec_GFp_simple_oct2point:invalid
error:10098010:elliptic curve routines:o2i_ECPublicKey:EC lib
更新 3:
从 C++ 中,我将接收到的数据(密钥)写到一个文件中:
% % openssl asn1parse -inform DER -in data.bin
0:d=0 hl=3 l= 155 cons: SEQUENCE
3:d=1 hl=2 l= 16 cons: SEQUENCE
5:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
14:d=2 hl=2 l= 5 prim: OBJECT :secp521r1
21:d=1 hl=3 l= 134 prim: BIT STRING
%
% hexdump data.bin
0000000 8130 309b 0610 2a07 4886 3dce 0102 0506
0000010 812b 0004 0323 8681 0400 1300 bbb1 0739
0000020 9be7 0a4b 5c08 913e 630f 3858 d2dc eb58
0000030 d16d a028 cd3c a5bb 04d6 995a ff5d faa8
0000040 7e4b bd20 d011 34c0 ce8c bfce 5cff 13d5
0000050 0c6d e89f 34b3 0acd 683d 7b51 fa00 8b4a
0000060 f351 90fd 6019 ba79 068f 7c89 4475 81bc
0000070 e4a7 bace 0bb4 c612 8930 8b64 4291 3214
0000080 c7f3 a5ff c582 4e23 5ce0 7fc1 7bad 1a3d
0000090 edac f79b 8f56 777c 47d2 79ff 4c88
000009
十六进制编码的导出 SPKI(WebCrypto 导出的结果):
私人的:
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIApK1m/qpIAZ1iENhtXJxng4bdur6YV2SpMs+uFtSiJ/n96HbjVkqSENavv7vblIow+i5QUhaOkqSNWi0B7x695C6hgYkDgYYABAATsbs5B+ebSwoIXD6RD2NYONzSWOtt0SigPM27pdYEWpld/6j6S34gvRHQwDSMzs6//1zVE20Mn+izNM0KPWhRewD6SotR8/2QGWB5uo8GiXx1RLyBp+TOurQLEsYwiWSLkUIUMvPH/6WCxSNO4FzBf617PRqs7Zv3Vo98d9JH/3mITA==
上市:
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAE7G7OQfnm0sKCFw+kQ9jWDjc0ljrbdEooDzNu6XWBFqZXf+o+kt+IL0R0MA0jM7Ov/9c1RNtDJ/oszTNCj1oUXsA+kqLUfP9kBlgebqPBol8dUS8gafkzrq0CxLGMIlki5FCFDLzx/+lgsUjTuBcwX+tez0arO2b91aPfHfSR/95iEw=
更新 4:
私钥 jwk 格式:
{
"crv":"P-521",
"d":"AKStZv6qSAGdYhDYbVycZ4OG3bq-mFdkqTLPrhbUoif5_eh241ZKkhDWr7-725SKMPouUFIWjpKkjVotAe8eveQu",
"ext":true,
"key_ops":["sign"],
"kty":"EC",
"x":"ABOxuzkH55tLCghcPpEPY1g43NJY623RKKA8zbul1gRamV3_qPpLfiC9EdDANIzOzr__XNUTbQyf6LM0zQo9aFF7",
"y":"APpKi1Hz_ZAZYHm6jwaJfHVEvIGn5M66tAsSxjCJZIuRQhQy88f_pYLFI07gXMF_rXs9Gqztm_dWj3x30kf_eYhM"
}