1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: x509certificate_mscryptimpl.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_xmlsecurity.hxx"
33 #include <sal/config.h>
35 #include "x509certificate_mscryptimpl.hxx"
36 #include "certificateextension_xmlsecimpl.hxx"
43 #include <rtl/locale.h>
44 #include <osl/nlsupport.h>
45 #include <osl/process.h>
50 using namespace ::com::sun::star::uno
;
51 using namespace ::com::sun::star::security
;
52 using ::rtl::OUString
;
54 using ::com::sun::star::security::XCertificate
;
55 using ::com::sun::star::util::DateTime
;
57 #define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) )
59 /*Resturns the index withing rRawString where sTypeName starts and where it ends.
60 The starting index is pair.first. The ending index in pair.second points
61 one char after the last character of the type.
63 "S" or "CN" (without ""). Do not use spaces at the beginning of the type name.
64 If the type name is not found then pair.first and pair.second are -1.
66 std::pair
< sal_Int32
, sal_Int32
>
67 findTypeInDN(const OUString
& rRawString
, const OUString
& sTypeName
)
69 std::pair
< sal_Int32
, sal_Int32
> retVal
;
70 bool bInEscape
= false;
71 bool bInValue
= false;
73 sal_Int32 nTypeNameStart
= 0;
74 sal_Int32 length
= rRawString
.getLength();
76 for (sal_Int32 i
= 0; i
< length
; i
++)
78 sal_Unicode c
= rRawString
[i
];
84 OUString sType
= rRawString
.copy(nTypeNameStart
, i
- nTypeNameStart
);
86 if (sType
.equalsIgnoreAsciiCase(sTypeName
))
97 //If this is the quote is the first of the couple which enclose the
98 //whole value, because the value contains special characters
99 //then we just drop it. That is, this character must be followed by
100 //a character which is not '"'.
101 if ( i
+ 1 < length
&& rRawString
[i
+1] == '"')
104 bInValue
= !bInValue
; //value is enclosed in " "
108 //This quote is escaped by a preceding quote and therefore is
115 //The comma separate the attribute value pairs.
116 //If the comma is not part of a value (the value would then be enclosed in '"'),
117 //then we have reached the end of the value
120 //The next char is the start of the new type
121 nTypeNameStart
= i
+ 1;
126 //Found the Type Name, but there can still be spaces after the last comma
127 //and the beginning of the type.
132 sal_Unicode c
= rRawString
[nTypeNameStart
];
133 if (c
!= ' ' && c
!= '\t')
138 // search end (one after last letter)
139 sal_Int32 nTypeNameEnd
= nTypeNameStart
;
143 sal_Unicode c
= rRawString
[nTypeNameEnd
];
144 if (c
== ' ' || c
== '\t' || c
== '=')
148 retVal
= std::make_pair(nTypeNameStart
, nTypeNameEnd
);
152 retVal
= std::make_pair(-1, -1);
159 MS Crypto uses the 'S' tag (equal to the 'ST' tag in NSS), but the NSS can't recognise
160 it, so the 'S' tag should be changed to 'ST' tag. However I am not sure if this is necessary
161 anymore, because we provide always the signers certificate when signing. So libmlsec can find
162 the private key based on the provided certificate (X509Certificate element) and does not need
163 the issuer name (X509IssuerName element). The issuer name in the xml signature has also no
164 effect for the signature nor the certificate validation.
165 In many RFCs, for example 4519, on speaks of 'ST'. However, the certificate does not contain
166 strings for type names. Instead it uses OIDs.
169 OUString
replaceTagSWithTagST(OUString oldDN
)
171 std::pair
<sal_Int32
, sal_Int32
> pairIndex
= findTypeInDN(oldDN
, OUSTR("S"));
173 if (pairIndex
.first
!= -1)
175 OUString newDN
= oldDN
.copy(0, pairIndex
.first
);
176 newDN
+= OUSTR("ST");
177 newDN
+= oldDN
.copy(pairIndex
.second
);
184 X509Certificate_MSCryptImpl :: X509Certificate_MSCryptImpl() :
185 m_pCertContext( NULL
)
189 X509Certificate_MSCryptImpl :: ~X509Certificate_MSCryptImpl() {
190 if( m_pCertContext
!= NULL
) {
191 CertFreeCertificateContext( m_pCertContext
) ;
195 //Methods from XCertificate
196 sal_Int16 SAL_CALL
X509Certificate_MSCryptImpl :: getVersion() throw ( ::com::sun::star::uno::RuntimeException
) {
197 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
198 return ( char )m_pCertContext
->pCertInfo
->dwVersion
;
204 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getSerialNumber() throw ( ::com::sun::star::uno::RuntimeException
) {
205 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
206 Sequence
< sal_Int8
> serial( m_pCertContext
->pCertInfo
->SerialNumber
.cbData
) ;
207 for( unsigned int i
= 0 ; i
< m_pCertContext
->pCertInfo
->SerialNumber
.cbData
; i
++ )
208 serial
[i
] = *( m_pCertContext
->pCertInfo
->SerialNumber
.pbData
+ m_pCertContext
->pCertInfo
->SerialNumber
.cbData
- i
- 1 ) ;
216 ::rtl::OUString SAL_CALL
X509Certificate_MSCryptImpl :: getIssuerName() throw ( ::com::sun::star::uno::RuntimeException
) {
217 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
221 cbIssuer
= CertNameToStr(
222 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
223 &( m_pCertContext
->pCertInfo
->Issuer
),
224 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
228 // Here the cbIssuer count the last 0x00 , take care.
229 if( cbIssuer
!= 0 ) {
230 issuer
= new char[ cbIssuer
] ;
232 throw RuntimeException() ;
234 cbIssuer
= CertNameToStr(
235 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
236 &( m_pCertContext
->pCertInfo
->Issuer
),
237 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
241 if( cbIssuer
<= 0 ) {
243 throw RuntimeException() ;
246 // By CP , for correct encoding
247 sal_uInt16 encoding
;
248 rtl_Locale
*pLocale
= NULL
;
249 osl_getProcessLocale( &pLocale
) ;
250 encoding
= osl_getTextEncodingFromLocale( pLocale
) ;
253 if(issuer
[cbIssuer
-1] == 0) cbIssuer
--; //delimit the last 0x00;
254 OUString
xIssuer(issuer
, cbIssuer
,encoding
) ; //By CP
257 return replaceTagSWithTagST(xIssuer
);
266 ::rtl::OUString SAL_CALL
X509Certificate_MSCryptImpl :: getSubjectName() throw ( ::com::sun::star::uno::RuntimeException
) {
267 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
271 cbSubject
= CertNameToStr(
272 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
273 &( m_pCertContext
->pCertInfo
->Subject
),
274 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
278 if( cbSubject
!= 0 ) {
279 subject
= new char[ cbSubject
] ;
280 if( subject
== NULL
)
281 throw RuntimeException() ;
283 cbSubject
= CertNameToStr(
284 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
285 &( m_pCertContext
->pCertInfo
->Subject
),
286 CERT_X500_NAME_STR
| CERT_NAME_STR_REVERSE_FLAG
,
290 if( cbSubject
<= 0 ) {
292 throw RuntimeException() ;
295 // By CP , for correct encoding
296 sal_uInt16 encoding
;
297 rtl_Locale
*pLocale
= NULL
;
298 osl_getProcessLocale( &pLocale
) ;
299 encoding
= osl_getTextEncodingFromLocale( pLocale
) ;
302 if(subject
[cbSubject
-1] == 0) cbSubject
--; //delimit the last 0x00;
303 OUString
xSubject(subject
, cbSubject
,encoding
) ; //By CP
306 return replaceTagSWithTagST(xSubject
);
315 ::com::sun::star::util::DateTime SAL_CALL
X509Certificate_MSCryptImpl :: getNotValidBefore() 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
->NotBefore
), &localFileTime
))
323 if( FileTimeToSystemTime( &localFileTime
, &explTime
) ) {
324 //Convert the time to readable local time
325 dateTime
.HundredthSeconds
= explTime
.wMilliseconds
/ 100 ;
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::util::DateTime SAL_CALL
X509Certificate_MSCryptImpl :: getNotValidAfter() throw ( ::com::sun::star::uno::RuntimeException
) {
342 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
343 SYSTEMTIME explTime
;
345 FILETIME localFileTime
;
347 if (FileTimeToLocalFileTime(&( m_pCertContext
->pCertInfo
->NotAfter
), &localFileTime
))
349 if( FileTimeToSystemTime( &localFileTime
, &explTime
) ) {
350 //Convert the time to readable local time
351 dateTime
.HundredthSeconds
= explTime
.wMilliseconds
/ 100 ;
352 dateTime
.Seconds
= explTime
.wSecond
;
353 dateTime
.Minutes
= explTime
.wMinute
;
354 dateTime
.Hours
= explTime
.wHour
;
355 dateTime
.Day
= explTime
.wDay
;
356 dateTime
.Month
= explTime
.wMonth
;
357 dateTime
.Year
= explTime
.wYear
;
367 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getIssuerUniqueID() throw ( ::com::sun::star::uno::RuntimeException
) {
368 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
369 Sequence
< sal_Int8
> issuerUid( m_pCertContext
->pCertInfo
->IssuerUniqueId
.cbData
) ;
370 for( unsigned int i
= 0 ; i
< m_pCertContext
->pCertInfo
->IssuerUniqueId
.cbData
; i
++ )
371 issuerUid
[i
] = *( m_pCertContext
->pCertInfo
->IssuerUniqueId
.pbData
+ i
) ;
379 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getSubjectUniqueID() throw ( ::com::sun::star::uno::RuntimeException
) {
380 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
) {
381 Sequence
< sal_Int8
> subjectUid( m_pCertContext
->pCertInfo
->SubjectUniqueId
.cbData
) ;
382 for( unsigned int i
= 0 ; i
< m_pCertContext
->pCertInfo
->SubjectUniqueId
.cbData
; i
++ )
383 subjectUid
[i
] = *( m_pCertContext
->pCertInfo
->SubjectUniqueId
.pbData
+ i
) ;
391 ::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
) {
392 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
&& m_pCertContext
->pCertInfo
->cExtension
!= 0 ) {
393 CertificateExtension_XmlSecImpl
* xExtn
;
394 CERT_EXTENSION
* pExtn
;
395 Sequence
< Reference
< XCertificateExtension
> > xExtns( m_pCertContext
->pCertInfo
->cExtension
) ;
397 for( unsigned int i
= 0; i
< m_pCertContext
->pCertInfo
->cExtension
; i
++ ) {
398 pExtn
= &(m_pCertContext
->pCertInfo
->rgExtension
[i
]) ;
400 xExtn
= new CertificateExtension_XmlSecImpl() ;
402 throw RuntimeException() ;
404 xExtn
->setCertExtn( pExtn
->Value
.pbData
, pExtn
->Value
.cbData
, ( unsigned char* )pExtn
->pszObjId
, strlen( pExtn
->pszObjId
), sal::static_int_cast
<sal_Bool
>(pExtn
->fCritical
) ) ;
415 ::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
) {
416 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
&& m_pCertContext
->pCertInfo
->cExtension
!= 0 ) {
417 CertificateExtension_XmlSecImpl
* xExtn
;
418 CERT_EXTENSION
* pExtn
;
419 Sequence
< Reference
< XCertificateExtension
> > xExtns( m_pCertContext
->pCertInfo
->cExtension
) ;
422 for( unsigned int i
= 0; i
< m_pCertContext
->pCertInfo
->cExtension
; i
++ ) {
423 pExtn
= &( m_pCertContext
->pCertInfo
->rgExtension
[i
] ) ;
425 //TODO: Compare the oid
427 xExtn
= new CertificateExtension_XmlSecImpl() ;
429 throw RuntimeException() ;
431 xExtn
->setCertExtn( pExtn
->Value
.pbData
, pExtn
->Value
.cbData
, ( unsigned char* )pExtn
->pszObjId
, strlen( pExtn
->pszObjId
), sal::static_int_cast
<sal_Bool
>(pExtn
->fCritical
) ) ;
442 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl :: getEncoded() throw ( ::com::sun::star::uno::RuntimeException
) {
443 if( m_pCertContext
!= NULL
&& m_pCertContext
->cbCertEncoded
> 0 ) {
444 Sequence
< sal_Int8
> rawCert( m_pCertContext
->cbCertEncoded
) ;
446 for( unsigned int i
= 0 ; i
< m_pCertContext
->cbCertEncoded
; i
++ )
447 rawCert
[i
] = *( m_pCertContext
->pbCertEncoded
+ i
) ;
456 void X509Certificate_MSCryptImpl :: setMswcryCert( const CERT_CONTEXT
* cert
) {
457 if( m_pCertContext
!= NULL
) {
458 CertFreeCertificateContext( m_pCertContext
) ;
459 m_pCertContext
= NULL
;
463 m_pCertContext
= CertDuplicateCertificateContext( cert
) ;
467 const CERT_CONTEXT
* X509Certificate_MSCryptImpl :: getMswcryCert() const {
468 if( m_pCertContext
!= NULL
) {
469 return m_pCertContext
;
475 void X509Certificate_MSCryptImpl :: setRawCert( Sequence
< sal_Int8
> rawCert
) throw ( ::com::sun::star::uno::RuntimeException
) {
476 if( m_pCertContext
!= NULL
) {
477 CertFreeCertificateContext( m_pCertContext
) ;
478 m_pCertContext
= NULL
;
481 if( rawCert
.getLength() != 0 ) {
482 m_pCertContext
= CertCreateCertificateContext( X509_ASN_ENCODING
, ( const BYTE
* )&rawCert
[0], rawCert
.getLength() ) ;
487 sal_Int64 SAL_CALL
X509Certificate_MSCryptImpl :: getSomething( const Sequence
< sal_Int8
>& aIdentifier
) throw( RuntimeException
) {
488 if( aIdentifier
.getLength() == 16 && 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(), aIdentifier
.getConstArray(), 16 ) ) {
489 return ( sal_Int64
)this ;
494 /* XUnoTunnel extension */
495 const Sequence
< sal_Int8
>& X509Certificate_MSCryptImpl :: getUnoTunnelId() {
496 static Sequence
< sal_Int8
>* pSeq
= 0 ;
498 ::osl::Guard
< ::osl::Mutex
> aGuard( ::osl::Mutex::getGlobalMutex() ) ;
500 static Sequence
< sal_Int8
> aSeq( 16 ) ;
501 rtl_createUuid( ( sal_uInt8
* )aSeq
.getArray() , 0 , sal_True
) ;
508 /* XUnoTunnel extension */
509 X509Certificate_MSCryptImpl
* X509Certificate_MSCryptImpl :: getImplementation( const Reference
< XInterface
> xObj
) {
510 Reference
< XUnoTunnel
> xUT( xObj
, UNO_QUERY
) ;
512 return ( X509Certificate_MSCryptImpl
* )xUT
->getSomething( getUnoTunnelId() ) ;
518 ::rtl::OUString
findOIDDescription(char *oid
)
520 OUString ouOID
= OUString::createFromAscii( oid
);
521 for (int i
=0; i
<nOID
; i
++)
523 OUString item
= OUString::createFromAscii( OIDs
[i
].oid
);
526 return OUString::createFromAscii( OIDs
[i
].desc
);
533 ::com::sun::star::uno::Sequence
< sal_Int8
> getThumbprint(const CERT_CONTEXT
* pCertContext
, DWORD dwPropId
)
535 if( pCertContext
!= NULL
)
538 unsigned char fingerprint
[20];
539 if (CertGetCertificateContextProperty(pCertContext
, dwPropId
, (void*)fingerprint
, &cbData
))
541 Sequence
< sal_Int8
> thumbprint( cbData
) ;
542 for( unsigned int i
= 0 ; i
< cbData
; i
++ )
544 thumbprint
[i
] = fingerprint
[i
];
551 DWORD e
= GetLastError();
559 ::rtl::OUString SAL_CALL
X509Certificate_MSCryptImpl::getSubjectPublicKeyAlgorithm()
560 throw ( ::com::sun::star::uno::RuntimeException
)
562 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
)
564 CRYPT_ALGORITHM_IDENTIFIER algorithm
= m_pCertContext
->pCertInfo
->SubjectPublicKeyInfo
.Algorithm
;
565 return findOIDDescription( algorithm
.pszObjId
) ;
573 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl::getSubjectPublicKeyValue()
574 throw ( ::com::sun::star::uno::RuntimeException
)
576 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
)
578 CRYPT_BIT_BLOB publicKey
= m_pCertContext
->pCertInfo
->SubjectPublicKeyInfo
.PublicKey
;
580 Sequence
< sal_Int8
> key( publicKey
.cbData
) ;
581 for( unsigned int i
= 0 ; i
< publicKey
.cbData
; i
++ )
583 key
[i
] = *(publicKey
.pbData
+ i
) ;
594 ::rtl::OUString SAL_CALL
X509Certificate_MSCryptImpl::getSignatureAlgorithm()
595 throw ( ::com::sun::star::uno::RuntimeException
)
597 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
)
599 CRYPT_ALGORITHM_IDENTIFIER algorithm
= m_pCertContext
->pCertInfo
->SignatureAlgorithm
;
600 return findOIDDescription( algorithm
.pszObjId
) ;
608 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl::getSHA1Thumbprint()
609 throw ( ::com::sun::star::uno::RuntimeException
)
611 return getThumbprint(m_pCertContext
, CERT_SHA1_HASH_PROP_ID
);
614 ::com::sun::star::uno::Sequence
< sal_Int8
> SAL_CALL
X509Certificate_MSCryptImpl::getMD5Thumbprint()
615 throw ( ::com::sun::star::uno::RuntimeException
)
617 return getThumbprint(m_pCertContext
, CERT_MD5_HASH_PROP_ID
);
620 sal_Int32 SAL_CALL
X509Certificate_MSCryptImpl::getCertificateUsage( )
621 throw ( ::com::sun::star::uno::RuntimeException
)
624 CERT_DATA_ENCIPHERMENT_KEY_USAGE
|
625 CERT_DIGITAL_SIGNATURE_KEY_USAGE
|
626 CERT_KEY_AGREEMENT_KEY_USAGE
|
627 CERT_KEY_CERT_SIGN_KEY_USAGE
|
628 CERT_KEY_ENCIPHERMENT_KEY_USAGE
|
629 CERT_NON_REPUDIATION_KEY_USAGE
|
630 CERT_OFFLINE_CRL_SIGN_KEY_USAGE
;
632 if( m_pCertContext
!= NULL
&& m_pCertContext
->pCertInfo
!= NULL
&& m_pCertContext
->pCertInfo
->cExtension
!= 0 )
634 CERT_EXTENSION
* pExtn
= CertFindExtension(
636 m_pCertContext
->pCertInfo
->cExtension
,
637 m_pCertContext
->pCertInfo
->rgExtension
);
641 CERT_KEY_USAGE_RESTRICTION_INFO keyUsage
;
642 DWORD length
= sizeof(CERT_KEY_USAGE_RESTRICTION_INFO
);
644 bool rc
= CryptDecodeObject(
649 CRYPT_DECODE_NOCOPY_FLAG
,
653 if (rc
&& keyUsage
.RestrictedKeyUsage
.cbData
!=0)
655 usage
= (sal_Int32
)keyUsage
.RestrictedKeyUsage
.pbData
;