【问题标题】:Exporting EC_POINT using point2hex in ASN1.DER and recreate with Java as an X.509在 ASN1.DER 中使用 point2hex 导出 EC_POINT 并使用 Java 重新创建为 X.509
【发布时间】:2019-07-23 13:31:47
【问题描述】:

我正在使用 OpenSSL 和 C++ 生成 ECDSA Prime256 密钥对,并尝试使用 Java 导入十六进制版本的公钥。我将从 C++ 获得的字节数组传递给 java 中的以下函数,该函数期望字节数组采用 X.509 编码格式。

public static PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
    EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
    KeyFactory kf = KeyFactory.getInstance(Constant.KEY_FACTORY_TYPE);
    PublicKey pub = kf.generatePublic(publicKeySpec);
    return pub;
}

我使用以下函数创建了一个椭圆曲线密钥对,该函数返回 EC_KEY*

EC_KEY* generate_keypair() {
    EC_KEY *eckey = EC_KEY_new();
    EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY_set_group(eckey, ecgroup);
    EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
    int kpGenerationStatus = EC_KEY_generate_key(eckey);
    if (kpGenerationStatus) {
        return eckey;
    }
    return nullptr;
}

鉴于上述函数返回的密钥对,我想将公钥导出为可以使用上述 java 方法导入的 ASN1.DER 格式。

我使用EC_POINT_point2hex()EC_POINT* 类型的公钥转换为十六进制形式,方法如下:

EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY *keypair = generate_keypair();
char *result = NULL;
BN_CTX *ctx;
ctx = BN_CTX_new();
const EC_POINT *pub = EC_KEY_get0_public_key(keypair);
result = EC_POINT_point2hex(ecgroup, pub, POINT_CONVERSION_UNCOMPRESSED, ctx);
printf("%s\n", result);

返回以下内容: 04F588CD1D7103A993D47E53D58C3F40BE8F570604CF2EA01A7657C1423EB19C51BC379F0BEE1FAA60BB9A07DE73EA9BEF7709C1C6429D4051B44F73A458FFB80D

当我使用 ASN.1 decoder 检查时,我看到一条消息,上面写着 Length over 48 bits not supported at position 1 并尝试使用 java 方法导入它,我收到如下错误:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): Should use short form for length

在将公钥从 EC_POINT* 导出到 X.509 编码的十六进制字符串时,我是否遗漏了什么,我可以导入该字符串以验证任何签名?

【问题讨论】:

    标签: c++ openssl x509 ecdsa asn1


    【解决方案1】:

    因为您想要 ASN1 base64 值,所以您的方向不正确。

    EC_POINT_point2hex 正在将内部公钥值转换为十六进制。它不是 ASN1 格式。

    你可以像这样从命令行生成你想要的:

    1. 生成 EC 私钥: openssl ecparam -name prime256v1 -genkey -noout -out key.pem
    2. DER(ASN1) 格式的额外公钥: openssl ec -in key.pem -pubout -outform der -out public.cer
    3. 转换为 base64 openssl base64 -in .\public.cer

    如果您将该输出粘贴到 ASN.1 解码器链接中,它可以正常工作。

    现在要将其转换为代码,您已经生成了 EC 密钥,但您想要的是以下步骤:

    1. 生成 ASN1 格式的公钥
    2. 将其转换为 base64

    要生成 ASN1 格式的公钥,您需要使用 i2d_EC_PUBKEY 方法集,然后使用 BIO_f_base64 过滤器转换为 base64。

    所以这是一个示例问题,当我将输出复制到 ASN.1 解码器链接时,它工作正常。

    #include <openssl/bio.h>
    #include <openssl/ec.h>
    #include <openssl/evp.h>
    #include <openssl/x509.h>
    
    EC_KEY* generate_keypair() {
        EC_KEY *eckey = EC_KEY_new();
        EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
        EC_KEY_set_group(eckey, ecgroup);
        EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
        int kpGenerationStatus = EC_KEY_generate_key(eckey);
        if (kpGenerationStatus) {
            return eckey;
        }
        return nullptr;
    }
    
    int main()
    {
        EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
        EC_KEY *keypair = generate_keypair();
        BIO* out = BIO_new(BIO_s_mem());
        BIO* b64 = BIO_new(BIO_f_base64());
        BIO_push(b64, out);
    
        i2d_EC_PUBKEY_bio(b64, keypair);
    
        BIO_flush(b64);
    
    
        // do what you want this the output in out memory BIO
    
        char* p;
        long length = BIO_get_mem_data(out, &p);
    
        // ensure null terminated but copying the buffer into a string to output...
        puts(std::string(p, length).c_str());
    
        BIO_free_all(out);
    }
    

    我无法在 Java 端完成,但如果它适用于手动 openssl 生成的 base64 字符串,那么它将适用于示例应用程序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-11
      • 2018-05-04
      • 2011-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-22
      • 1970-01-01
      相关资源
      最近更新 更多