v2: - Fixed calculation of cbPublicExp and cbModulus. - pass PublicExp in BE format to follow MSDN.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50925 Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/crypt32/cert.c | 81 +++++++++++++++++++++++++++++++++++++ dlls/crypt32/tests/encode.c | 13 +++++- 2 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 95e2746a7d5..920561b9c76 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -2689,11 +2689,92 @@ done: return !status; }
+static BOOL CNG_ImportRSAPubKey(CERT_PUBLIC_KEY_INFO *info, BCRYPT_KEY_HANDLE *key) +{ + DWORD size, rsakey_len, modulus_len; + BLOBHEADER *hdr; + RSAPUBKEY *rsapubkey; + const WCHAR *rsa_algo; + BCRYPT_ALG_HANDLE alg = NULL; + BCRYPT_RSAKEY_BLOB *rsakey; + BYTE *p; + NTSTATUS status; + + if (!info->PublicKey.cbData) + { + SetLastError(NTE_BAD_ALGID); + return FALSE; + } + + if (!CryptDecodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, info->PublicKey.pbData, + info->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &hdr, &size)) + { + WARN("CryptDecodeObjectEx failed\n"); + return FALSE; + } + + if (hdr->aiKeyAlg == CALG_RSA_KEYX) + rsa_algo = BCRYPT_RSA_ALGORITHM; + else if (hdr->aiKeyAlg == CALG_RSA_SIGN) + rsa_algo = BCRYPT_RSA_SIGN_ALGORITHM; + else + { + FIXME("Unsupported RSA algorithm: %#x\n", hdr->aiKeyAlg); + CryptMemFree(hdr); + SetLastError(NTE_BAD_ALGID); + return FALSE; + } + + if ((status = BCryptOpenAlgorithmProvider(&alg, rsa_algo, NULL, 0))) + goto done; + + rsapubkey = (RSAPUBKEY *)(hdr + 1); + + modulus_len = size - sizeof(*hdr) - sizeof(*rsapubkey); + if (modulus_len != rsapubkey->bitlen / 8) + FIXME("RSA pubkey has wrong modulus_len %u\n", modulus_len); + + rsakey_len = sizeof(*rsakey) + sizeof(ULONG) + modulus_len; + + if (!(rsakey = CryptMemAlloc(rsakey_len))) + { + status = STATUS_NO_MEMORY; + goto done; + } + + rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC; + rsakey->BitLength = rsapubkey->bitlen; + rsakey->cbPublicExp = sizeof(ULONG); + rsakey->cbModulus = modulus_len; + rsakey->cbPrime1 = 0; + rsakey->cbPrime2 = 0; + + p = (BYTE *)(rsakey + 1); + /* According to MSDN RSAPUBKEY.pubexp is in LE while + * BCRYPT_RSAKEY_BLOB is supposed to have it in BE format */ + *(ULONG *)p = RtlUlongByteSwap(rsapubkey->pubexp); + p += sizeof(ULONG); + memcpy(p, rsapubkey + 1, modulus_len); + + status = BCryptImportKeyPair(alg, NULL, BCRYPT_RSAPUBLIC_BLOB, key, (BYTE *)rsakey, rsakey_len, 0); + CryptMemFree(rsakey); + +done: + CryptMemFree(hdr); + if (alg) BCryptCloseAlgorithmProvider(alg, 0); + if (status) SetLastError(RtlNtStatusToDosError(status)); + return !status; +} + BOOL CNG_ImportPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key) { if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY)) return CNG_ImportECCPubKey(pubKeyInfo, key);
+ + if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_RSA_RSA)) + return CNG_ImportRSAPubKey(pubKeyInfo, key); + FIXME("Unsupported public key type: %s\n", debugstr_a(pubKeyInfo->Algorithm.pszObjId)); SetLastError(NTE_BAD_ALGID); return FALSE; diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c index 38d2f84db14..7c72eba1b9b 100644 --- a/dlls/crypt32/tests/encode.c +++ b/dlls/crypt32/tests/encode.c @@ -30,6 +30,7 @@
static BOOL (WINAPI *pCryptDecodeObjectEx)(DWORD,LPCSTR,const BYTE*,DWORD,DWORD,PCRYPT_DECODE_PARA,void*,DWORD*); static BOOL (WINAPI *pCryptEncodeObjectEx)(DWORD,LPCSTR,const void*,DWORD,PCRYPT_ENCODE_PARA,void*,DWORD*); +static DWORD (WINAPI *pBCryptDestroyKey)(BCRYPT_KEY_HANDLE);
struct encodedInt { @@ -8400,6 +8401,7 @@ static void testImportPublicKey(HCRYPTPROV csp, PCERT_PUBLIC_KEY_INFO info) { BOOL ret; HCRYPTKEY key; + BCRYPT_KEY_HANDLE key2; PCCERT_CONTEXT context; DWORD dwSize; ALG_ID ai; @@ -8469,6 +8471,12 @@ static void testImportPublicKey(HCRYPTPROV csp, PCERT_PUBLIC_KEY_INFO info) &context->pCertInfo->SubjectPublicKeyInfo, 0, 0, NULL, &key); ok(ret, "CryptImportPublicKeyInfoEx failed: %08x\n", GetLastError()); CryptDestroyKey(key); + + ret = CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, + &context->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &key2); + ok(ret, "CryptImportPublicKeyInfoEx2 failed: %08x\n", GetLastError()); + if (pBCryptDestroyKey) pBCryptDestroyKey(key2); + CertFreeCertificateContext(context); } } @@ -8502,7 +8510,7 @@ START_TEST(encode) { static const DWORD encodings[] = { X509_ASN_ENCODING, PKCS_7_ASN_ENCODING, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING }; - HMODULE hCrypt32; + HMODULE hCrypt32, hBcrypt; DWORD i;
hCrypt32 = GetModuleHandleA("crypt32.dll"); @@ -8514,6 +8522,9 @@ START_TEST(encode) return; }
+ hBcrypt = GetModuleHandleA("bcrypt.dll"); + pBCryptDestroyKey = (void*)GetProcAddress(hBcrypt, "BCryptDestroyKey"); + for (i = 0; i < ARRAY_SIZE(encodings); i++) { test_encodeInt(encodings[i]);
On Mon, 2021-04-12 at 12:48 +0300, Dmitry Timoshkov wrote:
v2:
- Fixed calculation of cbPublicExp and cbModulus.
- pass PublicExp in BE format to follow MSDN.
Should the modulus also be converted to BE format?
Hans Leidekker hans@codeweavers.com wrote:
On Mon, 2021-04-12 at 12:48 +0300, Dmitry Timoshkov wrote:
v2:
- Fixed calculation of cbPublicExp and cbModulus.
- pass PublicExp in BE format to follow MSDN.
Should the modulus also be converted to BE format?
I'm not sure, though MSDN claims that the modulus provided by PUBLICKEYBLOB is also in LE format: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-mqqb/ade9efd...
I'd guess that gnutls verifies the modulus during the key creation. If the modulus also should be converted, taking into account that its length is variable, how would you propose to handle the conversion?
Dmitry Timoshkov dmitry@baikal.ru wrote:
Hans Leidekker hans@codeweavers.com wrote:
On Mon, 2021-04-12 at 12:48 +0300, Dmitry Timoshkov wrote:
v2:
- Fixed calculation of cbPublicExp and cbModulus.
- pass PublicExp in BE format to follow MSDN.
Should the modulus also be converted to BE format?
I'm not sure, though MSDN claims that the modulus provided by PUBLICKEYBLOB is also in LE format: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-mqqb/ade9efd...
I'd guess that gnutls verifies the modulus during the key creation. If the modulus also should be converted, taking into account that its length is variable, how would you propose to handle the conversion?
Taking into account that the modulus goes straight from the certificate data, I'd guess that the conversion is not needed.
On Mon, 2021-04-12 at 14:34 +0300, Dmitry Timoshkov wrote:
Dmitry Timoshkov dmitry@baikal.ru wrote:
Hans Leidekker hans@codeweavers.com wrote:
On Mon, 2021-04-12 at 12:48 +0300, Dmitry Timoshkov wrote:
v2:
- Fixed calculation of cbPublicExp and cbModulus.
- pass PublicExp in BE format to follow MSDN.
Should the modulus also be converted to BE format?
I'm not sure, though MSDN claims that the modulus provided by PUBLICKEYBLOB is also in LE format: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-mqqb/ade9efd...
I'd guess that gnutls verifies the modulus during the key creation. If the modulus also should be converted, taking into account that its length is variable, how would you propose to handle the conversion?
Taking into account that the modulus goes straight from the certificate data, I'd guess that the conversion is not needed.
You get a PUBLICKEYBLOB which is little-endian, so you do need to convert. You have calculated cbModulus already, so you can just replace the memcpy with a loop that stores the bytes in reverse order.