【问题标题】:Why ECDSA public key generated by OpenSSL doesn't match DNSKEY generated by DNS server?为什么 OpenSSL 生成的 ECDSA 公钥与 DNS 服务器生成的 DNSKEY 不匹配?
【发布时间】:2017-05-01 05:34:35
【问题描述】:

我正在尝试从 DNSSEC 算法 13 (ECDSAP256SHA256) 的已知私钥中获取公钥。我用了这个例子:https://stackoverflow.com/a/17062076/3090865

为我的密钥修改的内容如下:

// using figures on: https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
// gcc -Wall ecdsapubkey.c -o ecdsapubkey -lcrypto
#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>

int main()
{
     EC_KEY *eckey = NULL;
     EC_POINT *pub_key = NULL;
     const EC_GROUP *group = NULL;
     BIGNUM start;
     BIGNUM *res;
     BN_CTX *ctx;

     BN_init(&start);
     ctx = BN_CTX_new(); // ctx is an optional buffer to save time from allocating and deallocating memory whenever required

     res = &start;
     BN_hex2bn(&res,"589c51d2b528a99c1d19702f865284ec09e3e080606ddc3f56f0906268fd25e3");
     eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
     group = EC_KEY_get0_group(eckey);
     pub_key = EC_POINT_new(group);

     EC_KEY_set_private_key(eckey, res);

     /* pub_key is a new uninitialized `EC_POINT*`.  priv_key res is a `BIGNUM*`. */
     if (!EC_POINT_mul(group, pub_key, res, NULL, NULL, ctx))
       printf("Error at EC_POINT_mul.\n");

     EC_KEY_set_public_key(eckey, pub_key);

     char *cc = EC_POINT_point2hex(group, pub_key, 4, ctx);

     printf("%s", cc);

     BN_CTX_free(ctx);

     free(cc);

     return 0;
}

尝试:

$ gcc -lcrypto t.c
$ ./a.out | perl -e 'print pack "H*", <>' | base64
BDdZbz79hEKFi9bIlExzZEqPQVhNqcjJqaWSWnoBTYn21XEL7y4YQXnB8N4JWAy33inTD1CyEI20
TusbH6MSxyc=

这就是我在 DNS 服务器 (PowerDNS) 中的内容:

Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: WJxR0rUoqZwdGXAvhlKE7Anj4IBgbdw/VvCQYmj9JeM=

获取 DNSKEY 记录:

$ dig @127.0.0.1 +short example.com DNSKEY
257 3 13 JELaKnxPV49rnxShsHbS8MX9rfJZcpRKgqCHUn1WYyDLcXGDYYEQ8soL I9OLVJFN5Gn/4TjXF6g0T1IEBsuFew==

而且它绝对与我使用 OpenSSL 获得的密钥不匹配。为什么?

【问题讨论】:

  • DNSKEY RR 表示格式包括公钥的 Base64 编码。您的代码打印公钥的十六进制表示的 Base64 编码。
  • Perl 的 pack() 将十六进制转成二进制,所以应该是对的

标签: c dns openssl dnssec


【解决方案1】:

我没有考虑到的主要问题是这个密钥有不同的长度:从 openssl 返回的密钥是 65 字节长的二进制格式,而来自 DNS 服务器的密钥是 64 字节长。如果我在十六进制编辑器中查看两个键的二进制表示,我可以看到它们仅在第一个字节上有所不同。一些更详细的解释可以在这里找到:https://stackoverflow.com/a/43742420/3090865

所以,这段C代码可能会这样改写(还有一点就是用了不正确的曲线,应该是NID_X9_62_prime256v1):

#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>

int main()
{
     EC_KEY *eckey = NULL;
     EC_POINT *pub_key = NULL;
     const EC_GROUP *group = NULL;
     BIGNUM start;
     BIGNUM *res;
     BN_CTX *ctx;

     BN_init(&start);
     ctx = BN_CTX_new(); // ctx is an optional buffer to save time from allocating and deallocating memory whenever required

     res = &start;
     BN_hex2bn(&res,"589c51d2b528a99c1d19702f865284ec09e3e080606ddc3f56f0906268fd25e3");
     eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
     group = EC_KEY_get0_group(eckey);
     pub_key = EC_POINT_new(group);

     EC_KEY_set_private_key(eckey, res);

     /* pub_key is a new uninitialized `EC_POINT*`.  priv_key res is a `BIGNUM*`. */
     if (!EC_POINT_mul(group, pub_key, res, NULL, NULL, ctx))
       printf("Error at EC_POINT_mul.\n");

     EC_KEY_set_public_key(eckey, pub_key);

     char *cc = EC_POINT_point2hex(group, pub_key, 4, ctx);

     printf("%s", cc+2);

     BN_CTX_free(ctx);

     free(cc);

     return 0;
}

现在它匹配了:

$ ./a.out | perl -e 'print pack "H*", <>' | base64
JELaKnxPV49rnxShsHbS8MX9rfJZcpRKgqCHUn1WYyDLcXGDYYEQ8soLI9OLVJFN5Gn/4TjXF6g0
T1IEBsuFew==

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-05
    • 1970-01-01
    • 2013-07-14
    • 1970-01-01
    • 2013-10-31
    • 1970-01-01
    相关资源
    最近更新 更多