椭圆曲线例程:o2i_ECPublicKey:传递一个null参数:ec_asn1.c:1271:

时间:2018-01-06 18:11:02

标签: c openssl elliptic-curve diffie-hellman

我使用openssl EVP方法创建ECDH,由openssl elliptic curve diffie helman解释

在我获得其他对等公钥并根据它生成EVP_PKEY *时,一切正常。

在给定的链接中,它忽略了解码其他对等公钥的实现细节,并使用了get_peerkey伪函数:

/* Get the peer's public key, and provide the peer with our public key -
 * how this is done will be specific to your circumstances */
peerkey = get_peerkey(pkey);

在我的实现中,接收到其他对等公钥并将其存储在publickey2中,其大小存储在pub_len2中

size_t pub_len2 = 0;
const unsigned char *publickey2 = get_public_key(&pub_len2);

然后我尝试使用以下代码创建EVP_PKEY *:

EVP_PKEY *pkey3=NULL;

pkey3 = d2i_PublicKey(EVP_PKEY_EC, &pkey3, (const unsigned char **)&publickey2, pub_len2);

if(pkey3 == NULL) {

    ERR_print_errors_fp(stderr);
}

但是pkey3始终为null并且给出了以下错误!

139898837907104:error:10098043:elliptic curve routines:o2i_ECPublicKey:passed a null parameter:ec_asn1.c:1389:
139898837907104:error:0D09B00D:asn1 encoding routines:d2i_PublicKey:ASN1 lib:d2i_pu.c:123:

传递了null参数错误。

你们有什么想法吗?

编辑:

我设法通过使用来解决这个问题     pkey3 = d2i_PUBKEY(NULL,(const unsigned char **)& publickey2,pub_len2);

但是我遇到了另一个问题,主要的问题是这些公钥中的计算派生秘密在两个对等方面是不同的!

这是我的秘密派生程序,在两个同行中都是相同的,并且从提供的链接中完全复制和粘贴(有一些修改),我对修改原因做了一些评论:

unsigned char *derive_secret(EVP_PKEY *pkey, 
const unsigned char *peer_key, 
size_t peerkey_len, size_t *secret_len)
{
    EVP_PKEY_CTX *ctx;
    unsigned char *secret;
    //MY modification to get peer key of peer_key buffer.
    //peer_key is created by i2d_PUBKEY at peer side and 
    //received by network and is passed here
    EVP_PKEY *peerkey = d2i_PUBKEY(NULL, &peer_key, peerkey_len);

    //I also set the group of newly created EC
    EC_KEY_set_group(EVP_PKEY_get1_EC_KEY(peer_key),
        EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));

    //I also set the CONVERSION format to make sure!
    EC_KEY_set_conv_form(EVP_PKEY_get1_EC_KEY(peer_key), POINT_CONVERSION_COMPRESSED);

    /* Create the context for the shared secret derivation */
    if(NULL == (ctx = EVP_PKEY_CTX_new(pkey, NULL))) handleErrors();

    /* Initialise */
    if(1 != EVP_PKEY_derive_init(ctx)) handleErrors();

    /* Provide the peer public key */
    if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) handleErrors();

    /* Determine buffer length for shared secret */
    if(1 != EVP_PKEY_derive(ctx, NULL, secret_len)) handleErrors();

    /* Create the buffer */
    if(NULL == (secret = OPENSSL_malloc(*secret_len))) handleErrors();

    /* Derive the shared secret */
    if(1 != (EVP_PKEY_derive(ctx, secret, secret_len))) handleErrors();

    EVP_PKEY_CTX_free(ctx);
    EVP_PKEY_free(peerkey);
    EVP_PKEY_free(pkey);

    /* Never use a derived secret directly. Typically it is passed
     * through some hash function to produce a key */
    return secret;
}

我还在两边打印出已发送和接收的公钥,并确保公钥一元一块地收到,并且与创建和发送完全相同!

当我发现两个对等体的长字节公钥都相同时,还存在一些小问题! EC diffie-helman的公钥是否正常?

以下是以HEX格式显示的公钥

Peer1:
3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101032200026B0E07FE6177D23B0E6B776CF4CB0569735159D3261767FA5FC0A4636EF310C4

Peer2:

3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201010322000208AE9F32ECE16072428A5FC875A19B3913C4516419917E723AA4C2DC20105C0A

1 个答案:

答案 0 :(得分:2)

某些字节(前缀)相同:X.509格式的所有公钥(更准确地说,SubjectPublicKeyInfo)至少具有一个'对象标识符' (OID),对于给定算法(如EC)的所有密钥都是相同的,并且EC密钥也具有'参数'指定组/曲线,对于同一曲线上的所有键都是相同的 - 并且ECDH协议的键必须在同一条曲线上。这些相同的数据,加上实际的pubkeys具有不同的值但是 size 的事实,导致ASN.1 DER编码以相同的字节开头。

您发布的pubkey编码使用了大部分过时的显式曲线规范,请参阅rfc3279 sec 2.3.5(相当于X9.62或SEC1),这比现在更喜欢的并且经常更长要求'命名'规格。猜测,您使用1.1.0以下的OpenSSL库生成这些密钥,并且在使用i2d(或PEM_write)进行序列化(也称为编码)之前,未在EC_GROUP对象(或EC_KEY的组子对象)中设置asn1_flag。 您引用的维基页面在第3节和第34节中介绍了这一点; ECDH和命名曲线"虽然它也只提到私钥,但这也适用于公钥和证书 - 但公钥和(​​然后)证书是从私钥派生的,因此在私钥上设置asn1_flag就足够了。而且它并没有说''命名'现在是1.1.0中的默认值,不再需要显式设置。

