cid#1640468 Dereference after null check
[LibreOffice.git] / xmlsecurity / source / xmlsec / mscrypt / x509certificate_mscryptimpl.cxx
blobbd4ef701e6ed6fbd789c7e1a85662c38e13597f1
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 <string.h>
22 #include <sal/config.h>
23 #include <sal/log.hxx>
24 #include <comphelper/servicehelper.hxx>
25 #include <comphelper/windowserrorstring.hxx>
26 #include <cppuhelper/supportsservice.hxx>
27 #include "x509certificate_mscryptimpl.hxx"
28 #include <certificateextension_xmlsecimpl.hxx>
29 #include <biginteger.hxx>
30 #include "sanextension_mscryptimpl.hxx"
32 #include "oid.hxx"
34 #include <rtl/locale.h>
35 #include <rtl/ref.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <osl/nlsupport.h>
38 #include <osl/process.h>
39 #include <o3tl/char16_t2wchar_t.hxx>
40 #include <o3tl/string_view.hxx>
42 #include <memory>
43 #include <string_view>
44 #include <utility>
45 #include <vector>
46 #include <tools/time.hxx>
47 #include <svl/sigstruct.hxx>
49 using namespace com::sun::star;
50 using namespace ::com::sun::star::uno ;
51 using namespace ::com::sun::star::security ;
53 using ::com::sun::star::security::XCertificate ;
54 using ::com::sun::star::util::DateTime ;
56 /*Returns the index within rRawString where sTypeName starts and where it ends.
57 The starting index is pair.first. The ending index in pair.second points
58 one char after the last character of the type.
59 sTypeName can be
60 "S" or "CN" (without ""). Do not use spaces at the beginning of the type name.
61 If the type name is not found then pair.first and pair.second are -1.
63 static std::pair< sal_Int32, sal_Int32 >
64 findTypeInDN(const OUString& rRawString, std::u16string_view sTypeName)
66 std::pair< sal_Int32, sal_Int32 > retVal;
67 bool bInEscape = false;
68 bool bInValue = false;
69 bool bFound = false;
70 sal_Int32 nTypeNameStart = 0;
71 sal_Int32 length = rRawString.getLength();
73 for (sal_Int32 i = 0; i < length; i++)
75 sal_Unicode c = rRawString[i];
77 if (c == '=')
79 if (! bInValue)
81 std::u16string_view sType = rRawString.subView(nTypeNameStart, i - nTypeNameStart);
82 sType = o3tl::trim(sType);
83 if (o3tl::equalsIgnoreAsciiCase(sType, sTypeName))
85 bFound = true;
86 break;
90 else if (c == '"')
92 if (!bInEscape)
94 //If this is the quote is the first of the couple which enclose the
95 //whole value, because the value contains special characters
96 //then we just drop it. That is, this character must be followed by
97 //a character which is not '"'.
98 if ( i + 1 < length && rRawString[i+1] == '"')
99 bInEscape = true;
100 else
101 bInValue = !bInValue; //value is enclosed in " "
103 else
105 //This quote is escaped by a preceding quote and therefore is
106 //part of the value
107 bInEscape = false;
110 else if (c == ',' || c == '+')
112 //The comma separate the attribute value pairs.
113 //If the comma is not part of a value (the value would then be enclosed in '"'),
114 //then we have reached the end of the value
115 if (!bInValue)
117 //The next char is the start of the new type
118 nTypeNameStart = i + 1;
123 //Found the Type Name, but there can still be spaces after the last comma
124 //and the beginning of the type.
125 if (bFound)
127 while (true)
129 sal_Unicode c = rRawString[nTypeNameStart];
130 if (c != ' ' && c != '\t')
131 //found
132 break;
133 nTypeNameStart ++;
135 // search end (one after last letter)
136 sal_Int32 nTypeNameEnd = nTypeNameStart;
137 nTypeNameEnd++;
138 while (true)
140 sal_Unicode c = rRawString[nTypeNameEnd];
141 if (c == ' ' || c == '\t' || c == '=')
142 break;
143 nTypeNameEnd++;
145 retVal = std::make_pair(nTypeNameStart, nTypeNameEnd);
147 else
149 retVal = std::make_pair(-1, -1);
151 return retVal;
156 MS Crypto uses the 'S' tag (equal to the 'ST' tag in NSS), but the NSS can't recognise
157 it, so the 'S' tag should be changed to 'ST' tag. However I am not sure if this is necessary
158 anymore, because we provide always the signers certificate when signing. So libmlsec can find
159 the private key based on the provided certificate (X509Certificate element) and does not need
160 the issuer name (X509IssuerName element). The issuer name in the xml signature has also no
161 effect for the signature nor the certificate validation.
162 In many RFCs, for example 4519, on speaks of 'ST'. However, the certificate does not contain
163 strings for type names. Instead it uses OIDs.
166 static OUString replaceTagSWithTagST(OUString const & oldDN)
168 std::pair<sal_Int32, sal_Int32 > pairIndex = findTypeInDN(oldDN, u"S");
170 if (pairIndex.first != -1)
172 return OUString::Concat(oldDN.subView(0, pairIndex.first))+"ST"
173 +oldDN.subView(pairIndex.second);
175 return oldDN;
177 /* end */
179 X509Certificate_MSCryptImpl::X509Certificate_MSCryptImpl() :
180 m_pCertContext( nullptr )
184 X509Certificate_MSCryptImpl::~X509Certificate_MSCryptImpl() {
185 if( m_pCertContext != nullptr ) {
186 CertFreeCertificateContext( m_pCertContext ) ;
190 //Methods from XCertificate
191 sal_Int16 SAL_CALL X509Certificate_MSCryptImpl::getVersion() {
192 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
193 return static_cast<char>(m_pCertContext->pCertInfo->dwVersion) ;
194 } else {
195 return -1 ;
199 css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSerialNumber() {
200 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
201 Sequence< sal_Int8 > serial( m_pCertContext->pCertInfo->SerialNumber.cbData ) ;
202 auto serialRange = asNonConstRange(serial);
203 for( unsigned int i = 0 ; i < m_pCertContext->pCertInfo->SerialNumber.cbData ; i ++ )
204 serialRange[i] = *( m_pCertContext->pCertInfo->SerialNumber.pbData + m_pCertContext->pCertInfo->SerialNumber.cbData - i - 1 ) ;
206 return serial ;
207 } else {
208 return Sequence< sal_Int8 >();
212 OUString SAL_CALL X509Certificate_MSCryptImpl::getIssuerName() {
213 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
214 DWORD cchIssuer = CertNameToStrW(
215 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
216 &( m_pCertContext->pCertInfo->Issuer ),
217 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
218 nullptr, 0
221 // Here the cbIssuer count the last 0x00 , take care.
222 if( cchIssuer != 0 ) {
223 auto issuer = std::make_unique<wchar_t[]>(cchIssuer);
225 cchIssuer = CertNameToStrW(
226 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
227 &( m_pCertContext->pCertInfo->Issuer ),
228 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
229 issuer.get(), cchIssuer
232 if( cchIssuer <= 0 ) {
233 throw RuntimeException() ;
236 if(issuer.get()[cchIssuer -1] == 0) cchIssuer--; //delimit the last 0x00;
237 OUString xIssuer(o3tl::toU(issuer.get()), cchIssuer) ;
239 return replaceTagSWithTagST(xIssuer);
240 } else {
241 return OUString() ;
243 } else {
244 return OUString() ;
248 OUString SAL_CALL X509Certificate_MSCryptImpl::getSubjectName()
250 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
252 DWORD cchSubject = CertNameToStrW(
253 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
254 &( m_pCertContext->pCertInfo->Subject ),
255 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
256 nullptr, 0
259 if( cchSubject != 0 )
261 auto subject = std::make_unique<wchar_t[]>(cchSubject);
263 cchSubject = CertNameToStrW(
264 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
265 &( m_pCertContext->pCertInfo->Subject ),
266 CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
267 subject.get(), cchSubject
270 if( cchSubject <= 0 ) {
271 throw RuntimeException() ;
274 OUString xSubject(o3tl::toU(subject.get()));
276 return replaceTagSWithTagST(xSubject);
277 } else
279 return OUString() ;
282 else
284 return OUString() ;
288 css::util::DateTime SAL_CALL X509Certificate_MSCryptImpl::getNotValidBefore() {
289 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
290 SYSTEMTIME explTime ;
291 DateTime dateTime ;
292 FILETIME localFileTime;
294 if (FileTimeToLocalFileTime(&( m_pCertContext->pCertInfo->NotBefore ), &localFileTime))
296 if( FileTimeToSystemTime( &localFileTime, &explTime ) ) {
297 //Convert the time to readable local time
298 dateTime.NanoSeconds = explTime.wMilliseconds * ::tools::Time::nanoPerMilli ;
299 dateTime.Seconds = explTime.wSecond ;
300 dateTime.Minutes = explTime.wMinute ;
301 dateTime.Hours = explTime.wHour ;
302 dateTime.Day = explTime.wDay ;
303 dateTime.Month = explTime.wMonth ;
304 dateTime.Year = explTime.wYear ;
308 return dateTime ;
309 } else {
310 return DateTime() ;
314 css::util::DateTime SAL_CALL X509Certificate_MSCryptImpl::getNotValidAfter() {
315 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
316 SYSTEMTIME explTime ;
317 DateTime dateTime ;
318 FILETIME localFileTime;
320 if (FileTimeToLocalFileTime(&( m_pCertContext->pCertInfo->NotAfter ), &localFileTime))
322 if( FileTimeToSystemTime( &localFileTime, &explTime ) ) {
323 //Convert the time to readable local time
324 dateTime.NanoSeconds = explTime.wMilliseconds * ::tools::Time::nanoPerMilli ;
325 dateTime.Seconds = explTime.wSecond ;
326 dateTime.Minutes = explTime.wMinute ;
327 dateTime.Hours = explTime.wHour ;
328 dateTime.Day = explTime.wDay ;
329 dateTime.Month = explTime.wMonth ;
330 dateTime.Year = explTime.wYear ;
334 return dateTime ;
335 } else {
336 return DateTime() ;
340 css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getIssuerUniqueID() {
341 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
342 Sequence< sal_Int8 > issuerUid( m_pCertContext->pCertInfo->IssuerUniqueId.cbData ) ;
343 auto issuerUidRange = asNonConstRange(issuerUid);
344 for( unsigned int i = 0 ; i < m_pCertContext->pCertInfo->IssuerUniqueId.cbData; i ++ )
345 issuerUidRange[i] = *( m_pCertContext->pCertInfo->IssuerUniqueId.pbData + i ) ;
347 return issuerUid ;
348 } else {
349 return Sequence< sal_Int8 >();
353 css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSubjectUniqueID() {
354 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
355 Sequence< sal_Int8 > subjectUid( m_pCertContext->pCertInfo->SubjectUniqueId.cbData ) ;
356 auto subjectUidRange = asNonConstRange(subjectUid);
357 for( unsigned int i = 0 ; i < m_pCertContext->pCertInfo->SubjectUniqueId.cbData; i ++ )
358 subjectUidRange[i] = *( m_pCertContext->pCertInfo->SubjectUniqueId.pbData + i ) ;
360 return subjectUid ;
361 } else {
362 return Sequence< sal_Int8 >();
366 css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > SAL_CALL X509Certificate_MSCryptImpl::getExtensions() {
367 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr && m_pCertContext->pCertInfo->cExtension != 0 ) {
368 rtl::Reference<CertificateExtension_XmlSecImpl> xExtn ;
369 Sequence< Reference< XCertificateExtension > > xExtns( m_pCertContext->pCertInfo->cExtension ) ;
370 auto pExtns = xExtns.getArray();
372 for( unsigned int i = 0; i < m_pCertContext->pCertInfo->cExtension; i++ ) {
373 CERT_EXTENSION* pExtn = &(m_pCertContext->pCertInfo->rgExtension[i]) ;
376 OUString objId = OUString::createFromAscii( pExtn->pszObjId );
378 if ( objId == "2.5.29.17" )
379 xExtn = reinterpret_cast<CertificateExtension_XmlSecImpl*>(new SanExtensionImpl());
380 else
381 xExtn = new CertificateExtension_XmlSecImpl;
383 xExtn->setCertExtn( pExtn->Value.pbData, pExtn->Value.cbData, reinterpret_cast<unsigned char*>(pExtn->pszObjId), strlen( pExtn->pszObjId ), pExtn->fCritical ) ;
385 pExtns[i] = xExtn ;
388 return xExtns ;
389 } else {
390 return Sequence< Reference< XCertificateExtension > >();
394 css::uno::Reference< css::security::XCertificateExtension > SAL_CALL X509Certificate_MSCryptImpl::findCertificateExtension( const css::uno::Sequence< sal_Int8 >& /*oid*/ ) {
395 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr && m_pCertContext->pCertInfo->cExtension != 0 ) {
396 rtl::Reference<CertificateExtension_XmlSecImpl> xExtn ;
398 for( unsigned int i = 0; i < m_pCertContext->pCertInfo->cExtension; i++ ) {
399 CERT_EXTENSION* pExtn = &( m_pCertContext->pCertInfo->rgExtension[i] ) ;
401 //TODO: Compare the oid
402 if( false ) {
403 xExtn = new CertificateExtension_XmlSecImpl;
404 xExtn->setCertExtn( pExtn->Value.pbData, pExtn->Value.cbData, reinterpret_cast<unsigned char*>(pExtn->pszObjId), strlen( pExtn->pszObjId ), pExtn->fCritical ) ;
408 return xExtn ;
409 } else {
410 return nullptr ;
415 css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getEncoded() {
416 if( m_pCertContext != nullptr && m_pCertContext->cbCertEncoded > 0 ) {
417 Sequence< sal_Int8 > rawCert( m_pCertContext->cbCertEncoded ) ;
418 auto prawCert = rawCert.getArray();
420 for( unsigned int i = 0 ; i < m_pCertContext->cbCertEncoded ; i ++ )
421 prawCert[i] = *( m_pCertContext->pbCertEncoded + i ) ;
423 return rawCert ;
424 } else {
425 return Sequence< sal_Int8 >();
429 //Helper methods
430 void X509Certificate_MSCryptImpl::setMswcryCert( const CERT_CONTEXT* cert ) {
431 if( m_pCertContext != nullptr ) {
432 CertFreeCertificateContext( m_pCertContext ) ;
433 m_pCertContext = nullptr ;
436 if( cert != nullptr ) {
437 m_pCertContext = CertDuplicateCertificateContext( cert ) ;
441 const CERT_CONTEXT* X509Certificate_MSCryptImpl::getMswcryCert() const {
442 if( m_pCertContext != nullptr ) {
443 return m_pCertContext ;
444 } else {
445 return nullptr ;
449 void X509Certificate_MSCryptImpl::setRawCert( Sequence< sal_Int8 > const & rawCert ) {
450 if( m_pCertContext != nullptr ) {
451 CertFreeCertificateContext( m_pCertContext ) ;
452 m_pCertContext = nullptr ;
455 if( rawCert.getLength() != 0 ) {
456 m_pCertContext = CertCreateCertificateContext( X509_ASN_ENCODING, reinterpret_cast<const sal_uInt8*>(&rawCert[0]), rawCert.getLength() ) ;
460 static OUString findOIDDescription(char const *oid)
462 OUString ouOID = OUString::createFromAscii( oid );
463 for (int i=0; i<nOID; i++)
465 OUString item = OUString::createFromAscii( OIDs[i].oid );
466 if (ouOID == item)
468 return OUString::createFromAscii( OIDs[i].desc );
472 return OUString() ;
475 static css::uno::Sequence< sal_Int8 > getThumbprint(const CERT_CONTEXT* pCertContext, DWORD dwPropId)
477 if( pCertContext != nullptr )
479 DWORD cbData = dwPropId == CERT_SHA256_HASH_PROP_ID ? 32 : 20;
480 unsigned char fingerprint[32];
481 if (CertGetCertificateContextProperty(pCertContext, dwPropId, fingerprint, &cbData))
483 Sequence< sal_Int8 > thumbprint( cbData ) ;
484 auto pthumbprint = thumbprint.getArray();
485 for( unsigned int i = 0 ; i < cbData ; i ++ )
487 pthumbprint[i] = fingerprint[i];
490 return thumbprint;
492 else
494 DWORD e = GetLastError();
495 cbData = e;
499 return Sequence< sal_Int8 >();
502 OUString SAL_CALL X509Certificate_MSCryptImpl::getSubjectPublicKeyAlgorithm()
504 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
506 CRYPT_ALGORITHM_IDENTIFIER algorithm = m_pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm;
507 return findOIDDescription( algorithm.pszObjId ) ;
509 else
511 return OUString() ;
515 css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSubjectPublicKeyValue()
517 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
519 CRYPT_BIT_BLOB publicKey = m_pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey;
521 Sequence< sal_Int8 > key( publicKey.cbData ) ;
522 auto keyRange = asNonConstRange(key);
523 for( unsigned int i = 0 ; i < publicKey.cbData ; i++ )
525 keyRange[i] = *(publicKey.pbData + i) ;
528 return key;
530 else
532 return Sequence< sal_Int8 >();
536 OUString SAL_CALL X509Certificate_MSCryptImpl::getSignatureAlgorithm()
538 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
540 CRYPT_ALGORITHM_IDENTIFIER algorithm = m_pCertContext->pCertInfo->SignatureAlgorithm;
541 return findOIDDescription( algorithm.pszObjId ) ;
543 else
545 return OUString() ;
549 uno::Sequence<sal_Int8> X509Certificate_MSCryptImpl::getSHA256Thumbprint()
551 return getThumbprint(m_pCertContext, CERT_SHA256_HASH_PROP_ID);
554 svl::crypto::SignatureMethodAlgorithm X509Certificate_MSCryptImpl::getSignatureMethodAlgorithm()
556 svl::crypto::SignatureMethodAlgorithm nRet = svl::crypto::SignatureMethodAlgorithm::RSA;
558 if (!m_pCertContext || !m_pCertContext->pCertInfo)
559 return nRet;
561 CRYPT_ALGORITHM_IDENTIFIER algorithm = m_pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm;
562 OString aObjId(algorithm.pszObjId);
563 if (aObjId == szOID_ECC_PUBLIC_KEY)
564 nRet = svl::crypto::SignatureMethodAlgorithm::ECDSA;
566 return nRet;
569 css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSHA1Thumbprint()
571 return getThumbprint(m_pCertContext, CERT_SHA1_HASH_PROP_ID);
574 css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getMD5Thumbprint()
576 return getThumbprint(m_pCertContext, CERT_MD5_HASH_PROP_ID);
579 CertificateKind SAL_CALL X509Certificate_MSCryptImpl::getCertificateKind()
581 return CertificateKind_X509;
584 sal_Int32 SAL_CALL X509Certificate_MSCryptImpl::getCertificateUsage( )
586 sal_Int32 usage =
587 CERT_DATA_ENCIPHERMENT_KEY_USAGE |
588 CERT_DIGITAL_SIGNATURE_KEY_USAGE |
589 CERT_KEY_AGREEMENT_KEY_USAGE |
590 CERT_KEY_CERT_SIGN_KEY_USAGE |
591 CERT_KEY_ENCIPHERMENT_KEY_USAGE |
592 CERT_NON_REPUDIATION_KEY_USAGE |
593 CERT_OFFLINE_CRL_SIGN_KEY_USAGE;
595 if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr && m_pCertContext->pCertInfo->cExtension != 0 )
597 CERT_EXTENSION* pExtn = CertFindExtension(
598 szOID_KEY_USAGE,
599 m_pCertContext->pCertInfo->cExtension,
600 m_pCertContext->pCertInfo->rgExtension);
602 if (pExtn != nullptr)
604 DWORD length = 0;
605 bool rc = CryptDecodeObject(
606 X509_ASN_ENCODING,
607 X509_KEY_USAGE,
608 pExtn->Value.pbData,
609 pExtn->Value.cbData,
611 nullptr,
612 &length);
614 if (!rc)
615 SAL_WARN("xmlsecurity.xmlsec", "CryptDecodeObject failed: " << WindowsErrorString(GetLastError()));
616 else
618 std::vector<char>buffer(length);
620 rc = CryptDecodeObject(
621 X509_ASN_ENCODING,
622 X509_KEY_USAGE,
623 pExtn->Value.pbData,
624 pExtn->Value.cbData,
626 buffer.data(),
627 &length);
629 CRYPT_BIT_BLOB *blob = reinterpret_cast<CRYPT_BIT_BLOB*>(buffer.data());
630 if (!rc)
631 SAL_WARN("xmlsecurity.xmlsec", "CryptDecodeObject failed: " << WindowsErrorString(GetLastError()));
632 else if (blob->cbData == 1)
633 usage = blob->pbData[0];
634 else
635 SAL_WARN("xmlsecurity.xmlsec", "CryptDecodeObject(X509_KEY_USAGE) returned unexpected amount of data: " << blob->cbData);
640 return usage;
643 /* XServiceInfo */
644 OUString SAL_CALL X509Certificate_MSCryptImpl::getImplementationName()
646 return "com.sun.star.xml.security.gpg.XCertificate_MsCryptImpl";
649 /* XServiceInfo */
650 sal_Bool SAL_CALL X509Certificate_MSCryptImpl::supportsService(const OUString& serviceName)
652 return cppu::supportsService(this, serviceName);
655 /* XServiceInfo */
656 Sequence<OUString> SAL_CALL X509Certificate_MSCryptImpl::getSupportedServiceNames()
658 return { OUString() };
661 namespace xmlsecurity {
663 // based on some guesswork and:
664 // https://datatracker.ietf.org/doc/html/rfc1485
665 // https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certnametostra#CERT_X500_NAME_STR
666 // the main problem appears to be that in values NSS uses \ escapes but CryptoAPI requires " quotes around value
667 static OUString CompatDNNSS(OUString const& rDN)
669 OUStringBuffer buf(rDN.getLength());
670 enum { DEFAULT, INVALUE, INQUOTE } state(DEFAULT);
671 for (sal_Int32 i = 0; i < rDN.getLength(); ++i)
673 if (state == DEFAULT)
675 buf.append(rDN[i]);
676 if (rDN[i] == '=')
678 if (rDN.getLength() == i+1)
680 break; // invalid?
682 else
684 buf.append('"');
685 state = INVALUE;
689 else if (state == INVALUE)
691 if (rDN[i] == '+' || rDN[i] == ',' || rDN[i] == ';')
693 buf.append("\"" + OUStringChar(rDN[i]));
694 state = DEFAULT;
696 else if (rDN[i] == '\\')
698 if (rDN.getLength() == i+1)
700 break; // invalid?
702 if (rDN[i+1] == '"')
704 buf.append('"');
706 buf.append(rDN[i+1]);
707 ++i;
709 else
711 buf.append(rDN[i]);
713 if (i+1 == rDN.getLength())
715 buf.append('"');
716 state = DEFAULT;
720 return buf.makeStringAndClear();
723 static bool EncodeDistinguishedName(std::u16string_view const rName, CERT_NAME_BLOB & rBlob)
725 LPCWSTR pszError;
726 if (!CertStrToNameW(X509_ASN_ENCODING,
727 reinterpret_cast<LPCWSTR>(rName.data()), CERT_X500_NAME_STR,
728 nullptr, nullptr, &rBlob.cbData, &pszError))
730 SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << OUString(o3tl::toU(pszError)));
731 return false;
733 rBlob.pbData = new BYTE[rBlob.cbData];
734 if (!CertStrToNameW(X509_ASN_ENCODING,
735 reinterpret_cast<LPCWSTR>(rName.data()), CERT_X500_NAME_STR,
736 nullptr, rBlob.pbData, &rBlob.cbData, &pszError))
738 SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << OUString(o3tl::toU(pszError)));
739 return false;
741 return true;
744 bool EqualDistinguishedNames(
745 std::u16string_view const rName1, std::u16string_view const rName2,
746 EqualMode const eMode)
748 if (eMode == COMPAT_BOTH && !rName1.empty() && rName1 == rName2)
749 { // handle case where both need to be converted
750 return true;
752 CERT_NAME_BLOB blob1;
753 if (!EncodeDistinguishedName(rName1, blob1))
755 return false;
757 CERT_NAME_BLOB blob2;
758 bool ret(false);
759 if (EncodeDistinguishedName(rName2, blob2))
761 ret = CertCompareCertificateName(X509_ASN_ENCODING,
762 &blob1, &blob2) == TRUE;
763 delete[] blob2.pbData;
765 if (!ret && eMode == COMPAT_2ND)
767 CERT_NAME_BLOB blob2compat;
768 if (!EncodeDistinguishedName(CompatDNNSS(OUString(rName2)), blob2compat))
770 delete[] blob1.pbData;
771 return false;
773 ret = CertCompareCertificateName(X509_ASN_ENCODING,
774 &blob1, &blob2compat) == TRUE;
775 delete[] blob2compat.pbData;
777 delete[] blob1.pbData;
778 return ret;
781 } // namespace xmlsecurity
783 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */