【问题标题】:How to decrypt data that is encrypted by OpenSSL with RSA_PKCS1_OAEP_PADDING using NCryptDecrypt?如何使用 NCryptDecrypt 使用 RSA_PKCS1_OAEP_PADDING 解密由 OpenSSL 加密的数据?
【发布时间】:2020-11-17 04:21:40
【问题描述】:

我很难解密使用 OpenSSL 和 RSA_PKCS1_OAEP_PADDING 填充选项加密的数据。

我正在做的是:

    BCRYPT_ALG_HANDLE hCryptAlg = NULL;
    BCRYPT_OAEP_PADDING_INFO paddingInfo = { 0 };
    DWORD cbDecryptedMessage;
    BYTE* pbDecryptedMessage = NULL;

    paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;

    // Open an algorithm handle.
    BCryptOpenAlgorithmProvider(&hCryptAlg, BCRYPT_RSA_ALGORITHM, NULL, 0);

    // Calculate the required buffer 
    NCryptDecrypt(m_hKeyContextFull, (LPBYTE)pEncrypted, encryptedLenInBytes, &paddingInfo, NULL, cbDecryptedMessage, &outputDataLen, NCRYPT_PAD_OAEP_FLAG | NCRYPT_SILENT_FLAG);

    // After required buffer is allocated...
    NCryptDecrypt(m_hKeyContextFull, (LPBYTE)pEncrypted, encryptedLenInBytes, &paddingInfo, pbDecryptedMessage, cbDecryptedMessage, &outputDataLen, NCRYPT_PAD_OAEP_FLAG | NCRYPT_SILENT_FLAG);

NTE_INVALID_PARAMETER (0x80090027) 失败。我尝试了不同的标志,但它们都不起作用。

注意:m_hKeyContextFull 已经使用 CryptAcquireCertificatePrivateKey 函数调用检索到:

 m_hSystemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, m_storeName.c_str());

 m_pCertWithKeys = CertFindCertificateInStore(m_hSystemStore, SupportedEncodings, 0, CERT_FIND_SUBJECT_STR, m_certName.c_str(), NULL);

 // Obtain the private key from the certificate.
 DWORD m_KeyContextSpec = 0;
 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE m_hKeyContextFull;
 CryptAcquireCertificatePrivateKey(m_pCertWithKeys, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, NULL, &m_hKeyContextFull, &m_KeyContextSpec, &m_KeyContextMustBeReleased);

注意:为了便于阅读,所有错误检查都已从代码中删除。

你知道我做错了什么吗?

谢谢。

【问题讨论】:

  • m_hKeyContextFull 来自哪里? RSA 密钥从哪里加载等?非常不完整的代码。发布一个完整的示例,而不仅仅是一个sn-p。
  • paddingInfo 无效。
  • 嗨 Henno,感谢您的评论,我实际上不想通过发布完整示例来增加复杂性,密钥是使用 CertOpenStore 从 Windows 密钥存储加载的,然后调用 CertFindCertificateInStore。所有用于加密和解密的单元测试都通过了。但问题是当我尝试解密使用 OpenSSL 加密的数据时,并指定了 RSA_PKCS1_OAEP_PADDING 标志。我可以通过 SoftHSM 解密它,但在 MSCRYPTO 中我只是失败了。我会更新问题。

标签: encryption cryptography


【解决方案1】:

我先让 paddingInfo 有效:

BCRYPT_OAEP_PADDING_INFO paddingInfo;
paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
paddingInfo.pbLabel = NULL;
paddingInfo.cbLabel = 0;

当前标准有空字符串标签(参见RFC 3447)。

另外,关于pbDecryptedMessage

如果该参数为NULL,该函数将计算解密数据所需的大小,并返回pcbResult参数指向的位置的大小。

所以无论如何它都不会解密。您需要先将缓冲区分配到正确的大小;很多 Windows API 都是这样工作的:首先用输出缓冲区 NULL 调用它,然后你以某种方式取回所需的大小,在这种情况下是 outputDataLen(你应该声明!)。然后将 outbufbuffer 分配到该大小,并使用新缓冲区和 cbDecryptedMessage 中的正确长度再次调用该函数。使用后,当然要再次释放缓冲区。但你的评论声称已经这样做了?

另一个可疑的事实:你使用 NCryptDecrypt 所以第一个参数,应该 是正确类型的 hKey,而 m_hKeyContextFull 似乎没有该类型。 CryptAcquireCertificatePrivateKey 会给你一个老式的钥匙把手。 您不能混合使用这些不同的 Windows CryptoAPI。 也许看看NcryptImportKey 函数,来转移它。

【讨论】:

  • 谢谢 Henno,我会尝试你的建议并告诉你结果。
  • NCryptImportKey 从内存缓冲区导入,密钥必须先导出到缓冲区,但结果是一样的,我无法让它工作。 OpenSSL 与 Microsoft CNG 和 KSP 有什么不兼容的地方吗?
  • 我可以成功解码以这种方式使用 openssl+OAEP 加密的字符串。这是有用的信息。
【解决方案2】:

我不熟悉NCrypt 系列接口,但我们最近在一个库中使用BCrypt 系列接口实现了类似的东西。这是有问题的函数,可以在更大的上下文中看到here

在我们的例子中,prvblbtypLEGACY_RSAPRIVATE_BLOBprvblbbufprvblbblen 被描述为 here

static
int
asymmetric_decrypt(
    const wchar_t * const prvblbtyp,
    const void * const prvblbbuf, const size_t prvblblen,
    const void * const ctbuf, const size_t ctlen,
    void ** const ptbuf, size_t * const ptlen)
{
    BCRYPT_ALG_HANDLE halg;
    int res;

    res = INT_MIN;
    if (BCryptOpenAlgorithmProvider(
            &halg, BCRYPT_RSA_ALGORITHM, NULL, 0) == STATUS_SUCCESS) {
        BCRYPT_KEY_HANDLE hkey;

        if (BCryptImportKeyPair(
                halg,
                NULL, prvblbtyp, &hkey,
                (void *)prvblbbuf, prvblblen,
                0) == STATUS_SUCCESS) {
            BCRYPT_OAEP_PADDING_INFO inf;
            ULONG len;

            inf.pszAlgId = BCRYPT_SHA1_ALGORITHM;
            inf.pbLabel = NULL;
            inf.cbLabel = 0;

            /*
             * decrypt first with a NULL output buffer.
             * this returns the size necessary for the buffer.
             */
            if (BCryptDecrypt(
                    hkey,
                    (void *)ctbuf, ctlen,
                    &inf,
                    NULL, 0,
                    NULL, 0, &len,
                    BCRYPT_PAD_OAEP) == STATUS_SUCCESS) {
                void * buf;

                /*
                 * allocate the required buffer
                 * and decrypt again
                 */
                res = -ENOMEM;
                buf = malloc(len);
                if (buf) {
                    res = INT_MIN;
                    if (BCryptDecrypt(
                            hkey,
                            (void *)ctbuf, ctlen,
                            &inf,
                            NULL, 0,
                            buf, len, &len,
                            BCRYPT_PAD_OAEP) == STATUS_SUCCESS) {
                        *ptbuf = buf;
                        *ptlen = len;
                        res = 0;
                    } else {
                        free(buf);
                    }
                }
            }

            BCryptDestroyKey(hkey);
        }

        BCryptCloseAlgorithmProvider(halg, 0);
    }

    return res;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-13
    • 2014-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多