新发布的代码:point_format仅在序列化(i2d或PEM_write)时才有意义,因此在反序列化的密钥上设置它只会被使用并且是免费的(不是重新序列化的)无用。 OTOH将EC组设置为其现有值(由反序列化设置)是无用的,但将其设置为任何其他值将导致混乱。 EC公钥是特定曲线上的点,不同的曲线具有完全不同的点 - 一条曲线上的点不是另一条曲线上的点。此外,使用get1函数而不是免费的结果会泄漏内存。

推导结果不同:(EC)DH的错误,我无法重现它。下面是您的派生代码,其中包含上面指出的一些修复程序以及一些与我的编码风格相匹配的微小更改,以及来自wiki的生成代码和一个驱动它们的简单主程序,当我运行它时,我得到一个带有公共前缀的pubkeys(更短)由于使用了命名形式)但正如预期的那样,推导结果相同:

$ cat SO48130343.c 
/* SO48130343 */
#include <stdio.h>
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ec.h>
#include <openssl/err.h>

void hex (unsigned char *p, size_t n){ while(n--) printf("%02x", *p++); }

void err (const char * msg){ fprintf(stderr, "%s:\n", msg); ERR_print_errors_fp(stderr); exit(1); }

EVP_PKEY * gen (void) {
  EVP_PKEY_CTX *pctx, *kctx;
  EVP_PKEY *params = NULL, *pkey = NULL;
  if( NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) ) err("CTX1_new");
  if( 1 != EVP_PKEY_paramgen_init(pctx) ) err("pg_init");
  if( 1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1) ) err("pg_curve");
  if( 1 != EVP_PKEY_paramgen(pctx, &params) ) err("pg");
  if( NULL == (kctx = EVP_PKEY_CTX_new(params, NULL)) ) err("CTX2_new");
  if( 1 != EVP_PKEY_keygen_init(kctx) ) err("kg_init");
  if( 1 != EVP_PKEY_keygen(kctx, &pkey) ) err("kg");
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
  EC_KEY_set_asn1_flag (pkey->pkey.ec, OPENSSL_EC_NAMED_CURVE);
  /* point format needed before 'sending' and this is convenient */
  EC_KEY_set_conv_form (pkey->pkey.ec, POINT_CONVERSION_COMPRESSED);
#else
  /* asn1_flag now default but point format still needed */
  EC_KEY_set_conv_form (EVP_PKEY_get0_EC_KEY (pkey), POINT_CONVERSION_COMPRESSED);
#endif
  EVP_PKEY_CTX_free(pctx);
  EVP_PKEY_CTX_free(kctx);
  EVP_PKEY_free(params);
  return pkey;
}
unsigned char * derive (EVP_PKEY * self,
    const unsigned char * peer_ptr, size_t peer_len, size_t *len_ptr){
  EVP_PKEY * peer = d2i_PUBKEY (NULL, &peer_ptr, peer_len);
  /* DON'T change EC_GROUP; point_format not needed on 'receive' */

  EVP_PKEY_CTX *ctx; unsigned char * buf_ptr;
  if( !(ctx = EVP_PKEY_CTX_new (self, NULL)) ) err("CTX_new");
  if( 1 != EVP_PKEY_derive_init(ctx) ) err("derive_init");
  if( 1 != EVP_PKEY_derive_set_peer(ctx, peer) ) err("derive_peer");
  if( 1 != EVP_PKEY_derive (ctx, NULL, len_ptr) ) err("derive1");
  if( !(buf_ptr = OPENSSL_malloc (*len_ptr)) ) err("malloc");
  if( 1 != EVP_PKEY_derive (ctx, buf_ptr, len_ptr) ) err("derive2");
  EVP_PKEY_CTX_free(ctx);
  EVP_PKEY_free(peer);
  return buf_ptr;
}

int main (void){
  EVP_PKEY * pkey1 = gen(), * pkey2 = gen();
  unsigned char pub1 [100], pub2 [100], *ptr1 = &pub1[0], *ptr2 = &pub2[0];
  size_t publen1 = i2d_PUBKEY (pkey1, &ptr1), publen2 = i2d_PUBKEY (pkey2, &ptr2);
  printf ("pub1="); hex(pub1, publen1); putchar('\n');
  printf ("pub2="); hex(pub2, publen2); putchar('\n');

  size_t len1, len2;
  unsigned char * out1 = derive (pkey1, pub2, publen2, &len1);
  unsigned char * out2 = derive (pkey2, pub1, publen1, &len2);
  printf ("prv1/pub2="); hex(out1, len1); putchar('\n');
  printf ("prv2/pub1="); hex(out2, len2); putchar('\n');
  /* don't bother freeing for Q&D test code */
  return 0;
}
$ gcc [details for my system redacted]
$ ./SO48130343.exe 
pub1=3039301306072a8648ce3d020106082a8648ce3d03010703220003302c6f990445ddd27b2c0ecd3a0cd33109eec44dea0edd538c6bfc98796885e3
pub2=3039301306072a8648ce3d020106082a8648ce3d0301070322000311940ba32c0b4d71f8785a884f7ea74cebed17e841e93a0fb1ccbeac32b2eb3b
prv1/pub2=84b7a84249f1e88741a751a05d34a43e4cb131e012181967e4f465c1f4bf3b35
prv2/pub1=84b7a84249f1e88741a751a05d34a43e4cb131e012181967e4f465c1f4bf3b35