[PATCH 1/2] crypt32: Add support for importing RSA private keys from PFX blobs.
Hans Leidekker
hans at codeweavers.com
Wed Jan 30 06:39:17 CST 2019
Signed-off-by: Hans Leidekker <hans at 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 c4fb34dbbc..bbdf3e45d2 100644
--- a/dlls/crypt32/pfx.c
+++ b/dlls/crypt32/pfx.c
@@ -56,6 +56,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 )
@@ -90,6 +92,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)
@@ -118,6 +122,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(*rsakey) + (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 )
@@ -129,6 +248,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( "(%p, %p, %08x)\n", pfx, password, flags );
@@ -168,6 +289,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() );
@@ -203,6 +325,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() );
@@ -216,6 +348,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 );
}
--
2.20.1
More information about the wine-devel
mailing list