Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/crypt32/pfx.c | 133 +++++++++++++++++++++++++++++++++++++ dlls/crypt32/tests/store.c | 27 +++++++- 2 files changed, 159 insertions(+), 1 deletion(-)
diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c index ce842cd087..59c6c93f53 100644 --- a/dlls/crypt32/pfx.c +++ b/dlls/crypt32/pfx.c @@ -50,6 +50,8 @@ MAKE_FUNCPTR(gnutls_pkcs12_import); MAKE_FUNCPTR(gnutls_pkcs12_init); MAKE_FUNCPTR(gnutls_pkcs12_simple_parse); MAKE_FUNCPTR(gnutls_x509_crt_export); +MAKE_FUNCPTR(gnutls_x509_privkey_export_rsa_raw2); +MAKE_FUNCPTR(gnutls_x509_privkey_get_pk_algorithm2); #undef MAKE_FUNCPTR
static void gnutls_log( int level, const char *msg ) @@ -84,6 +86,8 @@ BOOL gnutls_initialize(void) LOAD_FUNCPTR(gnutls_pkcs12_init) LOAD_FUNCPTR(gnutls_pkcs12_simple_parse) LOAD_FUNCPTR(gnutls_x509_crt_export) + LOAD_FUNCPTR(gnutls_x509_privkey_export_rsa_raw2) + LOAD_FUNCPTR(gnutls_x509_privkey_get_pk_algorithm2) #undef LOAD_FUNCPTR
if ((ret = pgnutls_global_init()) != GNUTLS_E_SUCCESS) @@ -112,6 +116,121 @@ void gnutls_uninitialize(void) wine_dlclose( libgnutls_handle, NULL, 0 ); libgnutls_handle = NULL; } + +#define RSA_MAGIC_KEY ('R' | ('S' << 8) | ('A' << 16) | ('2' << 24)) +#define RSA_PUBEXP 65537 + +static HCRYPTPROV import_key( gnutls_x509_privkey_t key, DWORD flags ) +{ + int i, ret; + unsigned int bitlen; + gnutls_datum_t m, e, d, p, q, u, e1, e2; + BLOBHEADER *hdr; + RSAPUBKEY *rsakey; + HCRYPTPROV prov = 0; + HCRYPTKEY cryptkey; + BYTE *buf, *src, *dst; + DWORD size; + + if ((ret = pgnutls_x509_privkey_get_pk_algorithm2( key, &bitlen )) < 0) + { + pgnutls_perror( ret ); + return 0; + } + + if (ret != GNUTLS_PK_RSA) + { + FIXME( "key algorithm %u not supported\n", ret ); + return 0; + } + + if ((ret = pgnutls_x509_privkey_export_rsa_raw2( key, &m, &e, &d, &p, &q, &u, &e1, &e2 )) < 0) + { + pgnutls_perror( ret ); + return 0; + } + + size = sizeof(hdr) + sizeof(RSAPUBKEY) + (bitlen * 9 / 16); + if (!(buf = heap_alloc( size ))) goto done; + + hdr = (BLOBHEADER *)buf; + hdr->bType = PRIVATEKEYBLOB; + hdr->bVersion = CUR_BLOB_VERSION; + hdr->reserved = 0; + hdr->aiKeyAlg = CALG_RSA_KEYX; + + rsakey = (RSAPUBKEY *)(hdr + 1); + rsakey->magic = RSA_MAGIC_KEY; + rsakey->bitlen = bitlen; + rsakey->pubexp = RSA_PUBEXP; + + dst = (BYTE *)(rsakey + 1); + if (m.size == bitlen / 8 + 1 && !m.data[0]) src = m.data + 1; + else if (m.size != bitlen / 8) goto done; + else src = m.data; + for (i = bitlen / 8 - 1; i >= 0; i--) *dst++ = src[i]; + + if (p.size == bitlen / 16 + 1 && !p.data[0]) src = p.data + 1; + else if (p.size != bitlen / 16) goto done; + else src = p.data; + for (i = bitlen / 16 - 1; i >= 0; i--) *dst++ = src[i]; + + if (q.size == bitlen / 16 + 1 && !q.data[0]) src = q.data + 1; + else if (q.size != bitlen / 16) goto done; + else src = q.data; + for (i = bitlen / 16 - 1; i >= 0; i--) *dst++ = src[i]; + + if (e1.size == bitlen / 16 + 1 && !e1.data[0]) src = e1.data + 1; + else if (e1.size != bitlen / 16) goto done; + else src = e1.data; + for (i = bitlen / 16 - 1; i >= 0; i--) *dst++ = src[i]; + + if (e2.size == bitlen / 16 + 1 && !e2.data[0]) src = e2.data + 1; + else if (e2.size != bitlen / 16) goto done; + else src = e2.data; + for (i = bitlen / 16 - 1; i >= 0; i--) *dst++ = src[i]; + + if (u.size == bitlen / 16 + 1 && !u.data[0]) src = u.data + 1; + else if (u.size != bitlen / 16) goto done; + else src = u.data; + for (i = bitlen / 16 - 1; i >= 0; i--) *dst++ = src[i]; + + if (d.size == bitlen / 8 + 1 && !d.data[0]) src = d.data + 1; + else if (d.size != bitlen / 8) goto done; + else src = d.data; + for (i = bitlen / 8 - 1; i >= 0; i--) *dst++ = src[i]; + + if (!CryptAcquireContextW( &prov, NULL, MS_ENHANCED_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET )) + { + if (GetLastError() != NTE_EXISTS) goto done; + if (!CryptAcquireContextW( &prov, NULL, MS_ENHANCED_PROV_W, PROV_RSA_FULL, 0 )) + { + WARN( "CryptAcquireContextW failed %08x\n", GetLastError() ); + goto done; + } + } + + if (!CryptImportKey( prov, buf, size, 0, flags, &cryptkey )) + { + WARN( "CryptImportKey failed %08x\n", GetLastError() ); + CryptReleaseContext( prov, 0 ); + prov = 0; + } + else CryptDestroyKey( cryptkey ); + +done: + free( m.data ); + free( e.data ); + free( d.data ); + free( p.data ); + free( q.data ); + free( u.data ); + free( e1.data ); + free( e2.data ); + heap_free( buf ); + return prov; +} + #endif
HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *password, DWORD flags ) @@ -123,6 +242,8 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor gnutls_x509_crt_t *chain; unsigned int chain_len, i; HCERTSTORE store = NULL; + CERT_KEY_CONTEXT key_ctx; + HCRYPTPROV prov = 0; int ret;
TRACE_(crypt)( "(%p, %p, %08x)\n", pfx, password, flags ); @@ -162,6 +283,7 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor goto error; }
+ if (!(prov = import_key( key, flags & CRYPT_EXPORTABLE ))) goto error; if (!(store = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, 0, 0, NULL ))) { WARN( "CertOpenStore failed %08x\n", GetLastError() ); @@ -197,6 +319,16 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor } heap_free( crt_data );
+ key_ctx.cbSize = sizeof(key_ctx); + key_ctx.hCryptProv = prov; + key_ctx.dwKeySpec = AT_KEYEXCHANGE; + if (!CertSetCertificateContextProperty( ctx, CERT_KEY_CONTEXT_PROP_ID, 0, &key_ctx )) + { + WARN( "CertSetCertificateContextProperty failed %08x\n", GetLastError() ); + CertFreeCertificateContext( ctx ); + goto error; + } + if (!CertAddCertificateContextToStore( store, ctx, CERT_STORE_ADD_ALWAYS, NULL )) { WARN( "CertAddCertificateContextToStore failed %08x\n", GetLastError() ); @@ -210,6 +342,7 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor return store;
error: + CryptReleaseContext( prov, 0 ); CertCloseStore( store, 0 ); pgnutls_pkcs12_deinit( p12 ); return NULL; diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 94aba7b1d1..b13fee6cd1 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -3284,7 +3284,11 @@ static void test_PFXImportCertStore(void) { HCERTSTORE store; CRYPT_DATA_BLOB pfx; - DWORD count; + const CERT_CONTEXT *cert; + CERT_KEY_CONTEXT key; + CERT_INFO *info; + DWORD count, size; + BOOL ret;
SetLastError( 0xdeadbeef ); store = PFXImportCertStore( NULL, NULL, 0 ); @@ -3298,6 +3302,27 @@ static void test_PFXImportCertStore(void) if (!store) return; count = countCertsInStore( store ); ok( count == 1, "got %u\n", count ); + + cert = CertFindCertificateInStore( store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL ); + ok( cert != NULL, "got %u\n", GetLastError() ); + ok( cert->dwCertEncodingType == X509_ASN_ENCODING, "got %u\n", cert->dwCertEncodingType ); + ok( cert->pbCertEncoded != NULL, "pbCertEncoded not set\n" ); + ok( cert->cbCertEncoded == 1123, "got %u\n", cert->cbCertEncoded ); + ok( cert->pCertInfo != NULL, "pCertInfo not set\n" ); + ok( cert->hCertStore == store, "got %p\n", cert->hCertStore ); + + info = cert->pCertInfo; + ok( info->dwVersion == CERT_V1, "got %u\n", info->dwVersion ); + ok( !strcmp(info->SignatureAlgorithm.pszObjId, szOID_RSA_SHA256RSA), + "got "%s"\n", info->SignatureAlgorithm.pszObjId ); + + size = sizeof(key); + ret = CertGetCertificateContextProperty( cert, CERT_KEY_CONTEXT_PROP_ID, &key, &size ); + ok( ret, "got %08x\n", GetLastError() ); + ok( key.cbSize == sizeof(key), "got %u\n", key.cbSize ); + ok( key.hCryptProv, "hCryptProv not set\n" ); + ok( key.dwKeySpec == AT_KEYEXCHANGE, "got %u\n", key.dwKeySpec ); + CertCloseStore( store, 0 ); }