2 * Copyright 2019 Hans Leidekker for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "wine/port.h"
23 #ifdef SONAME_LIBGNUTLS
24 #include <gnutls/pkcs12.h>
32 #include "wine/debug.h"
33 #include "wine/heap.h"
34 #include "wine/library.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(crypt
);
39 #ifdef SONAME_LIBGNUTLS
40 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
42 /* Not present in gnutls version < 3.0 */
43 int gnutls_pkcs12_simple_parse(gnutls_pkcs12_t p12
, const char *password
,
44 gnutls_x509_privkey_t
*key
, gnutls_x509_crt_t
**chain
, unsigned int *chain_len
,
45 gnutls_x509_crt_t
**extra_certs
, unsigned int *extra_certs_len
,
46 gnutls_x509_crl_t
* crl
, unsigned int flags
);
48 int gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t
, unsigned int*);
50 static void *libgnutls_handle
;
51 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
52 MAKE_FUNCPTR(gnutls_global_deinit
);
53 MAKE_FUNCPTR(gnutls_global_init
);
54 MAKE_FUNCPTR(gnutls_global_set_log_function
);
55 MAKE_FUNCPTR(gnutls_global_set_log_level
);
56 MAKE_FUNCPTR(gnutls_perror
);
57 MAKE_FUNCPTR(gnutls_pkcs12_deinit
);
58 MAKE_FUNCPTR(gnutls_pkcs12_import
);
59 MAKE_FUNCPTR(gnutls_pkcs12_init
);
60 MAKE_FUNCPTR(gnutls_pkcs12_simple_parse
);
61 MAKE_FUNCPTR(gnutls_x509_crt_export
);
62 MAKE_FUNCPTR(gnutls_x509_privkey_export_rsa_raw2
);
63 MAKE_FUNCPTR(gnutls_x509_privkey_get_pk_algorithm2
);
66 static void gnutls_log( int level
, const char *msg
)
68 TRACE( "<%d> %s", level
, msg
);
71 BOOL
gnutls_initialize(void)
75 if (!(libgnutls_handle
= wine_dlopen( SONAME_LIBGNUTLS
, RTLD_NOW
, NULL
, 0 )))
77 ERR_(winediag
)( "failed to load libgnutls, no support for pfx import/export\n" );
81 #define LOAD_FUNCPTR(f) \
82 if (!(p##f = wine_dlsym( libgnutls_handle, #f, NULL, 0 ))) \
84 ERR( "failed to load %s\n", #f ); \
88 LOAD_FUNCPTR(gnutls_global_deinit
)
89 LOAD_FUNCPTR(gnutls_global_init
)
90 LOAD_FUNCPTR(gnutls_global_set_log_function
)
91 LOAD_FUNCPTR(gnutls_global_set_log_level
)
92 LOAD_FUNCPTR(gnutls_perror
)
93 LOAD_FUNCPTR(gnutls_pkcs12_deinit
)
94 LOAD_FUNCPTR(gnutls_pkcs12_import
)
95 LOAD_FUNCPTR(gnutls_pkcs12_init
)
96 LOAD_FUNCPTR(gnutls_pkcs12_simple_parse
)
97 LOAD_FUNCPTR(gnutls_x509_crt_export
)
98 LOAD_FUNCPTR(gnutls_x509_privkey_export_rsa_raw2
)
99 LOAD_FUNCPTR(gnutls_x509_privkey_get_pk_algorithm2
)
102 if ((ret
= pgnutls_global_init()) != GNUTLS_E_SUCCESS
)
104 pgnutls_perror( ret
);
108 if (TRACE_ON( crypt
))
110 pgnutls_global_set_log_level( 4 );
111 pgnutls_global_set_log_function( gnutls_log
);
117 wine_dlclose( libgnutls_handle
, NULL
, 0 );
118 libgnutls_handle
= NULL
;
122 void gnutls_uninitialize(void)
124 pgnutls_global_deinit();
125 wine_dlclose( libgnutls_handle
, NULL
, 0 );
126 libgnutls_handle
= NULL
;
129 #define RSA_MAGIC_KEY ('R' | ('S' << 8) | ('A' << 16) | ('2' << 24))
130 #define RSA_PUBEXP 65537
132 static HCRYPTPROV
import_key( gnutls_x509_privkey_t key
, DWORD flags
)
136 gnutls_datum_t m
, e
, d
, p
, q
, u
, e1
, e2
;
141 BYTE
*buf
, *src
, *dst
;
144 if ((ret
= pgnutls_x509_privkey_get_pk_algorithm2( key
, &bitlen
)) < 0)
146 pgnutls_perror( ret
);
150 if (ret
!= GNUTLS_PK_RSA
)
152 FIXME( "key algorithm %u not supported\n", ret
);
156 if ((ret
= pgnutls_x509_privkey_export_rsa_raw2( key
, &m
, &e
, &d
, &p
, &q
, &u
, &e1
, &e2
)) < 0)
158 pgnutls_perror( ret
);
162 size
= sizeof(*hdr
) + sizeof(*rsakey
) + (bitlen
* 9 / 16);
163 if (!(buf
= heap_alloc( size
))) goto done
;
165 hdr
= (BLOBHEADER
*)buf
;
166 hdr
->bType
= PRIVATEKEYBLOB
;
167 hdr
->bVersion
= CUR_BLOB_VERSION
;
169 hdr
->aiKeyAlg
= CALG_RSA_KEYX
;
171 rsakey
= (RSAPUBKEY
*)(hdr
+ 1);
172 rsakey
->magic
= RSA_MAGIC_KEY
;
173 rsakey
->bitlen
= bitlen
;
174 rsakey
->pubexp
= RSA_PUBEXP
;
176 dst
= (BYTE
*)(rsakey
+ 1);
177 if (m
.size
== bitlen
/ 8 + 1 && !m
.data
[0]) src
= m
.data
+ 1;
178 else if (m
.size
!= bitlen
/ 8) goto done
;
180 for (i
= bitlen
/ 8 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
182 if (p
.size
== bitlen
/ 16 + 1 && !p
.data
[0]) src
= p
.data
+ 1;
183 else if (p
.size
!= bitlen
/ 16) goto done
;
185 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
187 if (q
.size
== bitlen
/ 16 + 1 && !q
.data
[0]) src
= q
.data
+ 1;
188 else if (q
.size
!= bitlen
/ 16) goto done
;
190 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
192 if (e1
.size
== bitlen
/ 16 + 1 && !e1
.data
[0]) src
= e1
.data
+ 1;
193 else if (e1
.size
!= bitlen
/ 16) goto done
;
195 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
197 if (e2
.size
== bitlen
/ 16 + 1 && !e2
.data
[0]) src
= e2
.data
+ 1;
198 else if (e2
.size
!= bitlen
/ 16) goto done
;
200 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
202 if (u
.size
== bitlen
/ 16 + 1 && !u
.data
[0]) src
= u
.data
+ 1;
203 else if (u
.size
!= bitlen
/ 16) goto done
;
205 for (i
= bitlen
/ 16 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
207 if (d
.size
== bitlen
/ 8 + 1 && !d
.data
[0]) src
= d
.data
+ 1;
208 else if (d
.size
!= bitlen
/ 8) goto done
;
210 for (i
= bitlen
/ 8 - 1; i
>= 0; i
--) *dst
++ = src
[i
];
212 if (!CryptAcquireContextW( &prov
, NULL
, MS_ENHANCED_PROV_W
, PROV_RSA_FULL
, CRYPT_NEWKEYSET
))
214 if (GetLastError() != NTE_EXISTS
) goto done
;
215 if (!CryptAcquireContextW( &prov
, NULL
, MS_ENHANCED_PROV_W
, PROV_RSA_FULL
, 0 ))
217 WARN( "CryptAcquireContextW failed %08x\n", GetLastError() );
222 if (!CryptImportKey( prov
, buf
, size
, 0, flags
, &cryptkey
))
224 WARN( "CryptImportKey failed %08x\n", GetLastError() );
225 CryptReleaseContext( prov
, 0 );
228 else CryptDestroyKey( cryptkey
);
243 static char *password_to_ascii( const WCHAR
*str
)
248 if (!(ret
= heap_alloc( (strlenW(str
) + 1) * sizeof(*ret
) ))) return NULL
;
251 if (*str
> 0x7f) WARN( "password contains non-ascii characters\n" );
260 HCERTSTORE WINAPI
PFXImportCertStore( CRYPT_DATA_BLOB
*pfx
, const WCHAR
*password
, DWORD flags
)
262 #ifdef SONAME_LIBGNUTLS
264 gnutls_datum_t pfx_data
;
265 gnutls_x509_privkey_t key
;
266 gnutls_x509_crt_t
*chain
;
267 unsigned int chain_len
, i
;
268 HCERTSTORE store
= NULL
;
269 CERT_KEY_CONTEXT key_ctx
;
274 TRACE( "(%p, %p, %08x)\n", pfx
, password
, flags
);
277 SetLastError( ERROR_INVALID_PARAMETER
);
280 if (flags
& ~(CRYPT_EXPORTABLE
|CRYPT_USER_KEYSET
|PKCS12_NO_PERSIST_KEY
))
282 FIXME( "flags %08x not supported\n", flags
);
285 if (password
&& !(pwd
= password_to_ascii( password
))) return NULL
;
287 if ((ret
= pgnutls_pkcs12_init( &p12
)) < 0)
289 pgnutls_perror( ret
);
293 pfx_data
.data
= pfx
->pbData
;
294 pfx_data
.size
= pfx
->cbData
;
295 if ((ret
= pgnutls_pkcs12_import( p12
, &pfx_data
, GNUTLS_X509_FMT_DER
, 0 )) < 0)
297 pgnutls_perror( ret
);
301 if ((ret
= pgnutls_pkcs12_simple_parse( p12
, pwd
? pwd
: "", &key
, &chain
, &chain_len
, NULL
, NULL
, NULL
, 0 )) < 0)
303 pgnutls_perror( ret
);
307 if (!(prov
= import_key( key
, flags
& CRYPT_EXPORTABLE
))) goto error
;
308 if (!(store
= CertOpenStore( CERT_STORE_PROV_MEMORY
, 0, 0, 0, NULL
)))
310 WARN( "CertOpenStore failed %08x\n", GetLastError() );
314 if (chain_len
> 1) FIXME( "handle certificate chain\n" );
315 for (i
= 0; i
< chain_len
; i
++)
321 if ((ret
= pgnutls_x509_crt_export( chain
[i
], GNUTLS_X509_FMT_DER
, NULL
, &size
)) != GNUTLS_E_SHORT_MEMORY_BUFFER
)
323 pgnutls_perror( ret
);
327 if (!(crt_data
= heap_alloc( size
))) goto error
;
328 if ((ret
= pgnutls_x509_crt_export( chain
[i
], GNUTLS_X509_FMT_DER
, crt_data
, &size
)) < 0)
330 pgnutls_perror( ret
);
331 heap_free( crt_data
);
335 if (!(ctx
= CertCreateContext( CERT_STORE_CERTIFICATE_CONTEXT
, X509_ASN_ENCODING
, crt_data
, size
, 0, NULL
)))
337 WARN( "CertCreateContext failed %08x\n", GetLastError() );
338 heap_free( crt_data
);
341 heap_free( crt_data
);
343 key_ctx
.cbSize
= sizeof(key_ctx
);
344 key_ctx
.hCryptProv
= prov
;
345 key_ctx
.dwKeySpec
= AT_KEYEXCHANGE
;
346 if (!CertSetCertificateContextProperty( ctx
, CERT_KEY_CONTEXT_PROP_ID
, 0, &key_ctx
))
348 WARN( "CertSetCertificateContextProperty failed %08x\n", GetLastError() );
349 CertFreeCertificateContext( ctx
);
353 if (!CertAddCertificateContextToStore( store
, ctx
, CERT_STORE_ADD_ALWAYS
, NULL
))
355 WARN( "CertAddCertificateContextToStore failed %08x\n", GetLastError() );
356 CertFreeCertificateContext( ctx
);
359 CertFreeCertificateContext( ctx
);
362 pgnutls_pkcs12_deinit( p12
);
366 CryptReleaseContext( prov
, 0 );
367 CertCloseStore( store
, 0 );
368 pgnutls_pkcs12_deinit( p12
);
373 FIXME( "(%p, %p, %08x)\n", pfx
, password
, flags
);
377 BOOL WINAPI
PFXVerifyPassword( CRYPT_DATA_BLOB
*pfx
, const WCHAR
*password
, DWORD flags
)
379 FIXME( "(%p, %p, %08x): stub\n", pfx
, password
, flags
);
383 BOOL WINAPI
PFXExportCertStore( HCERTSTORE store
, CRYPT_DATA_BLOB
*pfx
, const WCHAR
*password
, DWORD flags
)
385 return PFXExportCertStoreEx( store
, pfx
, password
, NULL
, flags
);
388 BOOL WINAPI
PFXExportCertStoreEx( HCERTSTORE store
, CRYPT_DATA_BLOB
*pfx
, const WCHAR
*password
, void *reserved
,
391 FIXME( "(%p, %p, %p, %p, %08x): stub\n", store
, pfx
, password
, reserved
, flags
);