1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
25 #include <xmlsec/base64.h>
27 #if !defined WIN32_LEAN_AND_MEAN
28 # define WIN32_LEAN_AND_MEAN
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>
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
) ;
65 struct CertErrorToString
{
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"},
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
)
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() {
121 CryptReleaseContext( 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 ;
163 OUString SAL_CALL
SecurityEnvironment_MSCryptImpl::getImplementationName() {
164 return "com.sun.star.xml.crypto.SecurityEnvironment";
168 sal_Bool SAL_CALL
SecurityEnvironment_MSCryptImpl::supportsService( const OUString
& serviceName
) {
169 return cppu::supportsService(this, serviceName
);
172 uno::Sequence
< OUString
> SAL_CALL
SecurityEnvironment_MSCryptImpl::getSupportedServiceNames() {
173 return { "com.sun.star.xml.crypto.SecurityEnvironment" };
176 HCRYPTPROV
SecurityEnvironment_MSCryptImpl::getCryptoProvider() {
180 void SecurityEnvironment_MSCryptImpl::setCryptoProvider( HCRYPTPROV aProv
) {
182 CryptReleaseContext( m_hProv
, 0 ) ;
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() {
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
) ;
233 // Based on sample code from MSDN
235 static OUString
get_system_name(const void *pvSystemStore
,
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
;
247 ppwszSystemName
= static_cast<LPCWSTR
>(pvSystemStore
);
249 return OUString(o3tl::toU(ppwszSystemName
));
254 static BOOL WINAPI
cert_enum_physical_store_callback(const void *,
256 LPCWSTR pwszStoreName
,
257 PCERT_PHYSICAL_STORE_INFO
,
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
);
269 static BOOL WINAPI
cert_enum_system_store_callback(const void *pvSystemStore
,
271 PCERT_SYSTEM_STORE_INFO
,
275 SAL_INFO("xmlsecurity.xmlsec", "System store: " << get_system_name(pvSystemStore
, dwFlags
));
277 if (!CertEnumPhysicalStore(pvSystemStore
,
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()));
296 //Methods from XSecurityEnvironment
297 uno::Sequence
< uno::Reference
< XCertificate
> > SecurityEnvironment_MSCryptImpl::getPersonalCertificates()
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
);
309 xcert
= MswcryCertContextToXCert( pCertContext
) ;
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
;
320 NCRYPT_KEY_HANDLE hCryptKey
;
323 CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER
, nullptr, nullptr, cert_enum_system_store_callback
);
326 hSystemKeyStore
= CertOpenSystemStoreW( 0, L
"MY" ) ;
327 if( hSystemKeyStore
!= nullptr ) {
328 pCertContext
= CertEnumCertificatesInStore( hSystemKeyStore
, 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
,
337 phCryptProvOrNCryptKey
,
341 // Not Privatekey found. SKIP this one.
342 pCertContext
= CertEnumCertificatesInStore( hSystemKeyStore
, pCertContext
);
345 // then TODO : Check the personal cert is valid or not.
347 xcert
= MswcryCertContextToXCert( pCertContext
) ;
349 certsList
.push_back( xcert
) ;
350 pCertContext
= CertEnumCertificatesInStore( hSystemKeyStore
, pCertContext
);
354 CertCloseStore( hSystemKeyStore
, CERT_CLOSE_STORE_CHECK_FLAG
) ;
357 length
= certsList
.size() ;
360 uno::Sequence
< uno::Reference
< XCertificate
> > certSeq( length
) ;
361 auto pcertSeq
= certSeq
.getArray();
363 for( const auto& rXCert
: certsList
) {
364 pcertSeq
[i
] = rXCert
;
371 return uno::Sequence
< uno::Reference
< XCertificate
> >() ;
375 uno::Reference
< XCertificate
> SecurityEnvironment_MSCryptImpl::getCertificate( const OUString
& issuerName
, const uno::Sequence
< sal_Int8
>& serialNumber
) {
377 rtl::Reference
<X509Certificate_MSCryptImpl
> xcert
;
378 PCCERT_CONTEXT pCertContext
= nullptr ;
379 HCERTSTORE hCertStore
= nullptr ;
380 CRYPT_INTEGER_BLOB cryptSerialNumber
;
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
,
393 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
| CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG
,
396 &certInfo
.Issuer
.cbData
, 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
,
408 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
| CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG
,
410 certInfo
.Issuer
.pbData
,
411 &certInfo
.Issuer
.cbData
, nullptr ) )
413 free( certInfo
.Issuer
.pbData
) ;
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
++ )
437 if(m_hKeyStore
== nullptr) continue ;
438 hCertStore
= m_hKeyStore
;
441 if(m_hCertStore
== nullptr) continue ;
442 hCertStore
= m_hCertStore
;
445 hCertStore
= CertOpenSystemStoreW( 0, L
"MY" ) ;
446 if(hCertStore
== nullptr || !m_bEnableDefault
) continue ;
449 hCertStore
= CertOpenSystemStoreW( 0, L
"Root" ) ;
450 if(hCertStore
== nullptr || !m_bEnableDefault
) continue ;
453 hCertStore
= CertOpenSystemStoreW( 0, L
"Trust" ) ;
454 if(hCertStore
== nullptr || !m_bEnableDefault
) continue ;
457 hCertStore
= CertOpenSystemStoreW( 0, L
"CA" ) ;
458 if(hCertStore
== nullptr || !m_bEnableDefault
) 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)
473 /*******************************************************************************
474 pCertContext = NULL ;
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 ;
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 ,
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 ,
514 continue ;// discard this cert;
517 if(strncmp(pszName , issuer , cbIssuer) != 0)
520 continue ;// discard this cert;
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;
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(
543 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
545 CERT_FIND_SUBJECT_CERT
,
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
);
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 ;
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
,
610 if (hCollectionStore
!= nullptr)
612 CertAddStoreToCollection (
615 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG
,
617 CertAddStoreToCollection (
620 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG
,
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
;
633 hAdditionalStore
= nullptr;
635 //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
636 bChain
= CertGetCertificateChain(
639 nullptr , //use current system time
642 CERT_CHAIN_REVOCATION_CHECK_CHAIN
| CERT_CHAIN_TIMESTAMP_TIME
,
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
;
667 pCertInChain
= nullptr ;
669 if( pCertInChain
!= nullptr ) {
670 pCert
= MswcryCertContextToXCert( pCertInChain
) ;
672 pxCertChain
[i
] = pCert
;
676 CertFreeCertificateChain( pChainContext
) ;
677 pChainContext
= nullptr ;
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
) ;
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()) ) ;
704 int nRet
= xmlSecBase64Decode_ex( chCert
, reinterpret_cast<xmlSecByte
*>(chCert
), xmlStrlen( chCert
), &certSize
) ;
705 if (nRet
< 0 || certSize
== 0)
711 uno::Sequence
<sal_Int8
> rawCert(comphelper::arrayToSequence
<sal_Int8
>(chCert
, certSize
));
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)
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
);
740 static bool CheckUnitTestStore(PCCERT_CHAIN_CONTEXT
const pChainContext
, DWORD ignoreFlags
)
743 static char const*const pVar
= getenv("LIBO_TEST_CRYPTOAPI_PKCS7");
748 if (pChainContext
->cChain
== 0)
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
))
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");
772 PCCERT_CONTEXT
const pRoot(pSimpleChain
->rgpElement
[pSimpleChain
->cElement
-1]->pCertContext
);
773 PCCERT_CONTEXT
const pIssuerCert
= CertFindCertificateInStore(
775 PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
777 CERT_FIND_SUBJECT_NAME
,
778 &pRoot
->pCertInfo
->Subject
,
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)
790 CertFreeCertificateContext(pIssuerCert
);
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;
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
,
845 if (hCollectionStore
!= nullptr)
847 CertAddStoreToCollection (
850 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG
,
852 CertAddStoreToCollection (
855 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG
,
857 CertAddStoreToCollection (
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(
872 nullptr , //use current system time
875 CERT_CHAIN_REVOCATION_CHECK_CHAIN
| CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
,
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(
910 nullptr , //use current system time
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
;
930 SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
935 //valid and revocation information available
936 SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
937 validity
= css::security::CertificateValidity::VALID
;
943 SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
944 validity
= css::security::CertificateValidity::INVALID
;
949 SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChain failed.");
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
);
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
;
985 characters
&= ~ css::security::CertificateCharacters::SELF_SIGNED
;
988 //Secondly, make sentence whether or not the cert has a private key.
990 BOOL fCallerFreeProv
;
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
,
998 phCryptProvOrNCryptKey
,
1002 characters
|= css::security::CertificateCharacters::HAS_PRIVATE_KEY
;
1004 if (hKey
&& fCallerFreeProv
)
1005 NCryptFreeObject(hKey
);
1007 characters
&= ~ css::security::CertificateCharacters::HAS_PRIVATE_KEY
;
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
) ;
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;
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: */