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 .
22 #include <sal/config.h>
23 #include <comphelper/servicehelper.hxx>
24 #include "x509certificate_mscryptimpl.hxx"
25 #include "certificateextension_xmlsecimpl.hxx"
26 #include "sanextension_mscryptimpl.hxx"
30 #include <rtl/locale.h>
31 #include <osl/nlsupport.h>
32 #include <osl/process.h>
34 #include <tools/time.hxx>
36 using namespace ::com::sun::star::uno
;
37 using namespace ::com::sun::star::security
;
39 using ::com::sun::star::security::XCertificate
;
40 using ::com::sun::star::util::DateTime
;
42 /*Resturns the index within rRawString where sTypeName starts and where it ends.
43 The starting index is pair.first. The ending index in pair.second points
44 one char after the last character of the type.
46 "S" or "CN" (without ""). Do not use spaces at the beginning of the type name.
47 If the type name is not found then pair.first and pair.second are -1.
49 std::pair
< sal_Int32
, sal_Int32
>
50 findTypeInDN(const OUString
& rRawString
, const OUString
& sTypeName
)
52 std::pair
< sal_Int32
, sal_Int32
> retVal
;
53 bool bInEscape
= false;
54 bool bInValue
= false;
56 sal_Int32 nTypeNameStart
= 0;
57 sal_Int32 length
= rRawString
.getLength();
59 for (sal_Int32 i
= 0; i
< length
; i
++)
61 sal_Unicode c
= rRawString
[i
];
67 OUString sType
= rRawString
.copy(nTypeNameStart
, i
- nTypeNameStart
);
69 if (sType
.equalsIgnoreAsciiCase(sTypeName
))
80 //If this is the quote is the first of the couple which enclose the
81 //whole value, because the value contains special characters
82 //then we just drop it. That is, this character must be followed by
83 //a character which is not '"'.
84 if ( i
+ 1 < length
&& rRawString
[i
+1] == '"')
87 bInValue
= !bInValue
; //value is enclosed in " "
91 //This quote is escaped by a preceding quote and therefore is
96 else if (c
== ',' || c
== '+')
98 //The comma separate the attribute value pairs.
99 //If the comma is not part of a value (the value would then be enclosed in '"'),
100 //then we have reached the end of the value
103 //The next char is the start of the new type
104 nTypeNameStart
= i
+ 1;
109 //Found the Type Name, but there can still be spaces after the last comma
110 //and the beginning of the type.
115 sal_Unicode c
= rRawString
[nTypeNameStart
];
116 if (c
!= ' ' && c
!= '\t')
121 // search end (one after last letter)
122 sal_Int32 nTypeNameEnd
= nTypeNameStart
;
126 sal_Unicode c
= rRawString
[nTypeNameEnd
];
127 if (c
== ' ' || c
== '\t' || c
== '=')
131 retVal
= std::make_pair(nTypeNameStart
, nTypeNameEnd
);
135 retVal
= std::make_pair(-1, -1);
142 MS Crypto uses the 'S' tag (equal to the 'ST' tag in NSS), but the NSS can't recognise
143 it, so the 'S' tag should be changed to 'ST' tag. However I am not sure if this is necessary
144 anymore, because we provide always the signers certificate when signing. So libmlsec can find
145 the private key based on the provided certificate (X509Certificate element) and does not need
146 the issuer name (X509IssuerName element). The issuer name in the xml signature has also no
147 effect for the signature nor the certificate validation.
148 In many RFCs, for example 4519, on speaks of 'ST'. However, the certificate does not contain
149 strings for type names. Instead it uses OIDs.
152 OUString
replaceTagSWithTagST(OUString oldDN
)
154 std::pair
<sal_Int32
, sal_Int32
> pairIndex
= findTypeInDN(oldDN
, "S");
156 if (pairIndex
.first
!= -1)
158 OUString newDN
= oldDN
.copy(0, pairIndex
.first
);
160 newDN
+= oldDN
.copy(pairIndex
.second
);
167 X509Certificate_MSCryptImpl :: X509Certificate_MSCryptImpl() :
168 m_pCertContext( NULL
)
172 X509Certificate_MSCryptImpl :: ~X509Certificate_MSCryptImpl() {
173 if( m_pCertContext
!= NULL
) {
174 CertFreeCertificateContext( m_pCertContext
) ;
178 //Methods from XCertificate
179 sal_Int16 SAL_CALL
X509Certificate_MSCryptImpl :: getVersion() throw ( ::com::sun::star::uno::RuntimeException
) {
180 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
181 return ( char )m_pCertContext
->pCertInfo
->dwVersion
;
187 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getSerialNumber() throw ( ::com::sun::star::uno::RuntimeException
) {
188 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
189 Sequence
< sal_Int8
> serial( m_pCertContext
->pCertInfo
->SerialNumber
.cbData
) ;
190 for( unsigned int i
= 0 ; i
< m_pCertContext
->pCertInfo
->SerialNumber
.cbData
; i
++ )
191 serial
[i
] = *( m_pCertContext
->pCertInfo
->SerialNumber
.pbData
+ m_pCertContext
->pCertInfo
->SerialNumber
.cbData
- i
- 1 ) ;
195 return Sequence
< sal_Int8
>();
199 OUString SAL_CALL
X509Certificate_MSCryptImpl :: getIssuerName() throw ( ::com::sun::star::uno::RuntimeException
) {
200 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
203 cbIssuer
= CertNameToStr(
204 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
205 &( m_pCertContext
->pCertInfo
->Issuer
),
206 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
210 // Here the cbIssuer count the last 0x00 , take care.
211 if( cbIssuer
!= 0 ) {
212 char* issuer
= new char[ cbIssuer
] ;
214 cbIssuer
= CertNameToStr(
215 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
216 &( m_pCertContext
->pCertInfo
->Issuer
),
217 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
221 if( cbIssuer
<= 0 ) {
223 throw RuntimeException() ;
226 // for correct encoding
227 sal_uInt16 encoding
;
228 rtl_Locale
*pLocale
= NULL
;
229 osl_getProcessLocale( &pLocale
) ;
230 encoding
= osl_getTextEncodingFromLocale( pLocale
) ;
232 if(issuer
[cbIssuer
-1] == 0) cbIssuer
--; //delimit the last 0x00;
233 OUString
xIssuer(issuer
, cbIssuer
,encoding
) ;
236 return replaceTagSWithTagST(xIssuer
);
245 OUString SAL_CALL
X509Certificate_MSCryptImpl :: getSubjectName() throw ( ::com::sun::star::uno::RuntimeException
)
247 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
)
251 cbSubject
= CertNameToStrW(
252 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
253 &( m_pCertContext
->pCertInfo
->Subject
),
254 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
260 wchar_t* subject
= new wchar_t[ cbSubject
] ;
262 cbSubject
= CertNameToStrW(
263 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
264 &( m_pCertContext
->pCertInfo
->Subject
),
265 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
269 if( cbSubject
<= 0 ) {
271 throw RuntimeException() ;
274 OUString
xSubject(reinterpret_cast<const sal_Unicode
*>(subject
));
277 return replaceTagSWithTagST(xSubject
);
289 ::com::sun::star::util::DateTime SAL_CALL
X509Certificate_MSCryptImpl :: getNotValidBefore() throw ( ::com::sun::star::uno::RuntimeException
) {
290 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
291 SYSTEMTIME explTime
;
293 FILETIME localFileTime
;
295 if (FileTimeToLocalFileTime(&( m_pCertContext
->pCertInfo
->NotBefore
), &localFileTime
))
297 if( FileTimeToSystemTime( &localFileTime
, &explTime
) ) {
298 //Convert the time to readable local time
299 dateTime
.NanoSeconds
= explTime
.wMilliseconds
* ::tools::Time::nanoPerMilli
;
300 dateTime
.Seconds
= explTime
.wSecond
;
301 dateTime
.Minutes
= explTime
.wMinute
;
302 dateTime
.Hours
= explTime
.wHour
;
303 dateTime
.Day
= explTime
.wDay
;
304 dateTime
.Month
= explTime
.wMonth
;
305 dateTime
.Year
= explTime
.wYear
;
315 ::com::sun::star::util::DateTime SAL_CALL
X509Certificate_MSCryptImpl :: getNotValidAfter() throw ( ::com::sun::star::uno::RuntimeException
) {
316 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
317 SYSTEMTIME explTime
;
319 FILETIME localFileTime
;
321 if (FileTimeToLocalFileTime(&( m_pCertContext
->pCertInfo
->NotAfter
), &localFileTime
))
323 if( FileTimeToSystemTime( &localFileTime
, &explTime
) ) {
324 //Convert the time to readable local time
325 dateTime
.NanoSeconds
= explTime
.wMilliseconds
* ::tools::Time::nanoPerMilli
;
326 dateTime
.Seconds
= explTime
.wSecond
;
327 dateTime
.Minutes
= explTime
.wMinute
;
328 dateTime
.Hours
= explTime
.wHour
;
329 dateTime
.Day
= explTime
.wDay
;
330 dateTime
.Month
= explTime
.wMonth
;
331 dateTime
.Year
= explTime
.wYear
;
341 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getIssuerUniqueID() throw ( ::com::sun::star::uno::RuntimeException
) {
342 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
343 Sequence
< sal_Int8
> issuerUid( m_pCertContext
->pCertInfo
->IssuerUniqueId
.cbData
) ;
344 for( unsigned int i
= 0 ; i
< m_pCertContext
->pCertInfo
->IssuerUniqueId
.cbData
; i
++ )
345 issuerUid
[i
] = *( m_pCertContext
->pCertInfo
->IssuerUniqueId
.pbData
+ i
) ;
349 return Sequence
< sal_Int8
>();
353 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getSubjectUniqueID() throw ( ::com::sun::star::uno::RuntimeException
) {
354 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
355 Sequence
< sal_Int8
> subjectUid( m_pCertContext
->pCertInfo
->SubjectUniqueId
.cbData
) ;
356 for( unsigned int i
= 0 ; i
< m_pCertContext
->pCertInfo
->SubjectUniqueId
.cbData
; i
++ )
357 subjectUid
[i
] = *( m_pCertContext
->pCertInfo
->SubjectUniqueId
.pbData
+ i
) ;
361 return Sequence
< sal_Int8
>();
365 ::com::sun::star::uno::Sequence
< ::com::sun::star::uno::Reference
< ::com::sun::star::security::XCertificateExtension
> > SAL_CALL
X509Certificate_MSCryptImpl :: getExtensions() throw ( ::com::sun::star::uno::RuntimeException
) {
366 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
&& m_pCertContext
->pCertInfo
->cExtension
!= 0 ) {
367 CertificateExtension_XmlSecImpl
* xExtn
;
368 Sequence
< Reference
< XCertificateExtension
> > xExtns( m_pCertContext
->pCertInfo
->cExtension
) ;
370 for( unsigned int i
= 0; i
< m_pCertContext
->pCertInfo
->cExtension
; i
++ ) {
371 CERT_EXTENSION
* pExtn
= &(m_pCertContext
->pCertInfo
->rgExtension
[i
]) ;
374 OUString objId
= OUString::createFromAscii( pExtn
->pszObjId
);
376 if ( objId
== "2.5.29.17" )
377 xExtn
= (CertificateExtension_XmlSecImpl
*) new SanExtensionImpl() ;
379 xExtn
= new CertificateExtension_XmlSecImpl() ;
381 throw RuntimeException() ;
383 xExtn
->setCertExtn( pExtn
->Value
.pbData
, pExtn
->Value
.cbData
, ( unsigned char* )pExtn
->pszObjId
, strlen( pExtn
->pszObjId
), pExtn
->fCritical
) ;
390 return Sequence
< Reference
< XCertificateExtension
> >();
394 ::com::sun::star::uno::Reference
< ::com::sun::star::security::XCertificateExtension
> SAL_CALL
X509Certificate_MSCryptImpl :: findCertificateExtension( const ::com::sun::star::uno::Sequence
< sal_Int8
>& /*oid*/ ) throw (::com::sun::star::uno::RuntimeException
) {
395 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
&& m_pCertContext
->pCertInfo
->cExtension
!= 0 ) {
396 CertificateExtension_XmlSecImpl
* xExtn
;
397 Sequence
< Reference
< XCertificateExtension
> > xExtns( m_pCertContext
->pCertInfo
->cExtension
) ;
400 for( unsigned int i
= 0; i
< m_pCertContext
->pCertInfo
->cExtension
; i
++ ) {
401 CERT_EXTENSION
* pExtn
= &( m_pCertContext
->pCertInfo
->rgExtension
[i
] ) ;
403 //TODO: Compare the oid
405 xExtn
= new CertificateExtension_XmlSecImpl() ;
407 throw RuntimeException() ;
409 xExtn
->setCertExtn( pExtn
->Value
.pbData
, pExtn
->Value
.cbData
, ( unsigned char* )pExtn
->pszObjId
, strlen( pExtn
->pszObjId
), pExtn
->fCritical
) ;
420 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getEncoded() throw ( ::com::sun::star::uno::RuntimeException
) {
421 if( m_pCertContext
!= NULL
&& m_pCertContext
->cbCertEncoded
> 0 ) {
422 Sequence
< sal_Int8
> rawCert( m_pCertContext
->cbCertEncoded
) ;
424 for( unsigned int i
= 0 ; i
< m_pCertContext
->cbCertEncoded
; i
++ )
425 rawCert
[i
] = *( m_pCertContext
->pbCertEncoded
+ i
) ;
429 return Sequence
< sal_Int8
>();
434 void X509Certificate_MSCryptImpl :: setMswcryCert( const CERT_CONTEXT
* cert
) {
435 if( m_pCertContext
!= NULL
) {
436 CertFreeCertificateContext( m_pCertContext
) ;
437 m_pCertContext
= NULL
;
441 m_pCertContext
= CertDuplicateCertificateContext( cert
) ;
445 const CERT_CONTEXT
* X509Certificate_MSCryptImpl :: getMswcryCert() const {
446 if( m_pCertContext
!= NULL
) {
447 return m_pCertContext
;
453 void X509Certificate_MSCryptImpl :: setRawCert( Sequence
< sal_Int8
> rawCert
) throw ( ::com::sun::star::uno::RuntimeException
) {
454 if( m_pCertContext
!= NULL
) {
455 CertFreeCertificateContext( m_pCertContext
) ;
456 m_pCertContext
= NULL
;
459 if( rawCert
.getLength() != 0 ) {
460 m_pCertContext
= CertCreateCertificateContext( X509_ASN_ENCODING
, ( const sal_uInt8
* )&rawCert
[0], rawCert
.getLength() ) ;
465 sal_Int64 SAL_CALL
X509Certificate_MSCryptImpl :: getSomething( const Sequence
< sal_Int8
>& aIdentifier
) throw( RuntimeException
) {
466 if( aIdentifier
.getLength() == 16 && 0 == memcmp( getUnoTunnelId().getConstArray(), aIdentifier
.getConstArray(), 16 ) ) {
467 return ( sal_Int64
)this ;
472 /* XUnoTunnel extension */
476 class theX509Certificate_MSCryptImplUnoTunnelId
: public rtl::Static
< UnoTunnelIdInit
, theX509Certificate_MSCryptImplUnoTunnelId
> {};
479 const Sequence
< sal_Int8
>& X509Certificate_MSCryptImpl :: getUnoTunnelId() {
480 return theX509Certificate_MSCryptImplUnoTunnelId::get().getSeq();
483 /* XUnoTunnel extension */
484 X509Certificate_MSCryptImpl
* X509Certificate_MSCryptImpl :: getImplementation( const Reference
< XInterface
>& rObj
) {
485 Reference
< XUnoTunnel
> xUT( rObj
, UNO_QUERY
) ;
487 return ( X509Certificate_MSCryptImpl
* )xUT
->getSomething( getUnoTunnelId() ) ;
492 OUString
findOIDDescription(char *oid
)
494 OUString ouOID
= OUString::createFromAscii( oid
);
495 for (int i
=0; i
<nOID
; i
++)
497 OUString item
= OUString::createFromAscii( OIDs
[i
].oid
);
500 return OUString::createFromAscii( OIDs
[i
].desc
);
507 ::com::sun::star::uno::Sequence
< sal_Int8
> getThumbprint(const CERT_CONTEXT
* pCertContext
, DWORD dwPropId
)
509 if( pCertContext
!= NULL
)
512 unsigned char fingerprint
[20];
513 if (CertGetCertificateContextProperty(pCertContext
, dwPropId
, (void*)fingerprint
, &cbData
))
515 Sequence
< sal_Int8
> thumbprint( cbData
) ;
516 for( unsigned int i
= 0 ; i
< cbData
; i
++ )
518 thumbprint
[i
] = fingerprint
[i
];
525 DWORD e
= GetLastError();
530 return Sequence
< sal_Int8
>();
533 OUString SAL_CALL
X509Certificate_MSCryptImpl::getSubjectPublicKeyAlgorithm()
534 throw ( ::com::sun::star::uno::RuntimeException
)
536 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
)
538 CRYPT_ALGORITHM_IDENTIFIER algorithm
= m_pCertContext
->pCertInfo
->SubjectPublicKeyInfo
.Algorithm
;
539 return findOIDDescription( algorithm
.pszObjId
) ;
547 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl::getSubjectPublicKeyValue()
548 throw ( ::com::sun::star::uno::RuntimeException
)
550 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
)
552 CRYPT_BIT_BLOB publicKey
= m_pCertContext
->pCertInfo
->SubjectPublicKeyInfo
.PublicKey
;
554 Sequence
< sal_Int8
> key( publicKey
.cbData
) ;
555 for( unsigned int i
= 0 ; i
< publicKey
.cbData
; i
++ )
557 key
[i
] = *(publicKey
.pbData
+ i
) ;
564 return Sequence
< sal_Int8
>();
568 OUString SAL_CALL
X509Certificate_MSCryptImpl::getSignatureAlgorithm()
569 throw ( ::com::sun::star::uno::RuntimeException
)
571 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
)
573 CRYPT_ALGORITHM_IDENTIFIER algorithm
= m_pCertContext
->pCertInfo
->SignatureAlgorithm
;
574 return findOIDDescription( algorithm
.pszObjId
) ;
582 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl::getSHA1Thumbprint()
583 throw ( ::com::sun::star::uno::RuntimeException
)
585 return getThumbprint(m_pCertContext
, CERT_SHA1_HASH_PROP_ID
);
588 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl::getMD5Thumbprint()
589 throw ( ::com::sun::star::uno::RuntimeException
)
591 return getThumbprint(m_pCertContext
, CERT_MD5_HASH_PROP_ID
);
594 sal_Int32 SAL_CALL
X509Certificate_MSCryptImpl::getCertificateUsage( )
595 throw ( ::com::sun::star::uno::RuntimeException
)
598 CERT_DATA_ENCIPHERMENT_KEY_USAGE
|
599 CERT_DIGITAL_SIGNATURE_KEY_USAGE
|
600 CERT_KEY_AGREEMENT_KEY_USAGE
|
601 CERT_KEY_CERT_SIGN_KEY_USAGE
|
602 CERT_KEY_ENCIPHERMENT_KEY_USAGE
|
603 CERT_NON_REPUDIATION_KEY_USAGE
|
604 CERT_OFFLINE_CRL_SIGN_KEY_USAGE
;
606 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
&& m_pCertContext
->pCertInfo
->cExtension
!= 0 )
608 CERT_EXTENSION
* pExtn
= CertFindExtension(
610 m_pCertContext
->pCertInfo
->cExtension
,
611 m_pCertContext
->pCertInfo
->rgExtension
);
615 CERT_KEY_USAGE_RESTRICTION_INFO keyUsage
;
616 DWORD length
= sizeof(CERT_KEY_USAGE_RESTRICTION_INFO
);
618 bool rc
= CryptDecodeObject(
623 CRYPT_DECODE_NOCOPY_FLAG
,
627 if (rc
&& keyUsage
.RestrictedKeyUsage
.cbData
!=0)
629 usage
= (sal_Int32
)keyUsage
.RestrictedKeyUsage
.pbData
;
637 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */