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>
17 #include <o3tl/numeric.hxx>
20 #include <systools/curlinit.hxx>
23 #include <rtl/character.hxx>
24 #include <rtl/strbuf.hxx>
25 #include <rtl/string.hxx>
26 #include <sal/log.hxx>
27 #include <tools/datetime.hxx>
28 #include <tools/stream.hxx>
29 #include <comphelper/base64.hxx>
30 #include <comphelper/hash.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/random.hxx>
33 #include <comphelper/scopeguard.hxx>
34 #include <comphelper/lok.hxx>
35 #include <com/sun/star/security/XCertificate.hpp>
36 #include <com/sun/star/uno/Sequence.hxx>
37 #include <o3tl/char16_t2wchar_t.hxx>
40 // NSS headers for PDF signing
50 // We use curl for RFC3161 time stamp requests
51 #include <curl/curl.h>
53 #include <com/sun/star/xml/crypto/DigestID.hpp>
54 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
59 // WinCrypt headers for PDF signing
60 // Note: this uses Windows 7 APIs and requires the relevant data types
64 #include <comphelper/windowserrorstring.hxx>
67 using namespace com::sun::star
;
71 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo
* /*slot*/, PRBool
/*retry*/, void *arg
)
73 return PL_strdup(static_cast<char *>(arg
));
76 // ASN.1 used in the (much simpler) time stamp request. From RFC3161
80 AlgorithmIdentifier ::= SEQUENCE {
81 algorithm OBJECT IDENTIFIER,
82 parameters ANY DEFINED BY algorithm OPTIONAL }
83 -- contains a value of the type
84 -- registered for use with the
85 -- algorithm object identifier value
87 MessageImprint ::= SEQUENCE {
88 hashAlgorithm AlgorithmIdentifier,
89 hashedMessage OCTET STRING }
92 struct MessageImprint
{
93 SECAlgorithmID hashAlgorithm
;
94 SECItem hashedMessage
;
98 Extension ::= SEQUENCE {
99 extnID OBJECT IDENTIFIER,
100 critical BOOLEAN DEFAULT FALSE,
101 extnValue OCTET STRING }
111 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
115 TSAPolicyId ::= OBJECT IDENTIFIER
117 TimeStampReq ::= SEQUENCE {
118 version INTEGER { v1(1) },
119 messageImprint MessageImprint,
120 --a hash algorithm OID and the hash value of the data to be
122 reqPolicy TSAPolicyId OPTIONAL,
123 nonce INTEGER OPTIONAL,
124 certReq BOOLEAN DEFAULT FALSE,
125 extensions [0] IMPLICIT Extensions OPTIONAL }
128 struct TimeStampReq
{
130 MessageImprint messageImprint
;
134 Extension
*extensions
;
138 * General name, defined by RFC 3280.
146 * List of general names (only one for now), defined by RFC 3280.
154 * Supplies different fields to identify a certificate, defined by RFC 5035.
159 SECItem serialNumber
;
163 * Supplies different fields that are used to identify certificates, defined by
168 SECAlgorithmID hashAlgorithm
;
170 IssuerSerial issuerSerial
;
174 * This attribute uses the ESSCertIDv2 structure, defined by RFC 5035.
176 struct SigningCertificateV2
180 SigningCertificateV2()
187 * GeneralName ::= CHOICE {
188 * otherName [0] OtherName,
189 * rfc822Name [1] IA5String,
190 * dNSName [2] IA5String,
191 * x400Address [3] ORAddress,
192 * directoryName [4] Name,
193 * ediPartyName [5] EDIPartyName,
194 * uniformResourceIdentifier [6] IA5String,
195 * iPAddress [7] OCTET STRING,
196 * registeredID [8] OBJECT IDENTIFIER
199 const SEC_ASN1Template GeneralNameTemplate
[] =
201 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralName
)},
202 {SEC_ASN1_INLINE
, offsetof(GeneralName
, name
), CERT_NameTemplate
, 0},
207 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
209 const SEC_ASN1Template GeneralNamesTemplate
[] =
211 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralNames
)},
212 {SEC_ASN1_INLINE
| SEC_ASN1_CONTEXT_SPECIFIC
| 4, offsetof(GeneralNames
, names
), GeneralNameTemplate
, 0},
217 * IssuerSerial ::= SEQUENCE {
218 * issuer GeneralNames,
219 * serialNumber CertificateSerialNumber
222 const SEC_ASN1Template IssuerSerialTemplate
[] =
224 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(IssuerSerial
)},
225 {SEC_ASN1_INLINE
, offsetof(IssuerSerial
, issuer
), GeneralNamesTemplate
, 0},
226 {SEC_ASN1_INTEGER
, offsetof(IssuerSerial
, serialNumber
), nullptr, 0},
232 * Hash ::= OCTET STRING
234 * ESSCertIDv2 ::= SEQUENCE {
235 * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},
237 * issuerSerial IssuerSerial OPTIONAL
241 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate
)
243 const SEC_ASN1Template ESSCertIDv2Template
[] =
245 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(ESSCertIDv2
)},
246 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, hashAlgorithm
), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate
), 0},
247 {SEC_ASN1_OCTET_STRING
, offsetof(ESSCertIDv2
, certHash
), nullptr, 0},
248 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, issuerSerial
), IssuerSerialTemplate
, 0},
253 * SigningCertificateV2 ::= SEQUENCE {
256 const SEC_ASN1Template SigningCertificateV2Template
[] =
258 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(SigningCertificateV2
)},
259 {SEC_ASN1_SEQUENCE_OF
, offsetof(SigningCertificateV2
, certs
), ESSCertIDv2Template
, 0},
263 struct PKIStatusInfo
{
265 SECItem statusString
;
269 const SEC_ASN1Template PKIStatusInfo_Template
[] =
271 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(PKIStatusInfo
) },
272 { SEC_ASN1_INTEGER
, offsetof(PKIStatusInfo
, status
), nullptr, 0 },
273 { SEC_ASN1_CONSTRUCTED
| SEC_ASN1_SEQUENCE
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, statusString
), nullptr, 0 },
274 { SEC_ASN1_BIT_STRING
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, failInfo
), nullptr, 0 },
278 const SEC_ASN1Template Any_Template
[] =
280 { SEC_ASN1_ANY
, 0, nullptr, sizeof(SECItem
) }
283 struct TimeStampResp
{
284 PKIStatusInfo status
;
285 SECItem timeStampToken
;
288 const SEC_ASN1Template TimeStampResp_Template
[] =
290 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampResp
) },
291 { SEC_ASN1_INLINE
, offsetof(TimeStampResp
, status
), PKIStatusInfo_Template
, 0 },
292 { SEC_ASN1_ANY
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampResp
, timeStampToken
), Any_Template
, 0 },
296 const SEC_ASN1Template MessageImprint_Template
[] =
298 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(MessageImprint
) },
299 { SEC_ASN1_INLINE
, offsetof(MessageImprint
, hashAlgorithm
), SECOID_AlgorithmIDTemplate
, 0 },
300 { SEC_ASN1_OCTET_STRING
, offsetof(MessageImprint
, hashedMessage
), nullptr, 0 },
304 const SEC_ASN1Template Extension_Template
[] =
306 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(Extension
) },
307 { SEC_ASN1_OBJECT_ID
, offsetof(Extension
, extnID
), nullptr, 0 },
308 { SEC_ASN1_BOOLEAN
, offsetof(Extension
, critical
), nullptr, 0 },
309 { SEC_ASN1_OCTET_STRING
, offsetof(Extension
, extnValue
), nullptr, 0 },
313 const SEC_ASN1Template Extensions_Template
[] =
315 { SEC_ASN1_SEQUENCE_OF
, 0, Extension_Template
, 0 }
318 const SEC_ASN1Template TimeStampReq_Template
[] =
320 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampReq
) },
321 { SEC_ASN1_INTEGER
, offsetof(TimeStampReq
, version
), nullptr, 0 },
322 { SEC_ASN1_INLINE
, offsetof(TimeStampReq
, messageImprint
), MessageImprint_Template
, 0 },
323 { SEC_ASN1_OBJECT_ID
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, reqPolicy
), nullptr, 0 },
324 { SEC_ASN1_INTEGER
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, nonce
), nullptr, 0 },
325 { SEC_ASN1_BOOLEAN
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, certReq
), nullptr, 0 },
326 { SEC_ASN1_OPTIONAL
| SEC_ASN1_CONTEXT_SPECIFIC
| 0, offsetof(TimeStampReq
, extensions
), Extensions_Template
, 0 },
330 size_t AppendToBuffer(char const *ptr
, size_t size
, size_t nmemb
, void *userdata
)
332 OStringBuffer
*pBuffer
= static_cast<OStringBuffer
*>(userdata
);
333 pBuffer
->append(ptr
, size
*nmemb
);
338 OUString
PKIStatusToString(int n
)
342 case 0: return u
"granted"_ustr
;
343 case 1: return u
"grantedWithMods"_ustr
;
344 case 2: return u
"rejection"_ustr
;
345 case 3: return u
"waiting"_ustr
;
346 case 4: return u
"revocationWarning"_ustr
;
347 case 5: return u
"revocationNotification"_ustr
;
348 default: return "unknown (" + OUString::number(n
) + ")";
352 OUString
PKIStatusInfoToString(const PKIStatusInfo
& rStatusInfo
)
354 OUString result
= u
"{status="_ustr
;
355 if (rStatusInfo
.status
.len
== 1)
356 result
+= PKIStatusToString(rStatusInfo
.status
.data
[0]);
358 result
+= "unknown (len=" + OUString::number(rStatusInfo
.status
.len
);
360 // FIXME: Perhaps look at rStatusInfo.statusString.data but note
361 // that we of course can't assume it contains proper UTF-8. After
362 // all, it is data from an external source. Also, RFC3161 claims
363 // it should be a SEQUENCE (1..MAX) OF UTF8String, but another
364 // source claimed it would be a single UTF8String, hmm?
366 // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO
373 // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
374 // not exported from libsmime, so copy them here. Sigh.
377 my_SEC_StringToOID(SECItem
*to
, const char *from
, PRUint32 len
)
379 PRUint32 decimal_numbers
= 0;
380 PRUint32 result_bytes
= 0;
382 PRUint8 result
[1024];
384 static const PRUint32 max_decimal
= 0xffffffff / 10;
385 static const char OIDstring
[] = {"OID."};
388 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
392 len
= PL_strlen(from
);
394 if (len
>= 4 && !PL_strncasecmp(from
, OIDstring
, 4)) {
395 from
+= 4; /* skip leading "OID." if present */
400 PORT_SetError(SEC_ERROR_BAD_DATA
);
404 PRUint32 decimal
= 0;
405 while (len
> 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from
))) {
406 PRUint32 addend
= *from
++ - '0';
408 if (decimal
> max_decimal
) /* overflow */
410 decimal
= (decimal
* 10) + addend
;
411 if (decimal
< addend
) /* overflow */
414 if (len
!= 0 && *from
!= '.') {
417 if (decimal_numbers
== 0) {
420 result
[0] = decimal
* 40;
422 } else if (decimal_numbers
== 1) {
425 result
[0] += decimal
;
427 /* encode the decimal number, */
429 PRUint32 num_bytes
= 0;
430 PRUint32 tmp
= decimal
;
436 ++num_bytes
; /* use one byte for a zero value */
437 if (num_bytes
+ result_bytes
> sizeof result
)
440 rp
= result
+ result_bytes
- 1;
441 rp
[tmp
] = static_cast<PRUint8
>(decimal
& 0x7f);
444 rp
[tmp
] = static_cast<PRUint8
>(decimal
| 0x80);
447 result_bytes
+= num_bytes
;
450 if (len
> 0) { /* skip trailing '.' */
455 /* now result contains result_bytes of data */
456 if (to
->data
&& to
->len
>= result_bytes
) {
457 to
->len
= result_bytes
;
458 PORT_Memcpy(to
->data
, result
, to
->len
);
461 SECItem result_item
= {siBuffer
, nullptr, 0 };
462 result_item
.data
= result
;
463 result_item
.len
= result_bytes
;
464 rv
= SECITEM_CopyItem(nullptr, to
, &result_item
);
470 my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute
**attrs
, SECOidTag oidtag
, PRBool only
)
473 NSSCMSAttribute
*attr1
, *attr2
;
475 if (attrs
== nullptr)
478 oid
= SECOID_FindOIDByTag(oidtag
);
482 while ((attr1
= *attrs
++) != nullptr) {
483 if (attr1
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr1
->type
.data
,
489 if (attr1
== nullptr)
495 while ((attr2
= *attrs
++) != nullptr) {
496 if (attr2
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr2
->type
.data
,
502 if (attr2
!= nullptr)
509 my_NSS_CMSArray_Add(PLArenaPool
*poolp
, void ***array
, void *obj
)
514 PORT_Assert(array
!= NULL
);
515 if (array
== nullptr)
518 if (*array
== nullptr) {
519 dest
= static_cast<void **>(PORT_ArenaAlloc(poolp
, 2 * sizeof(void *)));
524 dest
= static_cast<void **>(PORT_ArenaGrow (poolp
,
526 (n
+ 1) * sizeof(void *),
527 (n
+ 2) * sizeof(void *)));
540 my_NSS_CMSAttribute_GetType(const NSSCMSAttribute
*attr
)
544 typetag
= SECOID_FindOID(&(attr
->type
));
545 if (typetag
== nullptr)
546 return SEC_OID_UNKNOWN
;
548 return typetag
->offset
;
552 my_NSS_CMSAttributeArray_AddAttr(PLArenaPool
*poolp
, NSSCMSAttribute
***attrs
, NSSCMSAttribute
*attr
)
554 NSSCMSAttribute
*oattr
;
558 mark
= PORT_ArenaMark(poolp
);
560 /* find oidtag of attr */
561 type
= my_NSS_CMSAttribute_GetType(attr
);
563 /* see if we have one already */
564 oattr
= my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs
, type
, PR_FALSE
);
565 PORT_Assert (oattr
== NULL
);
566 if (oattr
!= nullptr)
567 goto loser
; /* XXX or would it be better to replace it? */
569 /* no, shove it in */
570 if (my_NSS_CMSArray_Add(poolp
, reinterpret_cast<void ***>(attrs
), static_cast<void *>(attr
)) != SECSuccess
)
573 PORT_ArenaUnmark(poolp
, mark
);
577 PORT_ArenaRelease(poolp
, mark
);
582 my_NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
584 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->unAuthAttr
), attr
);
588 my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
590 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->authAttr
), attr
);
593 NSSCMSMessage
*CreateCMSMessage(const PRTime
* time
,
594 NSSCMSSignedData
**cms_sd
,
595 NSSCMSSignerInfo
**cms_signer
,
596 CERTCertificate
*cert
,
599 NSSCMSMessage
*result
= NSS_CMSMessage_Create(nullptr);
602 SAL_WARN("svl.crypto", "NSS_CMSMessage_Create failed");
606 *cms_sd
= NSS_CMSSignedData_Create(result
);
609 SAL_WARN("svl.crypto", "NSS_CMSSignedData_Create failed");
610 NSS_CMSMessage_Destroy(result
);
614 NSSCMSContentInfo
*cms_cinfo
= NSS_CMSMessage_GetContentInfo(result
);
615 if (NSS_CMSContentInfo_SetContent_SignedData(result
, cms_cinfo
, *cms_sd
) != SECSuccess
)
617 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_SignedData failed");
618 NSS_CMSSignedData_Destroy(*cms_sd
);
619 NSS_CMSMessage_Destroy(result
);
623 cms_cinfo
= NSS_CMSSignedData_GetContentInfo(*cms_sd
);
625 // Attach NULL data as detached data
626 if (NSS_CMSContentInfo_SetContent_Data(result
, cms_cinfo
, nullptr, PR_TRUE
) != SECSuccess
)
628 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_Data failed");
629 NSS_CMSSignedData_Destroy(*cms_sd
);
630 NSS_CMSMessage_Destroy(result
);
634 // workaround: with legacy "dbm:", NSS can't find the private key - try out
635 // if it works, and fallback if it doesn't.
636 if (SECKEYPrivateKey
* pPrivateKey
= PK11_FindKeyByAnyCert(cert
, nullptr))
638 if (!comphelper::LibreOfficeKit::isActive())
640 // pPrivateKey only exists in the memory in the LOK case, don't delete it.
641 SECKEY_DestroyPrivateKey(pPrivateKey
);
643 *cms_signer
= NSS_CMSSignerInfo_Create(result
, cert
, SEC_OID_SHA256
);
647 pPrivateKey
= PK11_FindKeyByDERCert(cert
->slot
, cert
, nullptr);
648 SECKEYPublicKey
*const pPublicKey
= CERT_ExtractPublicKey(cert
);
649 if (pPublicKey
&& pPrivateKey
)
651 *cms_signer
= NSS_CMSSignerInfo_CreateWithSubjKeyID(result
, &cert
->subjectKeyID
, pPublicKey
, pPrivateKey
, SEC_OID_SHA256
);
652 SECKEY_DestroyPrivateKey(pPrivateKey
);
653 SECKEY_DestroyPublicKey(pPublicKey
);
656 // this is required in NSS_CMSSignerInfo_IncludeCerts()
657 // (and NSS_CMSSignerInfo_GetSigningCertificate() doesn't work)
658 (**cms_signer
).cert
= CERT_DupCertificate(cert
);
664 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_Create failed");
665 NSS_CMSSignedData_Destroy(*cms_sd
);
666 NSS_CMSMessage_Destroy(result
);
670 if (time
&& NSS_CMSSignerInfo_AddSigningTime(*cms_signer
, *time
) != SECSuccess
)
672 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddSigningTime failed");
673 NSS_CMSSignedData_Destroy(*cms_sd
);
674 NSS_CMSMessage_Destroy(result
);
678 if (NSS_CMSSignerInfo_IncludeCerts(*cms_signer
, NSSCMSCM_CertChain
, certUsageEmailSigner
) != SECSuccess
)
680 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_IncludeCerts failed");
681 NSS_CMSSignedData_Destroy(*cms_sd
);
682 NSS_CMSMessage_Destroy(result
);
686 if (NSS_CMSSignedData_AddCertificate(*cms_sd
, cert
) != SECSuccess
)
688 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddCertificate failed");
689 NSS_CMSSignedData_Destroy(*cms_sd
);
690 NSS_CMSMessage_Destroy(result
);
694 if (NSS_CMSSignedData_AddSignerInfo(*cms_sd
, *cms_signer
) != SECSuccess
)
696 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddSignerInfo failed");
697 NSS_CMSSignedData_Destroy(*cms_sd
);
698 NSS_CMSMessage_Destroy(result
);
702 if (NSS_CMSSignedData_SetDigestValue(*cms_sd
, SEC_OID_SHA256
, digest
) != SECSuccess
)
704 SAL_WARN("svl.crypto", "NSS_CMSSignedData_SetDigestValue failed");
705 NSS_CMSSignedData_Destroy(*cms_sd
);
706 NSS_CMSMessage_Destroy(result
);
713 #elif USE_CRYPTO_MSCAPI // ends USE_CRYPTO_NSS
715 /// Counts how many bytes are needed to encode a given length.
716 size_t GetDERLengthOfLength(size_t nLength
)
722 while (nLength
>> (nRet
* 8))
724 // Long form means one additional byte: the length of the length and
725 // the length itself.
731 /// Writes the length part of the header.
732 void WriteDERLength(SvStream
& rStream
, size_t nLength
)
734 size_t nLengthOfLength
= GetDERLengthOfLength(nLength
);
735 if (nLengthOfLength
== 1)
737 // We can use the short form.
738 rStream
.WriteUInt8(nLength
);
742 // 0x80 means that the we use the long form: the first byte is the length
743 // of length with the highest bit set to 1, not the actual length.
744 rStream
.WriteUInt8(0x80 | (nLengthOfLength
- 1));
745 for (size_t i
= 1; i
< nLengthOfLength
; ++i
)
746 rStream
.WriteUInt8(nLength
>> ((nLengthOfLength
- i
- 1) * 8));
749 const unsigned nASN1_INTEGER
= 0x02;
750 const unsigned nASN1_OCTET_STRING
= 0x04;
751 const unsigned nASN1_NULL
= 0x05;
752 const unsigned nASN1_OBJECT_IDENTIFIER
= 0x06;
753 const unsigned nASN1_SEQUENCE
= 0x10;
754 /// An explicit tag on a constructed value.
755 const unsigned nASN1_TAGGED_CONSTRUCTED
= 0xa0;
756 const unsigned nASN1_CONSTRUCTED
= 0x20;
758 /// Create payload for the 'signing-certificate' signed attribute.
759 bool CreateSigningCertificateAttribute(void const * pDerEncoded
, int nDerEncoded
, PCCERT_CONTEXT pCertContext
, SvStream
& rEncodedCertificate
)
761 // CryptEncodeObjectEx() does not support encoding arbitrary ASN.1
762 // structures, like SigningCertificateV2 from RFC 5035, so let's build it
765 // Count the certificate hash and put it to aHash.
766 // 2.16.840.1.101.3.4.2.1, i.e. sha256.
767 std::vector
<unsigned char> aSHA256
{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
769 HCRYPTPROV hProv
= 0;
770 if (!CryptAcquireContextW(&hProv
, nullptr, nullptr, PROV_RSA_AES
, CRYPT_VERIFYCONTEXT
))
772 SAL_WARN("svl.crypto", "CryptAcquireContext() failed");
776 HCRYPTHASH hHash
= 0;
777 if (!CryptCreateHash(hProv
, CALG_SHA_256
, 0, 0, &hHash
))
779 SAL_WARN("svl.crypto", "CryptCreateHash() failed");
783 if (!CryptHashData(hHash
, static_cast<const BYTE
*>(pDerEncoded
), nDerEncoded
, 0))
785 SAL_WARN("svl.crypto", "CryptHashData() failed");
790 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, nullptr, &nHash
, 0))
792 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash length");
796 std::vector
<unsigned char> aHash(nHash
);
797 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, aHash
.data(), &nHash
, 0))
799 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash");
803 CryptDestroyHash(hHash
);
804 CryptReleaseContext(hProv
, 0);
806 // Collect info for IssuerSerial.
807 BYTE
* pIssuer
= pCertContext
->pCertInfo
->Issuer
.pbData
;
808 DWORD nIssuer
= pCertContext
->pCertInfo
->Issuer
.cbData
;
809 BYTE
* pSerial
= pCertContext
->pCertInfo
->SerialNumber
.pbData
;
810 DWORD nSerial
= pCertContext
->pCertInfo
->SerialNumber
.cbData
;
811 // pSerial is LE, aSerial is BE.
812 std::vector
<BYTE
> aSerial(nSerial
);
813 for (size_t i
= 0; i
< nSerial
; ++i
)
814 aSerial
[i
] = *(pSerial
+ nSerial
- i
- 1);
816 // We now have all the info to count the lengths.
817 // The layout of the payload is:
818 // SEQUENCE: SigningCertificateV2
819 // SEQUENCE: SEQUENCE OF ESSCertIDv2
820 // SEQUENCE: ESSCertIDv2
821 // SEQUENCE: AlgorithmIdentifier
824 // OCTET STRING: certHash
825 // SEQUENCE: IssuerSerial
826 // SEQUENCE: GeneralNames
828 // SEQUENCE: Issuer blob
829 // INTEGER: CertificateSerialNumber
831 size_t nAlgorithm
= 1 + GetDERLengthOfLength(aSHA256
.size()) + aSHA256
.size();
832 size_t nParameters
= 1 + GetDERLengthOfLength(1);
833 size_t nAlgorithmIdentifier
= 1 + GetDERLengthOfLength(nAlgorithm
+ nParameters
) + nAlgorithm
+ nParameters
;
834 size_t nCertHash
= 1 + GetDERLengthOfLength(aHash
.size()) + aHash
.size();
835 size_t nName
= 1 + GetDERLengthOfLength(nIssuer
) + nIssuer
;
836 size_t nGeneralNames
= 1 + GetDERLengthOfLength(nName
) + nName
;
837 size_t nCertificateSerialNumber
= 1 + GetDERLengthOfLength(nSerial
) + nSerial
;
838 size_t nIssuerSerial
= 1 + GetDERLengthOfLength(nGeneralNames
+ nCertificateSerialNumber
) + nGeneralNames
+ nCertificateSerialNumber
;
839 size_t nESSCertIDv2
= 1 + GetDERLengthOfLength(nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
) + nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
;
840 size_t nESSCertIDv2s
= 1 + GetDERLengthOfLength(nESSCertIDv2
) + nESSCertIDv2
;
842 // Write SigningCertificateV2.
843 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
844 WriteDERLength(rEncodedCertificate
, nESSCertIDv2s
);
845 // Write SEQUENCE OF ESSCertIDv2.
846 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
847 WriteDERLength(rEncodedCertificate
, nESSCertIDv2
);
848 // Write ESSCertIDv2.
849 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
850 WriteDERLength(rEncodedCertificate
, nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
);
851 // Write AlgorithmIdentifier.
852 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
853 WriteDERLength(rEncodedCertificate
, nAlgorithm
+ nParameters
);
855 rEncodedCertificate
.WriteUInt8(nASN1_OBJECT_IDENTIFIER
);
856 WriteDERLength(rEncodedCertificate
, aSHA256
.size());
857 rEncodedCertificate
.WriteBytes(aSHA256
.data(), aSHA256
.size());
859 rEncodedCertificate
.WriteUInt8(nASN1_NULL
);
860 rEncodedCertificate
.WriteUInt8(0);
862 rEncodedCertificate
.WriteUInt8(nASN1_OCTET_STRING
);
863 WriteDERLength(rEncodedCertificate
, aHash
.size());
864 rEncodedCertificate
.WriteBytes(aHash
.data(), aHash
.size());
865 // Write IssuerSerial.
866 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
867 WriteDERLength(rEncodedCertificate
, nGeneralNames
+ nCertificateSerialNumber
);
868 // Write GeneralNames.
869 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
870 WriteDERLength(rEncodedCertificate
, nName
);
872 rEncodedCertificate
.WriteUInt8(nASN1_TAGGED_CONSTRUCTED
| 4);
873 WriteDERLength(rEncodedCertificate
, nIssuer
);
874 rEncodedCertificate
.WriteBytes(pIssuer
, nIssuer
);
875 // Write CertificateSerialNumber.
876 rEncodedCertificate
.WriteUInt8(nASN1_INTEGER
);
877 WriteDERLength(rEncodedCertificate
, nSerial
);
878 rEncodedCertificate
.WriteBytes(aSerial
.data(), aSerial
.size());
882 #endif // USE_CRYPTO_MSCAPI
884 } // anonymous namespace
886 namespace svl::crypto
{
888 std::vector
<unsigned char> DecodeHexString(std::string_view rHex
)
890 std::vector
<unsigned char> aRet
;
891 size_t nHexLen
= rHex
.size();
895 for (size_t i
= 0; i
< nHexLen
; ++i
)
898 sal_Int8 nParsed
= o3tl::convertToHex
<int>(rHex
[i
]);
901 SAL_WARN("svl.crypto", "DecodeHexString: invalid hex value");
908 aRet
.push_back(nByte
);
918 bool Signing::Sign(OStringBuffer
& rCMSHexBuffer
)
922 (void)m_rSigningContext
;
925 // Create the PKCS#7 object.
926 css::uno::Sequence
<sal_Int8
> aDerEncoded
;
927 if (m_rSigningContext
.m_xCertificate
.is())
929 aDerEncoded
= m_rSigningContext
.m_xCertificate
->getEncoded();
930 if (!aDerEncoded
.hasElements())
932 SAL_WARN("svl.crypto", "Crypto::Signing: empty certificate");
938 std::vector
<unsigned char> aHashResult
;
940 comphelper::Hash
aHash(comphelper::HashType::SHA256
);
942 for (const auto& pair
: m_dataBlocks
)
943 aHash
.update(static_cast<const unsigned char*>(pair
.first
), pair
.second
);
945 aHashResult
= aHash
.finalize();
948 digest
.data
= aHashResult
.data();
949 digest
.len
= aHashResult
.size();
951 PRTime now
= PR_Now();
953 // The context unit is milliseconds, PR_Now() unit is microseconds.
954 if (m_rSigningContext
.m_nSignatureTime
)
956 now
= m_rSigningContext
.m_nSignatureTime
* 1000;
960 m_rSigningContext
.m_nSignatureTime
= now
/ 1000;
963 if (!m_rSigningContext
.m_xCertificate
.is())
965 m_rSigningContext
.m_aDigest
= std::move(aHashResult
);
966 // No certificate is provided: don't actually sign -- just update the context with the
967 // parameters for the signing and return.
971 CERTCertificate
*cert
= CERT_DecodeCertFromPackage(reinterpret_cast<char *>(aDerEncoded
.getArray()), aDerEncoded
.getLength());
975 SAL_WARN("svl.crypto", "CERT_DecodeCertFromPackage failed");
979 NSSCMSSignedData
*cms_sd(nullptr);
980 NSSCMSSignerInfo
*cms_signer(nullptr);
981 NSSCMSMessage
*cms_msg
= CreateCMSMessage(nullptr, &cms_sd
, &cms_signer
, cert
, &digest
);
985 OString
pass(OUStringToOString( m_aSignPassword
, RTL_TEXTENCODING_UTF8
));
987 // Add the signing certificate as a signed attribute.
988 ESSCertIDv2
* aCertIDs
[2];
990 // Write ESSCertIDv2.hashAlgorithm.
991 aCertID
.hashAlgorithm
.algorithm
.data
= nullptr;
992 aCertID
.hashAlgorithm
.parameters
.data
= nullptr;
993 SECOID_SetAlgorithmID(nullptr, &aCertID
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
994 comphelper::ScopeGuard
aAlgoGuard(
995 [&aCertID
] () { SECOID_DestroyAlgorithmID(&aCertID
.hashAlgorithm
, false); } );
996 // Write ESSCertIDv2.certHash.
997 SECItem aCertHashItem
;
998 auto pDerEncoded
= reinterpret_cast<const unsigned char *>(aDerEncoded
.getArray());
999 std::vector
<unsigned char> aCertHashResult
= comphelper::Hash::calculateHash(pDerEncoded
, aDerEncoded
.getLength(), comphelper::HashType::SHA256
);
1000 aCertHashItem
.type
= siBuffer
;
1001 aCertHashItem
.data
= aCertHashResult
.data();
1002 aCertHashItem
.len
= aCertHashResult
.size();
1003 aCertID
.certHash
= aCertHashItem
;
1004 // Write ESSCertIDv2.issuerSerial.
1005 IssuerSerial aSerial
;
1007 aName
.name
= cert
->issuer
;
1008 aSerial
.issuer
.names
= aName
;
1009 aSerial
.serialNumber
= cert
->serialNumber
;
1010 aCertID
.issuerSerial
= aSerial
;
1011 // Write SigningCertificateV2.certs.
1012 aCertIDs
[0] = &aCertID
;
1013 aCertIDs
[1] = nullptr;
1014 SigningCertificateV2 aCertificate
;
1015 aCertificate
.certs
= &aCertIDs
[0];
1016 SECItem
* pEncodedCertificate
= SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate
, SigningCertificateV2Template
);
1017 if (!pEncodedCertificate
)
1019 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem() failed");
1023 NSSCMSAttribute aAttribute
;
1024 SECItem aAttributeValues
[2];
1025 SECItem
* pAttributeValues
[2];
1026 pAttributeValues
[0] = aAttributeValues
;
1027 pAttributeValues
[1] = nullptr;
1028 aAttributeValues
[0] = *pEncodedCertificate
;
1029 aAttributeValues
[1].type
= siBuffer
;
1030 aAttributeValues
[1].data
= nullptr;
1031 aAttributeValues
[1].len
= 0;
1032 aAttribute
.values
= pAttributeValues
;
1034 SECOidData aOidData
;
1035 aOidData
.oid
.data
= nullptr;
1037 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1038 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1039 * smime(16) id-aa(2) 47 }
1041 if (my_SEC_StringToOID(&aOidData
.oid
, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess
)
1043 SAL_WARN("svl.crypto", "my_SEC_StringToOID() failed");
1046 comphelper::ScopeGuard
aGuard(
1047 [&aOidData
] () { SECITEM_FreeItem(&aOidData
.oid
, false); } );
1048 aOidData
.offset
= SEC_OID_UNKNOWN
;
1049 aOidData
.desc
= "id-aa-signingCertificateV2";
1050 aOidData
.mechanism
= CKM_SHA_1
;
1051 aOidData
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
;
1052 aAttribute
.typeTag
= &aOidData
;
1053 aAttribute
.type
= aOidData
.oid
;
1054 aAttribute
.encoded
= PR_TRUE
;
1056 if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer
, &aAttribute
) != SECSuccess
)
1058 SAL_WARN("svl.crypto", "my_NSS_CMSSignerInfo_AddAuthAttr() failed");
1063 OStringBuffer response_buffer
;
1064 TimeStampResp response
;
1065 SECItem response_item
;
1066 NSSCMSAttribute timestamp
;
1068 SECItem
*valuesp
[2];
1069 valuesp
[0] = values
;
1070 valuesp
[1] = nullptr;
1073 //NOTE: All signed/authenticated attributes are to be added before the following hash computation
1074 if( !m_aSignTSA
.isEmpty() )
1076 // Create another CMS message with the same contents as cms_msg, because it doesn't seem
1077 // possible to encode a message twice (once to get something to timestamp, and then after
1078 // adding the timestamp attribute).
1080 NSSCMSSignedData
*ts_cms_sd
;
1081 NSSCMSSignerInfo
*ts_cms_signer
;
1082 NSSCMSMessage
*ts_cms_msg
= CreateCMSMessage(&now
, &ts_cms_sd
, &ts_cms_signer
, cert
, &digest
);
1088 PORT_Memcpy(ts_cms_signer
, cms_signer
, sizeof(NSSCMSSignerInfo
));
1090 SECItem ts_cms_output
;
1091 ts_cms_output
.data
= nullptr;
1092 ts_cms_output
.len
= 0;
1093 PLArenaPool
*ts_arena
= PORT_NewArena(10000);
1094 NSSCMSEncoderContext
*ts_cms_ecx
;
1095 ts_cms_ecx
= NSS_CMSEncoder_Start(ts_cms_msg
, nullptr, nullptr, &ts_cms_output
, ts_arena
, PDFSigningPKCS7PasswordCallback
,
1096 const_cast<char*>(pass
.getStr()), nullptr, nullptr, nullptr, nullptr);
1098 if (NSS_CMSEncoder_Finish(ts_cms_ecx
) != SECSuccess
)
1100 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Finish failed");
1104 // I have compared the ts_cms_output produced here with the cms_output produced below when
1105 // not actually calling my_NSS_CMSSignerInfo_AddUnauthAttr()), and they are identical.
1107 std::vector
<unsigned char> aTsHashResult
= comphelper::Hash::calculateHash(ts_cms_signer
->encDigest
.data
, ts_cms_signer
->encDigest
.len
, comphelper::HashType::SHA256
);
1109 ts_digest
.type
= siBuffer
;
1110 ts_digest
.data
= aTsHashResult
.data();
1111 ts_digest
.len
= aTsHashResult
.size();
1113 unsigned char cOne
= 1;
1114 unsigned char cTRUE
= 0xff; // under DER rules true is 0xff, false is 0x00
1115 src
.version
.type
= siUnsignedInteger
;
1116 src
.version
.data
= &cOne
;
1117 src
.version
.len
= sizeof(cOne
);
1119 src
.messageImprint
.hashAlgorithm
.algorithm
.data
= nullptr;
1120 src
.messageImprint
.hashAlgorithm
.parameters
.data
= nullptr;
1121 SECOID_SetAlgorithmID(nullptr, &src
.messageImprint
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
1122 src
.messageImprint
.hashedMessage
= ts_digest
;
1124 src
.reqPolicy
.type
= siBuffer
;
1125 src
.reqPolicy
.data
= nullptr;
1126 src
.reqPolicy
.len
= 0;
1128 unsigned int nNonce
= comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32
);
1129 src
.nonce
.type
= siUnsignedInteger
;
1130 src
.nonce
.data
= reinterpret_cast<unsigned char*>(&nNonce
);
1131 src
.nonce
.len
= sizeof(nNonce
);
1133 src
.certReq
.type
= siUnsignedInteger
;
1134 src
.certReq
.data
= &cTRUE
;
1135 src
.certReq
.len
= sizeof(cTRUE
);
1137 src
.extensions
= nullptr;
1139 SECItem
* timestamp_request
= SEC_ASN1EncodeItem(nullptr, nullptr, &src
, TimeStampReq_Template
);
1140 if (timestamp_request
== nullptr)
1142 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem failed");
1146 if (timestamp_request
->data
== nullptr)
1148 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem succeeded but got NULL data");
1149 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1153 SAL_INFO("svl.crypto", "request length=" << timestamp_request
->len
);
1155 // Send time stamp request to TSA server, receive response
1157 CURL
* curl
= curl_easy_init();
1159 struct curl_slist
* slist
= nullptr;
1163 SAL_WARN("svl.crypto", "curl_easy_init failed");
1164 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1168 ::InitCurl_easy(curl
);
1170 SAL_INFO("svl.crypto", "Setting curl to verbose: " << (curl_easy_setopt(curl
, CURLOPT_VERBOSE
, 1) == CURLE_OK
? "OK" : "FAIL"));
1172 if ((rc
= curl_easy_setopt(curl
, CURLOPT_URL
, OUStringToOString(m_aSignTSA
, RTL_TEXTENCODING_UTF8
).getStr())) != CURLE_OK
)
1174 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_URL) failed: " << curl_easy_strerror(rc
));
1175 curl_easy_cleanup(curl
);
1176 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1180 slist
= curl_slist_append(slist
, "Content-Type: application/timestamp-query");
1181 slist
= curl_slist_append(slist
, "Accept: application/timestamp-reply");
1183 if ((rc
= curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, slist
)) != CURLE_OK
)
1185 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " << curl_easy_strerror(rc
));
1186 curl_slist_free_all(slist
);
1187 curl_easy_cleanup(curl
);
1188 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1192 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, static_cast<tools::Long
>(timestamp_request
->len
))) != CURLE_OK
||
1193 (rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDS
, timestamp_request
->data
)) != CURLE_OK
)
1195 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc
));
1196 curl_easy_cleanup(curl
);
1197 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1201 if ((rc
= curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, &response_buffer
)) != CURLE_OK
||
1202 (rc
= curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, AppendToBuffer
)) != CURLE_OK
)
1204 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc
));
1205 curl_easy_cleanup(curl
);
1206 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1210 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POST
, 1)) != CURLE_OK
)
1212 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc
));
1213 curl_easy_cleanup(curl
);
1214 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1218 char error_buffer
[CURL_ERROR_SIZE
];
1219 if ((rc
= curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, error_buffer
)) != CURLE_OK
)
1221 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed: " << curl_easy_strerror(rc
));
1222 curl_easy_cleanup(curl
);
1223 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1227 // Use a ten second timeout
1228 if ((rc
= curl_easy_setopt(curl
, CURLOPT_TIMEOUT
, 10)) != CURLE_OK
||
1229 (rc
= curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, 10)) != CURLE_OK
)
1231 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc
));
1232 curl_easy_cleanup(curl
);
1233 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1237 if (curl_easy_perform(curl
) != CURLE_OK
)
1239 SAL_WARN("svl.crypto", "curl_easy_perform failed: " << error_buffer
);
1240 curl_easy_cleanup(curl
);
1241 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1245 SAL_INFO("svl.crypto", "PDF signing: got response, length=" << response_buffer
.getLength());
1247 curl_slist_free_all(slist
);
1248 curl_easy_cleanup(curl
);
1249 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1251 memset(&response
, 0, sizeof(response
));
1253 response_item
.type
= siBuffer
;
1254 response_item
.data
= reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer
.getStr()));
1255 response_item
.len
= response_buffer
.getLength();
1257 if (SEC_ASN1DecodeItem(nullptr, &response
, TimeStampResp_Template
, &response_item
) != SECSuccess
)
1259 SAL_WARN("svl.crypto", "SEC_ASN1DecodeItem failed");
1263 SAL_INFO("svl.crypto", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response
.status
));
1265 if (response
.status
.status
.len
!= 1 ||
1266 (response
.status
.status
.data
[0] != 0 && response
.status
.status
.data
[0] != 1))
1268 SAL_WARN("svl.crypto", "Timestamp request was not granted");
1272 // timestamp.type filled in below
1274 // Not sure if we actually need two entries in the values array, now when valuesp is an
1275 // array too, the pointer to the values array followed by a null pointer. But I don't feel
1276 // like experimenting.
1277 values
[0] = response
.timeStampToken
;
1278 values
[1].type
= siBuffer
;
1279 values
[1].data
= nullptr;
1282 timestamp
.values
= valuesp
;
1284 typetag
.oid
.data
= nullptr;
1285 // id-aa-timeStampToken OBJECT IDENTIFIER ::= { iso(1)
1286 // member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
1287 // smime(16) aa(2) 14 }
1288 if (my_SEC_StringToOID(&typetag
.oid
, "1.2.840.113549.1.9.16.2.14", 0) != SECSuccess
)
1290 SAL_WARN("svl.crypto", "SEC_StringToOID failed");
1293 typetag
.offset
= SEC_OID_UNKNOWN
; // ???
1294 typetag
.desc
= "id-aa-timeStampToken";
1295 typetag
.mechanism
= CKM_SHA_1
; // ???
1296 typetag
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
; // ???
1297 timestamp
.typeTag
= &typetag
;
1299 timestamp
.type
= typetag
.oid
; // ???
1301 timestamp
.encoded
= PR_TRUE
; // ???
1303 if (my_NSS_CMSSignerInfo_AddUnauthAttr(cms_signer
, ×tamp
) != SECSuccess
)
1305 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddUnauthAttr 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
);
2383 void Signing::appendHex(sal_Int8 nInt
, OStringBuffer
& rBuffer
)
2385 static const char pHexDigits
[] = { '0', '1', '2', '3', '4', '5', '6', '7',
2386 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
2387 rBuffer
.append( pHexDigits
[ (nInt
>> 4) & 15 ] );
2388 rBuffer
.append( pHexDigits
[ nInt
& 15 ] );
2391 bool CertificateOrName::Is() const
2393 return m_xCertificate
.is() || !m_aName
.isEmpty();
2397 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */