椭圆曲线 routines:o2i_ECPublicKey:passed 空 parameter:ec_asn1.c:1271:

elliptic curve routines:o2i_ECPublicKey:passed a null parameter:ec_asn1.c:1271:

我正在使用 openssl elliptic curve diffie helman

解释的 openssl EVP 方法创建 ECDH

一切正常,期望我从哪里获得其他同行的 public 密钥并基于它生成 EVP_PKEY*。

在给定的 link 中,它忽略了解码其他节点 public 密钥的实现细节,并使用了 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);

在我的实现中,另一个节点的 public 密钥被接收并存储在 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:

传递了空参数错误。

你们有什么想法吗?

编辑:

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

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

这是我的秘密推导过程,在两个同行中都是相同的,并且完全复制和粘贴(有一些修改)提供的link,我针对修改原因发表了一些评论:

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

我还在两面打印出已发送和已接收的 public 密钥,并确保 public 密钥是一体接收的,并且与创建和发送的密钥完全相同!

还有一些小问题,那就是当我发现一个长字节的 public 键对于两个对等点是相同的!这对于 EC diffie-helman 的 public 键来说是否正常?

这是以 HEX 格式显示的 public 键

Peer1:
3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101032200026B0E07FE6177D23B0E6B776CF4CB0569735159D3261767FA5FC0A4636EF310C4

Peer2:

3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201010322000208AE9F32ECE16072428A5FC875A19B3913C4516419917E723AA4C2DC20105C0A

一些字节(前缀)相同:X.509 格式的所有 public 密钥(更确切地说,SubjectPublicKeyInfo)至少有一个'object identifier'(OID),对于给定算法(如EC)的所有密钥都是相同的,EC密钥也有'parameters'指定一个group/curve,这对于所有密钥都是相同的在同一条曲线上——ECDH 协议的密钥必须在同一条曲线上。这个相同的数据,结合实际公钥具有不同值但相同 大小 的事实,导致 ASN.1 DER 编码以相同字节开始。

您发布的公钥编码使用大部分已过时的显式曲线规范,参见rfc3279 sec 2.3.5(相当于 X9.62 或 SEC1),它比现在首选且经常需要 'named' 规范。据推测,您使用 1.1.0 以下的 OpenSSL 库生成这些密钥,并且在序列化(也称为编码)之前没有在 EC_GROUP 对象(或 EC_KEY 的组子对象)中设置 asn1_flag ) 与 i2d(或 PEM_write)。 您引用的维基页面在第 3 节“ECDH 和命名曲线”中涵盖了这一点,尽管它仅在这也适用于 public 密钥和证书时提到了私钥——但是 public 密钥和(然后)证书是 私钥派生的,因此在私钥上设置 asn1_flag 就足够了。它并没有说 'named' 现在是 1.1.0 中的默认值,不再需要显式设置。

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

推导结果不同: (EC)DH 是完全错误的,我无法重现。下面是你的推导代码,上面指出了一些修复和一些微小的变化以匹配我的编码风格,加上来自 wiki 的生成代码和一个简单的主要驱动它们,当我 运行 这个我得到公钥与公共前缀(由于使用命名形式而更短)但正如预期的那样,推导结果相同:

$ 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