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/.
10 #include <sal/config.h>
14 #include <svl/cryptosign.hxx>
15 #include <svl/sigstruct.hxx>
16 #include <config_crypto.h>
19 #include <curlinit.hxx>
22 #include <rtl/character.hxx>
23 #include <rtl/strbuf.hxx>
24 #include <rtl/string.hxx>
25 #include <sal/log.hxx>
26 #include <tools/datetime.hxx>
27 #include <tools/stream.hxx>
28 #include <comphelper/base64.hxx>
29 #include <comphelper/hash.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/random.hxx>
32 #include <comphelper/scopeguard.hxx>
33 #include <comphelper/lok.hxx>
34 #include <com/sun/star/security/XCertificate.hpp>
35 #include <com/sun/star/uno/Sequence.hxx>
36 #include <o3tl/char16_t2wchar_t.hxx>
39 // NSS headers for PDF signing
49 // We use curl for RFC3161 time stamp requests
50 #include <curl/curl.h>
52 #include <com/sun/star/xml/crypto/DigestID.hpp>
53 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
58 // WinCrypt headers for PDF signing
59 // Note: this uses Windows 7 APIs and requires the relevant data types
63 #include <comphelper/windowserrorstring.hxx>
66 using namespace com::sun::star
;
71 void appendHex( sal_Int8 nInt
, OStringBuffer
& rBuffer
)
73 static const char pHexDigits
[] = { '0', '1', '2', '3', '4', '5', '6', '7',
74 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
75 rBuffer
.append( pHexDigits
[ (nInt
>> 4) & 15 ] );
76 rBuffer
.append( pHexDigits
[ nInt
& 15 ] );
81 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo
* /*slot*/, PRBool
/*retry*/, void *arg
)
83 return PL_strdup(static_cast<char *>(arg
));
86 // ASN.1 used in the (much simpler) time stamp request. From RFC3161
90 AlgorithmIdentifier ::= SEQUENCE {
91 algorithm OBJECT IDENTIFIER,
92 parameters ANY DEFINED BY algorithm OPTIONAL }
93 -- contains a value of the type
94 -- registered for use with the
95 -- algorithm object identifier value
97 MessageImprint ::= SEQUENCE {
98 hashAlgorithm AlgorithmIdentifier,
99 hashedMessage OCTET STRING }
102 struct MessageImprint
{
103 SECAlgorithmID hashAlgorithm
;
104 SECItem hashedMessage
;
108 Extension ::= SEQUENCE {
109 extnID OBJECT IDENTIFIER,
110 critical BOOLEAN DEFAULT FALSE,
111 extnValue OCTET STRING }
121 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
125 TSAPolicyId ::= OBJECT IDENTIFIER
127 TimeStampReq ::= SEQUENCE {
128 version INTEGER { v1(1) },
129 messageImprint MessageImprint,
130 --a hash algorithm OID and the hash value of the data to be
132 reqPolicy TSAPolicyId OPTIONAL,
133 nonce INTEGER OPTIONAL,
134 certReq BOOLEAN DEFAULT FALSE,
135 extensions [0] IMPLICIT Extensions OPTIONAL }
138 struct TimeStampReq
{
140 MessageImprint messageImprint
;
144 Extension
*extensions
;
148 * General name, defined by RFC 3280.
156 * List of general names (only one for now), defined by RFC 3280.
164 * Supplies different fields to identify a certificate, defined by RFC 5035.
169 SECItem serialNumber
;
173 * Supplies different fields that are used to identify certificates, defined by
178 SECAlgorithmID hashAlgorithm
;
180 IssuerSerial issuerSerial
;
184 * This attribute uses the ESSCertIDv2 structure, defined by RFC 5035.
186 struct SigningCertificateV2
190 SigningCertificateV2()
197 * GeneralName ::= CHOICE {
198 * otherName [0] OtherName,
199 * rfc822Name [1] IA5String,
200 * dNSName [2] IA5String,
201 * x400Address [3] ORAddress,
202 * directoryName [4] Name,
203 * ediPartyName [5] EDIPartyName,
204 * uniformResourceIdentifier [6] IA5String,
205 * iPAddress [7] OCTET STRING,
206 * registeredID [8] OBJECT IDENTIFIER
209 const SEC_ASN1Template GeneralNameTemplate
[] =
211 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralName
)},
212 {SEC_ASN1_INLINE
, offsetof(GeneralName
, name
), CERT_NameTemplate
, 0},
217 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
219 const SEC_ASN1Template GeneralNamesTemplate
[] =
221 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralNames
)},
222 {SEC_ASN1_INLINE
| SEC_ASN1_CONTEXT_SPECIFIC
| 4, offsetof(GeneralNames
, names
), GeneralNameTemplate
, 0},
227 * IssuerSerial ::= SEQUENCE {
228 * issuer GeneralNames,
229 * serialNumber CertificateSerialNumber
232 const SEC_ASN1Template IssuerSerialTemplate
[] =
234 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(IssuerSerial
)},
235 {SEC_ASN1_INLINE
, offsetof(IssuerSerial
, issuer
), GeneralNamesTemplate
, 0},
236 {SEC_ASN1_INTEGER
, offsetof(IssuerSerial
, serialNumber
), nullptr, 0},
242 * Hash ::= OCTET STRING
244 * ESSCertIDv2 ::= SEQUENCE {
245 * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},
247 * issuerSerial IssuerSerial OPTIONAL
251 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate
)
253 const SEC_ASN1Template ESSCertIDv2Template
[] =
255 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(ESSCertIDv2
)},
256 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, hashAlgorithm
), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate
), 0},
257 {SEC_ASN1_OCTET_STRING
, offsetof(ESSCertIDv2
, certHash
), nullptr, 0},
258 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, issuerSerial
), IssuerSerialTemplate
, 0},
263 * SigningCertificateV2 ::= SEQUENCE {
266 const SEC_ASN1Template SigningCertificateV2Template
[] =
268 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(SigningCertificateV2
)},
269 {SEC_ASN1_SEQUENCE_OF
, offsetof(SigningCertificateV2
, certs
), ESSCertIDv2Template
, 0},
273 struct PKIStatusInfo
{
275 SECItem statusString
;
279 const SEC_ASN1Template PKIStatusInfo_Template
[] =
281 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(PKIStatusInfo
) },
282 { SEC_ASN1_INTEGER
, offsetof(PKIStatusInfo
, status
), nullptr, 0 },
283 { SEC_ASN1_CONSTRUCTED
| SEC_ASN1_SEQUENCE
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, statusString
), nullptr, 0 },
284 { SEC_ASN1_BIT_STRING
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, failInfo
), nullptr, 0 },
288 const SEC_ASN1Template Any_Template
[] =
290 { SEC_ASN1_ANY
, 0, nullptr, sizeof(SECItem
) }
293 struct TimeStampResp
{
294 PKIStatusInfo status
;
295 SECItem timeStampToken
;
298 const SEC_ASN1Template TimeStampResp_Template
[] =
300 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampResp
) },
301 { SEC_ASN1_INLINE
, offsetof(TimeStampResp
, status
), PKIStatusInfo_Template
, 0 },
302 { SEC_ASN1_ANY
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampResp
, timeStampToken
), Any_Template
, 0 },
306 const SEC_ASN1Template MessageImprint_Template
[] =
308 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(MessageImprint
) },
309 { SEC_ASN1_INLINE
, offsetof(MessageImprint
, hashAlgorithm
), SECOID_AlgorithmIDTemplate
, 0 },
310 { SEC_ASN1_OCTET_STRING
, offsetof(MessageImprint
, hashedMessage
), nullptr, 0 },
314 const SEC_ASN1Template Extension_Template
[] =
316 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(Extension
) },
317 { SEC_ASN1_OBJECT_ID
, offsetof(Extension
, extnID
), nullptr, 0 },
318 { SEC_ASN1_BOOLEAN
, offsetof(Extension
, critical
), nullptr, 0 },
319 { SEC_ASN1_OCTET_STRING
, offsetof(Extension
, extnValue
), nullptr, 0 },
323 const SEC_ASN1Template Extensions_Template
[] =
325 { SEC_ASN1_SEQUENCE_OF
, 0, Extension_Template
, 0 }
328 const SEC_ASN1Template TimeStampReq_Template
[] =
330 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampReq
) },
331 { SEC_ASN1_INTEGER
, offsetof(TimeStampReq
, version
), nullptr, 0 },
332 { SEC_ASN1_INLINE
, offsetof(TimeStampReq
, messageImprint
), MessageImprint_Template
, 0 },
333 { SEC_ASN1_OBJECT_ID
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, reqPolicy
), nullptr, 0 },
334 { SEC_ASN1_INTEGER
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, nonce
), nullptr, 0 },
335 { SEC_ASN1_BOOLEAN
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, certReq
), nullptr, 0 },
336 { SEC_ASN1_OPTIONAL
| SEC_ASN1_CONTEXT_SPECIFIC
| 0, offsetof(TimeStampReq
, extensions
), Extensions_Template
, 0 },
340 size_t AppendToBuffer(char const *ptr
, size_t size
, size_t nmemb
, void *userdata
)
342 OStringBuffer
*pBuffer
= static_cast<OStringBuffer
*>(userdata
);
343 pBuffer
->append(ptr
, size
*nmemb
);
348 OUString
PKIStatusToString(int n
)
352 case 0: return "granted";
353 case 1: return "grantedWithMods";
354 case 2: return "rejection";
355 case 3: return "waiting";
356 case 4: return "revocationWarning";
357 case 5: return "revocationNotification";
358 default: return "unknown (" + OUString::number(n
) + ")";
362 OUString
PKIStatusInfoToString(const PKIStatusInfo
& rStatusInfo
)
364 OUString result
= "{status=";
365 if (rStatusInfo
.status
.len
== 1)
366 result
+= PKIStatusToString(rStatusInfo
.status
.data
[0]);
368 result
+= "unknown (len=" + OUString::number(rStatusInfo
.status
.len
);
370 // FIXME: Perhaps look at rStatusInfo.statusString.data but note
371 // that we of course can't assume it contains proper UTF-8. After
372 // all, it is data from an external source. Also, RFC3161 claims
373 // it should be a SEQUENCE (1..MAX) OF UTF8String, but another
374 // source claimed it would be a single UTF8String, hmm?
376 // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO
383 // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
384 // not exported from libsmime, so copy them here. Sigh.
387 my_SEC_StringToOID(SECItem
*to
, const char *from
, PRUint32 len
)
389 PRUint32 decimal_numbers
= 0;
390 PRUint32 result_bytes
= 0;
392 PRUint8 result
[1024];
394 static const PRUint32 max_decimal
= 0xffffffff / 10;
395 static const char OIDstring
[] = {"OID."};
398 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
402 len
= PL_strlen(from
);
404 if (len
>= 4 && !PL_strncasecmp(from
, OIDstring
, 4)) {
405 from
+= 4; /* skip leading "OID." if present */
410 PORT_SetError(SEC_ERROR_BAD_DATA
);
414 PRUint32 decimal
= 0;
415 while (len
> 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from
))) {
416 PRUint32 addend
= *from
++ - '0';
418 if (decimal
> max_decimal
) /* overflow */
420 decimal
= (decimal
* 10) + addend
;
421 if (decimal
< addend
) /* overflow */
424 if (len
!= 0 && *from
!= '.') {
427 if (decimal_numbers
== 0) {
430 result
[0] = decimal
* 40;
432 } else if (decimal_numbers
== 1) {
435 result
[0] += decimal
;
437 /* encode the decimal number, */
439 PRUint32 num_bytes
= 0;
440 PRUint32 tmp
= decimal
;
446 ++num_bytes
; /* use one byte for a zero value */
447 if (num_bytes
+ result_bytes
> sizeof result
)
450 rp
= result
+ result_bytes
- 1;
451 rp
[tmp
] = static_cast<PRUint8
>(decimal
& 0x7f);
454 rp
[tmp
] = static_cast<PRUint8
>(decimal
| 0x80);
457 result_bytes
+= num_bytes
;
460 if (len
> 0) { /* skip trailing '.' */
465 /* now result contains result_bytes of data */
466 if (to
->data
&& to
->len
>= result_bytes
) {
467 to
->len
= result_bytes
;
468 PORT_Memcpy(to
->data
, result
, to
->len
);
471 SECItem result_item
= {siBuffer
, nullptr, 0 };
472 result_item
.data
= result
;
473 result_item
.len
= result_bytes
;
474 rv
= SECITEM_CopyItem(nullptr, to
, &result_item
);
480 my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute
**attrs
, SECOidTag oidtag
, PRBool only
)
483 NSSCMSAttribute
*attr1
, *attr2
;
485 if (attrs
== nullptr)
488 oid
= SECOID_FindOIDByTag(oidtag
);
492 while ((attr1
= *attrs
++) != nullptr) {
493 if (attr1
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr1
->type
.data
,
499 if (attr1
== nullptr)
505 while ((attr2
= *attrs
++) != nullptr) {
506 if (attr2
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr2
->type
.data
,
512 if (attr2
!= nullptr)
519 my_NSS_CMSArray_Add(PLArenaPool
*poolp
, void ***array
, void *obj
)
524 PORT_Assert(array
!= NULL
);
525 if (array
== nullptr)
528 if (*array
== nullptr) {
529 dest
= static_cast<void **>(PORT_ArenaAlloc(poolp
, 2 * sizeof(void *)));
534 dest
= static_cast<void **>(PORT_ArenaGrow (poolp
,
536 (n
+ 1) * sizeof(void *),
537 (n
+ 2) * sizeof(void *)));
550 my_NSS_CMSAttribute_GetType(const NSSCMSAttribute
*attr
)
554 typetag
= SECOID_FindOID(&(attr
->type
));
555 if (typetag
== nullptr)
556 return SEC_OID_UNKNOWN
;
558 return typetag
->offset
;
562 my_NSS_CMSAttributeArray_AddAttr(PLArenaPool
*poolp
, NSSCMSAttribute
***attrs
, NSSCMSAttribute
*attr
)
564 NSSCMSAttribute
*oattr
;
568 mark
= PORT_ArenaMark(poolp
);
570 /* find oidtag of attr */
571 type
= my_NSS_CMSAttribute_GetType(attr
);
573 /* see if we have one already */
574 oattr
= my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs
, type
, PR_FALSE
);
575 PORT_Assert (oattr
== NULL
);
576 if (oattr
!= nullptr)
577 goto loser
; /* XXX or would it be better to replace it? */
579 /* no, shove it in */
580 if (my_NSS_CMSArray_Add(poolp
, reinterpret_cast<void ***>(attrs
), static_cast<void *>(attr
)) != SECSuccess
)
583 PORT_ArenaUnmark(poolp
, mark
);
587 PORT_ArenaRelease(poolp
, mark
);
592 my_NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
594 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->unAuthAttr
), attr
);
598 my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
600 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->authAttr
), attr
);
603 NSSCMSMessage
*CreateCMSMessage(const PRTime
* time
,
604 NSSCMSSignedData
**cms_sd
,
605 NSSCMSSignerInfo
**cms_signer
,
606 CERTCertificate
*cert
,
609 NSSCMSMessage
*result
= NSS_CMSMessage_Create(nullptr);
612 SAL_WARN("svl.crypto", "NSS_CMSMessage_Create failed");
616 *cms_sd
= NSS_CMSSignedData_Create(result
);
619 SAL_WARN("svl.crypto", "NSS_CMSSignedData_Create failed");
620 NSS_CMSMessage_Destroy(result
);
624 NSSCMSContentInfo
*cms_cinfo
= NSS_CMSMessage_GetContentInfo(result
);
625 if (NSS_CMSContentInfo_SetContent_SignedData(result
, cms_cinfo
, *cms_sd
) != SECSuccess
)
627 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_SignedData failed");
628 NSS_CMSSignedData_Destroy(*cms_sd
);
629 NSS_CMSMessage_Destroy(result
);
633 cms_cinfo
= NSS_CMSSignedData_GetContentInfo(*cms_sd
);
635 // Attach NULL data as detached data
636 if (NSS_CMSContentInfo_SetContent_Data(result
, cms_cinfo
, nullptr, PR_TRUE
) != SECSuccess
)
638 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_Data failed");
639 NSS_CMSSignedData_Destroy(*cms_sd
);
640 NSS_CMSMessage_Destroy(result
);
644 // workaround: with legacy "dbm:", NSS can't find the private key - try out
645 // if it works, and fallback if it doesn't.
646 if (SECKEYPrivateKey
* pPrivateKey
= PK11_FindKeyByAnyCert(cert
, nullptr))
648 if (!comphelper::LibreOfficeKit::isActive())
650 // pPrivateKey only exists in the memory in the LOK case, don't delete it.
651 SECKEY_DestroyPrivateKey(pPrivateKey
);
653 *cms_signer
= NSS_CMSSignerInfo_Create(result
, cert
, SEC_OID_SHA256
);
657 pPrivateKey
= PK11_FindKeyByDERCert(cert
->slot
, cert
, nullptr);
658 SECKEYPublicKey
*const pPublicKey
= CERT_ExtractPublicKey(cert
);
659 if (pPublicKey
&& pPrivateKey
)
661 *cms_signer
= NSS_CMSSignerInfo_CreateWithSubjKeyID(result
, &cert
->subjectKeyID
, pPublicKey
, pPrivateKey
, SEC_OID_SHA256
);
662 SECKEY_DestroyPrivateKey(pPrivateKey
);
663 SECKEY_DestroyPublicKey(pPublicKey
);
666 // this is required in NSS_CMSSignerInfo_IncludeCerts()
667 // (and NSS_CMSSignerInfo_GetSigningCertificate() doesn't work)
668 (**cms_signer
).cert
= CERT_DupCertificate(cert
);
674 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_Create failed");
675 NSS_CMSSignedData_Destroy(*cms_sd
);
676 NSS_CMSMessage_Destroy(result
);
680 if (time
&& NSS_CMSSignerInfo_AddSigningTime(*cms_signer
, *time
) != SECSuccess
)
682 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddSigningTime failed");
683 NSS_CMSSignedData_Destroy(*cms_sd
);
684 NSS_CMSMessage_Destroy(result
);
688 if (NSS_CMSSignerInfo_IncludeCerts(*cms_signer
, NSSCMSCM_CertChain
, certUsageEmailSigner
) != SECSuccess
)
690 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_IncludeCerts failed");
691 NSS_CMSSignedData_Destroy(*cms_sd
);
692 NSS_CMSMessage_Destroy(result
);
696 if (NSS_CMSSignedData_AddCertificate(*cms_sd
, cert
) != SECSuccess
)
698 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddCertificate failed");
699 NSS_CMSSignedData_Destroy(*cms_sd
);
700 NSS_CMSMessage_Destroy(result
);
704 if (NSS_CMSSignedData_AddSignerInfo(*cms_sd
, *cms_signer
) != SECSuccess
)
706 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddSignerInfo failed");
707 NSS_CMSSignedData_Destroy(*cms_sd
);
708 NSS_CMSMessage_Destroy(result
);
712 if (NSS_CMSSignedData_SetDigestValue(*cms_sd
, SEC_OID_SHA256
, digest
) != SECSuccess
)
714 SAL_WARN("svl.crypto", "NSS_CMSSignedData_SetDigestValue failed");
715 NSS_CMSSignedData_Destroy(*cms_sd
);
716 NSS_CMSMessage_Destroy(result
);
723 #elif USE_CRYPTO_MSCAPI // ends USE_CRYPTO_NSS
725 /// Counts how many bytes are needed to encode a given length.
726 size_t GetDERLengthOfLength(size_t nLength
)
732 while (nLength
>> (nRet
* 8))
734 // Long form means one additional byte: the length of the length and
735 // the length itself.
741 /// Writes the length part of the header.
742 void WriteDERLength(SvStream
& rStream
, size_t nLength
)
744 size_t nLengthOfLength
= GetDERLengthOfLength(nLength
);
745 if (nLengthOfLength
== 1)
747 // We can use the short form.
748 rStream
.WriteUInt8(nLength
);
752 // 0x80 means that the we use the long form: the first byte is the length
753 // of length with the highest bit set to 1, not the actual length.
754 rStream
.WriteUInt8(0x80 | (nLengthOfLength
- 1));
755 for (size_t i
= 1; i
< nLengthOfLength
; ++i
)
756 rStream
.WriteUInt8(nLength
>> ((nLengthOfLength
- i
- 1) * 8));
759 const unsigned nASN1_INTEGER
= 0x02;
760 const unsigned nASN1_OCTET_STRING
= 0x04;
761 const unsigned nASN1_NULL
= 0x05;
762 const unsigned nASN1_OBJECT_IDENTIFIER
= 0x06;
763 const unsigned nASN1_SEQUENCE
= 0x10;
764 /// An explicit tag on a constructed value.
765 const unsigned nASN1_TAGGED_CONSTRUCTED
= 0xa0;
766 const unsigned nASN1_CONSTRUCTED
= 0x20;
768 /// Create payload for the 'signing-certificate' signed attribute.
769 bool CreateSigningCertificateAttribute(void const * pDerEncoded
, int nDerEncoded
, PCCERT_CONTEXT pCertContext
, SvStream
& rEncodedCertificate
)
771 // CryptEncodeObjectEx() does not support encoding arbitrary ASN.1
772 // structures, like SigningCertificateV2 from RFC 5035, so let's build it
775 // Count the certificate hash and put it to aHash.
776 // 2.16.840.1.101.3.4.2.1, i.e. sha256.
777 std::vector
<unsigned char> aSHA256
{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
779 HCRYPTPROV hProv
= 0;
780 if (!CryptAcquireContextW(&hProv
, nullptr, nullptr, PROV_RSA_AES
, CRYPT_VERIFYCONTEXT
))
782 SAL_WARN("svl.crypto", "CryptAcquireContext() failed");
786 HCRYPTHASH hHash
= 0;
787 if (!CryptCreateHash(hProv
, CALG_SHA_256
, 0, 0, &hHash
))
789 SAL_WARN("svl.crypto", "CryptCreateHash() failed");
793 if (!CryptHashData(hHash
, static_cast<const BYTE
*>(pDerEncoded
), nDerEncoded
, 0))
795 SAL_WARN("svl.crypto", "CryptHashData() failed");
800 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, nullptr, &nHash
, 0))
802 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash length");
806 std::vector
<unsigned char> aHash(nHash
);
807 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, aHash
.data(), &nHash
, 0))
809 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash");
813 CryptDestroyHash(hHash
);
814 CryptReleaseContext(hProv
, 0);
816 // Collect info for IssuerSerial.
817 BYTE
* pIssuer
= pCertContext
->pCertInfo
->Issuer
.pbData
;
818 DWORD nIssuer
= pCertContext
->pCertInfo
->Issuer
.cbData
;
819 BYTE
* pSerial
= pCertContext
->pCertInfo
->SerialNumber
.pbData
;
820 DWORD nSerial
= pCertContext
->pCertInfo
->SerialNumber
.cbData
;
821 // pSerial is LE, aSerial is BE.
822 std::vector
<BYTE
> aSerial(nSerial
);
823 for (size_t i
= 0; i
< nSerial
; ++i
)
824 aSerial
[i
] = *(pSerial
+ nSerial
- i
- 1);
826 // We now have all the info to count the lengths.
827 // The layout of the payload is:
828 // SEQUENCE: SigningCertificateV2
829 // SEQUENCE: SEQUENCE OF ESSCertIDv2
830 // SEQUENCE: ESSCertIDv2
831 // SEQUENCE: AlgorithmIdentifier
834 // OCTET STRING: certHash
835 // SEQUENCE: IssuerSerial
836 // SEQUENCE: GeneralNames
838 // SEQUENCE: Issuer blob
839 // INTEGER: CertificateSerialNumber
841 size_t nAlgorithm
= 1 + GetDERLengthOfLength(aSHA256
.size()) + aSHA256
.size();
842 size_t nParameters
= 1 + GetDERLengthOfLength(1);
843 size_t nAlgorithmIdentifier
= 1 + GetDERLengthOfLength(nAlgorithm
+ nParameters
) + nAlgorithm
+ nParameters
;
844 size_t nCertHash
= 1 + GetDERLengthOfLength(aHash
.size()) + aHash
.size();
845 size_t nName
= 1 + GetDERLengthOfLength(nIssuer
) + nIssuer
;
846 size_t nGeneralNames
= 1 + GetDERLengthOfLength(nName
) + nName
;
847 size_t nCertificateSerialNumber
= 1 + GetDERLengthOfLength(nSerial
) + nSerial
;
848 size_t nIssuerSerial
= 1 + GetDERLengthOfLength(nGeneralNames
+ nCertificateSerialNumber
) + nGeneralNames
+ nCertificateSerialNumber
;
849 size_t nESSCertIDv2
= 1 + GetDERLengthOfLength(nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
) + nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
;
850 size_t nESSCertIDv2s
= 1 + GetDERLengthOfLength(nESSCertIDv2
) + nESSCertIDv2
;
852 // Write SigningCertificateV2.
853 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
854 WriteDERLength(rEncodedCertificate
, nESSCertIDv2s
);
855 // Write SEQUENCE OF ESSCertIDv2.
856 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
857 WriteDERLength(rEncodedCertificate
, nESSCertIDv2
);
858 // Write ESSCertIDv2.
859 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
860 WriteDERLength(rEncodedCertificate
, nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
);
861 // Write AlgorithmIdentifier.
862 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
863 WriteDERLength(rEncodedCertificate
, nAlgorithm
+ nParameters
);
865 rEncodedCertificate
.WriteUInt8(nASN1_OBJECT_IDENTIFIER
);
866 WriteDERLength(rEncodedCertificate
, aSHA256
.size());
867 rEncodedCertificate
.WriteBytes(aSHA256
.data(), aSHA256
.size());
869 rEncodedCertificate
.WriteUInt8(nASN1_NULL
);
870 rEncodedCertificate
.WriteUInt8(0);
872 rEncodedCertificate
.WriteUInt8(nASN1_OCTET_STRING
);
873 WriteDERLength(rEncodedCertificate
, aHash
.size());
874 rEncodedCertificate
.WriteBytes(aHash
.data(), aHash
.size());
875 // Write IssuerSerial.
876 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
877 WriteDERLength(rEncodedCertificate
, nGeneralNames
+ nCertificateSerialNumber
);
878 // Write GeneralNames.
879 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
880 WriteDERLength(rEncodedCertificate
, nName
);
882 rEncodedCertificate
.WriteUInt8(nASN1_TAGGED_CONSTRUCTED
| 4);
883 WriteDERLength(rEncodedCertificate
, nIssuer
);
884 rEncodedCertificate
.WriteBytes(pIssuer
, nIssuer
);
885 // Write CertificateSerialNumber.
886 rEncodedCertificate
.WriteUInt8(nASN1_INTEGER
);
887 WriteDERLength(rEncodedCertificate
, nSerial
);
888 rEncodedCertificate
.WriteBytes(aSerial
.data(), aSerial
.size());
892 #endif // USE_CRYPTO_MSCAPI
894 } // anonymous namespace
896 namespace svl::crypto
{
898 static int AsHex(char ch
)
901 if (rtl::isAsciiDigit(static_cast<unsigned char>(ch
)))
905 if (ch
>= 'a' && ch
<= 'f')
907 else if (ch
>= 'A' && ch
<= 'F')
916 std::vector
<unsigned char> DecodeHexString(std::string_view rHex
)
918 std::vector
<unsigned char> aRet
;
919 size_t nHexLen
= rHex
.size();
923 for (size_t i
= 0; i
< nHexLen
; ++i
)
926 sal_Int8 nParsed
= AsHex(rHex
[i
]);
929 SAL_WARN("svl.crypto", "DecodeHexString: invalid hex value");
936 aRet
.push_back(nByte
);
946 bool Signing::Sign(OStringBuffer
& rCMSHexBuffer
)
952 // Create the PKCS#7 object.
953 css::uno::Sequence
<sal_Int8
> aDerEncoded
= m_xCertificate
->getEncoded();
954 if (!aDerEncoded
.hasElements())
956 SAL_WARN("svl.crypto", "Crypto::Signing: empty certificate");
961 CERTCertificate
*cert
= CERT_DecodeCertFromPackage(reinterpret_cast<char *>(aDerEncoded
.getArray()), aDerEncoded
.getLength());
965 SAL_WARN("svl.crypto", "CERT_DecodeCertFromPackage failed");
969 std::vector
<unsigned char> aHashResult
;
971 comphelper::Hash
aHash(comphelper::HashType::SHA256
);
973 for (const auto& pair
: m_dataBlocks
)
974 aHash
.update(static_cast<const unsigned char*>(pair
.first
), pair
.second
);
976 aHashResult
= aHash
.finalize();
979 digest
.data
= aHashResult
.data();
980 digest
.len
= aHashResult
.size();
982 PRTime now
= PR_Now();
983 NSSCMSSignedData
*cms_sd(nullptr);
984 NSSCMSSignerInfo
*cms_signer(nullptr);
985 NSSCMSMessage
*cms_msg
= CreateCMSMessage(nullptr, &cms_sd
, &cms_signer
, cert
, &digest
);
989 OString
pass(OUStringToOString( m_aSignPassword
, RTL_TEXTENCODING_UTF8
));
992 OStringBuffer response_buffer
;
993 TimeStampResp response
;
994 SECItem response_item
;
995 NSSCMSAttribute timestamp
;
999 valuesp
[1] = nullptr;
1002 if( !m_aSignTSA
.isEmpty() )
1004 // Create another CMS message with the same contents as cms_msg, because it doesn't seem
1005 // possible to encode a message twice (once to get something to timestamp, and then after
1006 // adding the timestamp attribute).
1008 NSSCMSSignedData
*ts_cms_sd
;
1009 NSSCMSSignerInfo
*ts_cms_signer
;
1010 NSSCMSMessage
*ts_cms_msg
= CreateCMSMessage(&now
, &ts_cms_sd
, &ts_cms_signer
, cert
, &digest
);
1016 SECItem ts_cms_output
;
1017 ts_cms_output
.data
= nullptr;
1018 ts_cms_output
.len
= 0;
1019 PLArenaPool
*ts_arena
= PORT_NewArena(10000);
1020 NSSCMSEncoderContext
*ts_cms_ecx
;
1021 ts_cms_ecx
= NSS_CMSEncoder_Start(ts_cms_msg
, nullptr, nullptr, &ts_cms_output
, ts_arena
, PDFSigningPKCS7PasswordCallback
,
1022 const_cast<char*>(pass
.getStr()), nullptr, nullptr, nullptr, nullptr);
1024 if (NSS_CMSEncoder_Finish(ts_cms_ecx
) != SECSuccess
)
1026 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Finish failed");
1030 // I have compared the ts_cms_output produced here with the cms_output produced below when
1031 // not actually calling my_NSS_CMSSignerInfo_AddUnauthAttr()), and they are identical.
1033 std::vector
<unsigned char> aTsHashResult
= comphelper::Hash::calculateHash(ts_cms_signer
->encDigest
.data
, ts_cms_signer
->encDigest
.len
, comphelper::HashType::SHA256
);
1035 ts_digest
.type
= siBuffer
;
1036 ts_digest
.data
= aTsHashResult
.data();
1037 ts_digest
.len
= aTsHashResult
.size();
1039 unsigned char cOne
= 1;
1040 src
.version
.type
= siUnsignedInteger
;
1041 src
.version
.data
= &cOne
;
1042 src
.version
.len
= sizeof(cOne
);
1044 src
.messageImprint
.hashAlgorithm
.algorithm
.data
= nullptr;
1045 src
.messageImprint
.hashAlgorithm
.parameters
.data
= nullptr;
1046 SECOID_SetAlgorithmID(nullptr, &src
.messageImprint
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
1047 src
.messageImprint
.hashedMessage
= ts_digest
;
1049 src
.reqPolicy
.type
= siBuffer
;
1050 src
.reqPolicy
.data
= nullptr;
1051 src
.reqPolicy
.len
= 0;
1053 unsigned int nNonce
= comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32
);
1054 src
.nonce
.type
= siUnsignedInteger
;
1055 src
.nonce
.data
= reinterpret_cast<unsigned char*>(&nNonce
);
1056 src
.nonce
.len
= sizeof(nNonce
);
1058 src
.certReq
.type
= siUnsignedInteger
;
1059 src
.certReq
.data
= &cOne
;
1060 src
.certReq
.len
= sizeof(cOne
);
1062 src
.extensions
= nullptr;
1064 SECItem
* timestamp_request
= SEC_ASN1EncodeItem(nullptr, nullptr, &src
, TimeStampReq_Template
);
1065 if (timestamp_request
== nullptr)
1067 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem failed");
1071 if (timestamp_request
->data
== nullptr)
1073 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem succeeded but got NULL data");
1074 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1078 SAL_INFO("svl.crypto", "request length=" << timestamp_request
->len
);
1080 // Send time stamp request to TSA server, receive response
1082 CURL
* curl
= curl_easy_init();
1084 struct curl_slist
* slist
= nullptr;
1088 SAL_WARN("svl.crypto", "curl_easy_init failed");
1089 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1093 ::InitCurl_easy(curl
);
1095 SAL_INFO("svl.crypto", "Setting curl to verbose: " << (curl_easy_setopt(curl
, CURLOPT_VERBOSE
, 1) == CURLE_OK
? "OK" : "FAIL"));
1097 if ((rc
= curl_easy_setopt(curl
, CURLOPT_URL
, OUStringToOString(m_aSignTSA
, RTL_TEXTENCODING_UTF8
).getStr())) != CURLE_OK
)
1099 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_URL) failed: " << curl_easy_strerror(rc
));
1100 curl_easy_cleanup(curl
);
1101 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1105 slist
= curl_slist_append(slist
, "Content-Type: application/timestamp-query");
1106 slist
= curl_slist_append(slist
, "Accept: application/timestamp-reply");
1108 if ((rc
= curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, slist
)) != CURLE_OK
)
1110 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " << curl_easy_strerror(rc
));
1111 curl_slist_free_all(slist
);
1112 curl_easy_cleanup(curl
);
1113 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1117 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, static_cast<tools::Long
>(timestamp_request
->len
))) != CURLE_OK
||
1118 (rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDS
, timestamp_request
->data
)) != CURLE_OK
)
1120 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc
));
1121 curl_easy_cleanup(curl
);
1122 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1126 if ((rc
= curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, &response_buffer
)) != CURLE_OK
||
1127 (rc
= curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, AppendToBuffer
)) != CURLE_OK
)
1129 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc
));
1130 curl_easy_cleanup(curl
);
1131 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1135 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POST
, 1)) != CURLE_OK
)
1137 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc
));
1138 curl_easy_cleanup(curl
);
1139 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1143 char error_buffer
[CURL_ERROR_SIZE
];
1144 if ((rc
= curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, error_buffer
)) != CURLE_OK
)
1146 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed: " << curl_easy_strerror(rc
));
1147 curl_easy_cleanup(curl
);
1148 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1152 // Use a ten second timeout
1153 if ((rc
= curl_easy_setopt(curl
, CURLOPT_TIMEOUT
, 10)) != CURLE_OK
||
1154 (rc
= curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, 10)) != CURLE_OK
)
1156 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc
));
1157 curl_easy_cleanup(curl
);
1158 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1162 if (curl_easy_perform(curl
) != CURLE_OK
)
1164 SAL_WARN("svl.crypto", "curl_easy_perform failed: " << error_buffer
);
1165 curl_easy_cleanup(curl
);
1166 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1170 SAL_INFO("svl.crypto", "PDF signing: got response, length=" << response_buffer
.getLength());
1172 curl_slist_free_all(slist
);
1173 curl_easy_cleanup(curl
);
1174 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1176 memset(&response
, 0, sizeof(response
));
1178 response_item
.type
= siBuffer
;
1179 response_item
.data
= reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer
.getStr()));
1180 response_item
.len
= response_buffer
.getLength();
1182 if (SEC_ASN1DecodeItem(nullptr, &response
, TimeStampResp_Template
, &response_item
) != SECSuccess
)
1184 SAL_WARN("svl.crypto", "SEC_ASN1DecodeItem failed");
1188 SAL_INFO("svl.crypto", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response
.status
));
1190 if (response
.status
.status
.len
!= 1 ||
1191 (response
.status
.status
.data
[0] != 0 && response
.status
.status
.data
[0] != 1))
1193 SAL_WARN("svl.crypto", "Timestamp request was not granted");
1197 // timestamp.type filled in below
1199 // Not sure if we actually need two entries in the values array, now when valuesp is an
1200 // array too, the pointer to the values array followed by a null pointer. But I don't feel
1201 // like experimenting.
1202 values
[0] = response
.timeStampToken
;
1203 values
[1].type
= siBuffer
;
1204 values
[1].data
= nullptr;
1207 timestamp
.values
= valuesp
;
1209 typetag
.oid
.data
= nullptr;
1210 // id-aa-timeStampToken OBJECT IDENTIFIER ::= { iso(1)
1211 // member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
1212 // smime(16) aa(2) 14 }
1213 if (my_SEC_StringToOID(&typetag
.oid
, "1.2.840.113549.1.9.16.2.14", 0) != SECSuccess
)
1215 SAL_WARN("svl.crypto", "SEC_StringToOID failed");
1218 typetag
.offset
= SEC_OID_UNKNOWN
; // ???
1219 typetag
.desc
= "id-aa-timeStampToken";
1220 typetag
.mechanism
= CKM_SHA_1
; // ???
1221 typetag
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
; // ???
1222 timestamp
.typeTag
= &typetag
;
1224 timestamp
.type
= typetag
.oid
; // ???
1226 timestamp
.encoded
= PR_TRUE
; // ???
1228 if (my_NSS_CMSSignerInfo_AddUnauthAttr(cms_signer
, ×tamp
) != SECSuccess
)
1230 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddUnauthAttr failed");
1235 // Add the signing certificate as a signed attribute.
1236 ESSCertIDv2
* aCertIDs
[2];
1237 ESSCertIDv2 aCertID
;
1238 // Write ESSCertIDv2.hashAlgorithm.
1239 aCertID
.hashAlgorithm
.algorithm
.data
= nullptr;
1240 aCertID
.hashAlgorithm
.parameters
.data
= nullptr;
1241 SECOID_SetAlgorithmID(nullptr, &aCertID
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
1242 comphelper::ScopeGuard
aAlgoGuard(
1243 [&aCertID
] () { SECOID_DestroyAlgorithmID(&aCertID
.hashAlgorithm
, false); } );
1244 // Write ESSCertIDv2.certHash.
1245 SECItem aCertHashItem
;
1246 auto pDerEncoded
= reinterpret_cast<const unsigned char *>(aDerEncoded
.getArray());
1247 std::vector
<unsigned char> aCertHashResult
= comphelper::Hash::calculateHash(pDerEncoded
, aDerEncoded
.getLength(), comphelper::HashType::SHA256
);
1248 aCertHashItem
.type
= siBuffer
;
1249 aCertHashItem
.data
= aCertHashResult
.data();
1250 aCertHashItem
.len
= aCertHashResult
.size();
1251 aCertID
.certHash
= aCertHashItem
;
1252 // Write ESSCertIDv2.issuerSerial.
1253 IssuerSerial aSerial
;
1255 aName
.name
= cert
->issuer
;
1256 aSerial
.issuer
.names
= aName
;
1257 aSerial
.serialNumber
= cert
->serialNumber
;
1258 aCertID
.issuerSerial
= aSerial
;
1259 // Write SigningCertificateV2.certs.
1260 aCertIDs
[0] = &aCertID
;
1261 aCertIDs
[1] = nullptr;
1262 SigningCertificateV2 aCertificate
;
1263 aCertificate
.certs
= &aCertIDs
[0];
1264 SECItem
* pEncodedCertificate
= SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate
, SigningCertificateV2Template
);
1265 if (!pEncodedCertificate
)
1267 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem() failed");
1271 NSSCMSAttribute aAttribute
;
1272 SECItem aAttributeValues
[2];
1273 SECItem
* pAttributeValues
[2];
1274 pAttributeValues
[0] = aAttributeValues
;
1275 pAttributeValues
[1] = nullptr;
1276 aAttributeValues
[0] = *pEncodedCertificate
;
1277 aAttributeValues
[1].type
= siBuffer
;
1278 aAttributeValues
[1].data
= nullptr;
1279 aAttributeValues
[1].len
= 0;
1280 aAttribute
.values
= pAttributeValues
;
1282 SECOidData aOidData
;
1283 aOidData
.oid
.data
= nullptr;
1285 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1286 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1287 * smime(16) id-aa(2) 47 }
1289 if (my_SEC_StringToOID(&aOidData
.oid
, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess
)
1291 SAL_WARN("svl.crypto", "my_SEC_StringToOID() failed");
1294 comphelper::ScopeGuard
aGuard(
1295 [&aOidData
] () { SECITEM_FreeItem(&aOidData
.oid
, false); } );
1296 aOidData
.offset
= SEC_OID_UNKNOWN
;
1297 aOidData
.desc
= "id-aa-signingCertificateV2";
1298 aOidData
.mechanism
= CKM_SHA_1
;
1299 aOidData
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
;
1300 aAttribute
.typeTag
= &aOidData
;
1301 aAttribute
.type
= aOidData
.oid
;
1302 aAttribute
.encoded
= PR_TRUE
;
1304 if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer
, &aAttribute
) != SECSuccess
)
1306 SAL_WARN("svl.crypto", "my_NSS_CMSSignerInfo_AddAuthAttr() failed");
1311 cms_output
.data
= nullptr;
1313 PLArenaPool
*arena
= PORT_NewArena(10000);
1314 const ::comphelper::ScopeGuard
aScopeGuard(
1315 [&arena
]() mutable { PORT_FreeArena(arena
, true); } );
1316 NSSCMSEncoderContext
*cms_ecx
;
1318 // Possibly it would work to even just pass NULL for the password callback function and its
1319 // argument here. After all, at least with the hardware token and associated software I tested
1320 // with, the software itself pops up a dialog asking for the PIN (password). But I am not going
1321 // to test it and risk locking up my token...
1323 cms_ecx
= NSS_CMSEncoder_Start(cms_msg
, nullptr, nullptr, &cms_output
, arena
, PDFSigningPKCS7PasswordCallback
,
1324 const_cast<char*>(pass
.getStr()), nullptr, nullptr, nullptr, nullptr);
1328 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Start failed");
1332 if (NSS_CMSEncoder_Finish(cms_ecx
) != SECSuccess
)
1334 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Finish failed");
1338 if (cms_output
.len
*2 > MAX_SIGNATURE_CONTENT_LENGTH
)
1340 SAL_WARN("svl.crypto", "Signature requires more space (" << cms_output
.len
*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH
<< ")");
1341 NSS_CMSMessage_Destroy(cms_msg
);
1345 for (unsigned int i
= 0; i
< cms_output
.len
; i
++)
1346 appendHex(cms_output
.data
[i
], rCMSHexBuffer
);
1348 SECITEM_FreeItem(pEncodedCertificate
, PR_TRUE
);
1349 NSS_CMSMessage_Destroy(cms_msg
);
1353 #elif USE_CRYPTO_MSCAPI // ends USE_CRYPTO_NSS
1355 PCCERT_CONTEXT pCertContext
= CertCreateCertificateContext(X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, reinterpret_cast<const BYTE
*>(aDerEncoded
.getArray()), aDerEncoded
.getLength());
1356 if (pCertContext
== nullptr)
1358 SAL_WARN("svl.crypto", "CertCreateCertificateContext failed: " << WindowsErrorString(GetLastError()));
1362 CRYPT_SIGN_MESSAGE_PARA aPara
= {};
1363 aPara
.cbSize
= sizeof(aPara
);
1364 aPara
.dwMsgEncodingType
= PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
;
1365 aPara
.pSigningCert
= pCertContext
;
1366 aPara
.HashAlgorithm
.pszObjId
= const_cast<LPSTR
>(szOID_NIST_sha256
);
1367 aPara
.HashAlgorithm
.Parameters
.cbData
= 0;
1369 aPara
.rgpMsgCert
= &pCertContext
;
1371 NCRYPT_KEY_HANDLE hCryptKey
= 0;
1372 DWORD dwFlags
= CRYPT_ACQUIRE_CACHE_FLAG
| CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG
;
1373 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE
* phCryptProvOrNCryptKey
= &hCryptKey
;
1377 if (!CryptAcquireCertificatePrivateKey(pCertContext
,
1380 phCryptProvOrNCryptKey
,
1384 SAL_WARN("svl.crypto", "CryptAcquireCertificatePrivateKey failed: " << WindowsErrorString(GetLastError()));
1385 CertFreeCertificateContext(pCertContext
);
1388 assert(!bFreeNeeded
);
1390 CMSG_SIGNER_ENCODE_INFO aSignerInfo
= {};
1391 aSignerInfo
.cbSize
= sizeof(aSignerInfo
);
1392 aSignerInfo
.pCertInfo
= pCertContext
->pCertInfo
;
1393 aSignerInfo
.hNCryptKey
= hCryptKey
;
1394 aSignerInfo
.dwKeySpec
= nKeySpec
;
1395 aSignerInfo
.HashAlgorithm
.pszObjId
= const_cast<LPSTR
>(szOID_NIST_sha256
);
1396 aSignerInfo
.HashAlgorithm
.Parameters
.cbData
= 0;
1398 // Add the signing certificate as a signed attribute.
1399 CRYPT_INTEGER_BLOB aCertificateBlob
;
1400 SvMemoryStream aEncodedCertificate
;
1401 if (!CreateSigningCertificateAttribute(aDerEncoded
.getArray(), aDerEncoded
.getLength(), pCertContext
, aEncodedCertificate
))
1403 SAL_WARN("svl.crypto", "CreateSigningCertificateAttribute() failed");
1406 aCertificateBlob
.pbData
= const_cast<BYTE
*>(static_cast<const BYTE
*>(aEncodedCertificate
.GetData()));
1407 aCertificateBlob
.cbData
= aEncodedCertificate
.GetSize();
1408 CRYPT_ATTRIBUTE aCertificateAttribute
;
1410 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1411 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1412 * smime(16) id-aa(2) 47 }
1414 aCertificateAttribute
.pszObjId
= const_cast<LPSTR
>("1.2.840.113549.1.9.16.2.47");
1415 aCertificateAttribute
.cValue
= 1;
1416 aCertificateAttribute
.rgValue
= &aCertificateBlob
;
1417 aSignerInfo
.cAuthAttr
= 1;
1418 aSignerInfo
.rgAuthAttr
= &aCertificateAttribute
;
1420 CMSG_SIGNED_ENCODE_INFO aSignedInfo
= {};
1421 aSignedInfo
.cbSize
= sizeof(aSignedInfo
);
1422 aSignedInfo
.cSigners
= 1;
1423 aSignedInfo
.rgSigners
= &aSignerInfo
;
1425 CERT_BLOB aCertBlob
;
1427 aCertBlob
.cbData
= pCertContext
->cbCertEncoded
;
1428 aCertBlob
.pbData
= pCertContext
->pbCertEncoded
;
1430 aSignedInfo
.cCertEncoded
= 1;
1431 aSignedInfo
.rgCertEncoded
= &aCertBlob
;
1433 HCRYPTMSG hMsg
= CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
1441 SAL_WARN("svl.crypto", "CryptMsgOpenToEncode failed: " << WindowsErrorString(GetLastError()));
1442 CertFreeCertificateContext(pCertContext
);
1446 for (size_t i
= 0; i
< m_dataBlocks
.size(); ++i
)
1448 const bool last
= (i
== m_dataBlocks
.size() - 1);
1449 if (!CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(m_dataBlocks
[i
].first
), m_dataBlocks
[i
].second
, last
))
1451 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
1452 CryptMsgClose(hMsg
);
1453 CertFreeCertificateContext(pCertContext
);
1458 PCRYPT_TIMESTAMP_CONTEXT pTsContext
= nullptr;
1460 if( !m_aSignTSA
.isEmpty() )
1462 HCRYPTMSG hDecodedMsg
= CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
1470 SAL_WARN("svl.crypto", "CryptMsgOpenToDecode failed: " << WindowsErrorString(GetLastError()));
1471 CryptMsgClose(hMsg
);
1472 CertFreeCertificateContext(pCertContext
);
1476 DWORD nTsSigLen
= 0;
1478 if (!CryptMsgGetParam(hMsg
, CMSG_BARE_CONTENT_PARAM
, 0, nullptr, &nTsSigLen
))
1480 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1481 CryptMsgClose(hDecodedMsg
);
1482 CryptMsgClose(hMsg
);
1483 CertFreeCertificateContext(pCertContext
);
1487 SAL_INFO("svl.crypto", "nTsSigLen=" << nTsSigLen
);
1489 std::unique_ptr
<BYTE
[]> pTsSig(new BYTE
[nTsSigLen
]);
1491 if (!CryptMsgGetParam(hMsg
, CMSG_BARE_CONTENT_PARAM
, 0, pTsSig
.get(), &nTsSigLen
))
1493 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1494 CryptMsgClose(hDecodedMsg
);
1495 CryptMsgClose(hMsg
);
1496 CertFreeCertificateContext(pCertContext
);
1500 if (!CryptMsgUpdate(hDecodedMsg
, pTsSig
.get(), nTsSigLen
, TRUE
))
1502 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
1503 CryptMsgClose(hDecodedMsg
);
1504 CryptMsgClose(hMsg
);
1505 CertFreeCertificateContext(pCertContext
);
1509 DWORD nDecodedSignerInfoLen
= 0;
1510 if (!CryptMsgGetParam(hDecodedMsg
, CMSG_SIGNER_INFO_PARAM
, 0, nullptr, &nDecodedSignerInfoLen
))
1512 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
1513 CryptMsgClose(hDecodedMsg
);
1514 CryptMsgClose(hMsg
);
1515 CertFreeCertificateContext(pCertContext
);
1519 std::unique_ptr
<BYTE
[]> pDecodedSignerInfoBuf(new BYTE
[nDecodedSignerInfoLen
]);
1521 if (!CryptMsgGetParam(hDecodedMsg
, CMSG_SIGNER_INFO_PARAM
, 0, pDecodedSignerInfoBuf
.get(), &nDecodedSignerInfoLen
))
1523 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
1524 CryptMsgClose(hDecodedMsg
);
1525 CryptMsgClose(hMsg
);
1526 CertFreeCertificateContext(pCertContext
);
1530 CMSG_SIGNER_INFO
*pDecodedSignerInfo
= reinterpret_cast<CMSG_SIGNER_INFO
*>(pDecodedSignerInfoBuf
.get());
1532 CRYPT_TIMESTAMP_PARA aTsPara
;
1533 unsigned int nNonce
= comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32
);
1535 aTsPara
.pszTSAPolicyId
= nullptr;
1536 aTsPara
.fRequestCerts
= TRUE
;
1537 aTsPara
.Nonce
.cbData
= sizeof(nNonce
);
1538 aTsPara
.Nonce
.pbData
= reinterpret_cast<BYTE
*>(&nNonce
);
1539 aTsPara
.cExtension
= 0;
1540 aTsPara
.rgExtension
= nullptr;
1542 if (!CryptRetrieveTimeStamp(o3tl::toW(m_aSignTSA
.getStr()),
1547 pDecodedSignerInfo
->EncryptedHash
.pbData
,
1548 pDecodedSignerInfo
->EncryptedHash
.cbData
,
1553 SAL_WARN("svl.crypto", "CryptRetrieveTimeStamp failed: " << WindowsErrorString(GetLastError()));
1554 CryptMsgClose(hDecodedMsg
);
1555 CryptMsgClose(hMsg
);
1556 CertFreeCertificateContext(pCertContext
);
1560 SAL_INFO("svl.crypto", "Time stamp size is " << pTsContext
->cbEncoded
<< " bytes");
1562 // I tried to use CryptMsgControl() with CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR to add the
1563 // timestamp, but that failed with "The parameter is incorrect". Probably it is too late to
1564 // modify the message once its data has already been encoded as part of the
1565 // CryptMsgGetParam() with CMSG_BARE_CONTENT_PARAM above. So close the message and re-do its
1566 // creation steps, but now with an amended aSignerInfo.
1568 CRYPT_INTEGER_BLOB aTimestampBlob
;
1569 aTimestampBlob
.cbData
= pTsContext
->cbEncoded
;
1570 aTimestampBlob
.pbData
= pTsContext
->pbEncoded
;
1572 CRYPT_ATTRIBUTE aTimestampAttribute
;
1573 aTimestampAttribute
.pszObjId
= const_cast<LPSTR
>(
1574 "1.2.840.113549.1.9.16.2.14");
1575 aTimestampAttribute
.cValue
= 1;
1576 aTimestampAttribute
.rgValue
= &aTimestampBlob
;
1578 aSignerInfo
.cUnauthAttr
= 1;
1579 aSignerInfo
.rgUnauthAttr
= &aTimestampAttribute
;
1581 CryptMsgClose(hMsg
);
1583 hMsg
= CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
1590 for (size_t i
= 0; i
< m_dataBlocks
.size(); ++i
)
1592 const bool last
= (i
== m_dataBlocks
.size() - 1);
1594 !CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(m_dataBlocks
[i
].first
), m_dataBlocks
[i
].second
, last
))
1596 SAL_WARN("svl.crypto", "Re-creating the message failed: " << WindowsErrorString(GetLastError()));
1597 CryptMemFree(pTsContext
);
1598 CryptMsgClose(hDecodedMsg
);
1599 CryptMsgClose(hMsg
);
1600 CertFreeCertificateContext(pCertContext
);
1605 CryptMsgClose(hDecodedMsg
);
1610 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, nullptr, &nSigLen
))
1612 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1614 CryptMemFree(pTsContext
);
1615 CryptMsgClose(hMsg
);
1616 CertFreeCertificateContext(pCertContext
);
1620 if (nSigLen
*2 > MAX_SIGNATURE_CONTENT_LENGTH
)
1622 SAL_WARN("svl.crypto", "Signature requires more space (" << nSigLen
*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH
<< ")");
1624 CryptMemFree(pTsContext
);
1625 CryptMsgClose(hMsg
);
1626 CertFreeCertificateContext(pCertContext
);
1630 SAL_INFO("svl.crypto", "Signature size is " << nSigLen
<< " bytes");
1631 std::unique_ptr
<BYTE
[]> pSig(new BYTE
[nSigLen
]);
1633 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, pSig
.get(), &nSigLen
))
1635 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1637 CryptMemFree(pTsContext
);
1638 CryptMsgClose(hMsg
);
1639 CertFreeCertificateContext(pCertContext
);
1643 // Release resources
1645 CryptMemFree(pTsContext
);
1646 CryptMsgClose(hMsg
);
1647 CertFreeCertificateContext(pCertContext
);
1649 for (unsigned int i
= 0; i
< nSigLen
; i
++)
1650 appendHex(pSig
[i
], rCMSHexBuffer
);
1653 #endif // USE_CRYPTO_MSCAPI
1654 #endif // USE_CRYPTO_ANY
1660 /// Similar to NSS_CMSAttributeArray_FindAttrByOidTag(), but works directly with a SECOidData.
1661 NSSCMSAttribute
* CMSAttributeArray_FindAttrByOidData(NSSCMSAttribute
** attrs
, SECOidData
const * oid
, PRBool only
)
1663 NSSCMSAttribute
* attr1
, *attr2
;
1665 if (attrs
== nullptr)
1671 while ((attr1
= *attrs
++) != nullptr)
1673 if (attr1
->type
.len
== oid
->oid
.len
&& PORT_Memcmp(attr1
->type
.data
,
1679 if (attr1
== nullptr)
1685 while ((attr2
= *attrs
++) != nullptr)
1687 if (attr2
->type
.len
== oid
->oid
.len
&& PORT_Memcmp(attr2
->type
.data
,
1693 if (attr2
!= nullptr)
1699 /// Same as SEC_StringToOID(), which is private to us.
1700 SECStatus
StringToOID(SECItem
* to
, const char* from
, PRUint32 len
)
1702 PRUint32 decimal_numbers
= 0;
1703 PRUint32 result_bytes
= 0;
1705 PRUint8 result
[1024];
1707 static const PRUint32 max_decimal
= 0xffffffff / 10;
1708 static const char OIDstring
[] = {"OID."};
1712 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
1717 len
= PL_strlen(from
);
1719 if (len
>= 4 && !PL_strncasecmp(from
, OIDstring
, 4))
1721 from
+= 4; /* skip leading "OID." if present */
1727 PORT_SetError(SEC_ERROR_BAD_DATA
);
1732 PRUint32 decimal
= 0;
1733 while (len
> 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from
)))
1735 PRUint32 addend
= *from
++ - '0';
1737 if (decimal
> max_decimal
) /* overflow */
1739 decimal
= (decimal
* 10) + addend
;
1740 if (decimal
< addend
) /* overflow */
1743 if (len
!= 0 && *from
!= '.')
1747 if (decimal_numbers
== 0)
1751 result
[0] = decimal
* 40;
1754 else if (decimal_numbers
== 1)
1758 result
[0] += decimal
;
1762 /* encode the decimal number, */
1764 PRUint32 num_bytes
= 0;
1765 PRUint32 tmp
= decimal
;
1772 ++num_bytes
; /* use one byte for a zero value */
1773 if (static_cast<size_t>(num_bytes
) + result_bytes
> sizeof result
)
1776 rp
= result
+ result_bytes
- 1;
1777 rp
[tmp
] = static_cast<PRUint8
>(decimal
& 0x7f);
1781 rp
[tmp
] = static_cast<PRUint8
>(decimal
| 0x80);
1784 result_bytes
+= num_bytes
;
1787 if (len
> 0) /* skip trailing '.' */
1794 /* now result contains result_bytes of data */
1795 if (to
->data
&& to
->len
>= result_bytes
)
1797 to
->len
= result_bytes
;
1798 PORT_Memcpy(to
->data
, result
, to
->len
);
1803 SECItem result_item
= {siBuffer
, nullptr, 0 };
1804 result_item
.data
= result
;
1805 result_item
.len
= result_bytes
;
1806 rv
= SECITEM_CopyItem(nullptr, to
, &result_item
);
1811 #elif USE_CRYPTO_MSCAPI // ends USE_CRYPTO_NSS
1813 /// Verifies a non-detached signature using CryptoAPI.
1814 bool VerifyNonDetachedSignature(const std::vector
<unsigned char>& aData
, const std::vector
<BYTE
>& rExpectedHash
)
1816 HCRYPTPROV hProv
= 0;
1817 if (!CryptAcquireContextW(&hProv
, nullptr, nullptr, PROV_RSA_AES
, CRYPT_VERIFYCONTEXT
))
1819 SAL_WARN("svl.crypto", "CryptAcquireContext() failed");
1823 HCRYPTHASH hHash
= 0;
1824 if (!CryptCreateHash(hProv
, CALG_SHA1
, 0, 0, &hHash
))
1826 SAL_WARN("svl.crypto", "CryptCreateHash() failed");
1830 if (!CryptHashData(hHash
, aData
.data(), aData
.size(), 0))
1832 SAL_WARN("svl.crypto", "CryptHashData() failed");
1836 DWORD nActualHash
= 0;
1837 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, nullptr, &nActualHash
, 0))
1839 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash length");
1843 std::vector
<unsigned char> aActualHash(nActualHash
);
1844 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, aActualHash
.data(), &nActualHash
, 0))
1846 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash");
1850 CryptDestroyHash(hHash
);
1851 CryptReleaseContext(hProv
, 0);
1853 return aActualHash
.size() == rExpectedHash
.size() &&
1854 !std::memcmp(aActualHash
.data(), rExpectedHash
.data(), aActualHash
.size());
1857 OUString
GetSubjectName(PCCERT_CONTEXT pCertContext
)
1859 OUString subjectName
;
1861 // Get Subject name size.
1862 DWORD dwData
= CertGetNameStringW(pCertContext
,
1863 CERT_NAME_SIMPLE_DISPLAY_TYPE
,
1870 SAL_WARN("svl.crypto", "ValidateSignature: CertGetNameString failed");
1874 // Allocate memory for subject name.
1875 LPWSTR szName
= static_cast<LPWSTR
>(
1876 LocalAlloc(LPTR
, dwData
* sizeof(WCHAR
)));
1879 SAL_WARN("svl.crypto", "ValidateSignature: Unable to allocate memory for subject name");
1883 // Get subject name.
1884 if (!CertGetNameStringW(pCertContext
,
1885 CERT_NAME_SIMPLE_DISPLAY_TYPE
,
1892 SAL_WARN("svl.crypto", "ValidateSignature: CertGetNameString failed");
1896 subjectName
= o3tl::toU(szName
);
1901 #endif // USE_CRYPTO_MSCAPI
1904 void ensureNssInit()
1906 // e.g. tdf#122599 ensure NSS library is initialized for NSS_CMSMessage_CreateFromDER
1907 css::uno::Reference
<css::xml::crypto::XNSSInitializer
>
1908 xNSSInitializer
= css::xml::crypto::NSSInitializer::create(comphelper::getProcessComponentContext());
1910 // this calls NSS_Init
1911 xNSSInitializer
->getDigestContext(css::xml::crypto::DigestID::SHA256
,
1912 uno::Sequence
<beans::NamedValue
>());
1915 } // anonymous namespace
1917 bool Signing::Verify(const std::vector
<unsigned char>& aData
,
1918 const bool bNonDetached
,
1919 const std::vector
<unsigned char>& aSignature
,
1920 SignatureInformation
& rInformation
)
1923 // ensure NSS_Init() is called before using NSS_CMSMessage_CreateFromDER
1924 static std::once_flag aInitOnce
;
1925 std::call_once(aInitOnce
, ensureNssInit
);
1927 // Validate the signature.
1928 SECItem aSignatureItem
;
1929 aSignatureItem
.data
= const_cast<unsigned char*>(aSignature
.data());
1930 aSignatureItem
.len
= aSignature
.size();
1931 NSSCMSMessage
* pCMSMessage
= NSS_CMSMessage_CreateFromDER(&aSignatureItem
,
1935 /*pwfn_arg=*/nullptr,
1936 /*decrypt_key_cb=*/nullptr,
1937 /*decrypt_key_cb_arg=*/nullptr);
1938 if (!NSS_CMSMessage_IsSigned(pCMSMessage
))
1940 SAL_WARN("svl.crypto", "ValidateSignature: message is not signed");
1944 NSSCMSContentInfo
* pCMSContentInfo
= NSS_CMSMessage_ContentLevel(pCMSMessage
, 0);
1945 if (!pCMSContentInfo
)
1947 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSMessage_ContentLevel() failed");
1951 auto pCMSSignedData
= static_cast<NSSCMSSignedData
*>(NSS_CMSContentInfo_GetContent(pCMSContentInfo
));
1952 if (!pCMSSignedData
)
1954 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSContentInfo_GetContent() failed");
1958 // Import certificates from the signed data temporarily, so it'll be
1959 // possible to verify the signature, even if we didn't have the certificate
1961 std::vector
<CERTCertificate
*> aDocumentCertificates
;
1962 for (size_t i
= 0; pCMSSignedData
->rawCerts
[i
]; ++i
)
1963 aDocumentCertificates
.push_back(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), pCMSSignedData
->rawCerts
[i
], nullptr, 0, 0));
1965 NSSCMSSignerInfo
* pCMSSignerInfo
= NSS_CMSSignedData_GetSignerInfo(pCMSSignedData
, 0);
1966 if (!pCMSSignerInfo
)
1968 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSSignedData_GetSignerInfo() failed");
1972 SECItem aAlgorithm
= NSS_CMSSignedData_GetDigestAlgs(pCMSSignedData
)[0]->algorithm
;
1973 SECOidTag eOidTag
= SECOID_FindOIDTag(&aAlgorithm
);
1975 // Map a sign algorithm to a digest algorithm.
1976 // See NSS_CMSUtil_MapSignAlgs(), which is private to us.
1979 case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION
:
1980 eOidTag
= SEC_OID_SHA1
;
1982 case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION
:
1983 eOidTag
= SEC_OID_SHA256
;
1985 case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION
:
1986 eOidTag
= SEC_OID_SHA512
;
1992 HASH_HashType eHashType
= HASH_GetHashTypeByOidTag(eOidTag
);
1993 HASHContext
* pHASHContext
= HASH_Create(eHashType
);
1996 SAL_WARN("svl.crypto", "ValidateSignature: HASH_Create() failed");
2000 // We have a hash, update it with the byte ranges.
2001 HASH_Update(pHASHContext
, aData
.data(), aData
.size());
2003 // Find out what is the expected length of the hash.
2004 unsigned int nMaxResultLen
= 0;
2008 nMaxResultLen
= comphelper::SHA1_HASH_LENGTH
;
2009 rInformation
.nDigestID
= xml::crypto::DigestID::SHA1
;
2011 case SEC_OID_SHA256
:
2012 nMaxResultLen
= comphelper::SHA256_HASH_LENGTH
;
2013 rInformation
.nDigestID
= xml::crypto::DigestID::SHA256
;
2015 case SEC_OID_SHA512
:
2016 nMaxResultLen
= comphelper::SHA512_HASH_LENGTH
;
2017 rInformation
.nDigestID
= xml::crypto::DigestID::SHA512
;
2020 SAL_WARN("svl.crypto", "ValidateSignature: unrecognized algorithm");
2024 auto pActualResultBuffer
= static_cast<unsigned char*>(PORT_Alloc(nMaxResultLen
));
2025 unsigned int nActualResultLen
;
2026 HASH_End(pHASHContext
, pActualResultBuffer
, &nActualResultLen
, nMaxResultLen
);
2028 CERTCertificate
* pCertificate
= NSS_CMSSignerInfo_GetSigningCertificate(pCMSSignerInfo
, CERT_GetDefaultCertDB());
2031 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSSignerInfo_GetSigningCertificate() failed");
2036 uno::Sequence
<sal_Int8
> aDerCert(pCertificate
->derCert
.len
);
2037 auto aDerCertRange
= asNonConstRange(aDerCert
);
2038 for (size_t i
= 0; i
< pCertificate
->derCert
.len
; ++i
)
2039 aDerCertRange
[i
] = pCertificate
->derCert
.data
[i
];
2040 OUStringBuffer aBuffer
;
2041 comphelper::Base64::encode(aBuffer
, aDerCert
);
2042 SignatureInformation::X509Data temp
;
2043 temp
.emplace_back();
2044 temp
.back().X509Certificate
= aBuffer
.makeStringAndClear();
2045 temp
.back().X509Subject
= OUString(pCertificate
->subjectName
, PL_strlen(pCertificate
->subjectName
), RTL_TEXTENCODING_UTF8
);
2046 rInformation
.X509Datas
.clear();
2047 rInformation
.X509Datas
.emplace_back(temp
);
2050 PRTime nSigningTime
;
2051 // This may fail, in which case the date should be taken from the PDF's dictionary's "M" key,
2052 // so not critical for PDF at least.
2053 if (NSS_CMSSignerInfo_GetSigningTime(pCMSSignerInfo
, &nSigningTime
) == SECSuccess
)
2055 // First convert the UTC UNIX timestamp to a tools::DateTime.
2056 // nSigningTime is in microseconds.
2057 DateTime aDateTime
= DateTime::CreateFromUnixTime(static_cast<double>(nSigningTime
) / 1000000);
2059 // Then convert to a local UNO DateTime.
2060 aDateTime
.ConvertToLocalTime();
2061 rInformation
.stDateTime
= aDateTime
.GetUNODateTime();
2062 if (rInformation
.ouDateTime
.isEmpty())
2064 OUStringBuffer rBuffer
;
2065 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetYear()));
2066 rBuffer
.append('-');
2067 if (aDateTime
.GetMonth() < 10)
2068 rBuffer
.append('0');
2069 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetMonth()));
2070 rBuffer
.append('-');
2071 if (aDateTime
.GetDay() < 10)
2072 rBuffer
.append('0');
2073 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetDay()));
2074 rInformation
.ouDateTime
= rBuffer
.makeStringAndClear();
2078 // Check if we have a signing certificate attribute.
2079 SECOidData aOidData
;
2080 aOidData
.oid
.data
= nullptr;
2082 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
2083 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
2084 * smime(16) id-aa(2) 47 }
2086 if (StringToOID(&aOidData
.oid
, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess
)
2088 SAL_WARN("svl.crypto", "StringToOID() failed");
2091 aOidData
.offset
= SEC_OID_UNKNOWN
;
2092 aOidData
.desc
= "id-aa-signingCertificateV2";
2093 aOidData
.mechanism
= CKM_SHA_1
;
2094 aOidData
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
;
2095 NSSCMSAttribute
* pAttribute
= CMSAttributeArray_FindAttrByOidData(pCMSSignerInfo
->authAttr
, &aOidData
, PR_TRUE
);
2097 rInformation
.bHasSigningCertificate
= true;
2099 SECItem
* pContentInfoContentData
= pCMSSignedData
->contentInfo
.content
.data
;
2100 if (bNonDetached
&& pContentInfoContentData
&& pContentInfoContentData
->data
)
2102 // Not a detached signature.
2103 if (!std::memcmp(pActualResultBuffer
, pContentInfoContentData
->data
, nMaxResultLen
) && nActualResultLen
== pContentInfoContentData
->len
)
2104 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2108 // Detached, the usual case.
2109 SECItem aActualResultItem
;
2110 aActualResultItem
.data
= pActualResultBuffer
;
2111 aActualResultItem
.len
= nActualResultLen
;
2112 if (NSS_CMSSignerInfo_Verify(pCMSSignerInfo
, &aActualResultItem
, nullptr) == SECSuccess
)
2113 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2116 // Everything went fine
2117 SECITEM_FreeItem(&aOidData
.oid
, false);
2118 PORT_Free(pActualResultBuffer
);
2119 HASH_Destroy(pHASHContext
);
2120 NSS_CMSSignerInfo_Destroy(pCMSSignerInfo
);
2121 for (auto pDocumentCertificate
: aDocumentCertificates
)
2122 CERT_DestroyCertificate(pDocumentCertificate
);
2126 #elif USE_CRYPTO_MSCAPI // ends USE_CRYPTO_NSS
2128 // Open a message for decoding.
2129 HCRYPTMSG hMsg
= CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
2137 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgOpenToDecode() failed");
2141 // Update the message with the encoded header blob.
2142 if (!CryptMsgUpdate(hMsg
, aSignature
.data(), aSignature
.size(), TRUE
))
2144 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the header failed: " << WindowsErrorString(GetLastError()));
2148 // Update the message with the content blob.
2149 if (!CryptMsgUpdate(hMsg
, aData
.data(), aData
.size(), FALSE
))
2151 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError()));
2155 if (!CryptMsgUpdate(hMsg
, nullptr, 0, TRUE
))
2157 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the last content failed: " << WindowsErrorString(GetLastError()));
2161 // Get the CRYPT_ALGORITHM_IDENTIFIER from the message.
2162 DWORD nDigestID
= 0;
2163 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_HASH_ALGORITHM_PARAM
, 0, nullptr, &nDigestID
))
2165 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
2168 std::unique_ptr
<BYTE
[]> pDigestBytes(new BYTE
[nDigestID
]);
2169 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_HASH_ALGORITHM_PARAM
, 0, pDigestBytes
.get(), &nDigestID
))
2171 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
2174 auto pDigestID
= reinterpret_cast<CRYPT_ALGORITHM_IDENTIFIER
*>(pDigestBytes
.get());
2175 if (std::string_view(szOID_NIST_sha256
) == pDigestID
->pszObjId
)
2176 rInformation
.nDigestID
= xml::crypto::DigestID::SHA256
;
2177 else if (std::string_view(szOID_RSA_SHA1RSA
) == pDigestID
->pszObjId
|| std::string_view(szOID_OIWSEC_sha1
) == pDigestID
->pszObjId
)
2178 rInformation
.nDigestID
= xml::crypto::DigestID::SHA1
;
2180 // Don't error out here, we can still verify the message digest correctly, just the digest ID won't be set.
2181 SAL_WARN("svl.crypto", "ValidateSignature: unhandled algorithm identifier '"<<pDigestID
->pszObjId
<<"'");
2183 // Get the signer CERT_INFO from the message.
2184 DWORD nSignerCertInfo
= 0;
2185 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_CERT_INFO_PARAM
, 0, nullptr, &nSignerCertInfo
))
2187 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2190 std::unique_ptr
<BYTE
[]> pSignerCertInfoBuf(new BYTE
[nSignerCertInfo
]);
2191 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_CERT_INFO_PARAM
, 0, pSignerCertInfoBuf
.get(), &nSignerCertInfo
))
2193 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2196 PCERT_INFO pSignerCertInfo
= reinterpret_cast<PCERT_INFO
>(pSignerCertInfoBuf
.get());
2198 // Open a certificate store in memory using CERT_STORE_PROV_MSG, which
2199 // initializes it with the certificates from the message.
2200 HCERTSTORE hStoreHandle
= CertOpenStore(CERT_STORE_PROV_MSG
,
2201 PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
2207 SAL_WARN("svl.crypto", "ValidateSignature: CertOpenStore() failed");
2211 // Find the signer's certificate in the store.
2212 PCCERT_CONTEXT pSignerCertContext
= CertGetSubjectCertificateFromStore(hStoreHandle
,
2213 PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
2215 if (!pSignerCertContext
)
2217 SAL_WARN("svl.crypto", "ValidateSignature: CertGetSubjectCertificateFromStore() failed");
2222 // Write rInformation.ouX509Certificate.
2223 uno::Sequence
<sal_Int8
> aDerCert(pSignerCertContext
->cbCertEncoded
);
2224 std::copy_n(pSignerCertContext
->pbCertEncoded
, pSignerCertContext
->cbCertEncoded
,
2225 aDerCert
.getArray());
2226 OUStringBuffer aBuffer
;
2227 comphelper::Base64::encode(aBuffer
, aDerCert
);
2228 SignatureInformation::X509Data temp
;
2229 temp
.emplace_back();
2230 temp
.back().X509Certificate
= aBuffer
.makeStringAndClear();
2231 temp
.back().X509Subject
= GetSubjectName(pSignerCertContext
);
2232 rInformation
.X509Datas
.clear();
2233 rInformation
.X509Datas
.emplace_back(temp
);
2238 // Not a detached signature.
2239 DWORD nContentParam
= 0;
2240 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, nullptr, &nContentParam
))
2242 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2246 std::vector
<BYTE
> aContentParam(nContentParam
);
2247 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, aContentParam
.data(), &nContentParam
))
2249 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2253 if (VerifyNonDetachedSignature(aData
, aContentParam
))
2254 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2258 // Detached, the usual case.
2259 // Use the CERT_INFO from the signer certificate to verify the signature.
2260 if (CryptMsgControl(hMsg
, 0, CMSG_CTRL_VERIFY_SIGNATURE
, pSignerCertContext
->pCertInfo
))
2261 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2264 // Check if we have a signing certificate attribute.
2265 DWORD nSignedAttributes
= 0;
2266 if (CryptMsgGetParam(hMsg
, CMSG_SIGNER_AUTH_ATTR_PARAM
, 0, nullptr, &nSignedAttributes
))
2268 std::unique_ptr
<BYTE
[]> pSignedAttributesBuf(new BYTE
[nSignedAttributes
]);
2269 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_AUTH_ATTR_PARAM
, 0, pSignedAttributesBuf
.get(), &nSignedAttributes
))
2271 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() authenticated failed");
2274 auto pSignedAttributes
= reinterpret_cast<PCRYPT_ATTRIBUTES
>(pSignedAttributesBuf
.get());
2275 for (size_t nAttr
= 0; nAttr
< pSignedAttributes
->cAttr
; ++nAttr
)
2277 CRYPT_ATTRIBUTE
& rAttr
= pSignedAttributes
->rgAttr
[nAttr
];
2279 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
2280 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
2281 * smime(16) id-aa(2) 47 }
2283 if (std::string_view("1.2.840.113549.1.9.16.2.47") == rAttr
.pszObjId
)
2285 rInformation
.bHasSigningCertificate
= true;
2291 // Get the unauthorized attributes.
2292 nSignedAttributes
= 0;
2293 if (CryptMsgGetParam(hMsg
, CMSG_SIGNER_UNAUTH_ATTR_PARAM
, 0, nullptr, &nSignedAttributes
))
2295 std::unique_ptr
<BYTE
[]> pSignedAttributesBuf(new BYTE
[nSignedAttributes
]);
2296 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_UNAUTH_ATTR_PARAM
, 0, pSignedAttributesBuf
.get(), &nSignedAttributes
))
2298 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() unauthenticated failed");
2301 auto pSignedAttributes
= reinterpret_cast<PCRYPT_ATTRIBUTES
>(pSignedAttributesBuf
.get());
2302 for (size_t nAttr
= 0; nAttr
< pSignedAttributes
->cAttr
; ++nAttr
)
2304 CRYPT_ATTRIBUTE
& rAttr
= pSignedAttributes
->rgAttr
[nAttr
];
2306 if (std::string_view("1.2.840.113549.1.9.16.2.14") == rAttr
.pszObjId
)
2308 PCRYPT_TIMESTAMP_CONTEXT pTsContext
;
2309 if (!CryptVerifyTimeStampSignature(rAttr
.rgValue
->pbData
, rAttr
.rgValue
->cbData
, nullptr, 0, nullptr, &pTsContext
, nullptr, nullptr))
2311 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
2315 DateTime aDateTime
= DateTime::CreateFromWin32FileDateTime(pTsContext
->pTimeStamp
->ftTime
.dwLowDateTime
, pTsContext
->pTimeStamp
->ftTime
.dwHighDateTime
);
2317 // Then convert to a local UNO DateTime.
2318 aDateTime
.ConvertToLocalTime();
2319 rInformation
.stDateTime
= aDateTime
.GetUNODateTime();
2320 if (rInformation
.ouDateTime
.isEmpty())
2322 OUStringBuffer rBuffer
;
2323 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetYear()));
2324 rBuffer
.append('-');
2325 if (aDateTime
.GetMonth() < 10)
2326 rBuffer
.append('0');
2327 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetMonth()));
2328 rBuffer
.append('-');
2329 if (aDateTime
.GetDay() < 10)
2330 rBuffer
.append('0');
2331 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetDay()));
2332 rInformation
.ouDateTime
= rBuffer
.makeStringAndClear();
2339 CertCloseStore(hStoreHandle
, CERT_CLOSE_STORE_FORCE_FLAG
);
2340 CryptMsgClose(hMsg
);
2352 bool Signing::Verify(SvStream
& rStream
,
2353 const std::vector
<std::pair
<size_t, size_t>>& aByteRanges
,
2354 const bool bNonDetached
,
2355 const std::vector
<unsigned char>& aSignature
,
2356 SignatureInformation
& rInformation
)
2359 std::vector
<unsigned char> buffer
;
2361 // Copy the byte ranges into a single buffer.
2362 for (const auto& rByteRange
: aByteRanges
)
2364 rStream
.Seek(rByteRange
.first
);
2365 const size_t size
= buffer
.size();
2366 buffer
.resize(size
+ rByteRange
.second
);
2367 rStream
.ReadBytes(buffer
.data() + size
, rByteRange
.second
);
2370 return Verify(buffer
, bNonDetached
, aSignature
, rInformation
);
2385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */