1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <sal/config.h>
21
22 #include <cstddef>
23 #include <string.h>
24
25 #if !defined WIN32_LEAN_AND_MEAN
26 # define WIN32_LEAN_AND_MEAN
27 #endif
28 #include <Windows.h>
29 #include <WinCrypt.h>
30 #include <sal/macros.h>
31 #include <osl/thread.h>
32 #include "securityenvironment_mscryptimpl.hxx"
33
34 #include "x509certificate_mscryptimpl.hxx"
35 #include <comphelper/servicehelper.hxx>
36 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
37
38 #include <xmlsec-wrapper.h>
39 #include "akmngr.hxx"
40
41 #include <biginteger.hxx>
42
43 #include <comphelper/windowserrorstring.hxx>
44 #include <sal/log.hxx>
45 #include <rtl/locale.h>
46 #include <osl/nlsupport.h>
47 #include <osl/process.h>
48 #include <o3tl/char16_t2wchar_t.hxx>
49 #include <svl/cryptosign.hxx>
50
51 using namespace ::com::sun::star;
52 using namespace ::com::sun::star::lang ;
53 using ::com::sun::star::lang::XMultiServiceFactory ;
54 using ::com::sun::star::lang::XSingleServiceFactory ;
55
56 using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
57 using ::com::sun::star::security::XCertificate ;
58
59 static X509Certificate_MSCryptImpl* MswcryCertContextToXCert( PCCERT_CONTEXT cert ) ;
60
61 struct CertErrorToString{
62 DWORD error;
63 char const * name;
64 };
65
66 CertErrorToString const arErrStrings[] =
67 {
68 { 0x00000000, "CERT_TRUST_NO_ERROR"},
69 { 0x00000001, "CERT_TRUST_IS_NOT_TIME_VALID"},
70 { 0x00000002, "CERT_TRUST_IS_NOT_TIME_NESTED"},
71 { 0x00000004, "CERT_TRUST_IS_REVOKED" },
72 { 0x00000008, "CERT_TRUST_IS_NOT_SIGNATURE_VALID" },
73 { 0x00000010, "CERT_TRUST_IS_NOT_SIGNATURE_VALID"},
74 { 0x00000020, "CERT_TRUST_IS_UNTRUSTED_ROOT"},
75 { 0x00000040, "CERT_TRUST_REVOCATION_STATUS_UNKNOWN"},
76 { 0x00000080, "CERT_TRUST_IS_CYCLIC"},
77 { 0x00000100, "CERT_TRUST_INVALID_EXTENSION"},
78 { 0x00000200, "CERT_TRUST_INVALID_POLICY_CONSTRAINTS"},
79 { 0x00000400, "CERT_TRUST_INVALID_BASIC_CONSTRAINTS"},
80 { 0x00000800, "CERT_TRUST_INVALID_NAME_CONSTRAINTS"},
81 { 0x00001000, "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT"},
82 { 0x00002000, "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT"},
83 { 0x00004000, "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT"},
84 { 0x00008000, "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT"},
85 { 0x01000000, "CERT_TRUST_IS_OFFLINE_REVOCATION"},
86 { 0x02000000, "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY"},
87 { 0x04000000, "CERT_TRUST_IS_EXPLICIT_DISTRUST"},
88 { 0x08000000, "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT"},
89 //Chain errors
90 { 0x00010000, "CERT_TRUST_IS_PARTIAL_CHAIN"},
91 { 0x00020000, "CERT_TRUST_CTL_IS_NOT_TIME_VALID"},
92 { 0x00040000, "CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID"},
93 { 0x00080000, "CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE"}
94 };
95
traceTrustStatus(DWORD err)96 static void traceTrustStatus(DWORD err)
97 {
98 if (err == 0)
99 SAL_INFO("xmlsecurity.xmlsec", " " << arErrStrings[0].name);
100 for (std::size_t i = 1; i < SAL_N_ELEMENTS(arErrStrings); i++)
101 {
102 if (arErrStrings[i].error & err)
103 SAL_INFO("xmlsecurity.xmlsec", " " << arErrStrings[i].name);
104 }
105 }
106
SecurityEnvironment_MSCryptImpl(const uno::Reference<uno::XComponentContext> & xContext)107 SecurityEnvironment_MSCryptImpl::SecurityEnvironment_MSCryptImpl( const uno::Reference< uno::XComponentContext >& xContext ) : m_hProv( NULL ) , m_pszContainer( nullptr ) , m_hKeyStore( nullptr ), m_hCertStore( nullptr ), m_hMySystemStore(nullptr), m_hRootSystemStore(nullptr), m_hTrustSystemStore(nullptr), m_hCaSystemStore(nullptr), m_bEnableDefault( false ){
108
109 m_xServiceManager.set(xContext, uno::UNO_QUERY);
110 }
111
~SecurityEnvironment_MSCryptImpl()112 SecurityEnvironment_MSCryptImpl::~SecurityEnvironment_MSCryptImpl() {
113
114 if( m_hProv != NULL ) {
115 CryptReleaseContext( m_hProv, 0 ) ;
116 m_hProv = NULL ;
117 }
118
119 if( m_pszContainer != nullptr ) {
120 //TODO: Don't know whether or not it should be released now.
121 m_pszContainer = nullptr ;
122 }
123
124 if( m_hCertStore != nullptr ) {
125 CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
126 m_hCertStore = nullptr ;
127 }
128
129 if( m_hKeyStore != nullptr ) {
130 CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
131 m_hKeyStore = nullptr ;
132 }
133
134 //i120675, close the store handles
135 if( m_hMySystemStore != nullptr ) {
136 CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
137 m_hMySystemStore = nullptr ;
138 }
139
140 if( m_hRootSystemStore != nullptr ) {
141 CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
142 m_hRootSystemStore = nullptr ;
143 }
144
145 if( m_hTrustSystemStore != nullptr ) {
146 CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
147 m_hTrustSystemStore = nullptr ;
148 }
149
150 if( m_hCaSystemStore != nullptr ) {
151 CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
152 m_hCaSystemStore = nullptr ;
153 }
154 }
155
156 /* XServiceInfo */
getImplementationName()157 OUString SAL_CALL SecurityEnvironment_MSCryptImpl::getImplementationName() {
158 return "com.sun.star.xml.crypto.SecurityEnvironment";
159 }
160
161 /* XServiceInfo */
supportsService(const OUString & serviceName)162 sal_Bool SAL_CALL SecurityEnvironment_MSCryptImpl::supportsService( const OUString& serviceName) {
163 uno::Sequence< OUString > seqServiceNames = getSupportedServiceNames() ;
164 const OUString* pArray = seqServiceNames.getConstArray() ;
165 for( sal_Int32 i = 0 ; i < seqServiceNames.getLength() ; i ++ ) {
166 if( *( pArray + i ) == serviceName )
167 return true ;
168 }
169 return false ;
170 }
171
172 /* XServiceInfo */
getSupportedServiceNames()173 uno::Sequence< OUString > SAL_CALL SecurityEnvironment_MSCryptImpl::getSupportedServiceNames() {
174 uno::Sequence<OUString> seqServiceNames { "com.sun.star.xml.crypto.SecurityEnvironment" };
175 return seqServiceNames ;
176 }
177
178 /* XUnoTunnel */
179 UNO3_GETIMPLEMENTATION_IMPL(SecurityEnvironment_MSCryptImpl);
180
getCryptoProvider()181 HCRYPTPROV SecurityEnvironment_MSCryptImpl::getCryptoProvider() {
182 return m_hProv ;
183 }
184
setCryptoProvider(HCRYPTPROV aProv)185 void SecurityEnvironment_MSCryptImpl::setCryptoProvider( HCRYPTPROV aProv ) {
186 if( m_hProv != NULL ) {
187 CryptReleaseContext( m_hProv, 0 ) ;
188 m_hProv = NULL ;
189 }
190
191 if( aProv != NULL ) {
192 m_hProv = aProv ;
193 }
194 }
195
getKeyContainer()196 LPCTSTR SecurityEnvironment_MSCryptImpl::getKeyContainer() {
197 return m_pszContainer ;
198 }
199
setKeyContainer(LPCTSTR aKeyContainer)200 void SecurityEnvironment_MSCryptImpl::setKeyContainer( LPCTSTR aKeyContainer ) {
201 //TODO: Don't know whether or not it should be copied.
202 m_pszContainer = aKeyContainer ;
203 }
204
205
getCryptoSlot()206 HCERTSTORE SecurityEnvironment_MSCryptImpl::getCryptoSlot() {
207 return m_hKeyStore ;
208 }
209
setCryptoSlot(HCERTSTORE aSlot)210 void SecurityEnvironment_MSCryptImpl::setCryptoSlot( HCERTSTORE aSlot) {
211 if( m_hKeyStore != nullptr ) {
212 CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
213 m_hKeyStore = nullptr ;
214 }
215
216 if( aSlot != nullptr ) {
217 m_hKeyStore = CertDuplicateStore( aSlot ) ;
218 }
219 }
220
getCertDb()221 HCERTSTORE SecurityEnvironment_MSCryptImpl::getCertDb() {
222 return m_hCertStore ;
223 }
224
setCertDb(HCERTSTORE aCertDb)225 void SecurityEnvironment_MSCryptImpl::setCertDb( HCERTSTORE aCertDb ) {
226 if( m_hCertStore != nullptr ) {
227 CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
228 m_hCertStore = nullptr ;
229 }
230
231 if( aCertDb != nullptr ) {
232 m_hCertStore = CertDuplicateStore( aCertDb ) ;
233 }
234 }
235
236 #ifdef SAL_LOG_INFO
237
238 // Based on sample code from MSDN
239
get_system_name(const void * pvSystemStore,DWORD dwFlags)240 static OUString get_system_name(const void *pvSystemStore,
241 DWORD dwFlags)
242 {
243 LPCWSTR ppwszSystemName;
244 if (dwFlags & CERT_SYSTEM_STORE_RELOCATE_FLAG)
245 {
246 _CERT_SYSTEM_STORE_RELOCATE_PARA const * pRelocatePara;
247 pRelocatePara = static_cast<_CERT_SYSTEM_STORE_RELOCATE_PARA const *>(pvSystemStore);
248 ppwszSystemName = pRelocatePara->pwszSystemStore;
249 }
250 else
251 {
252 ppwszSystemName = static_cast<LPCWSTR>(pvSystemStore);
253 }
254 return o3tl::toU(ppwszSystemName);
255 }
256
257 extern "C" {
258
cert_enum_physical_store_callback(const void *,DWORD dwFlags,LPCWSTR pwszStoreName,PCERT_PHYSICAL_STORE_INFO,void *,void *)259 static BOOL WINAPI cert_enum_physical_store_callback(const void *,
260 DWORD dwFlags,
261 LPCWSTR pwszStoreName,
262 PCERT_PHYSICAL_STORE_INFO,
263 void *,
264 void *)
265 {
266 OUString name(o3tl::toU(pwszStoreName));
267 if (dwFlags & CERT_PHYSICAL_STORE_PREDEFINED_ENUM_FLAG)
268 name += " (implicitly created)";
269 SAL_INFO("xmlsecurity.xmlsec", " Physical store: " << name);
270
271 return TRUE;
272 }
273
cert_enum_system_store_callback(const void * pvSystemStore,DWORD dwFlags,PCERT_SYSTEM_STORE_INFO,void *,void *)274 static BOOL WINAPI cert_enum_system_store_callback(const void *pvSystemStore,
275 DWORD dwFlags,
276 PCERT_SYSTEM_STORE_INFO,
277 void *,
278 void *)
279 {
280 SAL_INFO("xmlsecurity.xmlsec", "System store: " << get_system_name(pvSystemStore, dwFlags));
281
282 if (!CertEnumPhysicalStore(pvSystemStore,
283 dwFlags,
284 nullptr,
285 cert_enum_physical_store_callback))
286 {
287 DWORD dwErr = GetLastError();
288 if (!(ERROR_FILE_NOT_FOUND == dwErr ||
289 ERROR_NOT_SUPPORTED == dwErr))
290 {
291 SAL_WARN("xmlsecurity.xmlsec", "CertEnumPhysicalStore failed:" << WindowsErrorString(GetLastError()));
292 }
293 }
294 return TRUE;
295 }
296
297 }
298
299 #endif
300
301 //Methods from XSecurityEnvironment
getPersonalCertificates()302 uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::getPersonalCertificates()
303 {
304 sal_Int32 length ;
305 X509Certificate_MSCryptImpl* xcert ;
306 std::vector< X509Certificate_MSCryptImpl* > certsList ;
307 PCCERT_CONTEXT pCertContext = nullptr;
308
309 //firstly, we try to find private keys in given key store.
310 if( m_hKeyStore != nullptr ) {
311 pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
312 while (pCertContext)
313 {
314 xcert = MswcryCertContextToXCert( pCertContext ) ;
315 if( xcert != nullptr )
316 certsList.push_back( xcert ) ;
317 pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
318 }
319 }
320
321 //Thirdly, we try to find certificate from system default key store.
322 if( m_bEnableDefault ) {
323 HCERTSTORE hSystemKeyStore ;
324 DWORD dwKeySpec;
325 NCRYPT_KEY_HANDLE hCryptKey;
326
327 #ifdef SAL_LOG_INFO
328 CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, nullptr, nullptr, cert_enum_system_store_callback);
329 #endif
330
331 hSystemKeyStore = CertOpenSystemStoreW( 0, L"MY" ) ;
332 if( hSystemKeyStore != nullptr ) {
333 pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
334 while (pCertContext)
335 {
336 // for checking whether the certificate is a personal certificate or not.
337 DWORD dwFlags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
338 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hCryptKey;
339 if(!(CryptAcquireCertificatePrivateKey(pCertContext,
340 dwFlags,
341 nullptr,
342 phCryptProvOrNCryptKey,
343 &dwKeySpec,
344 nullptr)))
345 {
346 // Not Privatekey found. SKIP this one.
347 pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
348 continue;
349 }
350 // then TODO : Check the personal cert is valid or not.
351
352 xcert = MswcryCertContextToXCert( pCertContext ) ;
353 if( xcert != nullptr )
354 certsList.push_back( xcert ) ;
355 pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
356 }
357 }
358
359 CertCloseStore( hSystemKeyStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
360 }
361
362 length = certsList.size() ;
363 if( length != 0 ) {
364 int i = 0;
365 uno::Sequence< uno::Reference< XCertificate > > certSeq( length ) ;
366
367 for( const auto& rXCert : certsList ) {
368 certSeq[i] = rXCert ;
369 ++i;
370 }
371
372 return certSeq ;
373 }
374
375 return uno::Sequence< uno::Reference< XCertificate > >() ;
376 }
377
378
getCertificate(const OUString & issuerName,const uno::Sequence<sal_Int8> & serialNumber)379 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const uno::Sequence< sal_Int8 >& serialNumber ) {
380 unsigned int i ;
381 X509Certificate_MSCryptImpl *xcert = nullptr ;
382 PCCERT_CONTEXT pCertContext = nullptr ;
383 HCERTSTORE hCertStore = nullptr ;
384 CRYPT_INTEGER_BLOB cryptSerialNumber ;
385 CERT_INFO certInfo ;
386
387 // for correct encoding
388 sal_uInt16 encoding ;
389 rtl_Locale *pLocale = nullptr ;
390 osl_getProcessLocale( &pLocale ) ;
391 encoding = osl_getTextEncodingFromLocale( pLocale ) ;
392
393 //Create cert info from issue and serial
394 LPCWSTR pszName = o3tl::toW( issuerName.getStr() );
395
396 if( ! ( CertStrToNameW(
397 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
398 pszName ,
399 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
400 nullptr ,
401 nullptr ,
402 &certInfo.Issuer.cbData, nullptr ) )
403 ) {
404 return nullptr ;
405 }
406
407 certInfo.Issuer.pbData = static_cast<BYTE*>(malloc( certInfo.Issuer.cbData ));
408 if(!certInfo.Issuer.pbData)
409 throw uno::RuntimeException() ;
410
411 if( ! ( CertStrToNameW(
412 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
413 pszName ,
414 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
415 nullptr ,
416 certInfo.Issuer.pbData ,
417 &certInfo.Issuer.cbData, nullptr ) )
418 ) {
419 free( certInfo.Issuer.pbData ) ;
420 return nullptr ;
421 }
422
423 //Get the SerialNumber
424 cryptSerialNumber.cbData = serialNumber.getLength() ;
425 cryptSerialNumber.pbData = static_cast<BYTE*>(malloc( cryptSerialNumber.cbData));
426 if (!cryptSerialNumber.pbData)
427 {
428 free( certInfo.Issuer.pbData ) ;
429 throw uno::RuntimeException() ;
430 }
431 for( i = 0; i < cryptSerialNumber.cbData; i ++ )
432 cryptSerialNumber.pbData[i] = serialNumber[ cryptSerialNumber.cbData - i - 1 ] ;
433
434 certInfo.SerialNumber.cbData = cryptSerialNumber.cbData ;
435 certInfo.SerialNumber.pbData = cryptSerialNumber.pbData ;
436
437 // Get the Cert from all store.
438 for( i = 0 ; i < 6 ; i ++ )
439 {
440 switch(i)
441 {
442 case 0:
443 if(m_hKeyStore == nullptr) continue ;
444 hCertStore = m_hKeyStore ;
445 break;
446 case 1:
447 if(m_hCertStore == nullptr) continue ;
448 hCertStore = m_hCertStore ;
449 break;
450 case 2:
451 hCertStore = CertOpenSystemStoreW( 0, L"MY" ) ;
452 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
453 break;
454 case 3:
455 hCertStore = CertOpenSystemStoreW( 0, L"Root" ) ;
456 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
457 break;
458 case 4:
459 hCertStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
460 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
461 break;
462 case 5:
463 hCertStore = CertOpenSystemStoreW( 0, L"CA" ) ;
464 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
465 break;
466 default:
467 i=6;
468 continue;
469 }
470
471 /*******************************************************************************
472 * This code reserved for remind us there are another way to find one cert by
473 * IssuerName&serialnumber. You can use the code to replaced the function
474 * CertFindCertificateInStore IF and ONLY IF you must find one special cert in
475 * certStore but can not be found by CertFindCertificateInStore , then , you
476 * should also change the same part in libxmlsec/.../src/mscrypto/x509vfy.c#875.
477 * By Chandler Peng(chandler.peng@sun.com)
478 *****/
479 /*******************************************************************************
480 pCertContext = NULL ;
481 found = 0;
482 do{
483 // 1. enum the certs has same string in the issuer string.
484 pCertContext = CertEnumCertificatesInStore( hCertStore , pCertContext ) ;
485 if( pCertContext != NULL )
486 {
487 // 2. check the cert's issuer name .
488 char* issuer = NULL ;
489 DWORD cbIssuer = 0 ;
490
491 cbIssuer = CertNameToStr(
492 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
493 &( pCertContext->pCertInfo->Issuer ),
494 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
495 NULL, 0
496 ) ;
497
498 if( cbIssuer == 0 ) continue ; // discard this cert;
499
500 issuer = (char *)malloc( cbIssuer ) ;
501 if( issuer == NULL ) // discard this cert;
502 {
503 free( cryptSerialNumber.pbData) ;
504 free( certInfo.Issuer.pbData ) ;
505 CertFreeCertificateContext( pCertContext ) ;
506 if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
507 throw RuntimeException() ;
508 }
509
510 cbIssuer = CertNameToStr(
511 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
512 &( pCertContext->pCertInfo->Issuer ),
513 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
514 issuer, cbIssuer
515 ) ;
516
517 if( cbIssuer <= 0 )
518 {
519 free( issuer ) ;
520 continue ;// discard this cert;
521 }
522
523 if(strncmp(pszName , issuer , cbIssuer) != 0)
524 {
525 free( issuer ) ;
526 continue ;// discard this cert;
527 }
528 free( issuer ) ;
529
530 // 3. check the serial number.
531 if( memcmp( cryptSerialNumber.pbData , pCertContext->pCertInfo->SerialNumber.pbData , cryptSerialNumber.cbData ) != 0 )
532 {
533 continue ;// discard this cert;
534 }
535
536 // 4. confirm and break;
537 found = 1;
538 break ;
539 }
540
541 }while(pCertContext);
542
543 if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
544 if( found != 0 ) break; // Found the certificate.
545 ********************************************************************************/
546
547 pCertContext = CertFindCertificateInStore(
548 hCertStore,
549 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
550 0,
551 CERT_FIND_SUBJECT_CERT,
552 &certInfo,
553 nullptr
554 ) ;
555
556 if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
557 if( pCertContext != nullptr ) break ; // Found the certificate.
558
559 }
560
561 free(cryptSerialNumber.pbData);
562 free(certInfo.Issuer.pbData);
563
564 if( pCertContext != nullptr ) {
565 xcert = MswcryCertContextToXCert(pCertContext);
566 CertFreeCertificateContext(pCertContext);
567 } else {
568 xcert = nullptr ;
569 }
570
571 return xcert ;
572 }
573
getCertificate(const OUString & issuerName,const OUString & serialNumber)574 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const OUString& serialNumber ) {
575 uno::Sequence< sal_Int8 > serial = xmlsecurity::numericStringToBigInteger( serialNumber ) ;
576 return getCertificate( issuerName, serial ) ;
577 }
578
buildCertificatePath(const uno::Reference<XCertificate> & begin)579 uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::buildCertificatePath( const uno::Reference< XCertificate >& begin ) {
580 PCCERT_CHAIN_CONTEXT pChainContext ;
581 PCCERT_CONTEXT pCertContext ;
582 const X509Certificate_MSCryptImpl* xcert ;
583
584 CERT_ENHKEY_USAGE enhKeyUsage ;
585 CERT_USAGE_MATCH certUsage ;
586 CERT_CHAIN_PARA chainPara ;
587
588 enhKeyUsage.cUsageIdentifier = 0 ;
589 enhKeyUsage.rgpszUsageIdentifier = nullptr ;
590 certUsage.dwType = USAGE_MATCH_TYPE_AND ;
591 certUsage.Usage = enhKeyUsage ;
592 chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
593 chainPara.RequestedUsage = certUsage ;
594
595 uno::Reference< XUnoTunnel > xCertTunnel( begin, uno::UNO_QUERY_THROW ) ;
596 xcert = reinterpret_cast<X509Certificate_MSCryptImpl*>(xCertTunnel->getSomething( X509Certificate_MSCryptImpl::getUnoTunnelId() ));
597 if( xcert == nullptr ) {
598 throw uno::RuntimeException() ;
599 }
600
601 pCertContext = xcert->getMswcryCert() ;
602
603 pChainContext = nullptr ;
604
605 BOOL bChain = FALSE;
606 if( pCertContext != nullptr )
607 {
608 HCERTSTORE hAdditionalStore = nullptr;
609 HCERTSTORE hCollectionStore = nullptr;
610 if (m_hCertStore && m_hKeyStore)
611 {
612 //Merge m_hCertStore and m_hKeyStore into one store.
613 hCollectionStore = CertOpenStore(
614 CERT_STORE_PROV_COLLECTION ,
615 0 ,
616 NULL ,
617 0 ,
618 nullptr
619 ) ;
620 if (hCollectionStore != nullptr)
621 {
622 CertAddStoreToCollection (
623 hCollectionStore ,
624 m_hCertStore ,
625 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
626 0) ;
627 CertAddStoreToCollection (
628 hCollectionStore ,
629 m_hCertStore ,
630 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
631 0) ;
632 hAdditionalStore = hCollectionStore;
633 }
634
635 }
636
637 //if the merge of both stores failed then we add only m_hCertStore
638 if (hAdditionalStore == nullptr && m_hCertStore)
639 hAdditionalStore = m_hCertStore;
640 else if (hAdditionalStore == nullptr && m_hKeyStore)
641 hAdditionalStore = m_hKeyStore;
642 else
643 hAdditionalStore = nullptr;
644
645 //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
646 bChain = CertGetCertificateChain(
647 nullptr ,
648 pCertContext ,
649 nullptr , //use current system time
650 hAdditionalStore,
651 &chainPara ,
652 CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_TIMESTAMP_TIME ,
653 nullptr ,
654 &pChainContext);
655 if (!bChain)
656 pChainContext = nullptr;
657
658 //Close the additional store
659 CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
660 }
661
662 if(bChain && pChainContext != nullptr && pChainContext->cChain > 0 )
663 {
664 PCCERT_CONTEXT pCertInChain ;
665 PCERT_SIMPLE_CHAIN pCertChain ;
666 X509Certificate_MSCryptImpl* pCert ;
667
668 pCertChain = pChainContext->rgpChain[0] ;
669 if( pCertChain->cElement ) {
670 uno::Sequence< uno::Reference< XCertificate > > xCertChain( pCertChain->cElement ) ;
671
672 for( unsigned int i = 0 ; i < pCertChain->cElement ; i ++ ) {
673 if( pCertChain->rgpElement[i] )
674 pCertInChain = pCertChain->rgpElement[i]->pCertContext ;
675 else
676 pCertInChain = nullptr ;
677
678 if( pCertInChain != nullptr ) {
679 pCert = MswcryCertContextToXCert( pCertInChain ) ;
680 if( pCert != nullptr )
681 xCertChain[i] = pCert ;
682 }
683 }
684
685 CertFreeCertificateChain( pChainContext ) ;
686 pChainContext = nullptr ;
687
688 return xCertChain ;
689 }
690 }
691 if (pChainContext)
692 CertFreeCertificateChain(pChainContext);
693
694 return uno::Sequence< uno::Reference < XCertificate > >();
695 }
696
createCertificateFromRaw(const uno::Sequence<sal_Int8> & rawCertificate)697 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromRaw( const uno::Sequence< sal_Int8 >& rawCertificate ) {
698 X509Certificate_MSCryptImpl* xcert ;
699
700 if( rawCertificate.getLength() > 0 ) {
701 xcert = new X509Certificate_MSCryptImpl() ;
702 xcert->setRawCert( rawCertificate ) ;
703 } else {
704 xcert = nullptr ;
705 }
706
707 return xcert ;
708 }
709
createCertificateFromAscii(const OUString & asciiCertificate)710 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromAscii( const OUString& asciiCertificate ) {
711
712 OString oscert = OUStringToOString( asciiCertificate , RTL_TEXTENCODING_ASCII_US ) ;
713
714 xmlChar* chCert = xmlStrndup( reinterpret_cast<const xmlChar*>(oscert.getStr()), static_cast<int>(oscert.getLength()) ) ;
715
716 xmlSecSize certSize = xmlSecBase64Decode( chCert, chCert, xmlStrlen( chCert ) ) ;
717
718 uno::Sequence< sal_Int8 > rawCert( certSize ) ;
719 for( xmlSecSize i = 0 ; i < certSize ; i ++ )
720 rawCert[i] = *( chCert + i ) ;
721
722 xmlFree( chCert ) ;
723
724 return createCertificateFromRaw( rawCert ) ;
725 }
726
727
getCertStoreForIntermediatCerts(const uno::Sequence<uno::Reference<css::security::XCertificate>> & seqCerts)728 static HCERTSTORE getCertStoreForIntermediatCerts(
729 const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
730 {
731 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, nullptr);
732 if (store == nullptr)
733 return nullptr;
734
735 for (int i = 0; i < seqCerts.getLength(); i++)
736 {
737 SAL_INFO("xmlsecurity.xmlsec", "Added temporary certificate: " << seqCerts[i]->getSubjectName());
738
739 uno::Sequence<sal_Int8> data = seqCerts[i]->getEncoded();
740 PCCERT_CONTEXT cert = CertCreateCertificateContext(
741 X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(&data[0]), data.getLength());
742 //Adding the certificate creates a copy and not just increases the ref count
743 //Therefore we free later the certificate that we now add
744 CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_ALWAYS, nullptr);
745 CertFreeCertificateContext(cert);
746 }
747 return store;
748 }
749
750 //We return only valid or invalid, as long as the API documentation expresses
751 //explicitly that all validation steps are carried out even if one or several
752 //errors occur. See also
753 //http://wiki.openoffice.org/wiki/Certificate_Path_Validation#Validation_status
verifyCertificate(const uno::Reference<css::security::XCertificate> & aCert,const uno::Sequence<uno::Reference<css::security::XCertificate>> & seqCerts)754 sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate(
755 const uno::Reference< css::security::XCertificate >& aCert,
756 const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
757 {
758 sal_Int32 validity = 0;
759 PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
760 PCCERT_CONTEXT pCertContext = nullptr;
761
762 uno::Reference< XUnoTunnel > xCertTunnel( aCert, uno::UNO_QUERY_THROW ) ;
763
764 SAL_INFO("xmlsecurity.xmlsec", "Start verification of certificate: " << aCert->getSubjectName());
765
766 auto xcert = reinterpret_cast<const X509Certificate_MSCryptImpl*>
767 (xCertTunnel->getSomething( X509Certificate_MSCryptImpl::getUnoTunnelId() ));
768 if( xcert == nullptr ) {
769 throw uno::RuntimeException() ;
770 }
771
772 pCertContext = xcert->getMswcryCert() ;
773
774 CERT_ENHKEY_USAGE enhKeyUsage ;
775 CERT_USAGE_MATCH certUsage ;
776 CERT_CHAIN_PARA chainPara = {};
777
778 //Prepare parameter for CertGetCertificateChain
779 enhKeyUsage.cUsageIdentifier = 0 ;
780 enhKeyUsage.rgpszUsageIdentifier = nullptr ;
781 certUsage.dwType = USAGE_MATCH_TYPE_AND ;
782 certUsage.Usage = enhKeyUsage ;
783 chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
784 chainPara.RequestedUsage = certUsage ;
785
786
787 HCERTSTORE hCollectionStore = nullptr;
788 HCERTSTORE hIntermediateCertsStore = nullptr;
789 BOOL bChain = FALSE;
790 if( pCertContext != nullptr )
791 {
792 hIntermediateCertsStore =
793 getCertStoreForIntermediatCerts(seqCerts);
794
795 //Merge m_hCertStore and m_hKeyStore and the store of the intermediate
796 //certificates into one store.
797 hCollectionStore = CertOpenStore(
798 CERT_STORE_PROV_COLLECTION ,
799 0 ,
800 NULL ,
801 0 ,
802 nullptr
803 ) ;
804 if (hCollectionStore != nullptr)
805 {
806 CertAddStoreToCollection (
807 hCollectionStore ,
808 m_hCertStore ,
809 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
810 0) ;
811 CertAddStoreToCollection (
812 hCollectionStore ,
813 m_hCertStore ,
814 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
815 0) ;
816 CertAddStoreToCollection (
817 hCollectionStore,
818 hIntermediateCertsStore,
819 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG,
820 0);
821
822 }
823
824 //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
825 //We do not check revocation of the root. In most cases there are none.
826 //Then we would get CERT_TRUST_REVOCATION_STATUS_UNKNOWN
827 SAL_INFO("xmlsecurity.xmlsec", "Verifying cert using revocation information.");
828 bChain = CertGetCertificateChain(
829 nullptr ,
830 pCertContext ,
831 nullptr , //use current system time
832 hCollectionStore,
833 &chainPara ,
834 CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
835 nullptr ,
836 &pChainContext);
837
838 if (bChain && pChainContext->cChain > 0)
839 {
840 SAL_INFO("xmlsecurity.xmlsec", "Overall error status (all chains):");
841 traceTrustStatus(pChainContext->TrustStatus.dwErrorStatus);
842 //highest quality chains come first
843 PCERT_SIMPLE_CHAIN pSimpleChain = pChainContext->rgpChain[0];
844 SAL_INFO("xmlsecurity.xmlsec", "Error status of first chain:");
845 traceTrustStatus(pSimpleChain->TrustStatus.dwErrorStatus);
846
847 //CERT_TRUST_REVOCATION_STATUS_UNKNOWN is also set if a certificate
848 //has no AIA(OCSP) or CRLDP extension and there is no CRL locally installed.
849 DWORD revocationFlags = CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
850 CERT_TRUST_IS_OFFLINE_REVOCATION;
851 DWORD otherErrorsMask = ~revocationFlags;
852 if( !(pSimpleChain->TrustStatus.dwErrorStatus & otherErrorsMask))
853
854 {
855 //No errors except maybe those caused by missing revocation information
856 //Check if there are errors
857 if ( pSimpleChain->TrustStatus.dwErrorStatus & revocationFlags)
858 {
859 //No revocation information. Because MSDN documentation is not
860 //clear about if all other tests are performed if an error occurs,
861 //we test again, without requiring revocation checking.
862 CertFreeCertificateChain(pChainContext);
863 pChainContext = nullptr;
864 SAL_INFO("xmlsecurity.xmlsec", "Checking again but without requiring revocation information.");
865 bChain = CertGetCertificateChain(
866 nullptr ,
867 pCertContext ,
868 nullptr , //use current system time
869 hCollectionStore,
870 &chainPara ,
871 0,
872 nullptr ,
873 &pChainContext);
874 if (bChain
875 && pChainContext->cChain > 0
876 && pChainContext->rgpChain[0]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
877 {
878 SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
879 validity = css::security::CertificateValidity::VALID;
880 }
881 else
882 {
883 SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
884 }
885 }
886 else
887 {
888 //valid and revocation information available
889 SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
890 validity = css::security::CertificateValidity::VALID;
891 }
892 }
893 else
894 {
895 //invalid
896 SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
897 validity = css::security::CertificateValidity::INVALID ;
898 }
899 }
900 else
901 {
902 SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChaine failed.");
903 }
904 }
905
906 if (pChainContext)
907 {
908 CertFreeCertificateChain(pChainContext);
909 pChainContext = nullptr;
910 }
911
912 //Close the additional store, do not destroy the contained certs
913 CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
914 //Close the temporary store containing the intermediate certificates and make
915 //sure all certificates are deleted.
916 CertCloseStore(hIntermediateCertsStore, CERT_CLOSE_STORE_CHECK_FLAG);
917
918 return validity ;
919 }
920
getCertificateCharacters(const css::uno::Reference<css::security::XCertificate> & aCert)921 sal_Int32 SecurityEnvironment_MSCryptImpl::getCertificateCharacters( const css::uno::Reference< css::security::XCertificate >& aCert ) {
922 sal_Int32 characters ;
923 PCCERT_CONTEXT pCertContext ;
924 const X509Certificate_MSCryptImpl* xcert ;
925
926 uno::Reference< XUnoTunnel > xCertTunnel( aCert, uno::UNO_QUERY_THROW ) ;
927 xcert = reinterpret_cast<X509Certificate_MSCryptImpl*>(xCertTunnel->getSomething( X509Certificate_MSCryptImpl::getUnoTunnelId() ));
928 if( xcert == nullptr ) {
929 throw uno::RuntimeException() ;
930 }
931
932 pCertContext = xcert->getMswcryCert() ;
933
934 characters = 0x00000000 ;
935
936 //Firstly, make sentence whether or not the cert is self-signed.
937 if( CertCompareCertificateName( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->Subject), &(pCertContext->pCertInfo->Issuer) ) ) {
938 characters |= css::security::CertificateCharacters::SELF_SIGNED ;
939 } else {
940 characters &= ~ css::security::CertificateCharacters::SELF_SIGNED ;
941 }
942
943 //Secondly, make sentence whether or not the cert has a private key.
944 {
945 BOOL fCallerFreeProv ;
946 DWORD dwKeySpec ;
947 NCRYPT_KEY_HANDLE hKey = 0;
948 DWORD dwFlags = CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
949 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hKey;
950 if( CryptAcquireCertificatePrivateKey( pCertContext ,
951 dwFlags,
952 nullptr ,
953 phCryptProvOrNCryptKey,
954 &dwKeySpec,
955 &fCallerFreeProv )
956 ) {
957 characters |= css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
958
959 if (hKey && fCallerFreeProv)
960 NCryptFreeObject(hKey);
961 } else {
962 characters &= ~ css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
963 }
964 }
965 return characters ;
966 }
967
enableDefaultCrypt(bool enable)968 void SecurityEnvironment_MSCryptImpl::enableDefaultCrypt( bool enable ) {
969 m_bEnableDefault = enable ;
970 }
971
defaultEnabled()972 bool SecurityEnvironment_MSCryptImpl::defaultEnabled() {
973 return m_bEnableDefault ;
974 }
975
MswcryCertContextToXCert(PCCERT_CONTEXT cert)976 static X509Certificate_MSCryptImpl* MswcryCertContextToXCert( PCCERT_CONTEXT cert )
977 {
978 X509Certificate_MSCryptImpl* xcert ;
979
980 if( cert != nullptr ) {
981 xcert = new X509Certificate_MSCryptImpl() ;
982 xcert->setMswcryCert( cert ) ;
983 } else {
984 xcert = nullptr ;
985 }
986
987 return xcert ;
988 }
989
getSecurityEnvironmentInformation()990 OUString SecurityEnvironment_MSCryptImpl::getSecurityEnvironmentInformation()
991 {
992 return "Microsoft Crypto API";
993 }
994
createKeysManager()995 xmlSecKeysMngrPtr SecurityEnvironment_MSCryptImpl::createKeysManager() {
996
997 xmlSecKeysMngrPtr pKeysMngr = nullptr ;
998
999 /*-
1000 * The following lines is based on the of xmlsec-mscrypto crypto engine
1001 */
1002 pKeysMngr = xmlsecurity::MSCryptoAppliedKeysMngrCreate() ;
1003 if( pKeysMngr == nullptr )
1004 throw uno::RuntimeException() ;
1005
1006 /*-
1007 * Adopt system default certificate store.
1008 */
1009 if( defaultEnabled() ) {
1010 //Add system key store into the keys manager.
1011 m_hMySystemStore = CertOpenSystemStoreW( 0, L"MY" ) ;
1012 if( m_hMySystemStore != nullptr ) {
1013 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptKeyStore( pKeysMngr, m_hMySystemStore ) < 0 ) {
1014 CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1015 m_hMySystemStore = nullptr;
1016 throw uno::RuntimeException() ;
1017 }
1018 m_hMySystemStore = nullptr;
1019 }
1020
1021 //Add system root store into the keys manager.
1022 m_hRootSystemStore = CertOpenSystemStoreW( 0, L"Root" ) ;
1023 if( m_hRootSystemStore != nullptr ) {
1024 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptTrustedStore( pKeysMngr, m_hRootSystemStore ) < 0 ) {
1025 CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1026 m_hRootSystemStore = nullptr;
1027 throw uno::RuntimeException() ;
1028 }
1029 m_hRootSystemStore = nullptr;
1030 }
1031
1032 //Add system trusted store into the keys manager.
1033 m_hTrustSystemStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
1034 if( m_hTrustSystemStore != nullptr ) {
1035 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hTrustSystemStore ) < 0 ) {
1036 CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1037 m_hTrustSystemStore = nullptr;
1038 throw uno::RuntimeException() ;
1039 }
1040 m_hTrustSystemStore = nullptr;
1041 }
1042
1043 //Add system CA store into the keys manager.
1044 m_hCaSystemStore = CertOpenSystemStoreW( 0, L"CA" ) ;
1045 if( m_hCaSystemStore != nullptr ) {
1046 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hCaSystemStore ) < 0 ) {
1047 CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1048 m_hCaSystemStore = nullptr;
1049 throw uno::RuntimeException() ;
1050 }
1051 m_hCaSystemStore = nullptr;
1052 }
1053 }
1054
1055 return pKeysMngr ;
1056 }
destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr)1057 void SecurityEnvironment_MSCryptImpl::destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) {
1058 if( pKeysMngr != nullptr ) {
1059 xmlSecKeysMngrDestroy( pKeysMngr ) ;
1060 }
1061 }
1062
1063 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_xml_crypto_SecurityEnvironment_get_implementation(uno::XComponentContext * pCtx,uno::Sequence<uno::Any> const &)1064 com_sun_star_xml_crypto_SecurityEnvironment_get_implementation(
1065 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
1066 {
1067 return cppu::acquire(new SecurityEnvironment_MSCryptImpl(pCtx));
1068 }
1069
1070 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1071