代码之家  ›  专栏  ›  技术社区  ›  The Quantum Physicist

openssl:读取一个EC密钥,然后再写一次,它是不同的。

  •  2
  • The Quantum Physicist  · 技术社区  · 6 年前

    我为支持ECC的OpenSSL编写了一个包装器。我正试图读取用它生成的私钥

    openssl ecparam -name secp384r1 -genkey -noout -out privkey.pem
    

    并将其与openssl在将密钥读取到 EVP_PKEY EC_KEY 再把它打印成一个字符串。阅读后的结果不一样。

    简而言之:

    1. 读取密钥
    2. 保存到 执行副总裁
    3. 再写一遍

    结果不匹配。我的节目很大,所以 I produced an MCVE that demonstrates the problem .

    我怀疑问题的发生是因为我在给一个 ECKEY ,然后从 执行副总裁 ,这是通用的。我在这里猜测是因为输入说它是EC,但输出没有这么说。我不知道如何解决这个问题,因为我看不到直接从 ECKEY 到一个文件(生物对象)。我的评估正确吗?

    请告知。


    编辑: 我被要求将整个代码放在注释中,这样就可以:

    #include <iostream>
    
    #include <openssl/bio.h>
    #include <openssl/err.h>
    #include <openssl/ec.h>
    #include <openssl/pem.h>
    
    EC_KEY* ecKey = nullptr;
    EVP_PKEY* pkey = nullptr;
    
    void setPrivateKeyFromPEM(const std::string& pemkey)
    {
        pkey = EVP_PKEY_new();
    
        BIO* bio = BIO_new(BIO_s_mem());
    
        int bio_write_ret = BIO_write(
            bio, static_cast<const char*>(pemkey.c_str()), pemkey.size());
        if (bio_write_ret <= 0) {
            throw std::runtime_error("error1");
        }
    
        if (!PEM_read_bio_PrivateKey(bio, &pkey, NULL, NULL)) {
            throw std::runtime_error("error1.5");
        }
    
        EC_KEY* eckey_local = EVP_PKEY_get1_EC_KEY(pkey);
    
        if (!eckey_local) {
            throw std::runtime_error("error2");
        } else {
            ecKey = eckey_local;
            EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
        }
    }
    
    std::string getPrivateKeyAsPEM()
    {
        if (!pkey) {
            throw std::runtime_error("error3");
        }
    
        BIO* outbio = BIO_new(BIO_s_mem());
    
        if (!PEM_write_bio_PrivateKey(outbio, pkey, NULL, NULL, 0, 0,
                                      NULL)) {
            throw std::runtime_error("error4");
        }
    
        std::string keyStr;
        int         priKeyLen = BIO_pending(outbio);
        keyStr.resize(priKeyLen);
        BIO_read(outbio, (void*)&(keyStr.front()), priKeyLen);
        return keyStr;
    }
    
    int main()
    {
        std::string expectedPrivKey =
            "-----BEGIN EC PRIVATE KEY-----\n"
            "MIGkAgEBBDBNK0jwKqqf8zkM+Z2l++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy\n"
            "1kLvY2gIvlmgBwYFK4EEACKhZANiAAQvPxAK2RhvH/k5inDa9oMxUZPvvb9fq8G3\n"
            "9dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/DaYm12r3GTaRzOBQmxSItStk\n"
            "lpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=\n"
            "-----END EC PRIVATE KEY-----\n";
    
        setPrivateKeyFromPEM(expectedPrivKey);
        // compare priv key
        {
            std::string privKeyRead = getPrivateKeyAsPEM();
            std::cout << privKeyRead << std::endl;
            std::cout<<expectedPrivKey<<std::endl;
        }
    
        return 0;
    }
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   rustyx    6 年前

    PEM_write_bio_ECPrivateKey 在openssl 1.0.2中也可用,只缺少文档。

    存储的密钥相同,区别仅在于编码。

    标签 -----BEGIN PRIVATE KEY----- 表示PEM编码的ASN.1格式。

    标签 -----BEGIN EC PRIVATE KEY----- 表示PEM编码的ANSI X9.62键。

    比较: key 1 VS key 2 . 注意,键2不包含键类型OID,键本身是相同的。

    要编写EC密钥格式,只需使用:

        if (!PEM_write_bio_ECPrivateKey(outbio, ecKey, NULL, NULL, 0, 0, NULL)) {
    

    coliru-demo

        2
  •  2
  •   dbush    6 年前

    虽然输出不完全匹配,但它们实际上表示相同的键。

    您的代码输出以下内容:

    -----BEGIN PRIVATE KEY-----
    MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBNK0jwKqqf8zkM+Z2l
    ++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy1kLvY2gIvlmhZANiAAQvPxAK2Rhv
    H/k5inDa9oMxUZPvvb9fq8G39dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/
    DaYm12r3GTaRzOBQmxSItStklpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=
    -----END PRIVATE KEY-----
    
    -----BEGIN EC PRIVATE KEY-----
    MIGkAgEBBDBNK0jwKqqf8zkM+Z2l++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy
    1kLvY2gIvlmgBwYFK4EEACKhZANiAAQvPxAK2RhvH/k5inDa9oMxUZPvvb9fq8G3
    9dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/DaYm12r3GTaRzOBQmxSItStk
    lpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=
    -----END EC PRIVATE KEY-----
    

    如果仔细观察打印的第一个密钥,即代码导入/导出的密钥,它将标记为“begin private key”,而不是“begin ec private key”。它也开始于 MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGe 但在那之后,它就和原来的钥匙一样了。事实上,如果您将两个PEM都放在文件中并运行 openssl ec -in {filename} -text ,两者都将输出相同的内容:

    read EC key
    Private-Key: (384 bit)
    priv:
        4d:2b:48:f0:2a:aa:9f:f3:39:0c:f9:9d:a5:fb:ef:
        6b:f1:bc:dd:4d:2f:d7:0a:80:78:37:52:74:ed:d3:
        f1:a4:1c:b2:27:21:9b:86:f2:32:d6:42:ef:63:68:
        08:be:59
    pub: 
        04:2f:3f:10:0a:d9:18:6f:1f:f9:39:8a:70:da:f6:
        83:31:51:93:ef:bd:bf:5f:ab:c1:b7:f5:d2:96:d6:
        d4:be:cb:08:5e:8e:72:9e:bb:fe:3c:1d:70:17:83:
        1d:a0:ea:a3:09:8c:43:e9:71:3c:bf:0d:a6:26:d7:
        6a:f7:19:36:91:cc:e0:50:9b:14:88:b5:2b:64:96:
        94:20:e6:f7:f6:dc:57:3d:7c:5a:d0:f4:09:d0:2a:
        b6:f5:76:04:e4:a3:14
    ASN1 OID: secp384r1
    NIST CURVE: P-384
    writing EC key
    -----BEGIN EC PRIVATE KEY-----
    MIGkAgEBBDBNK0jwKqqf8zkM+Z2l++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy
    1kLvY2gIvlmgBwYFK4EEACKhZANiAAQvPxAK2RhvH/k5inDa9oMxUZPvvb9fq8G3
    9dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/DaYm12r3GTaRzOBQmxSItStk
    lpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=
    -----END EC PRIVATE KEY-----
    

    开始的额外部分是导出 EVP_PKEY 说明密钥是EC密钥的密钥。

    你是不是把 EC_KEY 直接使用 PEM_write_bio_ECPrivateKey(outbio, ecKey, NULL, NULL, 0, 0, NULL) 输出将完全相同。