From: Benoît Legat <benoit.legat@gmail.com> --- dlls/crypt32/pfx.c | 116 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c index 6d134163f90..3d0caaf8cdd 100644 --- a/dlls/crypt32/pfx.c +++ b/dlls/crypt32/pfx.c @@ -243,6 +243,108 @@ BOOL WINAPI PFXExportCertStore( HCERTSTORE store, CRYPT_DATA_BLOB *pfx, const WC return PFXExportCertStoreEx( store, pfx, password, NULL, flags ); } +static void reverse_bytes( const BYTE *src, BYTE *dst, DWORD len ) +{ + DWORD i; + for (i = 0; i < len; i++) dst[i] = src[len - i - 1]; +} + +static BYTE *export_capi_key( HCRYPTPROV prov, DWORD key_spec, DWORD *out_size ) +{ + HCRYPTKEY hkey; + BYTE *capi_blob = NULL, *bcrypt_blob = NULL; + DWORD capi_size, bitlen, mod_len, prime_len, pos, out_pos; + BLOBHEADER *hdr; + RSAPUBKEY *rsakey; + BCRYPT_RSAKEY_BLOB *rsa_hdr; + + if (!CryptGetUserKey( prov, key_spec, &hkey )) + { + WARN( "CryptGetUserKey failed %08lx\n", GetLastError() ); + return NULL; + } + + /* Query size. */ + capi_size = 0; + if (!CryptExportKey( hkey, 0, PRIVATEKEYBLOB, 0, NULL, &capi_size )) + { + WARN( "CryptExportKey size query failed %08lx\n", GetLastError() ); + CryptDestroyKey( hkey ); + return NULL; + } + + capi_blob = CryptMemAlloc( capi_size ); + if (!capi_blob) + { + CryptDestroyKey( hkey ); + return NULL; + } + + if (!CryptExportKey( hkey, 0, PRIVATEKEYBLOB, 0, capi_blob, &capi_size )) + { + WARN( "CryptExportKey failed %08lx\n", GetLastError() ); + CryptMemFree( capi_blob ); + CryptDestroyKey( hkey ); + return NULL; + } + CryptDestroyKey( hkey ); + + hdr = (BLOBHEADER *)capi_blob; + rsakey = (RSAPUBKEY *)(hdr + 1); + bitlen = rsakey->bitlen; + mod_len = bitlen / 8; + prime_len = bitlen / 16; + + /* Build BCRYPT_RSAFULLPRIVATE_BLOB. */ + *out_size = sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(rsakey->pubexp) + mod_len * 2 + prime_len * 5; + bcrypt_blob = CryptMemAlloc( *out_size ); + if (!bcrypt_blob) + { + CryptMemFree( capi_blob ); + return NULL; + } + + rsa_hdr = (BCRYPT_RSAKEY_BLOB *)bcrypt_blob; + rsa_hdr->Magic = BCRYPT_RSAFULLPRIVATE_MAGIC; + rsa_hdr->BitLength = bitlen; + rsa_hdr->cbPublicExp = sizeof(rsakey->pubexp); + rsa_hdr->cbModulus = mod_len; + rsa_hdr->cbPrime1 = prime_len; + rsa_hdr->cbPrime2 = prime_len; + + out_pos = sizeof(BCRYPT_RSAKEY_BLOB); + /* PublicExp (big-endian). */ + reverse_bytes( (const BYTE *)&rsakey->pubexp, bcrypt_blob + out_pos, sizeof(rsakey->pubexp) ); + out_pos += sizeof(rsakey->pubexp); + + /* CAPI data starts after BLOBHEADER + RSAPUBKEY. */ + pos = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY); + + /* Modulus */ + reverse_bytes( capi_blob + pos, bcrypt_blob + out_pos, mod_len ); + pos += mod_len; out_pos += mod_len; + /* Prime1 */ + reverse_bytes( capi_blob + pos, bcrypt_blob + out_pos, prime_len ); + pos += prime_len; out_pos += prime_len; + /* Prime2 */ + reverse_bytes( capi_blob + pos, bcrypt_blob + out_pos, prime_len ); + pos += prime_len; out_pos += prime_len; + /* Exponent1 */ + reverse_bytes( capi_blob + pos, bcrypt_blob + out_pos, prime_len ); + pos += prime_len; out_pos += prime_len; + /* Exponent2 */ + reverse_bytes( capi_blob + pos, bcrypt_blob + out_pos, prime_len ); + pos += prime_len; out_pos += prime_len; + /* Coefficient */ + reverse_bytes( capi_blob + pos, bcrypt_blob + out_pos, prime_len ); + pos += prime_len; out_pos += prime_len; + /* PrivateExponent */ + reverse_bytes( capi_blob + pos, bcrypt_blob + out_pos, mod_len ); + + CryptMemFree( capi_blob ); + return bcrypt_blob; +} + BOOL WINAPI PFXExportCertStoreEx( HCERTSTORE store, CRYPT_DATA_BLOB *pfx, const WCHAR *password, void *reserved, DWORD flags ) { @@ -312,6 +414,20 @@ BOOL WINAPI PFXExportCertStoreEx( HCERTSTORE store, CRYPT_DATA_BLOB *pfx, const return FALSE; } } + else if (key_ctx.dwKeySpec == AT_KEYEXCHANGE || key_ctx.dwKeySpec == AT_SIGNATURE) + { + key_blob = export_capi_key( key_ctx.hCryptProv, key_ctx.dwKeySpec, &key_blob_size ); + if (!key_blob) + { + WARN( "failed to export CAPI key\n" ); + if (flags & REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY) + { + SetLastError( NTE_NOT_FOUND ); + CertFreeCertificateContext( cert ); + return FALSE; + } + } + } else { FIXME( "dwKeySpec %lu not supported\n", key_ctx.dwKeySpec ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10532