summaryrefslogtreecommitdiffstats
path: root/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
new file mode 100644
index 000000000..f198f839f
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
@@ -0,0 +1,1118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstddef>
+#include <string.h>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <Windows.h>
+#include <WinCrypt.h>
+#include <sal/macros.h>
+#include <osl/thread.h>
+#include "securityenvironment_mscryptimpl.hxx"
+
+#include "x509certificate_mscryptimpl.hxx"
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <xmlsec-wrapper.h>
+#include "akmngr.hxx"
+
+#include <biginteger.hxx>
+
+#include <comphelper/windowserrorstring.hxx>
+#include <sal/log.hxx>
+#include <rtl/locale.h>
+#include <rtl/ref.hxx>
+#include <osl/nlsupport.h>
+#include <osl/process.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <svl/cryptosign.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang ;
+using ::com::sun::star::lang::XMultiServiceFactory ;
+using ::com::sun::star::lang::XSingleServiceFactory ;
+
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::security::XCertificate ;
+
+static rtl::Reference<X509Certificate_MSCryptImpl> MswcryCertContextToXCert( PCCERT_CONTEXT cert ) ;
+
+namespace {
+
+struct CertErrorToString{
+ DWORD error;
+ char const * name;
+};
+
+}
+
+CertErrorToString const arErrStrings[] =
+{
+ { 0x00000000, "CERT_TRUST_NO_ERROR"},
+ { 0x00000001, "CERT_TRUST_IS_NOT_TIME_VALID"},
+ { 0x00000002, "CERT_TRUST_IS_NOT_TIME_NESTED"},
+ { 0x00000004, "CERT_TRUST_IS_REVOKED" },
+ { 0x00000008, "CERT_TRUST_IS_NOT_SIGNATURE_VALID" },
+ { 0x00000010, "CERT_TRUST_IS_NOT_SIGNATURE_VALID"},
+ { 0x00000020, "CERT_TRUST_IS_UNTRUSTED_ROOT"},
+ { 0x00000040, "CERT_TRUST_REVOCATION_STATUS_UNKNOWN"},
+ { 0x00000080, "CERT_TRUST_IS_CYCLIC"},
+ { 0x00000100, "CERT_TRUST_INVALID_EXTENSION"},
+ { 0x00000200, "CERT_TRUST_INVALID_POLICY_CONSTRAINTS"},
+ { 0x00000400, "CERT_TRUST_INVALID_BASIC_CONSTRAINTS"},
+ { 0x00000800, "CERT_TRUST_INVALID_NAME_CONSTRAINTS"},
+ { 0x00001000, "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT"},
+ { 0x00002000, "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT"},
+ { 0x00004000, "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT"},
+ { 0x00008000, "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT"},
+ { 0x01000000, "CERT_TRUST_IS_OFFLINE_REVOCATION"},
+ { 0x02000000, "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY"},
+ { 0x04000000, "CERT_TRUST_IS_EXPLICIT_DISTRUST"},
+ { 0x08000000, "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT"},
+ //Chain errors
+ { 0x00010000, "CERT_TRUST_IS_PARTIAL_CHAIN"},
+ { 0x00020000, "CERT_TRUST_CTL_IS_NOT_TIME_VALID"},
+ { 0x00040000, "CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID"},
+ { 0x00080000, "CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE"}
+};
+
+static void traceTrustStatus(DWORD err)
+{
+ if (err == 0)
+ SAL_INFO("xmlsecurity.xmlsec", " " << arErrStrings[0].name);
+ for (std::size_t i = 1; i < SAL_N_ELEMENTS(arErrStrings); i++)
+ {
+ if (arErrStrings[i].error & err)
+ SAL_INFO("xmlsecurity.xmlsec", " " << arErrStrings[i].name);
+ }
+}
+
+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 ){
+
+ m_xServiceManager.set(xContext, uno::UNO_QUERY);
+}
+
+SecurityEnvironment_MSCryptImpl::~SecurityEnvironment_MSCryptImpl() {
+
+ if( m_hProv != 0 ) {
+ CryptReleaseContext( m_hProv, 0 ) ;
+ m_hProv = 0 ;
+ }
+
+ if( m_pszContainer != nullptr ) {
+ //TODO: Don't know whether or not it should be released now.
+ m_pszContainer = nullptr ;
+ }
+
+ if( m_hCertStore != nullptr ) {
+ CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hCertStore = nullptr ;
+ }
+
+ if( m_hKeyStore != nullptr ) {
+ CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hKeyStore = nullptr ;
+ }
+
+ //i120675, close the store handles
+ if( m_hMySystemStore != nullptr ) {
+ CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hMySystemStore = nullptr ;
+ }
+
+ if( m_hRootSystemStore != nullptr ) {
+ CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hRootSystemStore = nullptr ;
+ }
+
+ if( m_hTrustSystemStore != nullptr ) {
+ CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hTrustSystemStore = nullptr ;
+ }
+
+ if( m_hCaSystemStore != nullptr ) {
+ CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hCaSystemStore = nullptr ;
+ }
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SecurityEnvironment_MSCryptImpl::getImplementationName() {
+ return "com.sun.star.xml.crypto.SecurityEnvironment";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SecurityEnvironment_MSCryptImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+/* XServiceInfo */
+uno::Sequence< OUString > SAL_CALL SecurityEnvironment_MSCryptImpl::getSupportedServiceNames() {
+ return { "com.sun.star.xml.crypto.SecurityEnvironment" };
+}
+
+/* XUnoTunnel */
+UNO3_GETIMPLEMENTATION_IMPL(SecurityEnvironment_MSCryptImpl);
+
+HCRYPTPROV SecurityEnvironment_MSCryptImpl::getCryptoProvider() {
+ return m_hProv ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setCryptoProvider( HCRYPTPROV aProv ) {
+ if( m_hProv != 0 ) {
+ CryptReleaseContext( m_hProv, 0 ) ;
+ m_hProv = 0 ;
+ }
+
+ if( aProv != 0 ) {
+ m_hProv = aProv ;
+ }
+}
+
+LPCTSTR SecurityEnvironment_MSCryptImpl::getKeyContainer() {
+ return m_pszContainer ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setKeyContainer( LPCTSTR aKeyContainer ) {
+ //TODO: Don't know whether or not it should be copied.
+ m_pszContainer = aKeyContainer ;
+}
+
+
+HCERTSTORE SecurityEnvironment_MSCryptImpl::getCryptoSlot() {
+ return m_hKeyStore ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setCryptoSlot( HCERTSTORE aSlot) {
+ if( m_hKeyStore != nullptr ) {
+ CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hKeyStore = nullptr ;
+ }
+
+ if( aSlot != nullptr ) {
+ m_hKeyStore = CertDuplicateStore( aSlot ) ;
+ }
+}
+
+HCERTSTORE SecurityEnvironment_MSCryptImpl::getCertDb() {
+ return m_hCertStore ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setCertDb( HCERTSTORE aCertDb ) {
+ if( m_hCertStore != nullptr ) {
+ CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hCertStore = nullptr ;
+ }
+
+ if( aCertDb != nullptr ) {
+ m_hCertStore = CertDuplicateStore( aCertDb ) ;
+ }
+}
+
+#ifdef SAL_LOG_INFO
+
+// Based on sample code from MSDN
+
+static OUString get_system_name(const void *pvSystemStore,
+ DWORD dwFlags)
+{
+ LPCWSTR ppwszSystemName;
+ if (dwFlags & CERT_SYSTEM_STORE_RELOCATE_FLAG)
+ {
+ _CERT_SYSTEM_STORE_RELOCATE_PARA const * pRelocatePara;
+ pRelocatePara = static_cast<_CERT_SYSTEM_STORE_RELOCATE_PARA const *>(pvSystemStore);
+ ppwszSystemName = pRelocatePara->pwszSystemStore;
+ }
+ else
+ {
+ ppwszSystemName = static_cast<LPCWSTR>(pvSystemStore);
+ }
+ return OUString(o3tl::toU(ppwszSystemName));
+}
+
+extern "C" {
+
+static BOOL WINAPI cert_enum_physical_store_callback(const void *,
+ DWORD dwFlags,
+ LPCWSTR pwszStoreName,
+ PCERT_PHYSICAL_STORE_INFO,
+ void *,
+ void *)
+{
+ OUString name(o3tl::toU(pwszStoreName));
+ if (dwFlags & CERT_PHYSICAL_STORE_PREDEFINED_ENUM_FLAG)
+ name += " (implicitly created)";
+ SAL_INFO("xmlsecurity.xmlsec", " Physical store: " << name);
+
+ return TRUE;
+}
+
+static BOOL WINAPI cert_enum_system_store_callback(const void *pvSystemStore,
+ DWORD dwFlags,
+ PCERT_SYSTEM_STORE_INFO,
+ void *,
+ void *)
+{
+ SAL_INFO("xmlsecurity.xmlsec", "System store: " << get_system_name(pvSystemStore, dwFlags));
+
+ if (!CertEnumPhysicalStore(pvSystemStore,
+ dwFlags,
+ nullptr,
+ cert_enum_physical_store_callback))
+ {
+ DWORD dwErr = GetLastError();
+ if (!(ERROR_FILE_NOT_FOUND == dwErr ||
+ ERROR_NOT_SUPPORTED == dwErr))
+ {
+ SAL_WARN("xmlsecurity.xmlsec", "CertEnumPhysicalStore failed:" << WindowsErrorString(GetLastError()));
+ }
+ }
+ return TRUE;
+}
+
+}
+
+#endif
+
+//Methods from XSecurityEnvironment
+uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::getPersonalCertificates()
+{
+ sal_Int32 length ;
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+ std::vector< rtl::Reference<X509Certificate_MSCryptImpl> > certsList ;
+ PCCERT_CONTEXT pCertContext = nullptr;
+
+ //firstly, we try to find private keys in given key store.
+ if( m_hKeyStore != nullptr ) {
+ pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
+ while (pCertContext)
+ {
+ xcert = MswcryCertContextToXCert( pCertContext ) ;
+ if( xcert.is() )
+ certsList.push_back( xcert ) ;
+ pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
+ }
+ }
+
+ //Thirdly, we try to find certificate from system default key store.
+ if( m_bEnableDefault ) {
+ HCERTSTORE hSystemKeyStore ;
+ DWORD dwKeySpec;
+ NCRYPT_KEY_HANDLE hCryptKey;
+
+#ifdef SAL_LOG_INFO
+ CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, nullptr, nullptr, cert_enum_system_store_callback);
+#endif
+
+ hSystemKeyStore = CertOpenSystemStoreW( 0, L"MY" ) ;
+ if( hSystemKeyStore != nullptr ) {
+ pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
+ while (pCertContext)
+ {
+ // for checking whether the certificate is a personal certificate or not.
+ DWORD dwFlags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
+ HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hCryptKey;
+ if(!(CryptAcquireCertificatePrivateKey(pCertContext,
+ dwFlags,
+ nullptr,
+ phCryptProvOrNCryptKey,
+ &dwKeySpec,
+ nullptr)))
+ {
+ // Not Privatekey found. SKIP this one.
+ pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
+ continue;
+ }
+ // then TODO : Check the personal cert is valid or not.
+
+ xcert = MswcryCertContextToXCert( pCertContext ) ;
+ if( xcert.is() )
+ certsList.push_back( xcert ) ;
+ pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
+ }
+ }
+
+ CertCloseStore( hSystemKeyStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ }
+
+ length = certsList.size() ;
+ if( length != 0 ) {
+ int i = 0;
+ uno::Sequence< uno::Reference< XCertificate > > certSeq( length ) ;
+ auto pcertSeq = certSeq.getArray();
+
+ for( const auto& rXCert : certsList ) {
+ pcertSeq[i] = rXCert ;
+ ++i;
+ }
+
+ return certSeq ;
+ }
+
+ return uno::Sequence< uno::Reference< XCertificate > >() ;
+}
+
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const uno::Sequence< sal_Int8 >& serialNumber ) {
+ unsigned int i ;
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+ PCCERT_CONTEXT pCertContext = nullptr ;
+ HCERTSTORE hCertStore = nullptr ;
+ CRYPT_INTEGER_BLOB cryptSerialNumber ;
+ CERT_INFO certInfo ;
+
+ // for correct encoding
+ rtl_Locale *pLocale = nullptr ;
+ osl_getProcessLocale( &pLocale ) ;
+
+ //Create cert info from issue and serial
+ LPCWSTR pszName = o3tl::toW( issuerName.getStr() );
+
+ if( ! ( CertStrToNameW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ pszName ,
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
+ nullptr ,
+ nullptr ,
+ &certInfo.Issuer.cbData, nullptr ) )
+ ) {
+ return nullptr ;
+ }
+
+ certInfo.Issuer.pbData = static_cast<BYTE*>(malloc( certInfo.Issuer.cbData ));
+ if(!certInfo.Issuer.pbData)
+ throw uno::RuntimeException() ;
+
+ if( ! ( CertStrToNameW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ pszName ,
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
+ nullptr ,
+ certInfo.Issuer.pbData ,
+ &certInfo.Issuer.cbData, nullptr ) )
+ ) {
+ free( certInfo.Issuer.pbData ) ;
+ return nullptr ;
+ }
+
+ //Get the SerialNumber
+ cryptSerialNumber.cbData = serialNumber.getLength() ;
+ cryptSerialNumber.pbData = static_cast<BYTE*>(malloc( cryptSerialNumber.cbData));
+ if (!cryptSerialNumber.pbData)
+ {
+ free( certInfo.Issuer.pbData ) ;
+ throw uno::RuntimeException() ;
+ }
+ for( i = 0; i < cryptSerialNumber.cbData; i ++ )
+ cryptSerialNumber.pbData[i] = serialNumber[ cryptSerialNumber.cbData - i - 1 ] ;
+
+ certInfo.SerialNumber.cbData = cryptSerialNumber.cbData ;
+ certInfo.SerialNumber.pbData = cryptSerialNumber.pbData ;
+
+ // Get the Cert from all store.
+ for( i = 0 ; i < 6 ; i ++ )
+ {
+ switch(i)
+ {
+ case 0:
+ if(m_hKeyStore == nullptr) continue ;
+ hCertStore = m_hKeyStore ;
+ break;
+ case 1:
+ if(m_hCertStore == nullptr) continue ;
+ hCertStore = m_hCertStore ;
+ break;
+ case 2:
+ hCertStore = CertOpenSystemStoreW( 0, L"MY" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ case 3:
+ hCertStore = CertOpenSystemStoreW( 0, L"Root" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ case 4:
+ hCertStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ case 5:
+ hCertStore = CertOpenSystemStoreW( 0, L"CA" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ default:
+ i=6;
+ continue;
+ }
+
+/*******************************************************************************
+ * This code reserved for remind us there are another way to find one cert by
+ * IssuerName&serialnumber. You can use the code to replaced the function
+ * CertFindCertificateInStore IF and ONLY IF you must find one special cert in
+ * certStore but can not be found by CertFindCertificateInStore , then , you
+ * should also change the same part in libxmlsec/.../src/mscrypto/x509vfy.c#875.
+ * By Chandler Peng(chandler.peng@sun.com)
+ *****/
+/*******************************************************************************
+ pCertContext = NULL ;
+ found = 0;
+ do{
+ // 1. enum the certs has same string in the issuer string.
+ pCertContext = CertEnumCertificatesInStore( hCertStore , pCertContext ) ;
+ if( pCertContext != NULL )
+ {
+ // 2. check the cert's issuer name .
+ char* issuer = NULL ;
+ DWORD cbIssuer = 0 ;
+
+ cbIssuer = CertNameToStr(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( pCertContext->pCertInfo->Issuer ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ NULL, 0
+ ) ;
+
+ if( cbIssuer == 0 ) continue ; // discard this cert;
+
+ issuer = (char *)malloc( cbIssuer ) ;
+ if( issuer == NULL ) // discard this cert;
+ {
+ free( cryptSerialNumber.pbData) ;
+ free( certInfo.Issuer.pbData ) ;
+ CertFreeCertificateContext( pCertContext ) ;
+ if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ throw RuntimeException() ;
+ }
+
+ cbIssuer = CertNameToStr(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( pCertContext->pCertInfo->Issuer ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ issuer, cbIssuer
+ ) ;
+
+ if( cbIssuer <= 0 )
+ {
+ free( issuer ) ;
+ continue ;// discard this cert;
+ }
+
+ if(strncmp(pszName , issuer , cbIssuer) != 0)
+ {
+ free( issuer ) ;
+ continue ;// discard this cert;
+ }
+ free( issuer ) ;
+
+ // 3. check the serial number.
+ if( memcmp( cryptSerialNumber.pbData , pCertContext->pCertInfo->SerialNumber.pbData , cryptSerialNumber.cbData ) != 0 )
+ {
+ continue ;// discard this cert;
+ }
+
+ // 4. confirm and break;
+ found = 1;
+ break ;
+ }
+
+ }while(pCertContext);
+
+ if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ if( found != 0 ) break; // Found the certificate.
+********************************************************************************/
+
+ pCertContext = CertFindCertificateInStore(
+ hCertStore,
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_CERT,
+ &certInfo,
+ nullptr
+ ) ;
+
+ if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ if( pCertContext != nullptr ) break ; // Found the certificate.
+
+ }
+
+ free(cryptSerialNumber.pbData);
+ free(certInfo.Issuer.pbData);
+
+ if( pCertContext != nullptr ) {
+ xcert = MswcryCertContextToXCert(pCertContext);
+ CertFreeCertificateContext(pCertContext);
+ }
+
+ return xcert ;
+}
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const OUString& serialNumber ) {
+ uno::Sequence< sal_Int8 > serial = xmlsecurity::numericStringToBigInteger( serialNumber ) ;
+ return getCertificate( issuerName, serial ) ;
+}
+
+uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::buildCertificatePath( const uno::Reference< XCertificate >& begin ) {
+ PCCERT_CHAIN_CONTEXT pChainContext ;
+ PCCERT_CONTEXT pCertContext ;
+
+ CERT_ENHKEY_USAGE enhKeyUsage ;
+ CERT_USAGE_MATCH certUsage ;
+ CERT_CHAIN_PARA chainPara ;
+
+ enhKeyUsage.cUsageIdentifier = 0 ;
+ enhKeyUsage.rgpszUsageIdentifier = nullptr ;
+ certUsage.dwType = USAGE_MATCH_TYPE_AND ;
+ certUsage.Usage = enhKeyUsage ;
+ chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
+ chainPara.RequestedUsage = certUsage ;
+
+ uno::Reference< XUnoTunnel > xCertTunnel( begin, uno::UNO_QUERY_THROW ) ;
+ const auto* xcert = comphelper::getFromUnoTunnel<X509Certificate_MSCryptImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw uno::RuntimeException() ;
+ }
+
+ pCertContext = xcert->getMswcryCert() ;
+
+ pChainContext = nullptr ;
+
+ bool bChain = false;
+ if( pCertContext != nullptr )
+ {
+ HCERTSTORE hAdditionalStore = nullptr;
+ HCERTSTORE hCollectionStore = nullptr;
+ if (m_hCertStore && m_hKeyStore)
+ {
+ //Merge m_hCertStore and m_hKeyStore into one store.
+ hCollectionStore = CertOpenStore(
+ CERT_STORE_PROV_COLLECTION ,
+ 0 ,
+ 0 ,
+ 0 ,
+ nullptr
+ ) ;
+ if (hCollectionStore != nullptr)
+ {
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hCertStore ,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hKeyStore,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ hAdditionalStore = hCollectionStore;
+ }
+
+ }
+
+ //if the merge of both stores failed then we add only m_hCertStore
+ if (hAdditionalStore == nullptr && m_hCertStore)
+ hAdditionalStore = m_hCertStore;
+ else if (hAdditionalStore == nullptr && m_hKeyStore)
+ hAdditionalStore = m_hKeyStore;
+ else
+ hAdditionalStore = nullptr;
+
+ //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
+ bChain = CertGetCertificateChain(
+ nullptr ,
+ pCertContext ,
+ nullptr , //use current system time
+ hAdditionalStore,
+ &chainPara ,
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_TIMESTAMP_TIME ,
+ nullptr ,
+ &pChainContext);
+ if (!bChain)
+ pChainContext = nullptr;
+
+ //Close the additional store
+ CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ }
+
+ if(bChain && pChainContext != nullptr && pChainContext->cChain > 0 )
+ {
+ PCCERT_CONTEXT pCertInChain ;
+ PCERT_SIMPLE_CHAIN pCertChain ;
+ rtl::Reference<X509Certificate_MSCryptImpl> pCert ;
+
+ pCertChain = pChainContext->rgpChain[0] ;
+ if( pCertChain->cElement ) {
+ uno::Sequence< uno::Reference< XCertificate > > xCertChain( pCertChain->cElement ) ;
+ auto pxCertChain = xCertChain.getArray();
+
+ for( unsigned int i = 0 ; i < pCertChain->cElement ; i ++ ) {
+ if( pCertChain->rgpElement[i] )
+ pCertInChain = pCertChain->rgpElement[i]->pCertContext ;
+ else
+ pCertInChain = nullptr ;
+
+ if( pCertInChain != nullptr ) {
+ pCert = MswcryCertContextToXCert( pCertInChain ) ;
+ if( pCert.is() )
+ pxCertChain[i] = pCert ;
+ }
+ }
+
+ CertFreeCertificateChain( pChainContext ) ;
+ pChainContext = nullptr ;
+
+ return xCertChain ;
+ }
+ }
+ if (pChainContext)
+ CertFreeCertificateChain(pChainContext);
+
+ return uno::Sequence< uno::Reference < XCertificate > >();
+}
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromRaw( const uno::Sequence< sal_Int8 >& rawCertificate ) {
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+
+ if( rawCertificate.getLength() > 0 ) {
+ xcert = new X509Certificate_MSCryptImpl() ;
+ xcert->setRawCert( rawCertificate ) ;
+ }
+
+ return xcert ;
+}
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromAscii( const OUString& asciiCertificate ) {
+
+ OString oscert = OUStringToOString( asciiCertificate , RTL_TEXTENCODING_ASCII_US ) ;
+
+ xmlChar* chCert = xmlStrndup( reinterpret_cast<const xmlChar*>(oscert.getStr()), static_cast<int>(oscert.getLength()) ) ;
+
+ xmlSecSize certSize = xmlSecBase64Decode( chCert, chCert, xmlStrlen( chCert ) ) ;
+
+ uno::Sequence< sal_Int8 > rawCert( certSize ) ;
+ auto rawCertRange = asNonConstRange(rawCert);
+ for( xmlSecSize i = 0 ; i < certSize ; i ++ )
+ rawCertRange[i] = *( chCert + i ) ;
+
+ xmlFree( chCert ) ;
+
+ return createCertificateFromRaw( rawCert ) ;
+}
+
+
+static HCERTSTORE getCertStoreForIntermediatCerts(
+ const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
+{
+ HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, nullptr);
+ if (store == nullptr)
+ return nullptr;
+
+ for (int i = 0; i < seqCerts.getLength(); i++)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Added temporary certificate: " << seqCerts[i]->getSubjectName());
+
+ uno::Sequence<sal_Int8> data = seqCerts[i]->getEncoded();
+ PCCERT_CONTEXT cert = CertCreateCertificateContext(
+ X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(&data[0]), data.getLength());
+ //Adding the certificate creates a copy and not just increases the ref count
+ //Therefore we free later the certificate that we now add
+ CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_ALWAYS, nullptr);
+ CertFreeCertificateContext(cert);
+ }
+ return store;
+}
+
+static bool CheckUnitTestStore(PCCERT_CHAIN_CONTEXT const pChainContext, DWORD ignoreFlags)
+{
+ bool ret = false;
+ static char const*const pVar = getenv("LIBO_TEST_CRYPTOAPI_PKCS7");
+ if (!pVar)
+ {
+ return ret;
+ }
+ if (pChainContext->cChain == 0)
+ {
+ return ret;
+ }
+ PCERT_SIMPLE_CHAIN pSimpleChain = pChainContext->rgpChain[0];
+ // check if untrusted root is the only problem
+ if (pSimpleChain->TrustStatus.dwErrorStatus & ~(CERT_TRUST_IS_UNTRUSTED_ROOT | ignoreFlags))
+ {
+ return ret;
+ }
+
+ // leak this store, re-opening is a waste of time in tests
+ static HCERTSTORE const hExtra = CertOpenStore(
+ CERT_STORE_PROV_FILENAME_A,
+ PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ 0,
+ CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG,
+ OString(OString::Concat(pVar) + "/test.p7b").getStr());
+ assert(hExtra != nullptr);
+ if (pSimpleChain->cElement < 1)
+ {
+ SAL_WARN("xmlsecurity.xmlsec", "unexpected empty chain");
+ return ret;
+ }
+ PCCERT_CONTEXT const pRoot(pSimpleChain->rgpElement[pSimpleChain->cElement-1]->pCertContext);
+ PCCERT_CONTEXT const pIssuerCert = CertFindCertificateInStore(
+ hExtra,
+ PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_NAME,
+ &pRoot->pCertInfo->Subject,
+ nullptr);
+ if (pIssuerCert)
+ {
+ // check that it signed itself
+ DWORD flags = CERT_STORE_SIGNATURE_FLAG;
+ bool result = CertVerifySubjectCertificateContext(pRoot, pIssuerCert, &flags);
+ if (result && flags == 0)
+ {
+ ret = true;
+ }
+ }
+ CertFreeCertificateContext(pIssuerCert);
+ return ret;
+}
+
+//We return only valid or invalid, as long as the API documentation expresses
+//explicitly that all validation steps are carried out even if one or several
+//errors occur. See also
+//http://wiki.openoffice.org/wiki/Certificate_Path_Validation#Validation_status
+sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate(
+ const uno::Reference< css::security::XCertificate >& aCert,
+ const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
+{
+ sal_Int32 validity = css::security::CertificateValidity::INVALID;
+ PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
+ PCCERT_CONTEXT pCertContext = nullptr;
+
+ uno::Reference< XUnoTunnel > xCertTunnel( aCert, uno::UNO_QUERY_THROW ) ;
+
+ SAL_INFO("xmlsecurity.xmlsec", "Start verification of certificate: " << aCert->getSubjectName());
+
+ const auto* xcert = comphelper::getFromUnoTunnel<X509Certificate_MSCryptImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw uno::RuntimeException() ;
+ }
+
+ pCertContext = xcert->getMswcryCert() ;
+
+ CERT_ENHKEY_USAGE enhKeyUsage ;
+ CERT_USAGE_MATCH certUsage ;
+ CERT_CHAIN_PARA chainPara = {};
+
+ //Prepare parameter for CertGetCertificateChain
+ enhKeyUsage.cUsageIdentifier = 0 ;
+ enhKeyUsage.rgpszUsageIdentifier = nullptr ;
+ certUsage.dwType = USAGE_MATCH_TYPE_AND ;
+ certUsage.Usage = enhKeyUsage ;
+ chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
+ chainPara.RequestedUsage = certUsage ;
+
+
+ HCERTSTORE hCollectionStore = nullptr;
+ HCERTSTORE hIntermediateCertsStore = nullptr;
+ bool bChain = false;
+ if( pCertContext != nullptr )
+ {
+ hIntermediateCertsStore =
+ getCertStoreForIntermediatCerts(seqCerts);
+
+ //Merge m_hCertStore and m_hKeyStore and the store of the intermediate
+ //certificates into one store.
+ hCollectionStore = CertOpenStore(
+ CERT_STORE_PROV_COLLECTION ,
+ 0 ,
+ 0 ,
+ 0 ,
+ nullptr
+ ) ;
+ if (hCollectionStore != nullptr)
+ {
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hCertStore ,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hKeyStore,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ CertAddStoreToCollection (
+ hCollectionStore,
+ hIntermediateCertsStore,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG,
+ 0);
+
+ }
+
+ //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
+ //We do not check revocation of the root. In most cases there are none.
+ //Then we would get CERT_TRUST_REVOCATION_STATUS_UNKNOWN
+ SAL_INFO("xmlsecurity.xmlsec", "Verifying cert using revocation information.");
+ bChain = CertGetCertificateChain(
+ nullptr ,
+ pCertContext ,
+ nullptr , //use current system time
+ hCollectionStore,
+ &chainPara ,
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
+ nullptr ,
+ &pChainContext);
+
+ if (bChain && pChainContext->cChain > 0)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Overall error status (all chains):");
+ traceTrustStatus(pChainContext->TrustStatus.dwErrorStatus);
+ //highest quality chains come first
+ PCERT_SIMPLE_CHAIN pSimpleChain = pChainContext->rgpChain[0];
+ SAL_INFO("xmlsecurity.xmlsec", "Error status of first chain:");
+ traceTrustStatus(pSimpleChain->TrustStatus.dwErrorStatus);
+
+ //CERT_TRUST_REVOCATION_STATUS_UNKNOWN is also set if a certificate
+ //has no AIA(OCSP) or CRLDP extension and there is no CRL locally installed.
+ DWORD revocationFlags = CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
+ CERT_TRUST_IS_OFFLINE_REVOCATION;
+ DWORD otherErrorsMask = ~revocationFlags;
+ if (!(pSimpleChain->TrustStatus.dwErrorStatus & otherErrorsMask)
+ || CheckUnitTestStore(pChainContext, revocationFlags))
+
+ {
+ //No errors except maybe those caused by missing revocation information
+ //Check if there are errors
+ if ( pSimpleChain->TrustStatus.dwErrorStatus & revocationFlags)
+ {
+ //No revocation information. Because MSDN documentation is not
+ //clear about if all other tests are performed if an error occurs,
+ //we test again, without requiring revocation checking.
+ CertFreeCertificateChain(pChainContext);
+ pChainContext = nullptr;
+ SAL_INFO("xmlsecurity.xmlsec", "Checking again but without requiring revocation information.");
+ bChain = CertGetCertificateChain(
+ nullptr ,
+ pCertContext ,
+ nullptr , //use current system time
+ hCollectionStore,
+ &chainPara ,
+ 0,
+ nullptr ,
+ &pChainContext);
+ if (bChain
+ && pChainContext->cChain > 0
+ && pChainContext->rgpChain[0]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
+ validity = css::security::CertificateValidity::VALID;
+ }
+ else if (CheckUnitTestStore(pChainContext, 0))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "root certificate found in extra test store");
+ validity = css::security::CertificateValidity::VALID;
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
+ }
+ }
+ else
+ {
+ //valid and revocation information available
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
+ validity = css::security::CertificateValidity::VALID;
+ }
+ }
+ else
+ {
+ //invalid
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
+ validity = css::security::CertificateValidity::INVALID ;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChain failed.");
+ }
+ }
+
+ if (pChainContext)
+ {
+ CertFreeCertificateChain(pChainContext);
+ pChainContext = nullptr;
+ }
+
+ //Close the additional store, do not destroy the contained certs
+ CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ //Close the temporary store containing the intermediate certificates and make
+ //sure all certificates are deleted.
+ CertCloseStore(hIntermediateCertsStore, CERT_CLOSE_STORE_CHECK_FLAG);
+
+ return validity ;
+}
+
+sal_Int32 SecurityEnvironment_MSCryptImpl::getCertificateCharacters( const css::uno::Reference< css::security::XCertificate >& aCert ) {
+ sal_Int32 characters ;
+ PCCERT_CONTEXT pCertContext ;
+
+ uno::Reference< XUnoTunnel > xCertTunnel( aCert, uno::UNO_QUERY_THROW ) ;
+ const auto* xcert = comphelper::getFromUnoTunnel<X509Certificate_MSCryptImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw uno::RuntimeException() ;
+ }
+
+ pCertContext = xcert->getMswcryCert() ;
+
+ characters = 0x00000000 ;
+
+ //Firstly, make sentence whether or not the cert is self-signed.
+ if( CertCompareCertificateName( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->Subject), &(pCertContext->pCertInfo->Issuer) ) ) {
+ characters |= css::security::CertificateCharacters::SELF_SIGNED ;
+ } else {
+ characters &= ~ css::security::CertificateCharacters::SELF_SIGNED ;
+ }
+
+ //Secondly, make sentence whether or not the cert has a private key.
+ {
+ BOOL fCallerFreeProv ;
+ DWORD dwKeySpec ;
+ NCRYPT_KEY_HANDLE hKey = 0;
+ DWORD dwFlags = CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
+ HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hKey;
+ if( CryptAcquireCertificatePrivateKey( pCertContext ,
+ dwFlags,
+ nullptr ,
+ phCryptProvOrNCryptKey,
+ &dwKeySpec,
+ &fCallerFreeProv )
+ ) {
+ characters |= css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+
+ if (hKey && fCallerFreeProv)
+ NCryptFreeObject(hKey);
+ } else {
+ characters &= ~ css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+ }
+ }
+ return characters ;
+}
+
+void SecurityEnvironment_MSCryptImpl::enableDefaultCrypt( bool enable ) {
+ m_bEnableDefault = enable ;
+}
+
+bool SecurityEnvironment_MSCryptImpl::defaultEnabled() {
+ return m_bEnableDefault ;
+}
+
+static rtl::Reference<X509Certificate_MSCryptImpl> MswcryCertContextToXCert( PCCERT_CONTEXT cert )
+{
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+
+ if( cert != nullptr ) {
+ xcert = new X509Certificate_MSCryptImpl() ;
+ xcert->setMswcryCert( cert ) ;
+ }
+
+ return xcert ;
+}
+
+OUString SecurityEnvironment_MSCryptImpl::getSecurityEnvironmentInformation()
+{
+ return "Microsoft Crypto API";
+}
+
+xmlSecKeysMngrPtr SecurityEnvironment_MSCryptImpl::createKeysManager() {
+
+ /*-
+ * The following lines is based on the of xmlsec-mscrypto crypto engine
+ */
+ xmlSecKeysMngrPtr pKeysMngr = xmlsecurity::MSCryptoAppliedKeysMngrCreate() ;
+ if( pKeysMngr == nullptr )
+ throw uno::RuntimeException() ;
+
+ /*-
+ * Adopt system default certificate store.
+ */
+ if( defaultEnabled() ) {
+ //Add system key store into the keys manager.
+ m_hMySystemStore = CertOpenSystemStoreW( 0, L"MY" ) ;
+ if( m_hMySystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptKeyStore( pKeysMngr, m_hMySystemStore ) < 0 ) {
+ CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hMySystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hMySystemStore = nullptr;
+ }
+
+ //Add system root store into the keys manager.
+ m_hRootSystemStore = CertOpenSystemStoreW( 0, L"Root" ) ;
+ if( m_hRootSystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptTrustedStore( pKeysMngr, m_hRootSystemStore ) < 0 ) {
+ CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hRootSystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hRootSystemStore = nullptr;
+ }
+
+ //Add system trusted store into the keys manager.
+ m_hTrustSystemStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
+ if( m_hTrustSystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hTrustSystemStore ) < 0 ) {
+ CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hTrustSystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hTrustSystemStore = nullptr;
+ }
+
+ //Add system CA store into the keys manager.
+ m_hCaSystemStore = CertOpenSystemStoreW( 0, L"CA" ) ;
+ if( m_hCaSystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hCaSystemStore ) < 0 ) {
+ CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hCaSystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hCaSystemStore = nullptr;
+ }
+ }
+
+ return pKeysMngr ;
+}
+void SecurityEnvironment_MSCryptImpl::destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) {
+ if( pKeysMngr != nullptr ) {
+ xmlSecKeysMngrDestroy( pKeysMngr ) ;
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_SecurityEnvironment_get_implementation(
+ uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SecurityEnvironment_MSCryptImpl(pCtx));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */