将 PEM 编码的 X.509 证书加载到 Windows CryptoAPI

Posted

技术标签:

【中文标题】将 PEM 编码的 X.509 证书加载到 Windows CryptoAPI【英文标题】:Load an PEM encoded X.509 certificate into Windows CryptoAPI 【发布时间】:2010-11-16 21:36:56 【问题描述】:

我需要将 PEM 编码的 X.509 证书加载到 Windows Crypto API 上下文中以用于 C++。他们是拥有 -----BEGIN RSA XXX KEY----------END RSA XXX KEY----- 的人。我找到了 Python 和 .NET 的示例,但它们使用了与普通 Windows Crypto API 无关的特定功能。

我了解如何在获得 HCRYPTKEY 后进行加密/解密。 但是,我只是不知道如何在 .PEM 文件中导入 Base64 blob 并获得一个可以使用的HCRYPTKEY

我有一种奇怪的感觉,它不仅仅是打电话给CryptDecodeObject()

任何可以让我走上正轨的指针?我已经浪费了两天时间进行“试错”编程,但一无所获。

【问题讨论】:

【参考方案1】:

我目前面临同样的困难。我还没有完成对解决方案的编码,但据我了解,您需要去掉 ----- BEGIN 等 ----- 和 ----- END 等 ------ 标签并解码 Base64 .

这会给您留下一个 DER 编码的字符串,您需要对其进行解析以获取模数和公共指数。您可以从中填充 PUBLICKEYSTRUC 和 RSAPUBKEY 结构。祝你好运;-)

【讨论】:

使用 X509_ASN_ENCODING 和 RSA_CSP_PUBLICKEYBLOB 选项查看 CryptDecodeObjectEx。似乎可以正确解码和填充结构,但您可能仍需要交换某些部分的字节顺序。【参考方案2】:

我发现了以 PEM 格式导入 RSA 公钥的“神奇”调用序列。给你:

    使用 CryptStringToBinary 将密钥解码为二进制 blob;在 dwFlags 中传递 CRYPT_STRING_BASE64HEADER 使用 CryptDecodeObjectEx 将二进制密钥 blob 解码为 CERT_PUBLIC_KEY_INFO;通过 dwCertEncodingType 中的 X509_ASN_ENCODING 和 lpszStructType 中的 X509_PUBLIC_KEY_INFO 使用 CryptDecodeObjectEx 将来自 CERT_PUBLIC_KEY_INFO 的 PublicKey blob 解码为 RSA 密钥 blob;通过 dwCertEncodingType 中的 X509_ASN_ENCODING 和 lpszStructType 中的 RSA_CSP_PUBLICKEYBLOB 使用 CryptImportKey 导入 RSA 密钥 blob

【讨论】:

【参考方案3】:

KJKHyperion在他的answer中说:

我发现了以 PEM 格式导入 RSA 公钥的“神奇”调用序列。给你:

    使用 CryptStringToBinary 将密钥解码为二进制 blob;在 dwFlags 中传递 CRYPT_STRING_BASE64HEADER 使用 CryptDecodeObjectEx 将二进制密钥 blob 解码为 CERT_PUBLIC_KEY_INFO;通过 dwCertEncodingType 中的 X509_ASN_ENCODING 和 lpszStructType 中的 X509_PUBLIC_KEY_INFO 使用 CryptDecodeObjectEx 将来自 CERT_PUBLIC_KEY_INFO 的 PublicKey blob 解码为 RSA 密钥 blob;通过 dwCertEncodingType 中的 X509_ASN_ENCODING 和 lpszStructType 中的 RSA_CSP_PUBLICKEYBLOB 使用 CryptImportKey 导入 RSA 密钥 blob

这个序列确实帮助我理解了正在发生的事情,但它并没有按原样工作。第二次调用CryptDecodeObjectEx 给了我一个错误: “符合 ASN.1 错误标签值”。 经过多次尝试理解微软文档,我终于意识到第一次解码的输出不能再解码为ASN,实际上已经准备好导入了。有了这种理解,我在以下链接中找到了答案:

http://www.ms-news.net/f2748/problem-importing-public-key-4052577.html

以下是我自己的程序,它将公钥从 .pem 文件导入到 CryptApi 上下文:

int main()

    char           pemPubKey[2048];
    int            readLen;
    char           derPubKey[2048];
    size_t         derPubKeyLen = 2048;
    CERT_PUBLIC_KEY_INFO *publicKeyInfo;
    int            publicKeyInfoLen;
    HANDLE         hFile;
    HCRYPTPROV     hProv = 0;
    HCRYPTKEY      hKey = 0;

    /*
     * Read the public key cert from the file
     */
    hFile = CreateFileA( "c:\\pub.pem", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
    if ( hFile == INVALID_HANDLE_VALUE )
    
        fprintf( stderr, "Failed to open file. error: %d\n", GetLastError() );
    

    if ( !ReadFile( hFile, pemPubKey, 2048, &readLen, NULL ) )
    
        fprintf( stderr, "Failed to read file. error: %d\n", GetLastError() );
    

    /*
     * Convert from PEM format to DER format - removes header and footer and decodes from base64
     */
    if ( !CryptStringToBinaryA( pemPubKey, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen, NULL, NULL ) )
    
        fprintf( stderr, "CryptStringToBinary failed. Err: %d\n", GetLastError() );
    

    /*
     * Decode from DER format to CERT_PUBLIC_KEY_INFO
     */
    if ( !CryptDecodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen, 
                               CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen ) )
    
        fprintf( stderr, "CryptDecodeObjectEx 1 failed. Err: %p\n", GetLastError() );
        return -1;
    

    /*
     * Acquire context 
     */
    if( !CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) )
    
        
            printf( "CryptAcquireContext failed - err=0x%x.\n", GetLastError() );
            return -1;
        
    

    /*
     * Import the public key using the context
     */
    if ( !CryptImportPublicKeyInfo( hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey ) )
    
        fprintf( stderr, "CryptImportPublicKeyInfo failed. error: %d\n", GetLastError() );
        return -1;
    
    LocalFree( publicKeyInfo );

    /*
     * Now use hKey to encrypt whatever you need.
     */

    return 0;

【讨论】:

您能提供 WORKING 公钥吗?您的程序仍然不适合我..但我的密钥确实有效。 @socketpair 取决于实现,数字签名可能需要在验证之前完全字节反转

以上是关于将 PEM 编码的 X.509 证书加载到 Windows CryptoAPI的主要内容,如果未能解决你的问题,请参考以下文章

DER、CRT、CER、PEM证书格式介绍及转换方法

在java中将x509证书写入PEM格式的字符串?

如何在不使用 OpenSSL 的情况下使用私钥导出 base64 编码的 x.509 证书

从 X.509 证书中提取 PEM 公钥

OpenSSL生成公钥私钥

openssl