【问题标题】:OpenSSL i2d_ECPrivateKey() crashesOpenSSL i2d_ECPrivateKey() 崩溃
【发布时间】:2014-07-02 14:52:12
【问题描述】:

我有以下程序。它成功获取原始的 32 字节 EC 私钥数据,然后从中创建一个 EC_KEY。但是 i2d_ECPrivateKey 无法给出 DER 编码的私钥的大小,因为它崩溃了。有谁知道为什么以及如何解决这个问题?

#include "CBWIF.h"
#include <openssl/ssl.h>

int main(int argc, char * argv[]) {

    CBWIF wif;

    if (argc != 2)
        return EXIT_FAILURE;

    // Decode WIF string
    CBByteArray str;
    CBInitByteArrayFromString(&str, argv[1], false);
    CBInitWIFFromString(&wif, &str, false);
    CBDestroyByteArray(&str);

    // Get key
    uint8_t key[32];
    CBWIFGetPrivateKey(&wif, key);
    CBDestroyWIF(&wif);

    // Create OpenSSL key

    EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
    BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);
    if (!EC_KEY_set_private_key(eckey, bn)) 
        return EXIT_FAILURE;

    // Convert key to DER format

    int len = i2d_ECPrivateKey(eckey, NULL); // <-- CRASH HERE  
    unsigned char derkey[len];
    i2d_ECPrivateKey(eckey, (unsigned char **)&derkey);

    EC_KEY_free(eckey);

    // Encode DER key as hex

    char out[len*2+1];  
    CBBytesToString(derkey, 0, len, out, false);

    // Print to stdout      
    puts(out);

    return EXIT_SUCCESS;
}

CB_PRIVKEY_SIZE 是 32。我验证了来自CBWIFGetPrivateKey 的关键数据是正确的。程序崩溃并显示以下堆栈跟踪:

#0  0x00007ffff766cb03 in EC_POINT_point2oct () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#1  0x00007ffff7658124 in i2d_ECPrivateKey () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#2  0x0000000000400bf6 in main (argc=2, argv=0x7fffffffe038) at examples/WIF2DER.c:46

【问题讨论】:

标签: c openssl elliptic-curve


【解决方案1】:

原因是你不能在没有设置公钥的情况下运行 i2d_ECPrivateKey,这可能是由于 OpenSSL 中的一个错误。我生成了公钥并解决了其他一些问题,现在程序可以运行了:

#include "CBWIF.h"
#include <openssl/ssl.h>

int main(int argc, char * argv[]) {

    CBWIF wif;

    if (argc != 2)
        return EXIT_FAILURE;

    // Decode WIF string
    CBByteArray str;
    CBInitByteArrayFromString(&str, argv[1], false);
    CBInitWIFFromString(&wif, &str, false);
    CBDestroyByteArray(&str);

    // Get key
    uint8_t key[32];
    CBWIFGetPrivateKey(&wif, key);
    CBDestroyWIF(&wif);

    // Create OpenSSL key
    EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
    BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);

    if (!EC_KEY_set_private_key(eckey, bn)) 
        return EXIT_FAILURE;

    // Create public key as OpenSSL cannot do this easily
    EC_GROUP * group = EC_GROUP_new_by_curve_name(NID_secp256k1);
    EC_POINT * point = EC_POINT_new(group);
    BN_CTX * ctx = BN_CTX_new();

    EC_POINT_mul(group, point, bn, NULL, NULL, ctx);

    BN_CTX_free(ctx);
    EC_GROUP_free(group);
    BN_free(bn);

    if (!EC_KEY_set_public_key(eckey, point))
        return EXIT_FAILURE;

    EC_POINT_free(point);

    // Check the key
    if (!EC_KEY_check_key(eckey))
        return EXIT_FAILURE;

    // Convert key to DER format
    int len = i2d_ECPrivateKey(eckey, NULL);    
    unsigned char derkey[len];
    unsigned char * derkeyPtr = derkey;
    i2d_ECPrivateKey(eckey, &derkeyPtr);

    // Freeing the EC_KEY here crashes for some reason???

    // Encode DER key as hex

    char out[len*2+1];  
    CBBytesToString(derkey, 0, len, out, false);

    // Print to stdout
    puts(out);

    return EXIT_SUCCESS;
}

【讨论】:

    【解决方案2】:

    以下是 OpenSSL 在&lt;openssl src/crypto/ec/ec_ameth.c 中的使用方式。库中的所有其他类似用途都使用i2d_ECPrivateKey_bio。此外,您可以快速了解OPENSSL_EC_NAMED_CURVE 标志如何与V_ASN1_OBJECT 一起使用。

    unsigned char   *ep, *p;
    int         eplen, ptype;
    unsigned int    tmp_flags, old_flags;
    ...
    
    old_flags = EC_KEY_get_enc_flags(ec_key);
    tmp_flags = old_flags | EC_PKEY_NO_PARAMETERS;
    ...
    
    eplen = i2d_ECPrivateKey(ec_key, NULL);
    if (!eplen)
    {
        EC_KEY_set_enc_flags(ec_key, old_flags);
        ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
        return 0;
    }
    ep = (unsigned char *) OPENSSL_malloc(eplen);
    if (!ep)
    {
        EC_KEY_set_enc_flags(ec_key, old_flags);
        ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
        return 0;
    }
    p = ep;
    if (!i2d_ECPrivateKey(ec_key, &p))
    {
        EC_KEY_set_enc_flags(ec_key, old_flags);
        OPENSSL_free(ep);
        ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
        return 0;
    }
    ...
    

    还可以查看来自 OpenBSD 的 LibReSSL 项目的 Avoid a NULL dereference in i2d_ECPrivateKey() when an EC_KEY lacks the public key member

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-16
      • 2013-02-11
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多