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 <svl/cryptosign.hxx>
11 #include <svl/sigstruct.hxx>
12 #include <config_features.h>
14 #include <rtl/character.hxx>
15 #include <rtl/strbuf.hxx>
16 #include <rtl/string.hxx>
17 #include <sal/log.hxx>
18 #include <tools/datetime.hxx>
19 #include <tools/stream.hxx>
20 #include <comphelper/base64.hxx>
21 #include <comphelper/hash.hxx>
22 #include <comphelper/processfactory.hxx>
23 #include <comphelper/random.hxx>
24 #include <com/sun/star/security/XCertificate.hpp>
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <filter/msfilter/mscodec.hxx>
27 #include <o3tl/char16_t2wchar_t.hxx>
29 #if HAVE_FEATURE_NSS && !defined(_WIN32)
30 // NSS headers for PDF signing
38 // We use curl for RFC3161 time stamp requests
39 #include <curl/curl.h>
43 // WinCrypt headers for PDF signing
44 // Note: this uses Windows 7 APIs and requires the relevant data types
48 #include <comphelper/windowserrorstring.hxx>
53 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
54 #include <com/sun/star/xml/crypto/DigestID.hpp>
55 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
58 // Is this length truly the maximum possible, or just a number that
59 // seemed large enough when the author tested this (with some type of
60 // certificates)? I suspect the latter.
62 // Used to be 0x4000 = 16384, but a sample signed PDF (produced by
63 // some other software) provided by the customer has a signature
64 // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from
65 // Adobe has one that is 21942 bytes. So let's be careful. Pity this
66 // can't be dynamic, at least not without restructuring the code. Also
67 // note that the checks in the code for this being too small
68 // apparently are broken, if this overflows you end up with an invalid
69 // PDF. Need to fix that.
71 #define MAX_SIGNATURE_CONTENT_LENGTH 50000
74 using namespace com::sun::star
;
79 void appendHex( sal_Int8 nInt
, OStringBuffer
& rBuffer
)
81 static const char pHexDigits
[] = { '0', '1', '2', '3', '4', '5', '6', '7',
82 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
83 rBuffer
.append( pHexDigits
[ (nInt
>> 4) & 15 ] );
84 rBuffer
.append( pHexDigits
[ nInt
& 15 ] );
86 #endif // HAVE_FEATURE_NSS
88 #if HAVE_FEATURE_NSS && !defined(_WIN32)
90 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo
* /*slot*/, PRBool
/*retry*/, void *arg
)
92 return PL_strdup(static_cast<char *>(arg
));
95 // ASN.1 used in the (much simpler) time stamp request. From RFC3161
99 AlgorithmIdentifier ::= SEQUENCE {
100 algorithm OBJECT IDENTIFIER,
101 parameters ANY DEFINED BY algorithm OPTIONAL }
102 -- contains a value of the type
103 -- registered for use with the
104 -- algorithm object identifier value
106 MessageImprint ::= SEQUENCE {
107 hashAlgorithm AlgorithmIdentifier,
108 hashedMessage OCTET STRING }
111 struct MessageImprint
{
112 SECAlgorithmID hashAlgorithm
;
113 SECItem hashedMessage
;
117 Extension ::= SEQUENCE {
118 extnID OBJECT IDENTIFIER,
119 critical BOOLEAN DEFAULT FALSE,
120 extnValue OCTET STRING }
130 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
134 TSAPolicyId ::= OBJECT IDENTIFIER
136 TimeStampReq ::= SEQUENCE {
137 version INTEGER { v1(1) },
138 messageImprint MessageImprint,
139 --a hash algorithm OID and the hash value of the data to be
141 reqPolicy TSAPolicyId OPTIONAL,
142 nonce INTEGER OPTIONAL,
143 certReq BOOLEAN DEFAULT FALSE,
144 extensions [0] IMPLICIT Extensions OPTIONAL }
147 struct TimeStampReq
{
149 MessageImprint messageImprint
;
153 Extension
*extensions
;
157 * General name, defined by RFC 3280.
165 * List of general names (only one for now), defined by RFC 3280.
173 * Supplies different fields to identify a certificate, defined by RFC 5035.
178 SECItem serialNumber
;
182 * Supplies different fields that are used to identify certificates, defined by
187 SECAlgorithmID hashAlgorithm
;
189 IssuerSerial issuerSerial
;
193 * This attribute uses the ESSCertIDv2 structure, defined by RFC 5035.
195 struct SigningCertificateV2
199 SigningCertificateV2()
206 * GeneralName ::= CHOICE {
207 * otherName [0] OtherName,
208 * rfc822Name [1] IA5String,
209 * dNSName [2] IA5String,
210 * x400Address [3] ORAddress,
211 * directoryName [4] Name,
212 * ediPartyName [5] EDIPartyName,
213 * uniformResourceIdentifier [6] IA5String,
214 * iPAddress [7] OCTET STRING,
215 * registeredID [8] OBJECT IDENTIFIER
218 const SEC_ASN1Template GeneralNameTemplate
[] =
220 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralName
)},
221 {SEC_ASN1_INLINE
, offsetof(GeneralName
, name
), CERT_NameTemplate
, 0},
226 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
228 const SEC_ASN1Template GeneralNamesTemplate
[] =
230 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralNames
)},
231 {SEC_ASN1_INLINE
| SEC_ASN1_CONTEXT_SPECIFIC
| 4, offsetof(GeneralNames
, names
), GeneralNameTemplate
, 0},
236 * IssuerSerial ::= SEQUENCE {
237 * issuer GeneralNames,
238 * serialNumber CertificateSerialNumber
241 const SEC_ASN1Template IssuerSerialTemplate
[] =
243 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(IssuerSerial
)},
244 {SEC_ASN1_INLINE
, offsetof(IssuerSerial
, issuer
), GeneralNamesTemplate
, 0},
245 {SEC_ASN1_INTEGER
, offsetof(IssuerSerial
, serialNumber
), nullptr, 0},
251 * Hash ::= OCTET STRING
253 * ESSCertIDv2 ::= SEQUENCE {
254 * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},
256 * issuerSerial IssuerSerial OPTIONAL
260 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate
)
262 const SEC_ASN1Template ESSCertIDv2Template
[] =
264 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(ESSCertIDv2
)},
265 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, hashAlgorithm
), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate
), 0},
266 {SEC_ASN1_OCTET_STRING
, offsetof(ESSCertIDv2
, certHash
), nullptr, 0},
267 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, issuerSerial
), IssuerSerialTemplate
, 0},
272 * SigningCertificateV2 ::= SEQUENCE {
275 const SEC_ASN1Template SigningCertificateV2Template
[] =
277 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(SigningCertificateV2
)},
278 {SEC_ASN1_SEQUENCE_OF
, offsetof(SigningCertificateV2
, certs
), ESSCertIDv2Template
, 0},
282 struct PKIStatusInfo
{
284 SECItem statusString
;
288 const SEC_ASN1Template PKIStatusInfo_Template
[] =
290 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(PKIStatusInfo
) },
291 { SEC_ASN1_INTEGER
, offsetof(PKIStatusInfo
, status
), nullptr, 0 },
292 { SEC_ASN1_CONSTRUCTED
| SEC_ASN1_SEQUENCE
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, statusString
), nullptr, 0 },
293 { SEC_ASN1_BIT_STRING
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, failInfo
), nullptr, 0 },
297 const SEC_ASN1Template Any_Template
[] =
299 { SEC_ASN1_ANY
, 0, nullptr, sizeof(SECItem
) }
302 struct TimeStampResp
{
303 PKIStatusInfo status
;
304 SECItem timeStampToken
;
307 const SEC_ASN1Template TimeStampResp_Template
[] =
309 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampResp
) },
310 { SEC_ASN1_INLINE
, offsetof(TimeStampResp
, status
), PKIStatusInfo_Template
, 0 },
311 { SEC_ASN1_ANY
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampResp
, timeStampToken
), Any_Template
, 0 },
315 const SEC_ASN1Template MessageImprint_Template
[] =
317 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(MessageImprint
) },
318 { SEC_ASN1_INLINE
, offsetof(MessageImprint
, hashAlgorithm
), SECOID_AlgorithmIDTemplate
, 0 },
319 { SEC_ASN1_OCTET_STRING
, offsetof(MessageImprint
, hashedMessage
), nullptr, 0 },
323 const SEC_ASN1Template Extension_Template
[] =
325 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(Extension
) },
326 { SEC_ASN1_OBJECT_ID
, offsetof(Extension
, extnID
), nullptr, 0 },
327 { SEC_ASN1_BOOLEAN
, offsetof(Extension
, critical
), nullptr, 0 },
328 { SEC_ASN1_OCTET_STRING
, offsetof(Extension
, extnValue
), nullptr, 0 },
332 const SEC_ASN1Template Extensions_Template
[] =
334 { SEC_ASN1_SEQUENCE_OF
, 0, Extension_Template
, 0 }
337 const SEC_ASN1Template TimeStampReq_Template
[] =
339 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampReq
) },
340 { SEC_ASN1_INTEGER
, offsetof(TimeStampReq
, version
), nullptr, 0 },
341 { SEC_ASN1_INLINE
, offsetof(TimeStampReq
, messageImprint
), MessageImprint_Template
, 0 },
342 { SEC_ASN1_OBJECT_ID
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, reqPolicy
), nullptr, 0 },
343 { SEC_ASN1_INTEGER
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, nonce
), nullptr, 0 },
344 { SEC_ASN1_BOOLEAN
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, certReq
), nullptr, 0 },
345 { SEC_ASN1_OPTIONAL
| SEC_ASN1_CONTEXT_SPECIFIC
| 0, offsetof(TimeStampReq
, extensions
), Extensions_Template
, 0 },
349 size_t AppendToBuffer(char const *ptr
, size_t size
, size_t nmemb
, void *userdata
)
351 OStringBuffer
*pBuffer
= static_cast<OStringBuffer
*>(userdata
);
352 pBuffer
->append(ptr
, size
*nmemb
);
357 OUString
PKIStatusToString(int n
)
361 case 0: return "granted";
362 case 1: return "grantedWithMods";
363 case 2: return "rejection";
364 case 3: return "waiting";
365 case 4: return "revocationWarning";
366 case 5: return "revocationNotification";
367 default: return "unknown (" + OUString::number(n
) + ")";
371 OUString
PKIStatusInfoToString(const PKIStatusInfo
& rStatusInfo
)
373 OUString result
= "{status=";
374 if (rStatusInfo
.status
.len
== 1)
375 result
+= PKIStatusToString(rStatusInfo
.status
.data
[0]);
377 result
+= "unknown (len=" + OUString::number(rStatusInfo
.status
.len
);
379 // FIXME: Perhaps look at rStatusInfo.statusString.data but note
380 // that we of course can't assume it contains proper UTF-8. After
381 // all, it is data from an external source. Also, RFC3161 claims
382 // it should be a SEQUENCE (1..MAX) OF UTF8String, but another
383 // source claimed it would be a single UTF8String, hmm?
385 // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO
392 // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
393 // not exported from libsmime, so copy them here. Sigh.
396 my_SEC_StringToOID(SECItem
*to
, const char *from
, PRUint32 len
)
398 PRUint32 decimal_numbers
= 0;
399 PRUint32 result_bytes
= 0;
401 PRUint8 result
[1024];
403 static const PRUint32 max_decimal
= 0xffffffff / 10;
404 static const char OIDstring
[] = {"OID."};
407 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
411 len
= PL_strlen(from
);
413 if (len
>= 4 && !PL_strncasecmp(from
, OIDstring
, 4)) {
414 from
+= 4; /* skip leading "OID." if present */
419 PORT_SetError(SEC_ERROR_BAD_DATA
);
423 PRUint32 decimal
= 0;
424 while (len
> 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from
))) {
425 PRUint32 addend
= *from
++ - '0';
427 if (decimal
> max_decimal
) /* overflow */
429 decimal
= (decimal
* 10) + addend
;
430 if (decimal
< addend
) /* overflow */
433 if (len
!= 0 && *from
!= '.') {
436 if (decimal_numbers
== 0) {
439 result
[0] = decimal
* 40;
441 } else if (decimal_numbers
== 1) {
444 result
[0] += decimal
;
446 /* encode the decimal number, */
448 PRUint32 num_bytes
= 0;
449 PRUint32 tmp
= decimal
;
455 ++num_bytes
; /* use one byte for a zero value */
456 if (num_bytes
+ result_bytes
> sizeof result
)
459 rp
= result
+ result_bytes
- 1;
460 rp
[tmp
] = static_cast<PRUint8
>(decimal
& 0x7f);
463 rp
[tmp
] = static_cast<PRUint8
>(decimal
| 0x80);
466 result_bytes
+= num_bytes
;
469 if (len
> 0) { /* skip trailing '.' */
474 /* now result contains result_bytes of data */
475 if (to
->data
&& to
->len
>= result_bytes
) {
476 to
->len
= result_bytes
;
477 PORT_Memcpy(to
->data
, result
, to
->len
);
480 SECItem result_item
= {siBuffer
, nullptr, 0 };
481 result_item
.data
= result
;
482 result_item
.len
= result_bytes
;
483 rv
= SECITEM_CopyItem(nullptr, to
, &result_item
);
489 my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute
**attrs
, SECOidTag oidtag
, PRBool only
)
492 NSSCMSAttribute
*attr1
, *attr2
;
494 if (attrs
== nullptr)
497 oid
= SECOID_FindOIDByTag(oidtag
);
501 while ((attr1
= *attrs
++) != nullptr) {
502 if (attr1
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr1
->type
.data
,
508 if (attr1
== nullptr)
514 while ((attr2
= *attrs
++) != nullptr) {
515 if (attr2
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr2
->type
.data
,
521 if (attr2
!= nullptr)
528 my_NSS_CMSArray_Add(PLArenaPool
*poolp
, void ***array
, void *obj
)
533 PORT_Assert(array
!= NULL
);
534 if (array
== nullptr)
537 if (*array
== nullptr) {
538 dest
= static_cast<void **>(PORT_ArenaAlloc(poolp
, 2 * sizeof(void *)));
543 dest
= static_cast<void **>(PORT_ArenaGrow (poolp
,
545 (n
+ 1) * sizeof(void *),
546 (n
+ 2) * sizeof(void *)));
559 my_NSS_CMSAttribute_GetType(NSSCMSAttribute
*attr
)
563 typetag
= SECOID_FindOID(&(attr
->type
));
564 if (typetag
== nullptr)
565 return SEC_OID_UNKNOWN
;
567 return typetag
->offset
;
571 my_NSS_CMSAttributeArray_AddAttr(PLArenaPool
*poolp
, NSSCMSAttribute
***attrs
, NSSCMSAttribute
*attr
)
573 NSSCMSAttribute
*oattr
;
577 mark
= PORT_ArenaMark(poolp
);
579 /* find oidtag of attr */
580 type
= my_NSS_CMSAttribute_GetType(attr
);
582 /* see if we have one already */
583 oattr
= my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs
, type
, PR_FALSE
);
584 PORT_Assert (oattr
== NULL
);
585 if (oattr
!= nullptr)
586 goto loser
; /* XXX or would it be better to replace it? */
588 /* no, shove it in */
589 if (my_NSS_CMSArray_Add(poolp
, reinterpret_cast<void ***>(attrs
), static_cast<void *>(attr
)) != SECSuccess
)
592 PORT_ArenaUnmark(poolp
, mark
);
596 PORT_ArenaRelease(poolp
, mark
);
601 my_NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
603 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->unAuthAttr
), attr
);
607 my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
609 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->authAttr
), attr
);
612 NSSCMSMessage
*CreateCMSMessage(const PRTime
* time
,
613 NSSCMSSignedData
**cms_sd
,
614 NSSCMSSignerInfo
**cms_signer
,
615 CERTCertificate
*cert
,
618 NSSCMSMessage
*result
= NSS_CMSMessage_Create(nullptr);
621 SAL_WARN("svl.crypto", "NSS_CMSMessage_Create failed");
625 *cms_sd
= NSS_CMSSignedData_Create(result
);
628 SAL_WARN("svl.crypto", "NSS_CMSSignedData_Create failed");
629 NSS_CMSMessage_Destroy(result
);
633 NSSCMSContentInfo
*cms_cinfo
= NSS_CMSMessage_GetContentInfo(result
);
634 if (NSS_CMSContentInfo_SetContent_SignedData(result
, cms_cinfo
, *cms_sd
) != SECSuccess
)
636 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_SignedData failed");
637 NSS_CMSSignedData_Destroy(*cms_sd
);
638 NSS_CMSMessage_Destroy(result
);
642 cms_cinfo
= NSS_CMSSignedData_GetContentInfo(*cms_sd
);
644 // Attach NULL data as detached data
645 if (NSS_CMSContentInfo_SetContent_Data(result
, cms_cinfo
, nullptr, PR_TRUE
) != SECSuccess
)
647 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_Data failed");
648 NSS_CMSSignedData_Destroy(*cms_sd
);
649 NSS_CMSMessage_Destroy(result
);
653 *cms_signer
= NSS_CMSSignerInfo_Create(result
, cert
, SEC_OID_SHA256
);
656 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_Create failed");
657 NSS_CMSSignedData_Destroy(*cms_sd
);
658 NSS_CMSMessage_Destroy(result
);
662 if (time
&& NSS_CMSSignerInfo_AddSigningTime(*cms_signer
, *time
) != SECSuccess
)
664 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddSigningTime failed");
665 NSS_CMSSignedData_Destroy(*cms_sd
);
666 NSS_CMSMessage_Destroy(result
);
670 if (NSS_CMSSignerInfo_IncludeCerts(*cms_signer
, NSSCMSCM_CertChain
, certUsageEmailSigner
) != SECSuccess
)
672 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_IncludeCerts failed");
673 NSS_CMSSignedData_Destroy(*cms_sd
);
674 NSS_CMSMessage_Destroy(result
);
678 if (NSS_CMSSignedData_AddCertificate(*cms_sd
, cert
) != SECSuccess
)
680 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddCertificate failed");
681 NSS_CMSSignedData_Destroy(*cms_sd
);
682 NSS_CMSMessage_Destroy(result
);
686 if (NSS_CMSSignedData_AddSignerInfo(*cms_sd
, *cms_signer
) != SECSuccess
)
688 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddSignerInfo failed");
689 NSS_CMSSignedData_Destroy(*cms_sd
);
690 NSS_CMSMessage_Destroy(result
);
694 if (NSS_CMSSignedData_SetDigestValue(*cms_sd
, SEC_OID_SHA256
, digest
) != SECSuccess
)
696 SAL_WARN("svl.crypto", "NSS_CMSSignedData_SetDigestValue failed");
697 NSS_CMSSignedData_Destroy(*cms_sd
);
698 NSS_CMSMessage_Destroy(result
);
705 #endif // HAVE_FEATURE_NSS && !_WIN32
707 } // Anonymous namespace
713 /// Counts how many bytes are needed to encode a given length.
714 size_t GetDERLengthOfLength(size_t nLength
)
720 while (nLength
>> (nRet
* 8))
722 // Long form means one additional byte: the length of the length and
723 // the length itself.
729 /// Writes the length part of the header.
730 void WriteDERLength(SvStream
& rStream
, size_t nLength
)
732 size_t nLengthOfLength
= GetDERLengthOfLength(nLength
);
733 if (nLengthOfLength
== 1)
735 // We can use the short form.
736 rStream
.WriteUInt8(nLength
);
740 // 0x80 means that the we use the long form: the first byte is the length
741 // of length with the highest bit set to 1, not the actual length.
742 rStream
.WriteUInt8(0x80 | (nLengthOfLength
- 1));
743 for (size_t i
= 1; i
< nLengthOfLength
; ++i
)
744 rStream
.WriteUInt8(nLength
>> ((nLengthOfLength
- i
- 1) * 8));
747 const unsigned nASN1_INTEGER
= 0x02;
748 const unsigned nASN1_OCTET_STRING
= 0x04;
749 const unsigned nASN1_NULL
= 0x05;
750 const unsigned nASN1_OBJECT_IDENTIFIER
= 0x06;
751 const unsigned nASN1_SEQUENCE
= 0x10;
752 /// An explicit tag on a constructed value.
753 const unsigned nASN1_TAGGED_CONSTRUCTED
= 0xa0;
754 const unsigned nASN1_CONSTRUCTED
= 0x20;
756 /// Create payload for the 'signing-certificate' signed attribute.
757 bool CreateSigningCertificateAttribute(void const * pDerEncoded
, int nDerEncoded
, PCCERT_CONTEXT pCertContext
, SvStream
& rEncodedCertificate
)
759 // CryptEncodeObjectEx() does not support encoding arbitrary ASN.1
760 // structures, like SigningCertificateV2 from RFC 5035, so let's build it
763 // Count the certificate hash and put it to aHash.
764 // 2.16.840.1.101.3.4.2.1, i.e. sha256.
765 std::vector
<unsigned char> aSHA256
{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
767 HCRYPTPROV hProv
= 0;
768 if (!CryptAcquireContextW(&hProv
, nullptr, nullptr, PROV_RSA_AES
, CRYPT_VERIFYCONTEXT
))
770 SAL_WARN("svl.crypto", "CryptAcquireContext() failed");
774 HCRYPTHASH hHash
= 0;
775 if (!CryptCreateHash(hProv
, CALG_SHA_256
, 0, 0, &hHash
))
777 SAL_WARN("svl.crypto", "CryptCreateHash() failed");
781 if (!CryptHashData(hHash
, static_cast<const BYTE
*>(pDerEncoded
), nDerEncoded
, 0))
783 SAL_WARN("svl.crypto", "CryptHashData() failed");
788 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, nullptr, &nHash
, 0))
790 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash length");
794 std::vector
<unsigned char> aHash(nHash
);
795 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, aHash
.data(), &nHash
, 0))
797 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash");
801 CryptDestroyHash(hHash
);
802 CryptReleaseContext(hProv
, 0);
804 // Collect info for IssuerSerial.
805 BYTE
* pIssuer
= pCertContext
->pCertInfo
->Issuer
.pbData
;
806 DWORD nIssuer
= pCertContext
->pCertInfo
->Issuer
.cbData
;
807 BYTE
* pSerial
= pCertContext
->pCertInfo
->SerialNumber
.pbData
;
808 DWORD nSerial
= pCertContext
->pCertInfo
->SerialNumber
.cbData
;
809 // pSerial is LE, aSerial is BE.
810 std::vector
<BYTE
> aSerial(nSerial
);
811 for (size_t i
= 0; i
< nSerial
; ++i
)
812 aSerial
[i
] = *(pSerial
+ nSerial
- i
- 1);
814 // We now have all the info to count the lengths.
815 // The layout of the payload is:
816 // SEQUENCE: SigningCertificateV2
817 // SEQUENCE: SEQUENCE OF ESSCertIDv2
818 // SEQUENCE: ESSCertIDv2
819 // SEQUENCE: AlgorithmIdentifier
822 // OCTET STRING: certHash
823 // SEQUENCE: IssuerSerial
824 // SEQUENCE: GeneralNames
826 // SEQUENCE: Issuer blob
827 // INTEGER: CertificateSerialNumber
829 size_t nAlgorithm
= 1 + GetDERLengthOfLength(aSHA256
.size()) + aSHA256
.size();
830 size_t nParameters
= 1 + GetDERLengthOfLength(1);
831 size_t nAlgorithmIdentifier
= 1 + GetDERLengthOfLength(nAlgorithm
+ nParameters
) + nAlgorithm
+ nParameters
;
832 size_t nCertHash
= 1 + GetDERLengthOfLength(aHash
.size()) + aHash
.size();
833 size_t nName
= 1 + GetDERLengthOfLength(nIssuer
) + nIssuer
;
834 size_t nGeneralNames
= 1 + GetDERLengthOfLength(nName
) + nName
;
835 size_t nCertificateSerialNumber
= 1 + GetDERLengthOfLength(nSerial
) + nSerial
;
836 size_t nIssuerSerial
= 1 + GetDERLengthOfLength(nGeneralNames
+ nCertificateSerialNumber
) + nGeneralNames
+ nCertificateSerialNumber
;
837 size_t nESSCertIDv2
= 1 + GetDERLengthOfLength(nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
) + nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
;
838 size_t nESSCertIDv2s
= 1 + GetDERLengthOfLength(nESSCertIDv2
) + nESSCertIDv2
;
840 // Write SigningCertificateV2.
841 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
842 WriteDERLength(rEncodedCertificate
, nESSCertIDv2s
);
843 // Write SEQUENCE OF ESSCertIDv2.
844 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
845 WriteDERLength(rEncodedCertificate
, nESSCertIDv2
);
846 // Write ESSCertIDv2.
847 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
848 WriteDERLength(rEncodedCertificate
, nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
);
849 // Write AlgorithmIdentifier.
850 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
851 WriteDERLength(rEncodedCertificate
, nAlgorithm
+ nParameters
);
853 rEncodedCertificate
.WriteUInt8(nASN1_OBJECT_IDENTIFIER
);
854 WriteDERLength(rEncodedCertificate
, aSHA256
.size());
855 rEncodedCertificate
.WriteBytes(aSHA256
.data(), aSHA256
.size());
857 rEncodedCertificate
.WriteUInt8(nASN1_NULL
);
858 rEncodedCertificate
.WriteUInt8(0);
860 rEncodedCertificate
.WriteUInt8(nASN1_OCTET_STRING
);
861 WriteDERLength(rEncodedCertificate
, aHash
.size());
862 rEncodedCertificate
.WriteBytes(aHash
.data(), aHash
.size());
863 // Write IssuerSerial.
864 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
865 WriteDERLength(rEncodedCertificate
, nGeneralNames
+ nCertificateSerialNumber
);
866 // Write GeneralNames.
867 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
868 WriteDERLength(rEncodedCertificate
, nName
);
870 rEncodedCertificate
.WriteUInt8(nASN1_TAGGED_CONSTRUCTED
| 4);
871 WriteDERLength(rEncodedCertificate
, nIssuer
);
872 rEncodedCertificate
.WriteBytes(pIssuer
, nIssuer
);
873 // Write CertificateSerialNumber.
874 rEncodedCertificate
.WriteUInt8(nASN1_INTEGER
);
875 WriteDERLength(rEncodedCertificate
, nSerial
);
876 rEncodedCertificate
.WriteBytes(aSerial
.data(), aSerial
.size());
880 } // anonymous namespace
883 namespace svl::crypto
{
885 static int AsHex(char ch
)
888 if (rtl::isAsciiDigit(static_cast<unsigned char>(ch
)))
892 if (ch
>= 'a' && ch
<= 'f')
894 else if (ch
>= 'A' && ch
<= 'F')
903 std::vector
<unsigned char> DecodeHexString(const OString
& rHex
)
905 std::vector
<unsigned char> aRet
;
906 size_t nHexLen
= rHex
.getLength();
910 for (size_t i
= 0; i
< nHexLen
; ++i
)
913 sal_Int8 nParsed
= AsHex(rHex
[i
]);
916 SAL_WARN("svl.crypto", "DecodeHexString: invalid hex value");
923 aRet
.push_back(nByte
);
934 #if defined(SVL_CRYPTO_NSS) || defined(SVL_CRYPTO_MSCRYPTO)
936 bool Signing::Sign(OStringBuffer
& rCMSHexBuffer
)
938 // Create the PKCS#7 object.
939 css::uno::Sequence
<sal_Int8
> aDerEncoded
= m_xCertificate
->getEncoded();
940 if (!aDerEncoded
.hasElements())
942 SAL_WARN("svl.crypto", "Crypto::Signing: empty certificate");
948 CERTCertificate
*cert
= CERT_DecodeCertFromPackage(reinterpret_cast<char *>(aDerEncoded
.getArray()), aDerEncoded
.getLength());
952 SAL_WARN("svl.crypto", "CERT_DecodeCertFromPackage failed");
956 std::vector
<unsigned char> aHashResult
;
958 comphelper::Hash
aHash(comphelper::HashType::SHA256
);
960 for (const auto& pair
: m_dataBlocks
)
961 aHash
.update(static_cast<const unsigned char*>(pair
.first
), pair
.second
);
963 aHashResult
= aHash
.finalize();
966 digest
.data
= aHashResult
.data();
967 digest
.len
= aHashResult
.size();
969 PRTime now
= PR_Now();
970 NSSCMSSignedData
*cms_sd
;
971 NSSCMSSignerInfo
*cms_signer
;
972 NSSCMSMessage
*cms_msg
= CreateCMSMessage(nullptr, &cms_sd
, &cms_signer
, cert
, &digest
);
976 OString
pass(OUStringToOString( m_aSignPassword
, RTL_TEXTENCODING_UTF8
));
979 OStringBuffer response_buffer
;
980 TimeStampResp response
;
981 SECItem response_item
;
982 NSSCMSAttribute timestamp
;
986 valuesp
[1] = nullptr;
989 if( !m_aSignTSA
.isEmpty() )
991 // Create another CMS message with the same contents as cms_msg, because it doesn't seem
992 // possible to encode a message twice (once to get something to timestamp, and then after
993 // adding the timestamp attribute).
995 NSSCMSSignedData
*ts_cms_sd
;
996 NSSCMSSignerInfo
*ts_cms_signer
;
997 NSSCMSMessage
*ts_cms_msg
= CreateCMSMessage(&now
, &ts_cms_sd
, &ts_cms_signer
, cert
, &digest
);
1003 SECItem ts_cms_output
;
1004 ts_cms_output
.data
= nullptr;
1005 ts_cms_output
.len
= 0;
1006 PLArenaPool
*ts_arena
= PORT_NewArena(10000);
1007 NSSCMSEncoderContext
*ts_cms_ecx
;
1008 ts_cms_ecx
= NSS_CMSEncoder_Start(ts_cms_msg
, nullptr, nullptr, &ts_cms_output
, ts_arena
, PDFSigningPKCS7PasswordCallback
,
1009 const_cast<char*>(pass
.getStr()), nullptr, nullptr, nullptr, nullptr);
1011 if (NSS_CMSEncoder_Finish(ts_cms_ecx
) != SECSuccess
)
1013 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Finish failed");
1017 // I have compared the ts_cms_output produced here with the cms_output produced below when
1018 // not actually calling my_NSS_CMSSignerInfo_AddUnauthAttr()), and they are identical.
1020 std::vector
<unsigned char> aTsHashResult
= comphelper::Hash::calculateHash(ts_cms_signer
->encDigest
.data
, ts_cms_signer
->encDigest
.len
, comphelper::HashType::SHA256
);
1022 ts_digest
.type
= siBuffer
;
1023 ts_digest
.data
= aTsHashResult
.data();
1024 ts_digest
.len
= aTsHashResult
.size();
1026 unsigned char cOne
= 1;
1027 src
.version
.type
= siUnsignedInteger
;
1028 src
.version
.data
= &cOne
;
1029 src
.version
.len
= sizeof(cOne
);
1031 src
.messageImprint
.hashAlgorithm
.algorithm
.data
= nullptr;
1032 src
.messageImprint
.hashAlgorithm
.parameters
.data
= nullptr;
1033 SECOID_SetAlgorithmID(nullptr, &src
.messageImprint
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
1034 src
.messageImprint
.hashedMessage
= ts_digest
;
1036 src
.reqPolicy
.type
= siBuffer
;
1037 src
.reqPolicy
.data
= nullptr;
1038 src
.reqPolicy
.len
= 0;
1040 unsigned int nNonce
= comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32
);
1041 src
.nonce
.type
= siUnsignedInteger
;
1042 src
.nonce
.data
= reinterpret_cast<unsigned char*>(&nNonce
);
1043 src
.nonce
.len
= sizeof(nNonce
);
1045 src
.certReq
.type
= siUnsignedInteger
;
1046 src
.certReq
.data
= &cOne
;
1047 src
.certReq
.len
= sizeof(cOne
);
1049 src
.extensions
= nullptr;
1051 SECItem
* timestamp_request
= SEC_ASN1EncodeItem(nullptr, nullptr, &src
, TimeStampReq_Template
);
1052 if (timestamp_request
== nullptr)
1054 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem failed");
1058 if (timestamp_request
->data
== nullptr)
1060 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem succeeded but got NULL data");
1061 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1065 SAL_INFO("svl.crypto", "request length=" << timestamp_request
->len
);
1067 // Send time stamp request to TSA server, receive response
1069 CURL
* curl
= curl_easy_init();
1071 struct curl_slist
* slist
= nullptr;
1075 SAL_WARN("svl.crypto", "curl_easy_init failed");
1076 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1080 SAL_INFO("svl.crypto", "Setting curl to verbose: " << (curl_easy_setopt(curl
, CURLOPT_VERBOSE
, 1) == CURLE_OK
? "OK" : "FAIL"));
1082 if ((rc
= curl_easy_setopt(curl
, CURLOPT_URL
, OUStringToOString(m_aSignTSA
, RTL_TEXTENCODING_UTF8
).getStr())) != CURLE_OK
)
1084 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_URL) failed: " << curl_easy_strerror(rc
));
1085 curl_easy_cleanup(curl
);
1086 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1090 slist
= curl_slist_append(slist
, "Content-Type: application/timestamp-query");
1091 slist
= curl_slist_append(slist
, "Accept: application/timestamp-reply");
1093 if ((rc
= curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, slist
)) != CURLE_OK
)
1095 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " << curl_easy_strerror(rc
));
1096 curl_slist_free_all(slist
);
1097 curl_easy_cleanup(curl
);
1098 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1102 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, static_cast<tools::Long
>(timestamp_request
->len
))) != CURLE_OK
||
1103 (rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDS
, timestamp_request
->data
)) != CURLE_OK
)
1105 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc
));
1106 curl_easy_cleanup(curl
);
1107 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1111 if ((rc
= curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, &response_buffer
)) != CURLE_OK
||
1112 (rc
= curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, AppendToBuffer
)) != CURLE_OK
)
1114 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc
));
1115 curl_easy_cleanup(curl
);
1116 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1120 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POST
, 1)) != CURLE_OK
)
1122 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc
));
1123 curl_easy_cleanup(curl
);
1124 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1128 char error_buffer
[CURL_ERROR_SIZE
];
1129 if ((rc
= curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, error_buffer
)) != CURLE_OK
)
1131 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed: " << curl_easy_strerror(rc
));
1132 curl_easy_cleanup(curl
);
1133 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1137 // Use a ten second timeout
1138 if ((rc
= curl_easy_setopt(curl
, CURLOPT_TIMEOUT
, 10)) != CURLE_OK
||
1139 (rc
= curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, 10)) != CURLE_OK
)
1141 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc
));
1142 curl_easy_cleanup(curl
);
1143 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1147 if (curl_easy_perform(curl
) != CURLE_OK
)
1149 SAL_WARN("svl.crypto", "curl_easy_perform failed: " << error_buffer
);
1150 curl_easy_cleanup(curl
);
1151 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1155 SAL_INFO("svl.crypto", "PDF signing: got response, length=" << response_buffer
.getLength());
1157 curl_slist_free_all(slist
);
1158 curl_easy_cleanup(curl
);
1159 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
1161 memset(&response
, 0, sizeof(response
));
1163 response_item
.type
= siBuffer
;
1164 response_item
.data
= reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer
.getStr()));
1165 response_item
.len
= response_buffer
.getLength();
1167 if (SEC_ASN1DecodeItem(nullptr, &response
, TimeStampResp_Template
, &response_item
) != SECSuccess
)
1169 SAL_WARN("svl.crypto", "SEC_ASN1DecodeItem failed");
1173 SAL_INFO("svl.crypto", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response
.status
));
1175 if (response
.status
.status
.len
!= 1 ||
1176 (response
.status
.status
.data
[0] != 0 && response
.status
.status
.data
[0] != 1))
1178 SAL_WARN("svl.crypto", "Timestamp request was not granted");
1182 // timestamp.type filled in below
1184 // Not sure if we actually need two entries in the values array, now when valuesp is an
1185 // array too, the pointer to the values array followed by a null pointer. But I don't feel
1186 // like experimenting.
1187 values
[0] = response
.timeStampToken
;
1188 values
[1].type
= siBuffer
;
1189 values
[1].data
= nullptr;
1192 timestamp
.values
= valuesp
;
1194 typetag
.oid
.data
= nullptr;
1195 // id-aa-timeStampToken OBJECT IDENTIFIER ::= { iso(1)
1196 // member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
1197 // smime(16) aa(2) 14 }
1198 if (my_SEC_StringToOID(&typetag
.oid
, "1.2.840.113549.1.9.16.2.14", 0) != SECSuccess
)
1200 SAL_WARN("svl.crypto", "SEC_StringToOID failed");
1203 typetag
.offset
= SEC_OID_UNKNOWN
; // ???
1204 typetag
.desc
= "id-aa-timeStampToken";
1205 typetag
.mechanism
= CKM_SHA_1
; // ???
1206 typetag
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
; // ???
1207 timestamp
.typeTag
= &typetag
;
1209 timestamp
.type
= typetag
.oid
; // ???
1211 timestamp
.encoded
= PR_TRUE
; // ???
1213 if (my_NSS_CMSSignerInfo_AddUnauthAttr(cms_signer
, ×tamp
) != SECSuccess
)
1215 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddUnauthAttr failed");
1220 // Add the signing certificate as a signed attribute.
1221 ESSCertIDv2
* aCertIDs
[2];
1222 ESSCertIDv2 aCertID
;
1223 // Write ESSCertIDv2.hashAlgorithm.
1224 aCertID
.hashAlgorithm
.algorithm
.data
= nullptr;
1225 aCertID
.hashAlgorithm
.parameters
.data
= nullptr;
1226 SECOID_SetAlgorithmID(nullptr, &aCertID
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
1227 // Write ESSCertIDv2.certHash.
1228 SECItem aCertHashItem
;
1229 auto pDerEncoded
= reinterpret_cast<const unsigned char *>(aDerEncoded
.getArray());
1230 std::vector
<unsigned char> aCertHashResult
= comphelper::Hash::calculateHash(pDerEncoded
, aDerEncoded
.getLength(), comphelper::HashType::SHA256
);
1231 aCertHashItem
.type
= siBuffer
;
1232 aCertHashItem
.data
= aCertHashResult
.data();
1233 aCertHashItem
.len
= aCertHashResult
.size();
1234 aCertID
.certHash
= aCertHashItem
;
1235 // Write ESSCertIDv2.issuerSerial.
1236 IssuerSerial aSerial
;
1238 aName
.name
= cert
->issuer
;
1239 aSerial
.issuer
.names
= aName
;
1240 aSerial
.serialNumber
= cert
->serialNumber
;
1241 aCertID
.issuerSerial
= aSerial
;
1242 // Write SigningCertificateV2.certs.
1243 aCertIDs
[0] = &aCertID
;
1244 aCertIDs
[1] = nullptr;
1245 SigningCertificateV2 aCertificate
;
1246 aCertificate
.certs
= &aCertIDs
[0];
1247 SECItem
* pEncodedCertificate
= SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate
, SigningCertificateV2Template
);
1248 if (!pEncodedCertificate
)
1250 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem() failed");
1254 NSSCMSAttribute aAttribute
;
1255 SECItem aAttributeValues
[2];
1256 SECItem
* pAttributeValues
[2];
1257 pAttributeValues
[0] = aAttributeValues
;
1258 pAttributeValues
[1] = nullptr;
1259 aAttributeValues
[0] = *pEncodedCertificate
;
1260 aAttributeValues
[1].type
= siBuffer
;
1261 aAttributeValues
[1].data
= nullptr;
1262 aAttributeValues
[1].len
= 0;
1263 aAttribute
.values
= pAttributeValues
;
1265 SECOidData aOidData
;
1266 aOidData
.oid
.data
= nullptr;
1268 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1269 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1270 * smime(16) id-aa(2) 47 }
1272 if (my_SEC_StringToOID(&aOidData
.oid
, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess
)
1274 SAL_WARN("svl.crypto", "my_SEC_StringToOID() failed");
1277 aOidData
.offset
= SEC_OID_UNKNOWN
;
1278 aOidData
.desc
= "id-aa-signingCertificateV2";
1279 aOidData
.mechanism
= CKM_SHA_1
;
1280 aOidData
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
;
1281 aAttribute
.typeTag
= &aOidData
;
1282 aAttribute
.type
= aOidData
.oid
;
1283 aAttribute
.encoded
= PR_TRUE
;
1285 if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer
, &aAttribute
) != SECSuccess
)
1287 SAL_WARN("svl.crypto", "my_NSS_CMSSignerInfo_AddAuthAttr() failed");
1292 cms_output
.data
= nullptr;
1294 PLArenaPool
*arena
= PORT_NewArena(10000);
1295 NSSCMSEncoderContext
*cms_ecx
;
1297 // Possibly it would work to even just pass NULL for the password callback function and its
1298 // argument here. After all, at least with the hardware token and associated software I tested
1299 // with, the software itself pops up a dialog asking for the PIN (password). But I am not going
1300 // to test it and risk locking up my token...
1302 cms_ecx
= NSS_CMSEncoder_Start(cms_msg
, nullptr, nullptr, &cms_output
, arena
, PDFSigningPKCS7PasswordCallback
,
1303 const_cast<char*>(pass
.getStr()), nullptr, nullptr, nullptr, nullptr);
1307 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Start failed");
1311 if (NSS_CMSEncoder_Finish(cms_ecx
) != SECSuccess
)
1313 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Finish failed");
1317 if (cms_output
.len
*2 > MAX_SIGNATURE_CONTENT_LENGTH
)
1319 SAL_WARN("svl.crypto", "Signature requires more space (" << cms_output
.len
*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH
<< ")");
1320 NSS_CMSMessage_Destroy(cms_msg
);
1324 for (unsigned int i
= 0; i
< cms_output
.len
; i
++)
1325 appendHex(cms_output
.data
[i
], rCMSHexBuffer
);
1327 SECITEM_FreeItem(pEncodedCertificate
, PR_TRUE
);
1328 NSS_CMSMessage_Destroy(cms_msg
);
1333 PCCERT_CONTEXT pCertContext
= CertCreateCertificateContext(X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, reinterpret_cast<const BYTE
*>(aDerEncoded
.getArray()), aDerEncoded
.getLength());
1334 if (pCertContext
== nullptr)
1336 SAL_WARN("svl.crypto", "CertCreateCertificateContext failed: " << WindowsErrorString(GetLastError()));
1340 CRYPT_SIGN_MESSAGE_PARA aPara
= {};
1341 aPara
.cbSize
= sizeof(aPara
);
1342 aPara
.dwMsgEncodingType
= PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
;
1343 aPara
.pSigningCert
= pCertContext
;
1344 aPara
.HashAlgorithm
.pszObjId
= const_cast<LPSTR
>(szOID_NIST_sha256
);
1345 aPara
.HashAlgorithm
.Parameters
.cbData
= 0;
1347 aPara
.rgpMsgCert
= &pCertContext
;
1349 NCRYPT_KEY_HANDLE hCryptKey
= 0;
1350 DWORD dwFlags
= CRYPT_ACQUIRE_CACHE_FLAG
| CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG
;
1351 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE
* phCryptProvOrNCryptKey
= &hCryptKey
;
1355 if (!CryptAcquireCertificatePrivateKey(pCertContext
,
1358 phCryptProvOrNCryptKey
,
1362 SAL_WARN("svl.crypto", "CryptAcquireCertificatePrivateKey failed: " << WindowsErrorString(GetLastError()));
1363 CertFreeCertificateContext(pCertContext
);
1366 assert(!bFreeNeeded
);
1368 CMSG_SIGNER_ENCODE_INFO aSignerInfo
= {};
1369 aSignerInfo
.cbSize
= sizeof(aSignerInfo
);
1370 aSignerInfo
.pCertInfo
= pCertContext
->pCertInfo
;
1371 aSignerInfo
.hNCryptKey
= hCryptKey
;
1372 aSignerInfo
.dwKeySpec
= nKeySpec
;
1373 aSignerInfo
.HashAlgorithm
.pszObjId
= const_cast<LPSTR
>(szOID_NIST_sha256
);
1374 aSignerInfo
.HashAlgorithm
.Parameters
.cbData
= 0;
1376 // Add the signing certificate as a signed attribute.
1377 CRYPT_INTEGER_BLOB aCertificateBlob
;
1378 SvMemoryStream aEncodedCertificate
;
1379 if (!CreateSigningCertificateAttribute(aDerEncoded
.getArray(), aDerEncoded
.getLength(), pCertContext
, aEncodedCertificate
))
1381 SAL_WARN("svl.crypto", "CreateSigningCertificateAttribute() failed");
1384 aCertificateBlob
.pbData
= const_cast<BYTE
*>(static_cast<const BYTE
*>(aEncodedCertificate
.GetData()));
1385 aCertificateBlob
.cbData
= aEncodedCertificate
.GetSize();
1386 CRYPT_ATTRIBUTE aCertificateAttribute
;
1388 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1389 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1390 * smime(16) id-aa(2) 47 }
1392 aCertificateAttribute
.pszObjId
= const_cast<LPSTR
>("1.2.840.113549.1.9.16.2.47");
1393 aCertificateAttribute
.cValue
= 1;
1394 aCertificateAttribute
.rgValue
= &aCertificateBlob
;
1395 aSignerInfo
.cAuthAttr
= 1;
1396 aSignerInfo
.rgAuthAttr
= &aCertificateAttribute
;
1398 CMSG_SIGNED_ENCODE_INFO aSignedInfo
= {};
1399 aSignedInfo
.cbSize
= sizeof(aSignedInfo
);
1400 aSignedInfo
.cSigners
= 1;
1401 aSignedInfo
.rgSigners
= &aSignerInfo
;
1403 CERT_BLOB aCertBlob
;
1405 aCertBlob
.cbData
= pCertContext
->cbCertEncoded
;
1406 aCertBlob
.pbData
= pCertContext
->pbCertEncoded
;
1408 aSignedInfo
.cCertEncoded
= 1;
1409 aSignedInfo
.rgCertEncoded
= &aCertBlob
;
1411 HCRYPTMSG hMsg
= CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
1419 SAL_WARN("svl.crypto", "CryptMsgOpenToEncode failed: " << WindowsErrorString(GetLastError()));
1420 CertFreeCertificateContext(pCertContext
);
1424 for (size_t i
= 0; i
< m_dataBlocks
.size(); ++i
)
1426 const bool last
= (i
== m_dataBlocks
.size() - 1);
1427 if (!CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(m_dataBlocks
[i
].first
), m_dataBlocks
[i
].second
, last
))
1429 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
1430 CryptMsgClose(hMsg
);
1431 CertFreeCertificateContext(pCertContext
);
1436 PCRYPT_TIMESTAMP_CONTEXT pTsContext
= nullptr;
1438 if( !m_aSignTSA
.isEmpty() )
1440 HCRYPTMSG hDecodedMsg
= CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
1448 SAL_WARN("svl.crypto", "CryptMsgOpenToDecode failed: " << WindowsErrorString(GetLastError()));
1449 CryptMsgClose(hMsg
);
1450 CertFreeCertificateContext(pCertContext
);
1454 DWORD nTsSigLen
= 0;
1456 if (!CryptMsgGetParam(hMsg
, CMSG_BARE_CONTENT_PARAM
, 0, nullptr, &nTsSigLen
))
1458 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1459 CryptMsgClose(hDecodedMsg
);
1460 CryptMsgClose(hMsg
);
1461 CertFreeCertificateContext(pCertContext
);
1465 SAL_INFO("svl.crypto", "nTsSigLen=" << nTsSigLen
);
1467 std::unique_ptr
<BYTE
[]> pTsSig(new BYTE
[nTsSigLen
]);
1469 if (!CryptMsgGetParam(hMsg
, CMSG_BARE_CONTENT_PARAM
, 0, pTsSig
.get(), &nTsSigLen
))
1471 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1472 CryptMsgClose(hDecodedMsg
);
1473 CryptMsgClose(hMsg
);
1474 CertFreeCertificateContext(pCertContext
);
1478 if (!CryptMsgUpdate(hDecodedMsg
, pTsSig
.get(), nTsSigLen
, TRUE
))
1480 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
1481 CryptMsgClose(hDecodedMsg
);
1482 CryptMsgClose(hMsg
);
1483 CertFreeCertificateContext(pCertContext
);
1487 DWORD nDecodedSignerInfoLen
= 0;
1488 if (!CryptMsgGetParam(hDecodedMsg
, CMSG_SIGNER_INFO_PARAM
, 0, nullptr, &nDecodedSignerInfoLen
))
1490 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
1491 CryptMsgClose(hDecodedMsg
);
1492 CryptMsgClose(hMsg
);
1493 CertFreeCertificateContext(pCertContext
);
1497 std::unique_ptr
<BYTE
[]> pDecodedSignerInfoBuf(new BYTE
[nDecodedSignerInfoLen
]);
1499 if (!CryptMsgGetParam(hDecodedMsg
, CMSG_SIGNER_INFO_PARAM
, 0, pDecodedSignerInfoBuf
.get(), &nDecodedSignerInfoLen
))
1501 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
1502 CryptMsgClose(hDecodedMsg
);
1503 CryptMsgClose(hMsg
);
1504 CertFreeCertificateContext(pCertContext
);
1508 CMSG_SIGNER_INFO
*pDecodedSignerInfo
= reinterpret_cast<CMSG_SIGNER_INFO
*>(pDecodedSignerInfoBuf
.get());
1510 CRYPT_TIMESTAMP_PARA aTsPara
;
1511 unsigned int nNonce
= comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32
);
1513 aTsPara
.pszTSAPolicyId
= nullptr;
1514 aTsPara
.fRequestCerts
= TRUE
;
1515 aTsPara
.Nonce
.cbData
= sizeof(nNonce
);
1516 aTsPara
.Nonce
.pbData
= reinterpret_cast<BYTE
*>(&nNonce
);
1517 aTsPara
.cExtension
= 0;
1518 aTsPara
.rgExtension
= nullptr;
1520 if (!CryptRetrieveTimeStamp(o3tl::toW(m_aSignTSA
.getStr()),
1525 pDecodedSignerInfo
->EncryptedHash
.pbData
,
1526 pDecodedSignerInfo
->EncryptedHash
.cbData
,
1531 SAL_WARN("svl.crypto", "CryptRetrieveTimeStamp failed: " << WindowsErrorString(GetLastError()));
1532 CryptMsgClose(hDecodedMsg
);
1533 CryptMsgClose(hMsg
);
1534 CertFreeCertificateContext(pCertContext
);
1538 SAL_INFO("svl.crypto", "Time stamp size is " << pTsContext
->cbEncoded
<< " bytes");
1540 // I tried to use CryptMsgControl() with CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR to add the
1541 // timestamp, but that failed with "The parameter is incorrect". Probably it is too late to
1542 // modify the message once its data has already been encoded as part of the
1543 // CryptMsgGetParam() with CMSG_BARE_CONTENT_PARAM above. So close the message and re-do its
1544 // creation steps, but now with an amended aSignerInfo.
1546 CRYPT_INTEGER_BLOB aTimestampBlob
;
1547 aTimestampBlob
.cbData
= pTsContext
->cbEncoded
;
1548 aTimestampBlob
.pbData
= pTsContext
->pbEncoded
;
1550 CRYPT_ATTRIBUTE aTimestampAttribute
;
1551 aTimestampAttribute
.pszObjId
= const_cast<LPSTR
>(
1552 "1.2.840.113549.1.9.16.2.14");
1553 aTimestampAttribute
.cValue
= 1;
1554 aTimestampAttribute
.rgValue
= &aTimestampBlob
;
1556 aSignerInfo
.cUnauthAttr
= 1;
1557 aSignerInfo
.rgUnauthAttr
= &aTimestampAttribute
;
1559 CryptMsgClose(hMsg
);
1561 hMsg
= CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
1568 for (size_t i
= 0; i
< m_dataBlocks
.size(); ++i
)
1570 const bool last
= (i
== m_dataBlocks
.size() - 1);
1572 !CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(m_dataBlocks
[i
].first
), m_dataBlocks
[i
].second
, last
))
1574 SAL_WARN("svl.crypto", "Re-creating the message failed: " << WindowsErrorString(GetLastError()));
1575 CryptMemFree(pTsContext
);
1576 CryptMsgClose(hDecodedMsg
);
1577 CryptMsgClose(hMsg
);
1578 CertFreeCertificateContext(pCertContext
);
1583 CryptMsgClose(hDecodedMsg
);
1588 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, nullptr, &nSigLen
))
1590 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1592 CryptMemFree(pTsContext
);
1593 CryptMsgClose(hMsg
);
1594 CertFreeCertificateContext(pCertContext
);
1598 if (nSigLen
*2 > MAX_SIGNATURE_CONTENT_LENGTH
)
1600 SAL_WARN("svl.crypto", "Signature requires more space (" << nSigLen
*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH
<< ")");
1602 CryptMemFree(pTsContext
);
1603 CryptMsgClose(hMsg
);
1604 CertFreeCertificateContext(pCertContext
);
1608 SAL_INFO("svl.crypto", "Signature size is " << nSigLen
<< " bytes");
1609 std::unique_ptr
<BYTE
[]> pSig(new BYTE
[nSigLen
]);
1611 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, pSig
.get(), &nSigLen
))
1613 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1615 CryptMemFree(pTsContext
);
1616 CryptMsgClose(hMsg
);
1617 CertFreeCertificateContext(pCertContext
);
1621 // Release resources
1623 CryptMemFree(pTsContext
);
1624 CryptMsgClose(hMsg
);
1625 CertFreeCertificateContext(pCertContext
);
1627 for (unsigned int i
= 0; i
< nSigLen
; i
++)
1628 appendHex(pSig
[i
], rCMSHexBuffer
);
1634 bool Signing::Sign(OStringBuffer
&)
1638 #endif //!SVL_CRYPTO_NSS && !SVL_CRYPTO_MSCRYPTO
1643 #ifdef SVL_CRYPTO_NSS
1644 /// Similar to NSS_CMSAttributeArray_FindAttrByOidTag(), but works directly with a SECOidData.
1645 NSSCMSAttribute
* CMSAttributeArray_FindAttrByOidData(NSSCMSAttribute
** attrs
, SECOidData
const * oid
, PRBool only
)
1647 NSSCMSAttribute
* attr1
, *attr2
;
1649 if (attrs
== nullptr)
1655 while ((attr1
= *attrs
++) != nullptr)
1657 if (attr1
->type
.len
== oid
->oid
.len
&& PORT_Memcmp(attr1
->type
.data
,
1663 if (attr1
== nullptr)
1669 while ((attr2
= *attrs
++) != nullptr)
1671 if (attr2
->type
.len
== oid
->oid
.len
&& PORT_Memcmp(attr2
->type
.data
,
1677 if (attr2
!= nullptr)
1683 /// Same as SEC_StringToOID(), which is private to us.
1684 SECStatus
StringToOID(SECItem
* to
, const char* from
, PRUint32 len
)
1686 PRUint32 decimal_numbers
= 0;
1687 PRUint32 result_bytes
= 0;
1689 PRUint8 result
[1024];
1691 static const PRUint32 max_decimal
= 0xffffffff / 10;
1692 static const char OIDstring
[] = {"OID."};
1696 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
1701 len
= PL_strlen(from
);
1703 if (len
>= 4 && !PL_strncasecmp(from
, OIDstring
, 4))
1705 from
+= 4; /* skip leading "OID." if present */
1711 PORT_SetError(SEC_ERROR_BAD_DATA
);
1716 PRUint32 decimal
= 0;
1717 while (len
> 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from
)))
1719 PRUint32 addend
= *from
++ - '0';
1721 if (decimal
> max_decimal
) /* overflow */
1723 decimal
= (decimal
* 10) + addend
;
1724 if (decimal
< addend
) /* overflow */
1727 if (len
!= 0 && *from
!= '.')
1731 if (decimal_numbers
== 0)
1735 result
[0] = decimal
* 40;
1738 else if (decimal_numbers
== 1)
1742 result
[0] += decimal
;
1746 /* encode the decimal number, */
1748 PRUint32 num_bytes
= 0;
1749 PRUint32 tmp
= decimal
;
1756 ++num_bytes
; /* use one byte for a zero value */
1757 if (static_cast<size_t>(num_bytes
) + result_bytes
> sizeof result
)
1760 rp
= result
+ result_bytes
- 1;
1761 rp
[tmp
] = static_cast<PRUint8
>(decimal
& 0x7f);
1765 rp
[tmp
] = static_cast<PRUint8
>(decimal
| 0x80);
1768 result_bytes
+= num_bytes
;
1771 if (len
> 0) /* skip trailing '.' */
1778 /* now result contains result_bytes of data */
1779 if (to
->data
&& to
->len
>= result_bytes
)
1781 to
->len
= result_bytes
;
1782 PORT_Memcpy(to
->data
, result
, to
->len
);
1787 SECItem result_item
= {siBuffer
, nullptr, 0 };
1788 result_item
.data
= result
;
1789 result_item
.len
= result_bytes
;
1790 rv
= SECITEM_CopyItem(nullptr, to
, &result_item
);
1794 #elif defined SVL_CRYPTO_MSCRYPTO
1795 /// Verifies a non-detached signature using CryptoAPI.
1796 bool VerifyNonDetachedSignature(const std::vector
<unsigned char>& aData
, const std::vector
<BYTE
>& rExpectedHash
)
1798 HCRYPTPROV hProv
= 0;
1799 if (!CryptAcquireContextW(&hProv
, nullptr, nullptr, PROV_RSA_AES
, CRYPT_VERIFYCONTEXT
))
1801 SAL_WARN("svl.crypto", "CryptAcquireContext() failed");
1805 HCRYPTHASH hHash
= 0;
1806 if (!CryptCreateHash(hProv
, CALG_SHA1
, 0, 0, &hHash
))
1808 SAL_WARN("svl.crypto", "CryptCreateHash() failed");
1812 if (!CryptHashData(hHash
, aData
.data(), aData
.size(), 0))
1814 SAL_WARN("svl.crypto", "CryptHashData() failed");
1818 DWORD nActualHash
= 0;
1819 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, nullptr, &nActualHash
, 0))
1821 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash length");
1825 std::vector
<unsigned char> aActualHash(nActualHash
);
1826 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, aActualHash
.data(), &nActualHash
, 0))
1828 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash");
1832 CryptDestroyHash(hHash
);
1833 CryptReleaseContext(hProv
, 0);
1835 return aActualHash
.size() == rExpectedHash
.size() &&
1836 !std::memcmp(aActualHash
.data(), rExpectedHash
.data(), aActualHash
.size());
1839 OUString
GetSubjectName(PCCERT_CONTEXT pCertContext
)
1841 OUString subjectName
;
1843 // Get Subject name size.
1844 DWORD dwData
= CertGetNameStringW(pCertContext
,
1845 CERT_NAME_SIMPLE_DISPLAY_TYPE
,
1852 SAL_WARN("svl.crypto", "ValidateSignature: CertGetNameString failed");
1856 // Allocate memory for subject name.
1857 LPWSTR szName
= static_cast<LPWSTR
>(
1858 LocalAlloc(LPTR
, dwData
* sizeof(WCHAR
)));
1861 SAL_WARN("svl.crypto", "ValidateSignature: Unable to allocate memory for subject name");
1865 // Get subject name.
1866 if (!CertGetNameStringW(pCertContext
,
1867 CERT_NAME_SIMPLE_DISPLAY_TYPE
,
1874 SAL_WARN("svl.crypto", "ValidateSignature: CertGetNameString failed");
1878 subjectName
= o3tl::toU(szName
);
1887 #ifdef SVL_CRYPTO_NSS
1890 void ensureNssInit()
1892 // e.g. tdf#122599 ensure NSS library is initialized for NSS_CMSMessage_CreateFromDER
1893 css::uno::Reference
<css::xml::crypto::XNSSInitializer
>
1894 xNSSInitializer
= css::xml::crypto::NSSInitializer::create(comphelper::getProcessComponentContext());
1896 // this calls NSS_Init
1897 xNSSInitializer
->getDigestContext(css::xml::crypto::DigestID::SHA256
,
1898 uno::Sequence
<beans::NamedValue
>());
1903 bool Signing::Verify(const std::vector
<unsigned char>& aData
,
1904 const bool bNonDetached
,
1905 const std::vector
<unsigned char>& aSignature
,
1906 SignatureInformation
& rInformation
)
1908 #ifdef SVL_CRYPTO_NSS
1909 // ensure NSS_Init() is called before using NSS_CMSMessage_CreateFromDER
1910 static std::once_flag aInitOnce
;
1911 std::call_once(aInitOnce
, ensureNssInit
);
1913 // Validate the signature.
1914 SECItem aSignatureItem
;
1915 aSignatureItem
.data
= const_cast<unsigned char*>(aSignature
.data());
1916 aSignatureItem
.len
= aSignature
.size();
1917 NSSCMSMessage
* pCMSMessage
= NSS_CMSMessage_CreateFromDER(&aSignatureItem
,
1921 /*pwfn_arg=*/nullptr,
1922 /*decrypt_key_cb=*/nullptr,
1923 /*decrypt_key_cb_arg=*/nullptr);
1924 if (!NSS_CMSMessage_IsSigned(pCMSMessage
))
1926 SAL_WARN("svl.crypto", "ValidateSignature: message is not signed");
1930 NSSCMSContentInfo
* pCMSContentInfo
= NSS_CMSMessage_ContentLevel(pCMSMessage
, 0);
1931 if (!pCMSContentInfo
)
1933 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSMessage_ContentLevel() failed");
1937 auto pCMSSignedData
= static_cast<NSSCMSSignedData
*>(NSS_CMSContentInfo_GetContent(pCMSContentInfo
));
1938 if (!pCMSSignedData
)
1940 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSContentInfo_GetContent() failed");
1944 // Import certificates from the signed data temporarily, so it'll be
1945 // possible to verify the signature, even if we didn't have the certificate
1947 std::vector
<CERTCertificate
*> aDocumentCertificates
;
1948 for (size_t i
= 0; pCMSSignedData
->rawCerts
[i
]; ++i
)
1949 aDocumentCertificates
.push_back(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), pCMSSignedData
->rawCerts
[i
], nullptr, 0, 0));
1951 NSSCMSSignerInfo
* pCMSSignerInfo
= NSS_CMSSignedData_GetSignerInfo(pCMSSignedData
, 0);
1952 if (!pCMSSignerInfo
)
1954 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSSignedData_GetSignerInfo() failed");
1958 SECItem aAlgorithm
= NSS_CMSSignedData_GetDigestAlgs(pCMSSignedData
)[0]->algorithm
;
1959 SECOidTag eOidTag
= SECOID_FindOIDTag(&aAlgorithm
);
1961 // Map a sign algorithm to a digest algorithm.
1962 // See NSS_CMSUtil_MapSignAlgs(), which is private to us.
1965 case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION
:
1966 eOidTag
= SEC_OID_SHA1
;
1968 case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION
:
1969 eOidTag
= SEC_OID_SHA256
;
1971 case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION
:
1972 eOidTag
= SEC_OID_SHA512
;
1978 HASH_HashType eHashType
= HASH_GetHashTypeByOidTag(eOidTag
);
1979 HASHContext
* pHASHContext
= HASH_Create(eHashType
);
1982 SAL_WARN("svl.crypto", "ValidateSignature: HASH_Create() failed");
1986 // We have a hash, update it with the byte ranges.
1987 HASH_Update(pHASHContext
, aData
.data(), aData
.size());
1989 // Find out what is the expected length of the hash.
1990 unsigned int nMaxResultLen
= 0;
1994 nMaxResultLen
= comphelper::SHA1_HASH_LENGTH
;
1995 rInformation
.nDigestID
= xml::crypto::DigestID::SHA1
;
1997 case SEC_OID_SHA256
:
1998 nMaxResultLen
= comphelper::SHA256_HASH_LENGTH
;
1999 rInformation
.nDigestID
= xml::crypto::DigestID::SHA256
;
2001 case SEC_OID_SHA512
:
2002 nMaxResultLen
= comphelper::SHA512_HASH_LENGTH
;
2003 rInformation
.nDigestID
= xml::crypto::DigestID::SHA512
;
2006 SAL_WARN("svl.crypto", "ValidateSignature: unrecognized algorithm");
2010 auto pActualResultBuffer
= static_cast<unsigned char*>(PORT_Alloc(nMaxResultLen
));
2011 unsigned int nActualResultLen
;
2012 HASH_End(pHASHContext
, pActualResultBuffer
, &nActualResultLen
, nMaxResultLen
);
2014 CERTCertificate
* pCertificate
= NSS_CMSSignerInfo_GetSigningCertificate(pCMSSignerInfo
, CERT_GetDefaultCertDB());
2017 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSSignerInfo_GetSigningCertificate() failed");
2022 uno::Sequence
<sal_Int8
> aDerCert(pCertificate
->derCert
.len
);
2023 for (size_t i
= 0; i
< pCertificate
->derCert
.len
; ++i
)
2024 aDerCert
[i
] = pCertificate
->derCert
.data
[i
];
2025 OUStringBuffer aBuffer
;
2026 comphelper::Base64::encode(aBuffer
, aDerCert
);
2027 SignatureInformation::X509Data temp
;
2028 temp
.emplace_back();
2029 temp
.back().X509Certificate
= aBuffer
.makeStringAndClear();
2030 temp
.back().X509Subject
= OUString(pCertificate
->subjectName
, PL_strlen(pCertificate
->subjectName
), RTL_TEXTENCODING_UTF8
);
2031 rInformation
.X509Datas
.clear();
2032 rInformation
.X509Datas
.emplace_back(temp
);
2035 PRTime nSigningTime
;
2036 // This may fail, in which case the date should be taken from the PDF's dictionary's "M" key,
2037 // so not critical for PDF at least.
2038 if (NSS_CMSSignerInfo_GetSigningTime(pCMSSignerInfo
, &nSigningTime
) == SECSuccess
)
2040 // First convert the UTC UNIX timestamp to a tools::DateTime.
2041 // nSigningTime is in microseconds.
2042 DateTime aDateTime
= DateTime::CreateFromUnixTime(static_cast<double>(nSigningTime
) / 1000000);
2044 // Then convert to a local UNO DateTime.
2045 aDateTime
.ConvertToLocalTime();
2046 rInformation
.stDateTime
= aDateTime
.GetUNODateTime();
2047 if (rInformation
.ouDateTime
.isEmpty())
2049 OUStringBuffer rBuffer
;
2050 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetYear()));
2051 rBuffer
.append('-');
2052 if (aDateTime
.GetMonth() < 10)
2053 rBuffer
.append('0');
2054 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetMonth()));
2055 rBuffer
.append('-');
2056 if (aDateTime
.GetDay() < 10)
2057 rBuffer
.append('0');
2058 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetDay()));
2059 rInformation
.ouDateTime
= rBuffer
.makeStringAndClear();
2063 // Check if we have a signing certificate attribute.
2064 SECOidData aOidData
;
2065 aOidData
.oid
.data
= nullptr;
2067 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
2068 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
2069 * smime(16) id-aa(2) 47 }
2071 if (StringToOID(&aOidData
.oid
, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess
)
2073 SAL_WARN("svl.crypto", "StringToOID() failed");
2076 aOidData
.offset
= SEC_OID_UNKNOWN
;
2077 aOidData
.desc
= "id-aa-signingCertificateV2";
2078 aOidData
.mechanism
= CKM_SHA_1
;
2079 aOidData
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
;
2080 NSSCMSAttribute
* pAttribute
= CMSAttributeArray_FindAttrByOidData(pCMSSignerInfo
->authAttr
, &aOidData
, PR_TRUE
);
2082 rInformation
.bHasSigningCertificate
= true;
2084 SECItem
* pContentInfoContentData
= pCMSSignedData
->contentInfo
.content
.data
;
2085 if (bNonDetached
&& pContentInfoContentData
&& pContentInfoContentData
->data
)
2087 // Not a detached signature.
2088 if (!std::memcmp(pActualResultBuffer
, pContentInfoContentData
->data
, nMaxResultLen
) && nActualResultLen
== pContentInfoContentData
->len
)
2089 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2093 // Detached, the usual case.
2094 SECItem aActualResultItem
;
2095 aActualResultItem
.data
= pActualResultBuffer
;
2096 aActualResultItem
.len
= nActualResultLen
;
2097 if (NSS_CMSSignerInfo_Verify(pCMSSignerInfo
, &aActualResultItem
, nullptr) == SECSuccess
)
2098 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2101 // Everything went fine
2102 PORT_Free(pActualResultBuffer
);
2103 HASH_Destroy(pHASHContext
);
2104 NSS_CMSSignerInfo_Destroy(pCMSSignerInfo
);
2105 for (auto pDocumentCertificate
: aDocumentCertificates
)
2106 CERT_DestroyCertificate(pDocumentCertificate
);
2110 #elif defined SVL_CRYPTO_MSCRYPTO
2111 // Open a message for decoding.
2112 HCRYPTMSG hMsg
= CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
2120 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgOpenToDecode() failed");
2124 // Update the message with the encoded header blob.
2125 if (!CryptMsgUpdate(hMsg
, aSignature
.data(), aSignature
.size(), TRUE
))
2127 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the header failed: " << WindowsErrorString(GetLastError()));
2131 // Update the message with the content blob.
2132 if (!CryptMsgUpdate(hMsg
, aData
.data(), aData
.size(), FALSE
))
2134 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError()));
2138 if (!CryptMsgUpdate(hMsg
, nullptr, 0, TRUE
))
2140 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the last content failed: " << WindowsErrorString(GetLastError()));
2144 // Get the CRYPT_ALGORITHM_IDENTIFIER from the message.
2145 DWORD nDigestID
= 0;
2146 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_HASH_ALGORITHM_PARAM
, 0, nullptr, &nDigestID
))
2148 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
2151 std::unique_ptr
<BYTE
[]> pDigestBytes(new BYTE
[nDigestID
]);
2152 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_HASH_ALGORITHM_PARAM
, 0, pDigestBytes
.get(), &nDigestID
))
2154 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
2157 auto pDigestID
= reinterpret_cast<CRYPT_ALGORITHM_IDENTIFIER
*>(pDigestBytes
.get());
2158 if (OString(szOID_NIST_sha256
) == pDigestID
->pszObjId
)
2159 rInformation
.nDigestID
= xml::crypto::DigestID::SHA256
;
2160 else if (OString(szOID_RSA_SHA1RSA
) == pDigestID
->pszObjId
|| OString(szOID_OIWSEC_sha1
) == pDigestID
->pszObjId
)
2161 rInformation
.nDigestID
= xml::crypto::DigestID::SHA1
;
2163 // Don't error out here, we can still verify the message digest correctly, just the digest ID won't be set.
2164 SAL_WARN("svl.crypto", "ValidateSignature: unhandled algorithm identifier '"<<pDigestID
->pszObjId
<<"'");
2166 // Get the signer CERT_INFO from the message.
2167 DWORD nSignerCertInfo
= 0;
2168 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_CERT_INFO_PARAM
, 0, nullptr, &nSignerCertInfo
))
2170 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2173 std::unique_ptr
<BYTE
[]> pSignerCertInfoBuf(new BYTE
[nSignerCertInfo
]);
2174 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_CERT_INFO_PARAM
, 0, pSignerCertInfoBuf
.get(), &nSignerCertInfo
))
2176 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2179 PCERT_INFO pSignerCertInfo
= reinterpret_cast<PCERT_INFO
>(pSignerCertInfoBuf
.get());
2181 // Open a certificate store in memory using CERT_STORE_PROV_MSG, which
2182 // initializes it with the certificates from the message.
2183 HCERTSTORE hStoreHandle
= CertOpenStore(CERT_STORE_PROV_MSG
,
2184 PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
2190 SAL_WARN("svl.crypto", "ValidateSignature: CertOpenStore() failed");
2194 // Find the signer's certificate in the store.
2195 PCCERT_CONTEXT pSignerCertContext
= CertGetSubjectCertificateFromStore(hStoreHandle
,
2196 PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
2198 if (!pSignerCertContext
)
2200 SAL_WARN("svl.crypto", "ValidateSignature: CertGetSubjectCertificateFromStore() failed");
2205 // Write rInformation.ouX509Certificate.
2206 uno::Sequence
<sal_Int8
> aDerCert(pSignerCertContext
->cbCertEncoded
);
2207 for (size_t i
= 0; i
< pSignerCertContext
->cbCertEncoded
; ++i
)
2208 aDerCert
[i
] = pSignerCertContext
->pbCertEncoded
[i
];
2209 OUStringBuffer aBuffer
;
2210 comphelper::Base64::encode(aBuffer
, aDerCert
);
2211 SignatureInformation::X509Data temp
;
2212 temp
.emplace_back();
2213 temp
.back().X509Certificate
= aBuffer
.makeStringAndClear();
2214 temp
.back().X509Subject
= GetSubjectName(pSignerCertContext
);
2215 rInformation
.X509Datas
.clear();
2216 rInformation
.X509Datas
.emplace_back(temp
);
2221 // Not a detached signature.
2222 DWORD nContentParam
= 0;
2223 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, nullptr, &nContentParam
))
2225 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2229 std::vector
<BYTE
> aContentParam(nContentParam
);
2230 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, aContentParam
.data(), &nContentParam
))
2232 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2236 if (VerifyNonDetachedSignature(aData
, aContentParam
))
2237 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2241 // Detached, the usual case.
2242 // Use the CERT_INFO from the signer certificate to verify the signature.
2243 if (CryptMsgControl(hMsg
, 0, CMSG_CTRL_VERIFY_SIGNATURE
, pSignerCertContext
->pCertInfo
))
2244 rInformation
.nStatus
= xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
2247 // Check if we have a signing certificate attribute.
2248 DWORD nSignedAttributes
= 0;
2249 if (CryptMsgGetParam(hMsg
, CMSG_SIGNER_AUTH_ATTR_PARAM
, 0, nullptr, &nSignedAttributes
))
2251 std::unique_ptr
<BYTE
[]> pSignedAttributesBuf(new BYTE
[nSignedAttributes
]);
2252 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_AUTH_ATTR_PARAM
, 0, pSignedAttributesBuf
.get(), &nSignedAttributes
))
2254 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() authenticated failed");
2257 auto pSignedAttributes
= reinterpret_cast<PCRYPT_ATTRIBUTES
>(pSignedAttributesBuf
.get());
2258 for (size_t nAttr
= 0; nAttr
< pSignedAttributes
->cAttr
; ++nAttr
)
2260 CRYPT_ATTRIBUTE
& rAttr
= pSignedAttributes
->rgAttr
[nAttr
];
2262 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
2263 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
2264 * smime(16) id-aa(2) 47 }
2266 if (OString("1.2.840.113549.1.9.16.2.47") == rAttr
.pszObjId
)
2268 rInformation
.bHasSigningCertificate
= true;
2274 // Get the unauthorized attributes.
2275 nSignedAttributes
= 0;
2276 if (CryptMsgGetParam(hMsg
, CMSG_SIGNER_UNAUTH_ATTR_PARAM
, 0, nullptr, &nSignedAttributes
))
2278 std::unique_ptr
<BYTE
[]> pSignedAttributesBuf(new BYTE
[nSignedAttributes
]);
2279 if (!CryptMsgGetParam(hMsg
, CMSG_SIGNER_UNAUTH_ATTR_PARAM
, 0, pSignedAttributesBuf
.get(), &nSignedAttributes
))
2281 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() unauthenticated failed");
2284 auto pSignedAttributes
= reinterpret_cast<PCRYPT_ATTRIBUTES
>(pSignedAttributesBuf
.get());
2285 for (size_t nAttr
= 0; nAttr
< pSignedAttributes
->cAttr
; ++nAttr
)
2287 CRYPT_ATTRIBUTE
& rAttr
= pSignedAttributes
->rgAttr
[nAttr
];
2289 if (OString("1.2.840.113549.1.9.16.2.14") == rAttr
.pszObjId
)
2291 PCRYPT_TIMESTAMP_CONTEXT pTsContext
;
2292 if (!CryptVerifyTimeStampSignature(rAttr
.rgValue
->pbData
, rAttr
.rgValue
->cbData
, nullptr, 0, nullptr, &pTsContext
, nullptr, nullptr))
2294 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
2298 DateTime aDateTime
= DateTime::CreateFromWin32FileDateTime(pTsContext
->pTimeStamp
->ftTime
.dwLowDateTime
, pTsContext
->pTimeStamp
->ftTime
.dwHighDateTime
);
2300 // Then convert to a local UNO DateTime.
2301 aDateTime
.ConvertToLocalTime();
2302 rInformation
.stDateTime
= aDateTime
.GetUNODateTime();
2303 if (rInformation
.ouDateTime
.isEmpty())
2305 OUStringBuffer rBuffer
;
2306 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetYear()));
2307 rBuffer
.append('-');
2308 if (aDateTime
.GetMonth() < 10)
2309 rBuffer
.append('0');
2310 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetMonth()));
2311 rBuffer
.append('-');
2312 if (aDateTime
.GetDay() < 10)
2313 rBuffer
.append('0');
2314 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetDay()));
2315 rInformation
.ouDateTime
= rBuffer
.makeStringAndClear();
2322 CertCloseStore(hStoreHandle
, CERT_CLOSE_STORE_FORCE_FLAG
);
2323 CryptMsgClose(hMsg
);
2335 bool Signing::Verify(SvStream
& rStream
,
2336 const std::vector
<std::pair
<size_t, size_t>>& aByteRanges
,
2337 const bool bNonDetached
,
2338 const std::vector
<unsigned char>& aSignature
,
2339 SignatureInformation
& rInformation
)
2341 #if defined(SVL_CRYPTO_NSS) || defined(SVL_CRYPTO_MSCRYPTO)
2343 std::vector
<unsigned char> buffer
;
2345 // Copy the byte ranges into a single buffer.
2346 for (const auto& rByteRange
: aByteRanges
)
2348 rStream
.Seek(rByteRange
.first
);
2349 const size_t size
= buffer
.size();
2350 buffer
.resize(size
+ rByteRange
.second
);
2351 rStream
.ReadBytes(buffer
.data() + size
, rByteRange
.second
);
2354 return Verify(buffer
, bNonDetached
, aSignature
, rInformation
);
2369 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */