【问题标题】:RSA_public_decrypt and MS Crypto API equivalentRSA_public_decrypt 和 MS Crypto API 等效
【发布时间】:2013-01-09 18:38:08
【问题描述】:

我正在尝试开发许可证验证解决方案。使用 OpenSSL 的 RSA_private_encrypt 函数在服务器上对许可证进行编码。

对于 Mac OX X,我使用 RSA_public_decrypt,它就像一个魅力。在 Windows 上,我必须使用非常少的代码,所以我无法与 OpenSSL 或其他库链接,我必须使用 MS Crypto API。

我花了几天时间试图找出问题所在,但没有运气。我可以成功导入公钥,但我的成功到此结束。 我知道我需要使用 CAPI 反转字节顺序,所以这可能不是问题。

我已经尝试了所有方法,包括 CryptVerifyMessageSignatureWithKeyCryptDecodeObject 以使用不同的参数加载 blob,但仍然没有运气。

它总是以GetLastError() == CRYPT_E_ASN1_BADTAG 结尾,我认为这意味着 BLOB 不是 ASN1 格式的......谷歌没有告诉任何关于 RSA_private_encrypt 的输出格式......所以我完全迷路了。

这是基于 OpenSSL 的 OS X 代码:

void cr_license_init(const char* lic) {
    __cr_license_ = lic;
    unsigned char lic_encoded[CR_LIC_LEN];

    BIO* b64 = BIO_new(BIO_f_base64());
    BIO* licIn = BIO_new_mem_buf((void*)lic, -1);
    licIn = BIO_push(b64, licIn);

    if(BIO_read(licIn, lic_encoded, CR_LIC_LEN) == CR_LIC_LEN) {

        const unsigned char* key_data = license_pub_der;
        RSA* r = d2i_RSA_PUBKEY(NULL, &key_data, sizeof(license_pub_der));

        if(r != NULL) {
            if(__cr_license_data_ != NULL) {
                free((void*)__cr_license_data_);
            }
            __cr_license_data_ = malloc(CR_LIC_LEN);
            if(RSA_public_decrypt(CR_LIC_LEN, lic_encoded,
    (unsigned char*)__cr_license_data_, r, RSA_PKCS1_PADDING) &lt= 0) {
                free((void*)__cr_license_data_);
                __cr_license_data_ = NULL;
            }
            RSA_free(r);
        }
    }
    BIO_free_all(licIn);
}

这部分代码在 windows 上运行良好,所以我认为公钥不是问题。

__cr_license_ = lic;
unsigned char lic_encoded[CR_LIC_LEN];

DWORD dwSize;
if(CryptStringToBinaryA(__cr_license_, 0/*autocalculate*/, CRYPT_STRING_BASE64, lic_encoded, &dwSize, NULL, NULL) && dwSize == CR_LIC_LEN) {
HCRYPTPROV hProv;
if(CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
    PCERT_PUBLIC_KEY_INFO pki = NULL;
    DWORD dwKeySize;
    if(CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, license_pub_der, sizeof(license_pub_der), CRYPT_ENCODE_ALLOC_FLAG, NULL, &pki, &dwKeySize)) {
        HCRYPTKEY hKey = 0;
        if(CryptImportPublicKeyInfo( hProv, X509_ASN_ENCODING, pki, &hKey)) {

但在那之后我尝试对消息进行的任何操作都会导致CRYPT_E_ASN1_BADTAG。我用CryptMsgUpdateCryptDecodeObjectCryptVerifyMessageSignatureWithKey 尝试了CryptMsgOpenToDecode - 没有任何效果。

基本上我认为问题在于 owlstead 提到的 pkcs1 和 pkcs7 不兼容。有没有人有使用 MS CAPI 处理 pkcs1 格式导入/转换/等的经验?

非常感谢任何帮助甚至线索!提前致谢!

【问题讨论】:

  • 您可能希望在问题中包含一些代码。
  • 还显示密钥和编码文件的标题。该错误是针对无效的 ASN 标签标头
  • 是的,我看过这个帖子。这种情况下的问题不是关键。我可以使用 CryptImportPublicKeyInfo 成功导入密钥,所以我假设密钥不是这种情况。似乎问题出在编码的 BLOB 上。我认为 BLOB 与 ASN1 不兼容,应该以某种方式进行转换。
  • @Daniel 我希望 Windows 代码会引发您所指的 CRYPT_E_ASN1_BADTAG 错误。

标签: windows cryptography openssl rsa cryptoapi


【解决方案1】:

您正在混合更高和更低级别的签名格式。 OpenSSL 默认采用 PKCS#1 v1.5 签名,其中仅包含签名数据。 Windows 似乎采用 PKCS#7 容器。这些可能包含 PKCS#1 v1.5,但这些和其他数据使用 ASN.1 BER 标签/长度格式进行包装。如果 Microsoft API 尝试对此进行解码,它将假定原始签名是容器格式,并且解码将失败。

【讨论】:

  • 是否可以在签名中添加更高级别的编码(PKCS7)?我的意思是可能类似于 CryptEncodeObject?
  • 查看我对您问题的最新评论
  • 非常感谢您的澄清!将继续研究。有人知道可能的解决方案吗?
【解决方案2】:

除非这很明显以至于您已经尝试过但没有列出它,或者我误解了您的问题,否则我认为您应该使用CryptDecrypt 来解密许可证,而不是您在问题中提到的功能。请注意,由于您似乎使用带有 PKCS#1 v1.5 填充的 OpenSSL,而 CryptoAPI 似乎不支持该功能(尚未测试,但规范仅列出 PKCS#1 v2 OAEP),您可能必须使用 @987654322 @ 并在解密后手动验证并删除 PKCS#1 v1.5 填充。

【讨论】:

  • CryptDecrypt 不打算与私钥一起使用吗?
  • 我认为它与公钥一起使用时会返回 NTE_NO_KEY。
  • 您是否尝试过,或者您只是觉得可以?老实说,我不知道,但所有其他功能都在更高级别的加密容器上运行,而不是原始 RSA 块。如果它不起作用,您可以通过翻转一些位来欺骗它相信公钥是私钥(不确定如何将公钥标记为这样),或者使用没有任何填充的 CryptEncrypt 欺骗它(不是指定 CRYPT_OAEP),因为如果它不做任何填充,加密和解密是等效的操作。
  • 是的,我刚试过。这是行不通的。我不确定是否有可能(以及如何)欺骗它相信它是一个私钥......基本上我认为这可能是不可能的,因为用私钥加密(也称为“签名”)应该不同用公钥加密很多。
  • 加密和解密操作仅在添加填充/验证和删除填充方面有所不同。在原始 RSA 级别上,操作是相同的,只是使用了不同的密钥。此外,原始签名/验证操作仅在不同填充中与加密/解密不同,位级别的实际 RSA 操作是相同的。
【解决方案3】:

OpenSSL 导出带有 CryptoAPI 不期望的额外标头的密钥。

私钥的标头(ASN.1 表示法):

Offset| Len  |LenByte|
======+======+=======+======================================================================
     0|   630|      3| SEQUENCE : 
     4|     1|      1|    INTEGER : 0
     7|    13|      1|    SEQUENCE : 
     9|     9|      1|       OBJECT IDENTIFIER : rsaEncryption [1.2.840.113549.1.1.1]
    20|     0|      1|       NULL : 
    22|   608|      3|    OCTET STRING : 
                             ... actual key data go here ...

公钥标头(ASN.1 表示法):

Offset| Len  |LenByte|
======+======+=======+======================================================================
     0|   159|      2| SEQUENCE : 
     3|    13|      1|    SEQUENCE : 
     5|     9|      1|       OBJECT IDENTIFIER : rsaEncryption [1.2.840.113549.1.1.1]
    16|     0|      1|       NULL : 
    18|   141|      2|    BIT STRING UnusedBits:0 : 
                              ... actual key data go here ...

这些标头是导致 CryptDecodeObjectEx 阻塞的原因。它需要 RAW 密钥数据,没有任何标题。

所以,基本上,你需要:

  1. (可选)使用 CryptStringToBinary 将 .PEM 转换为 .DER。
  2. 检查 DER 是否以上述标头开头。为此,您需要读取 ASN.1 编码的数据。
  3. (可选)跳过上述标头并直接查找密钥数据(以 SEQUENCE 开头,其中包括 2 INTEGER 用于公钥或 9 INTEGER 用于私钥)。
  4. 将结果提供给 CryptDecodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB/PKCS_RSA_PRIVATE_KEY)。
  5. 使用 CryptImportKey 导入密钥。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-27
    • 2023-03-04
    • 1970-01-01
    • 2011-05-28
    • 2013-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多