tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / xmlsecurity / source / xmlsec / mscrypt / securityenvironment_mscryptimpl.cxx
bloba6258f92ad764f546080c2760a780e83485fb167
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <cstddef>
23 #include <string.h>
25 #include <xmlsec/base64.h>
27 #if !defined WIN32_LEAN_AND_MEAN
28 # define WIN32_LEAN_AND_MEAN
29 #endif
30 #include <Windows.h>
31 #include <WinCrypt.h>
32 #include <sal/macros.h>
33 #include <osl/thread.h>
34 #include "securityenvironment_mscryptimpl.hxx"
36 #include "x509certificate_mscryptimpl.hxx"
37 #include <comphelper/servicehelper.hxx>
38 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
39 #include <com/sun/star/uno/XComponentContext.hpp>
40 #include <cppuhelper/supportsservice.hxx>
41 #include "akmngr.hxx"
43 #include <biginteger.hxx>
45 #include <comphelper/sequence.hxx>
46 #include <comphelper/windowserrorstring.hxx>
47 #include <sal/log.hxx>
48 #include <rtl/locale.h>
49 #include <rtl/ref.hxx>
50 #include <osl/nlsupport.h>
51 #include <osl/process.h>
52 #include <o3tl/char16_t2wchar_t.hxx>
53 #include <svl/cryptosign.hxx>
55 using namespace ::com::sun::star;
56 using namespace ::com::sun::star::lang ;
58 using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
59 using ::com::sun::star::security::XCertificate ;
61 static rtl::Reference<X509Certificate_MSCryptImpl> MswcryCertContextToXCert( PCCERT_CONTEXT cert ) ;
63 namespace {
65 struct CertErrorToString{
66 DWORD error;
67 char const * name;
72 CertErrorToString const arErrStrings[] =
74 { 0x00000000, "CERT_TRUST_NO_ERROR"},
75 { 0x00000001, "CERT_TRUST_IS_NOT_TIME_VALID"},
76 { 0x00000002, "CERT_TRUST_IS_NOT_TIME_NESTED"},
77 { 0x00000004, "CERT_TRUST_IS_REVOKED" },
78 { 0x00000008, "CERT_TRUST_IS_NOT_SIGNATURE_VALID" },
79 { 0x00000010, "CERT_TRUST_IS_NOT_SIGNATURE_VALID"},
80 { 0x00000020, "CERT_TRUST_IS_UNTRUSTED_ROOT"},
81 { 0x00000040, "CERT_TRUST_REVOCATION_STATUS_UNKNOWN"},
82 { 0x00000080, "CERT_TRUST_IS_CYCLIC"},
83 { 0x00000100, "CERT_TRUST_INVALID_EXTENSION"},
84 { 0x00000200, "CERT_TRUST_INVALID_POLICY_CONSTRAINTS"},
85 { 0x00000400, "CERT_TRUST_INVALID_BASIC_CONSTRAINTS"},
86 { 0x00000800, "CERT_TRUST_INVALID_NAME_CONSTRAINTS"},
87 { 0x00001000, "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT"},
88 { 0x00002000, "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT"},
89 { 0x00004000, "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT"},
90 { 0x00008000, "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT"},
91 { 0x01000000, "CERT_TRUST_IS_OFFLINE_REVOCATION"},
92 { 0x02000000, "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY"},
93 { 0x04000000, "CERT_TRUST_IS_EXPLICIT_DISTRUST"},
94 { 0x08000000, "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT"},
95 //Chain errors
96 { 0x00010000, "CERT_TRUST_IS_PARTIAL_CHAIN"},
97 { 0x00020000, "CERT_TRUST_CTL_IS_NOT_TIME_VALID"},
98 { 0x00040000, "CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID"},
99 { 0x00080000, "CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE"}
102 static void traceTrustStatus(DWORD err)
104 if (err == 0)
105 SAL_INFO("xmlsecurity.xmlsec", " " << arErrStrings[0].name);
106 for (auto const & arErrStringIter : arErrStrings)
108 if (arErrStringIter.error & err)
109 SAL_INFO("xmlsecurity.xmlsec", " " << arErrStringIter.name);
113 SecurityEnvironment_MSCryptImpl::SecurityEnvironment_MSCryptImpl( const uno::Reference< uno::XComponentContext >& xContext ) : m_hProv( 0 ) , m_pszContainer( nullptr ) , m_hKeyStore( nullptr ), m_hCertStore( nullptr ), m_hMySystemStore(nullptr), m_hRootSystemStore(nullptr), m_hTrustSystemStore(nullptr), m_hCaSystemStore(nullptr), m_bEnableDefault( false ){
115 m_xServiceManager.set(xContext, uno::UNO_QUERY);
118 SecurityEnvironment_MSCryptImpl::~SecurityEnvironment_MSCryptImpl() {
120 if( m_hProv != 0 ) {
121 CryptReleaseContext( m_hProv, 0 ) ;
122 m_hProv = 0 ;
125 if( m_pszContainer != nullptr ) {
126 //TODO: Don't know whether or not it should be released now.
127 m_pszContainer = nullptr ;
130 if( m_hCertStore != nullptr ) {
131 CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
132 m_hCertStore = nullptr ;
135 if( m_hKeyStore != nullptr ) {
136 CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
137 m_hKeyStore = nullptr ;
140 //i120675, close the store handles
141 if( m_hMySystemStore != nullptr ) {
142 CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
143 m_hMySystemStore = nullptr ;
146 if( m_hRootSystemStore != nullptr ) {
147 CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
148 m_hRootSystemStore = nullptr ;
151 if( m_hTrustSystemStore != nullptr ) {
152 CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
153 m_hTrustSystemStore = nullptr ;
156 if( m_hCaSystemStore != nullptr ) {
157 CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
158 m_hCaSystemStore = nullptr ;
162 /* XServiceInfo */
163 OUString SAL_CALL SecurityEnvironment_MSCryptImpl::getImplementationName() {
164 return "com.sun.star.xml.crypto.SecurityEnvironment";
167 /* XServiceInfo */
168 sal_Bool SAL_CALL SecurityEnvironment_MSCryptImpl::supportsService( const OUString& serviceName) {
169 return cppu::supportsService(this, serviceName);
171 /* XServiceInfo */
172 uno::Sequence< OUString > SAL_CALL SecurityEnvironment_MSCryptImpl::getSupportedServiceNames() {
173 return { "com.sun.star.xml.crypto.SecurityEnvironment" };
176 HCRYPTPROV SecurityEnvironment_MSCryptImpl::getCryptoProvider() {
177 return m_hProv ;
180 void SecurityEnvironment_MSCryptImpl::setCryptoProvider( HCRYPTPROV aProv ) {
181 if( m_hProv != 0 ) {
182 CryptReleaseContext( m_hProv, 0 ) ;
183 m_hProv = 0 ;
186 if( aProv != 0 ) {
187 m_hProv = aProv ;
191 LPCTSTR SecurityEnvironment_MSCryptImpl::getKeyContainer() {
192 return m_pszContainer ;
195 void SecurityEnvironment_MSCryptImpl::setKeyContainer( LPCTSTR aKeyContainer ) {
196 //TODO: Don't know whether or not it should be copied.
197 m_pszContainer = aKeyContainer ;
201 HCERTSTORE SecurityEnvironment_MSCryptImpl::getCryptoSlot() {
202 return m_hKeyStore ;
205 void SecurityEnvironment_MSCryptImpl::setCryptoSlot( HCERTSTORE aSlot) {
206 if( m_hKeyStore != nullptr ) {
207 CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
208 m_hKeyStore = nullptr ;
211 if( aSlot != nullptr ) {
212 m_hKeyStore = CertDuplicateStore( aSlot ) ;
216 HCERTSTORE SecurityEnvironment_MSCryptImpl::getCertDb() {
217 return m_hCertStore ;
220 void SecurityEnvironment_MSCryptImpl::setCertDb( HCERTSTORE aCertDb ) {
221 if( m_hCertStore != nullptr ) {
222 CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
223 m_hCertStore = nullptr ;
226 if( aCertDb != nullptr ) {
227 m_hCertStore = CertDuplicateStore( aCertDb ) ;
231 #ifdef SAL_LOG_INFO
233 // Based on sample code from MSDN
235 static OUString get_system_name(const void *pvSystemStore,
236 DWORD dwFlags)
238 LPCWSTR ppwszSystemName;
239 if (dwFlags & CERT_SYSTEM_STORE_RELOCATE_FLAG)
241 _CERT_SYSTEM_STORE_RELOCATE_PARA const * pRelocatePara;
242 pRelocatePara = static_cast<_CERT_SYSTEM_STORE_RELOCATE_PARA const *>(pvSystemStore);
243 ppwszSystemName = pRelocatePara->pwszSystemStore;
245 else
247 ppwszSystemName = static_cast<LPCWSTR>(pvSystemStore);
249 return OUString(o3tl::toU(ppwszSystemName));
252 extern "C" {
254 static BOOL WINAPI cert_enum_physical_store_callback(const void *,
255 DWORD dwFlags,
256 LPCWSTR pwszStoreName,
257 PCERT_PHYSICAL_STORE_INFO,
258 void *,
259 void *)
261 OUString name(o3tl::toU(pwszStoreName));
262 if (dwFlags & CERT_PHYSICAL_STORE_PREDEFINED_ENUM_FLAG)
263 name += " (implicitly created)";
264 SAL_INFO("xmlsecurity.xmlsec", " Physical store: " << name);
266 return TRUE;
269 static BOOL WINAPI cert_enum_system_store_callback(const void *pvSystemStore,
270 DWORD dwFlags,
271 PCERT_SYSTEM_STORE_INFO,
272 void *,
273 void *)
275 SAL_INFO("xmlsecurity.xmlsec", "System store: " << get_system_name(pvSystemStore, dwFlags));
277 if (!CertEnumPhysicalStore(pvSystemStore,
278 dwFlags,
279 nullptr,
280 cert_enum_physical_store_callback))
282 DWORD dwErr = GetLastError();
283 if (!(ERROR_FILE_NOT_FOUND == dwErr ||
284 ERROR_NOT_SUPPORTED == dwErr))
286 SAL_WARN("xmlsecurity.xmlsec", "CertEnumPhysicalStore failed:" << WindowsErrorString(GetLastError()));
289 return TRUE;
294 #endif
296 //Methods from XSecurityEnvironment
297 uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::getPersonalCertificates()
299 sal_Int32 length ;
300 rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
301 std::vector< rtl::Reference<X509Certificate_MSCryptImpl> > certsList ;
302 PCCERT_CONTEXT pCertContext = nullptr;
304 //firstly, we try to find private keys in given key store.
305 if( m_hKeyStore != nullptr ) {
306 pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
307 while (pCertContext)
309 xcert = MswcryCertContextToXCert( pCertContext ) ;
310 if( xcert.is() )
311 certsList.push_back( xcert ) ;
312 pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
316 //Thirdly, we try to find certificate from system default key store.
317 if( m_bEnableDefault ) {
318 HCERTSTORE hSystemKeyStore ;
319 DWORD dwKeySpec;
320 NCRYPT_KEY_HANDLE hCryptKey;
322 #ifdef SAL_LOG_INFO
323 CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, nullptr, nullptr, cert_enum_system_store_callback);
324 #endif
326 hSystemKeyStore = CertOpenSystemStoreW( 0, L"MY" ) ;
327 if( hSystemKeyStore != nullptr ) {
328 pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
329 while (pCertContext)
331 // for checking whether the certificate is a personal certificate or not.
332 DWORD dwFlags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
333 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hCryptKey;
334 if(!(CryptAcquireCertificatePrivateKey(pCertContext,
335 dwFlags,
336 nullptr,
337 phCryptProvOrNCryptKey,
338 &dwKeySpec,
339 nullptr)))
341 // Not Privatekey found. SKIP this one.
342 pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
343 continue;
345 // then TODO : Check the personal cert is valid or not.
347 xcert = MswcryCertContextToXCert( pCertContext ) ;
348 if( xcert.is() )
349 certsList.push_back( xcert ) ;
350 pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
354 CertCloseStore( hSystemKeyStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
357 length = certsList.size() ;
358 if( length != 0 ) {
359 int i = 0;
360 uno::Sequence< uno::Reference< XCertificate > > certSeq( length ) ;
361 auto pcertSeq = certSeq.getArray();
363 for( const auto& rXCert : certsList ) {
364 pcertSeq[i] = rXCert ;
365 ++i;
368 return certSeq ;
371 return uno::Sequence< uno::Reference< XCertificate > >() ;
375 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const uno::Sequence< sal_Int8 >& serialNumber ) {
376 unsigned int i ;
377 rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
378 PCCERT_CONTEXT pCertContext = nullptr ;
379 HCERTSTORE hCertStore = nullptr ;
380 CRYPT_INTEGER_BLOB cryptSerialNumber ;
381 CERT_INFO certInfo ;
383 // for correct encoding
384 rtl_Locale *pLocale = nullptr ;
385 osl_getProcessLocale( &pLocale ) ;
387 //Create cert info from issue and serial
388 LPCWSTR pszName = o3tl::toW( issuerName.getStr() );
390 if( ! ( CertStrToNameW(
391 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
392 pszName ,
393 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
394 nullptr ,
395 nullptr ,
396 &certInfo.Issuer.cbData, nullptr ) )
398 return nullptr ;
401 certInfo.Issuer.pbData = static_cast<BYTE*>(malloc( certInfo.Issuer.cbData ));
402 if(!certInfo.Issuer.pbData)
403 throw uno::RuntimeException() ;
405 if( ! ( CertStrToNameW(
406 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
407 pszName ,
408 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
409 nullptr ,
410 certInfo.Issuer.pbData ,
411 &certInfo.Issuer.cbData, nullptr ) )
413 free( certInfo.Issuer.pbData ) ;
414 return nullptr ;
417 //Get the SerialNumber
418 cryptSerialNumber.cbData = serialNumber.getLength() ;
419 cryptSerialNumber.pbData = static_cast<BYTE*>(malloc( cryptSerialNumber.cbData));
420 if (!cryptSerialNumber.pbData)
422 free( certInfo.Issuer.pbData ) ;
423 throw uno::RuntimeException() ;
425 for( i = 0; i < cryptSerialNumber.cbData; i ++ )
426 cryptSerialNumber.pbData[i] = serialNumber[ cryptSerialNumber.cbData - i - 1 ] ;
428 certInfo.SerialNumber.cbData = cryptSerialNumber.cbData ;
429 certInfo.SerialNumber.pbData = cryptSerialNumber.pbData ;
431 // Get the Cert from all store.
432 for( i = 0 ; i < 6 ; i ++ )
434 switch(i)
436 case 0:
437 if(m_hKeyStore == nullptr) continue ;
438 hCertStore = m_hKeyStore ;
439 break;
440 case 1:
441 if(m_hCertStore == nullptr) continue ;
442 hCertStore = m_hCertStore ;
443 break;
444 case 2:
445 hCertStore = CertOpenSystemStoreW( 0, L"MY" ) ;
446 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
447 break;
448 case 3:
449 hCertStore = CertOpenSystemStoreW( 0, L"Root" ) ;
450 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
451 break;
452 case 4:
453 hCertStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
454 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
455 break;
456 case 5:
457 hCertStore = CertOpenSystemStoreW( 0, L"CA" ) ;
458 if(hCertStore == nullptr || !m_bEnableDefault) continue ;
459 break;
460 default:
461 i=6;
462 continue;
465 /*******************************************************************************
466 * This code reserved for remind us there are another way to find one cert by
467 * IssuerName&serialnumber. You can use the code to replaced the function
468 * CertFindCertificateInStore IF and ONLY IF you must find one special cert in
469 * certStore but can not be found by CertFindCertificateInStore , then , you
470 * should also change the same part in libxmlsec/.../src/mscrypto/x509vfy.c#875.
471 * By Chandler Peng(chandler.peng@sun.com)
472 *****/
473 /*******************************************************************************
474 pCertContext = NULL ;
475 found = 0;
477 // 1. enum the certs has same string in the issuer string.
478 pCertContext = CertEnumCertificatesInStore( hCertStore , pCertContext ) ;
479 if( pCertContext != NULL )
481 // 2. check the cert's issuer name .
482 char* issuer = NULL ;
483 DWORD cbIssuer = 0 ;
485 cbIssuer = CertNameToStr(
486 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
487 &( pCertContext->pCertInfo->Issuer ),
488 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
489 NULL, 0
492 if( cbIssuer == 0 ) continue ; // discard this cert;
494 issuer = (char *)malloc( cbIssuer ) ;
495 if( issuer == NULL ) // discard this cert;
497 free( cryptSerialNumber.pbData) ;
498 free( certInfo.Issuer.pbData ) ;
499 CertFreeCertificateContext( pCertContext ) ;
500 if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
501 throw RuntimeException() ;
504 cbIssuer = CertNameToStr(
505 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
506 &( pCertContext->pCertInfo->Issuer ),
507 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
508 issuer, cbIssuer
511 if( cbIssuer <= 0 )
513 free( issuer ) ;
514 continue ;// discard this cert;
517 if(strncmp(pszName , issuer , cbIssuer) != 0)
519 free( issuer ) ;
520 continue ;// discard this cert;
522 free( issuer ) ;
524 // 3. check the serial number.
525 if( memcmp( cryptSerialNumber.pbData , pCertContext->pCertInfo->SerialNumber.pbData , cryptSerialNumber.cbData ) != 0 )
527 continue ;// discard this cert;
530 // 4. confirm and break;
531 found = 1;
532 break ;
535 }while(pCertContext);
537 if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
538 if( found != 0 ) break; // Found the certificate.
539 ********************************************************************************/
541 pCertContext = CertFindCertificateInStore(
542 hCertStore,
543 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
545 CERT_FIND_SUBJECT_CERT,
546 &certInfo,
547 nullptr
550 if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
551 if( pCertContext != nullptr ) break ; // Found the certificate.
555 free(cryptSerialNumber.pbData);
556 free(certInfo.Issuer.pbData);
558 if( pCertContext != nullptr ) {
559 xcert = MswcryCertContextToXCert(pCertContext);
560 CertFreeCertificateContext(pCertContext);
563 return xcert ;
566 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const OUString& serialNumber ) {
567 uno::Sequence< sal_Int8 > serial = xmlsecurity::numericStringToBigInteger( serialNumber ) ;
568 return getCertificate( issuerName, serial ) ;
571 uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::buildCertificatePath( const uno::Reference< XCertificate >& begin ) {
572 PCCERT_CHAIN_CONTEXT pChainContext ;
573 PCCERT_CONTEXT pCertContext ;
575 CERT_ENHKEY_USAGE enhKeyUsage ;
576 CERT_USAGE_MATCH certUsage ;
577 CERT_CHAIN_PARA chainPara ;
579 enhKeyUsage.cUsageIdentifier = 0 ;
580 enhKeyUsage.rgpszUsageIdentifier = nullptr ;
581 certUsage.dwType = USAGE_MATCH_TYPE_AND ;
582 certUsage.Usage = enhKeyUsage ;
583 chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
584 chainPara.RequestedUsage = certUsage ;
586 const auto* xcert = dynamic_cast<X509Certificate_MSCryptImpl*>(begin.get());
587 if( xcert == nullptr ) {
588 throw uno::RuntimeException() ;
591 pCertContext = xcert->getMswcryCert() ;
593 pChainContext = nullptr ;
595 bool bChain = false;
596 if( pCertContext != nullptr )
598 HCERTSTORE hAdditionalStore = nullptr;
599 HCERTSTORE hCollectionStore = nullptr;
600 if (m_hCertStore && m_hKeyStore)
602 //Merge m_hCertStore and m_hKeyStore into one store.
603 hCollectionStore = CertOpenStore(
604 CERT_STORE_PROV_COLLECTION ,
608 nullptr
610 if (hCollectionStore != nullptr)
612 CertAddStoreToCollection (
613 hCollectionStore ,
614 m_hCertStore ,
615 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
616 0) ;
617 CertAddStoreToCollection (
618 hCollectionStore ,
619 m_hKeyStore,
620 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
621 0) ;
622 hAdditionalStore = hCollectionStore;
627 //if the merge of both stores failed then we add only m_hCertStore
628 if (hAdditionalStore == nullptr && m_hCertStore)
629 hAdditionalStore = m_hCertStore;
630 else if (hAdditionalStore == nullptr && m_hKeyStore)
631 hAdditionalStore = m_hKeyStore;
632 else
633 hAdditionalStore = nullptr;
635 //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
636 bChain = CertGetCertificateChain(
637 nullptr ,
638 pCertContext ,
639 nullptr , //use current system time
640 hAdditionalStore,
641 &chainPara ,
642 CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_TIMESTAMP_TIME ,
643 nullptr ,
644 &pChainContext);
645 if (!bChain)
646 pChainContext = nullptr;
648 //Close the additional store
649 CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
652 if(bChain && pChainContext != nullptr && pChainContext->cChain > 0 )
654 PCCERT_CONTEXT pCertInChain ;
655 PCERT_SIMPLE_CHAIN pCertChain ;
656 rtl::Reference<X509Certificate_MSCryptImpl> pCert ;
658 pCertChain = pChainContext->rgpChain[0] ;
659 if( pCertChain->cElement ) {
660 uno::Sequence< uno::Reference< XCertificate > > xCertChain( pCertChain->cElement ) ;
661 auto pxCertChain = xCertChain.getArray();
663 for( unsigned int i = 0 ; i < pCertChain->cElement ; i ++ ) {
664 if( pCertChain->rgpElement[i] )
665 pCertInChain = pCertChain->rgpElement[i]->pCertContext ;
666 else
667 pCertInChain = nullptr ;
669 if( pCertInChain != nullptr ) {
670 pCert = MswcryCertContextToXCert( pCertInChain ) ;
671 if( pCert.is() )
672 pxCertChain[i] = pCert ;
676 CertFreeCertificateChain( pChainContext ) ;
677 pChainContext = nullptr ;
679 return xCertChain ;
682 if (pChainContext)
683 CertFreeCertificateChain(pChainContext);
685 return uno::Sequence< uno::Reference < XCertificate > >();
688 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromRaw( const uno::Sequence< sal_Int8 >& rawCertificate ) {
689 rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
691 if( rawCertificate.getLength() > 0 ) {
692 xcert = new X509Certificate_MSCryptImpl() ;
693 xcert->setRawCert( rawCertificate ) ;
696 return xcert ;
699 uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromAscii( const OUString& asciiCertificate )
701 OString oscert = OUStringToOString( asciiCertificate , RTL_TEXTENCODING_ASCII_US ) ;
702 xmlChar* chCert = xmlStrndup( reinterpret_cast<const xmlChar*>(oscert.getStr()), static_cast<int>(oscert.getLength()) ) ;
703 xmlSecSize certSize;
704 int nRet = xmlSecBase64Decode_ex( chCert, reinterpret_cast<xmlSecByte*>(chCert), xmlStrlen( chCert ), &certSize ) ;
705 if (nRet < 0 || certSize == 0)
707 xmlFree(chCert);
708 return nullptr;
711 uno::Sequence<sal_Int8> rawCert(comphelper::arrayToSequence<sal_Int8>(chCert, certSize));
713 xmlFree( chCert ) ;
715 return createCertificateFromRaw( rawCert ) ;
718 static HCERTSTORE getCertStoreForIntermediatCerts(
719 const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
721 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, nullptr);
722 if (store == nullptr)
723 return nullptr;
725 for (const auto& i : seqCerts)
727 SAL_INFO("xmlsecurity.xmlsec", "Added temporary certificate: " << i->getSubjectName());
729 uno::Sequence<sal_Int8> data = i->getEncoded();
730 PCCERT_CONTEXT cert = CertCreateCertificateContext(
731 X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(&data[0]), data.getLength());
732 //Adding the certificate creates a copy and not just increases the ref count
733 //Therefore we free later the certificate that we now add
734 CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_ALWAYS, nullptr);
735 CertFreeCertificateContext(cert);
737 return store;
740 static bool CheckUnitTestStore(PCCERT_CHAIN_CONTEXT const pChainContext, DWORD ignoreFlags)
742 bool ret = false;
743 static char const*const pVar = getenv("LIBO_TEST_CRYPTOAPI_PKCS7");
744 if (!pVar)
746 return ret;
748 if (pChainContext->cChain == 0)
750 return ret;
752 PCERT_SIMPLE_CHAIN pSimpleChain = pChainContext->rgpChain[0];
753 // check if untrusted root is the only problem
754 if (pSimpleChain->TrustStatus.dwErrorStatus & ~(CERT_TRUST_IS_UNTRUSTED_ROOT | ignoreFlags))
756 return ret;
759 // leak this store, re-opening is a waste of time in tests
760 static HCERTSTORE const hExtra = CertOpenStore(
761 CERT_STORE_PROV_FILENAME_A,
762 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
764 CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG,
765 OString(OString::Concat(pVar) + "/test.p7b").getStr());
766 assert(hExtra != nullptr);
767 if (pSimpleChain->cElement < 1)
769 SAL_WARN("xmlsecurity.xmlsec", "unexpected empty chain");
770 return ret;
772 PCCERT_CONTEXT const pRoot(pSimpleChain->rgpElement[pSimpleChain->cElement-1]->pCertContext);
773 PCCERT_CONTEXT const pIssuerCert = CertFindCertificateInStore(
774 hExtra,
775 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
777 CERT_FIND_SUBJECT_NAME,
778 &pRoot->pCertInfo->Subject,
779 nullptr);
780 if (pIssuerCert)
782 // check that it signed itself
783 DWORD flags = CERT_STORE_SIGNATURE_FLAG;
784 bool result = CertVerifySubjectCertificateContext(pRoot, pIssuerCert, &flags);
785 if (result && flags == 0)
787 ret = true;
790 CertFreeCertificateContext(pIssuerCert);
791 return ret;
794 //We return only valid or invalid, as long as the API documentation expresses
795 //explicitly that all validation steps are carried out even if one or several
796 //errors occur. See also
797 //http://wiki.openoffice.org/wiki/Certificate_Path_Validation#Validation_status
798 sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate(
799 const uno::Reference< css::security::XCertificate >& aCert,
800 const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
802 sal_Int32 validity = css::security::CertificateValidity::INVALID;
803 PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
804 PCCERT_CONTEXT pCertContext = nullptr;
806 SAL_INFO("xmlsecurity.xmlsec", "Start verification of certificate: " << aCert->getSubjectName());
808 const auto* xcert = dynamic_cast<X509Certificate_MSCryptImpl*>(aCert.get());
809 if( xcert == nullptr ) {
810 throw uno::RuntimeException() ;
813 pCertContext = xcert->getMswcryCert() ;
815 CERT_ENHKEY_USAGE enhKeyUsage ;
816 CERT_USAGE_MATCH certUsage ;
817 CERT_CHAIN_PARA chainPara = {};
819 //Prepare parameter for CertGetCertificateChain
820 enhKeyUsage.cUsageIdentifier = 0 ;
821 enhKeyUsage.rgpszUsageIdentifier = nullptr ;
822 certUsage.dwType = USAGE_MATCH_TYPE_AND ;
823 certUsage.Usage = enhKeyUsage ;
824 chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
825 chainPara.RequestedUsage = certUsage ;
828 HCERTSTORE hCollectionStore = nullptr;
829 HCERTSTORE hIntermediateCertsStore = nullptr;
830 bool bChain = false;
831 if( pCertContext != nullptr )
833 hIntermediateCertsStore =
834 getCertStoreForIntermediatCerts(seqCerts);
836 //Merge m_hCertStore and m_hKeyStore and the store of the intermediate
837 //certificates into one store.
838 hCollectionStore = CertOpenStore(
839 CERT_STORE_PROV_COLLECTION ,
843 nullptr
845 if (hCollectionStore != nullptr)
847 CertAddStoreToCollection (
848 hCollectionStore ,
849 m_hCertStore ,
850 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
851 0) ;
852 CertAddStoreToCollection (
853 hCollectionStore ,
854 m_hKeyStore,
855 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
856 0) ;
857 CertAddStoreToCollection (
858 hCollectionStore,
859 hIntermediateCertsStore,
860 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG,
865 //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
866 //We do not check revocation of the root. In most cases there are none.
867 //Then we would get CERT_TRUST_REVOCATION_STATUS_UNKNOWN
868 SAL_INFO("xmlsecurity.xmlsec", "Verifying cert using revocation information.");
869 bChain = CertGetCertificateChain(
870 nullptr ,
871 pCertContext ,
872 nullptr , //use current system time
873 hCollectionStore,
874 &chainPara ,
875 CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
876 nullptr ,
877 &pChainContext);
879 if (bChain && pChainContext->cChain > 0)
881 SAL_INFO("xmlsecurity.xmlsec", "Overall error status (all chains):");
882 traceTrustStatus(pChainContext->TrustStatus.dwErrorStatus);
883 //highest quality chains come first
884 PCERT_SIMPLE_CHAIN pSimpleChain = pChainContext->rgpChain[0];
885 SAL_INFO("xmlsecurity.xmlsec", "Error status of first chain:");
886 traceTrustStatus(pSimpleChain->TrustStatus.dwErrorStatus);
888 //CERT_TRUST_REVOCATION_STATUS_UNKNOWN is also set if a certificate
889 //has no AIA(OCSP) or CRLDP extension and there is no CRL locally installed.
890 DWORD revocationFlags = CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
891 CERT_TRUST_IS_OFFLINE_REVOCATION;
892 DWORD otherErrorsMask = ~revocationFlags;
893 if (!(pSimpleChain->TrustStatus.dwErrorStatus & otherErrorsMask)
894 || CheckUnitTestStore(pChainContext, revocationFlags))
897 //No errors except maybe those caused by missing revocation information
898 //Check if there are errors
899 if ( pSimpleChain->TrustStatus.dwErrorStatus & revocationFlags)
901 //No revocation information. Because MSDN documentation is not
902 //clear about if all other tests are performed if an error occurs,
903 //we test again, without requiring revocation checking.
904 CertFreeCertificateChain(pChainContext);
905 pChainContext = nullptr;
906 SAL_INFO("xmlsecurity.xmlsec", "Checking again but without requiring revocation information.");
907 bChain = CertGetCertificateChain(
908 nullptr ,
909 pCertContext ,
910 nullptr , //use current system time
911 hCollectionStore,
912 &chainPara ,
914 nullptr ,
915 &pChainContext);
916 if (bChain
917 && pChainContext->cChain > 0
918 && pChainContext->rgpChain[0]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
920 SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
921 validity = css::security::CertificateValidity::VALID;
923 else if (CheckUnitTestStore(pChainContext, 0))
925 SAL_INFO("xmlsecurity.xmlsec", "root certificate found in extra test store");
926 validity = css::security::CertificateValidity::VALID;
928 else
930 SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
933 else
935 //valid and revocation information available
936 SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
937 validity = css::security::CertificateValidity::VALID;
940 else
942 //invalid
943 SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
944 validity = css::security::CertificateValidity::INVALID ;
947 else
949 SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChain failed.");
953 if (pChainContext)
955 CertFreeCertificateChain(pChainContext);
956 pChainContext = nullptr;
959 //Close the additional store, do not destroy the contained certs
960 CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
961 //Close the temporary store containing the intermediate certificates and make
962 //sure all certificates are deleted.
963 CertCloseStore(hIntermediateCertsStore, CERT_CLOSE_STORE_CHECK_FLAG);
965 return validity ;
968 sal_Int32 SecurityEnvironment_MSCryptImpl::getCertificateCharacters( const css::uno::Reference< css::security::XCertificate >& aCert ) {
969 sal_Int32 characters ;
970 PCCERT_CONTEXT pCertContext ;
972 const auto* xcert = dynamic_cast<X509Certificate_MSCryptImpl*>(aCert.get());
973 if( xcert == nullptr ) {
974 throw uno::RuntimeException() ;
977 pCertContext = xcert->getMswcryCert() ;
979 characters = 0x00000000 ;
981 //Firstly, make sentence whether or not the cert is self-signed.
982 if( CertCompareCertificateName( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->Subject), &(pCertContext->pCertInfo->Issuer) ) ) {
983 characters |= css::security::CertificateCharacters::SELF_SIGNED ;
984 } else {
985 characters &= ~ css::security::CertificateCharacters::SELF_SIGNED ;
988 //Secondly, make sentence whether or not the cert has a private key.
990 BOOL fCallerFreeProv ;
991 DWORD dwKeySpec ;
992 NCRYPT_KEY_HANDLE hKey = 0;
993 DWORD dwFlags = CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
994 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hKey;
995 if( CryptAcquireCertificatePrivateKey( pCertContext ,
996 dwFlags,
997 nullptr ,
998 phCryptProvOrNCryptKey,
999 &dwKeySpec,
1000 &fCallerFreeProv )
1002 characters |= css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
1004 if (hKey && fCallerFreeProv)
1005 NCryptFreeObject(hKey);
1006 } else {
1007 characters &= ~ css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
1010 return characters ;
1013 void SecurityEnvironment_MSCryptImpl::enableDefaultCrypt( bool enable ) {
1014 m_bEnableDefault = enable ;
1017 bool SecurityEnvironment_MSCryptImpl::defaultEnabled() {
1018 return m_bEnableDefault ;
1021 static rtl::Reference<X509Certificate_MSCryptImpl> MswcryCertContextToXCert( PCCERT_CONTEXT cert )
1023 rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
1025 if( cert != nullptr ) {
1026 xcert = new X509Certificate_MSCryptImpl() ;
1027 xcert->setMswcryCert( cert ) ;
1030 return xcert ;
1033 OUString SecurityEnvironment_MSCryptImpl::getSecurityEnvironmentInformation()
1035 return "Microsoft Crypto API";
1038 xmlSecKeysMngrPtr SecurityEnvironment_MSCryptImpl::createKeysManager() {
1041 * The following lines is based on the of xmlsec-mscrypto crypto engine
1043 xmlSecKeysMngrPtr pKeysMngr = xmlsecurity::MSCryptoAppliedKeysMngrCreate() ;
1044 if( pKeysMngr == nullptr )
1045 throw uno::RuntimeException() ;
1048 * Adopt system default certificate store.
1050 if( defaultEnabled() ) {
1051 //Add system key store into the keys manager.
1052 m_hMySystemStore = CertOpenSystemStoreW( 0, L"MY" ) ;
1053 if( m_hMySystemStore != nullptr ) {
1054 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptKeyStore( pKeysMngr, m_hMySystemStore ) < 0 ) {
1055 CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1056 m_hMySystemStore = nullptr;
1057 throw uno::RuntimeException() ;
1059 m_hMySystemStore = nullptr;
1062 //Add system root store into the keys manager.
1063 m_hRootSystemStore = CertOpenSystemStoreW( 0, L"Root" ) ;
1064 if( m_hRootSystemStore != nullptr ) {
1065 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptTrustedStore( pKeysMngr, m_hRootSystemStore ) < 0 ) {
1066 CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1067 m_hRootSystemStore = nullptr;
1068 throw uno::RuntimeException() ;
1070 m_hRootSystemStore = nullptr;
1073 //Add system trusted store into the keys manager.
1074 m_hTrustSystemStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
1075 if( m_hTrustSystemStore != nullptr ) {
1076 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hTrustSystemStore ) < 0 ) {
1077 CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1078 m_hTrustSystemStore = nullptr;
1079 throw uno::RuntimeException() ;
1081 m_hTrustSystemStore = nullptr;
1084 //Add system CA store into the keys manager.
1085 m_hCaSystemStore = CertOpenSystemStoreW( 0, L"CA" ) ;
1086 if( m_hCaSystemStore != nullptr ) {
1087 if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hCaSystemStore ) < 0 ) {
1088 CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
1089 m_hCaSystemStore = nullptr;
1090 throw uno::RuntimeException() ;
1092 m_hCaSystemStore = nullptr;
1096 return pKeysMngr ;
1098 void SecurityEnvironment_MSCryptImpl::destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) {
1099 if( pKeysMngr != nullptr ) {
1100 xmlSecKeysMngrDestroy( pKeysMngr ) ;
1104 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
1105 com_sun_star_xml_crypto_SecurityEnvironment_get_implementation(
1106 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
1108 return cppu::acquire(new SecurityEnvironment_MSCryptImpl(pCtx));
1111 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */