cdnsopenssldnssec

Why ECDSA public key generated by OpenSSL doesn't match DNSKEY generated by DNS server?


I am trying to get public key from known private key for DNSSEC algorithm 13 (ECDSAP256SHA256). I used this example: https://stackoverflow.com/a/17062076/3090865

Which modified for my key looks this:

// 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;
}

Trying:

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

This is what I have inside DNS server (PowerDNS):

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

Getting DNSKEY record:

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

And it absolutely doesn't match the key I got with OpenSSL. Why?


Solution

  • The main thing that I didn't take into account is that this keys has different length: key returned from openssl is 65 bytes long in binary format and key from DNS server is 64 bytes long. And if I looked binary represntation of both keys in hex editor I could see they are only differs by first byte. Some more detailed explanation may be found here: https://stackoverflow.com/a/43742420/3090865

    So, this C code may be rewrited like this (one more thing is that incorrect curve used, it should be 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;
    }
    

    And now it matches:

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