summaryrefslogtreecommitdiff
path: root/svl
diff options
context:
space:
mode:
authorAshod Nakashian <ashod.nakashian@collabora.co.uk>2017-07-09 09:42:01 -0400
committerAshod Nakashian <ashnakash@gmail.com>2017-07-17 00:12:10 +0200
commit4f17445c12dc26c4881c4e486215b58d26515f8d (patch)
treed5532f4a3129fe532b4ad4305a44a8817c7e8b41 /svl
parentc76c3655a394462b7b23bdfe6da4542fbdf30fbb (diff)
svl: move byte-array verification from vcl
Also use comphelper::Base64 and DateTime::CreateFromUnixTime to avoid depending on sax. Change-Id: If1853f8d9481c9caa0625a111707531bbc495f75 Reviewed-on: https://gerrit.libreoffice.org/39993 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Diffstat (limited to 'svl')
-rw-r--r--svl/Library_svl.mk19
-rw-r--r--svl/source/crypto/cryptosign.cxx637
2 files changed, 654 insertions, 2 deletions
diff --git a/svl/Library_svl.mk b/svl/Library_svl.mk
index e3969b1d90a3..97fa4df3add9 100644
--- a/svl/Library_svl.mk
+++ b/svl/Library_svl.mk
@@ -95,6 +95,25 @@ $(eval $(call gb_Library_use_system_win32_libs,svl,\
shlwapi \
))
+ifeq ($(OS),WNT)
+$(eval $(call gb_Library_add_defs,svl,\
+ -DSVL_CRYPTO_MSCRYPTO \
+))
+$(eval $(call gb_Library_use_system_win32_libs,svl,\
+ crypt32 \
+))
+else
+ifneq (,$(filter DESKTOP,$(BUILD_TYPE)))
+$(eval $(call gb_Library_add_defs,svl,\
+ -DSVL_CRYPTO_NSS \
+))
+$(eval $(call gb_Library_use_externals,svl,\
+ nss3 \
+ plc4 \
+))
+endif # BUILD_TYPE=DESKTOP
+endif
+
$(eval $(call gb_Library_add_exception_objects,svl,\
svl/source/config/asiancfg \
svl/source/config/cjkoptions \
diff --git a/svl/source/crypto/cryptosign.cxx b/svl/source/crypto/cryptosign.cxx
index 0c16ba6fbd94..d5304e5e2d2a 100644
--- a/svl/source/crypto/cryptosign.cxx
+++ b/svl/source/crypto/cryptosign.cxx
@@ -12,12 +12,12 @@
#include <rtl/character.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/string.hxx>
+#include <tools/datetime.hxx>
#include <tools/stream.hxx>
+#include <comphelper/base64.hxx>
#include <comphelper/random.hxx>
#include <comphelper/hash.hxx>
#include <comphelper/processfactory.hxx>
-// #include <comphelper/scopeguard.hxx>
-// #include <comphelper/string.hxx>
#include <com/sun/star/security/XCertificate.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <filter/msfilter/mscodec.hxx>
@@ -1646,6 +1646,639 @@ bool Signing::Sign(OStringBuffer& rCMSHexBuffer)
#endif
}
+namespace
+{
+#ifdef SVL_CRYPTO_NSS
+/// Similar to NSS_CMSAttributeArray_FindAttrByOidTag(), but works directly with a SECOidData.
+NSSCMSAttribute* CMSAttributeArray_FindAttrByOidData(NSSCMSAttribute** attrs, SECOidData* oid, PRBool only)
+{
+ NSSCMSAttribute* attr1, *attr2;
+
+ if (attrs == nullptr)
+ return nullptr;
+
+ if (oid == nullptr)
+ return nullptr;
+
+ while ((attr1 = *attrs++) != nullptr)
+ {
+ if (attr1->type.len == oid->oid.len && PORT_Memcmp(attr1->type.data,
+ oid->oid.data,
+ oid->oid.len) == 0)
+ break;
+ }
+
+ if (attr1 == nullptr)
+ return nullptr;
+
+ if (!only)
+ return attr1;
+
+ while ((attr2 = *attrs++) != nullptr)
+ {
+ if (attr2->type.len == oid->oid.len && PORT_Memcmp(attr2->type.data,
+ oid->oid.data,
+ oid->oid.len) == 0)
+ break;
+ }
+
+ if (attr2 != nullptr)
+ return nullptr;
+
+ return attr1;
+}
+
+/// Same as SEC_StringToOID(), which is private to us.
+SECStatus StringToOID(SECItem* to, const char* from, PRUint32 len)
+{
+ PRUint32 decimal_numbers = 0;
+ PRUint32 result_bytes = 0;
+ SECStatus rv;
+ PRUint8 result[1024];
+
+ static const PRUint32 max_decimal = (0xffffffff / 10);
+ static const char OIDstring[] = {"OID."};
+
+ if (!from || !to)
+ {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (!len)
+ {
+ len = PL_strlen(from);
+ }
+ if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4))
+ {
+ from += 4; /* skip leading "OID." if present */
+ len -= 4;
+ }
+ if (!len)
+ {
+bad_data:
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ do
+ {
+ PRUint32 decimal = 0;
+ while (len > 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from)))
+ {
+ PRUint32 addend = (*from++ - '0');
+ --len;
+ if (decimal > max_decimal) /* overflow */
+ goto bad_data;
+ decimal = (decimal * 10) + addend;
+ if (decimal < addend) /* overflow */
+ goto bad_data;
+ }
+ if (len != 0 && *from != '.')
+ {
+ goto bad_data;
+ }
+ if (decimal_numbers == 0)
+ {
+ if (decimal > 2)
+ goto bad_data;
+ result[0] = decimal * 40;
+ result_bytes = 1;
+ }
+ else if (decimal_numbers == 1)
+ {
+ if (decimal > 40)
+ goto bad_data;
+ result[0] += decimal;
+ }
+ else
+ {
+ /* encode the decimal number, */
+ PRUint8* rp;
+ PRUint32 num_bytes = 0;
+ PRUint32 tmp = decimal;
+ while (tmp)
+ {
+ num_bytes++;
+ tmp >>= 7;
+ }
+ if (!num_bytes)
+ ++num_bytes; /* use one byte for a zero value */
+ if (static_cast<size_t>(num_bytes) + result_bytes > sizeof result)
+ goto bad_data;
+ tmp = num_bytes;
+ rp = result + result_bytes - 1;
+ rp[tmp] = (PRUint8)(decimal & 0x7f);
+ decimal >>= 7;
+ while (--tmp > 0)
+ {
+ rp[tmp] = (PRUint8)(decimal | 0x80);
+ decimal >>= 7;
+ }
+ result_bytes += num_bytes;
+ }
+ ++decimal_numbers;
+ if (len > 0) /* skip trailing '.' */
+ {
+ ++from;
+ --len;
+ }
+ }
+ while (len > 0);
+ /* now result contains result_bytes of data */
+ if (to->data && to->len >= result_bytes)
+ {
+ PORT_Memcpy(to->data, result, to->len = result_bytes);
+ rv = SECSuccess;
+ }
+ else
+ {
+ SECItem result_item = {siBuffer, nullptr, 0 };
+ result_item.data = result;
+ result_item.len = result_bytes;
+ rv = SECITEM_CopyItem(nullptr, to, &result_item);
+ }
+ return rv;
+}
+#elif defined SVL_CRYPTO_MSCRYPTO
+/// Verifies a non-detached signature using CryptoAPI.
+bool VerifyNonDetachedSignature(SvStream& rStream, const std::vector<std::pair<size_t, size_t>>& rByteRanges, std::vector<BYTE>& rExpectedHash)
+{
+ HCRYPTPROV hProv = 0;
+ if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "CryptAcquireContext() failed");
+ return false;
+ }
+
+ HCRYPTHASH hHash = 0;
+ if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "CryptCreateHash() failed");
+ return false;
+ }
+
+ for (const auto& rByteRange : rByteRanges)
+ {
+ rStream.Seek(rByteRange.first);
+ const int nChunkLen = 4096;
+ std::vector<unsigned char> aBuffer(nChunkLen);
+ for (size_t nByte = 0; nByte < rByteRange.second;)
+ {
+ size_t nRemainingSize = rByteRange.second - nByte;
+ if (nRemainingSize < nChunkLen)
+ {
+ rStream.ReadBytes(aBuffer.data(), nRemainingSize);
+ if (!CryptHashData(hHash, aBuffer.data(), nRemainingSize, 0))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "CryptHashData() failed");
+ return false;
+ }
+ nByte = rByteRange.second;
+ }
+ else
+ {
+ rStream.ReadBytes(aBuffer.data(), nChunkLen);
+ if (!CryptHashData(hHash, aBuffer.data(), nChunkLen, 0))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "CryptHashData() failed");
+ return false;
+ }
+ nByte += nChunkLen;
+ }
+ }
+ }
+
+ DWORD nActualHash = 0;
+ if (!CryptGetHashParam(hHash, HP_HASHVAL, nullptr, &nActualHash, 0))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "CryptGetHashParam() failed to provide the hash length");
+ return false;
+ }
+
+ std::vector<unsigned char> aActualHash(nActualHash);
+ if (!CryptGetHashParam(hHash, HP_HASHVAL, aActualHash.data(), &nActualHash, 0))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "CryptGetHashParam() failed to provide the hash");
+ return false;
+ }
+
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hProv, 0);
+
+ if (!std::memcmp(aActualHash.data(), rExpectedHash.data(), aActualHash.size()) && aActualHash.size() == rExpectedHash.size())
+ return true;
+
+ return false;
+}
+#endif
+}
+
+bool Signing::Verify(SvStream& rStream,
+ const std::vector<std::pair<size_t, size_t>>& aByteRanges,
+ const bool bNonDetached,
+ const std::vector<unsigned char>& aSignature,
+ SignatureInformation& rInformation)
+{
+
+#ifdef SVL_CRYPTO_NSS
+ // Validate the signature. No need to call NSS_Init() here, assume that the
+ // caller did that already.
+
+ SECItem aSignatureItem;
+ aSignatureItem.data = const_cast<unsigned char*>(aSignature.data());
+ aSignatureItem.len = aSignature.size();
+ NSSCMSMessage* pCMSMessage = NSS_CMSMessage_CreateFromDER(&aSignatureItem,
+ /*cb=*/nullptr,
+ /*cb_arg=*/nullptr,
+ /*pwfn=*/nullptr,
+ /*pwfn_arg=*/nullptr,
+ /*decrypt_key_cb=*/nullptr,
+ /*decrypt_key_cb_arg=*/nullptr);
+ if (!NSS_CMSMessage_IsSigned(pCMSMessage))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: message is not signed");
+ return false;
+ }
+
+ NSSCMSContentInfo* pCMSContentInfo = NSS_CMSMessage_ContentLevel(pCMSMessage, 0);
+ if (!pCMSContentInfo)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSMessage_ContentLevel() failed");
+ return false;
+ }
+
+ auto pCMSSignedData = static_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(pCMSContentInfo));
+ if (!pCMSSignedData)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSContentInfo_GetContent() failed");
+ return false;
+ }
+
+ // Import certificates from the signed data temporarily, so it'll be
+ // possible to verify the signature, even if we didn't have the certificate
+ // previously.
+ std::vector<CERTCertificate*> aDocumentCertificates;
+ for (size_t i = 0; pCMSSignedData->rawCerts[i]; ++i)
+ aDocumentCertificates.push_back(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), pCMSSignedData->rawCerts[i], nullptr, 0, 0));
+
+ NSSCMSSignerInfo* pCMSSignerInfo = NSS_CMSSignedData_GetSignerInfo(pCMSSignedData, 0);
+ if (!pCMSSignerInfo)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSSignedData_GetSignerInfo() failed");
+ return false;
+ }
+
+ SECItem aAlgorithm = NSS_CMSSignedData_GetDigestAlgs(pCMSSignedData)[0]->algorithm;
+ SECOidTag eOidTag = SECOID_FindOIDTag(&aAlgorithm);
+
+ // Map a sign algorithm to a digest algorithm.
+ // See NSS_CMSUtil_MapSignAlgs(), which is private to us.
+ switch (eOidTag)
+ {
+ case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+ eOidTag = SEC_OID_SHA1;
+ break;
+ case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
+ eOidTag = SEC_OID_SHA256;
+ break;
+ case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
+ eOidTag = SEC_OID_SHA512;
+ break;
+ default:
+ break;
+ }
+
+ HASH_HashType eHashType = HASH_GetHashTypeByOidTag(eOidTag);
+ HASHContext* pHASHContext = HASH_Create(eHashType);
+ if (!pHASHContext)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: HASH_Create() failed");
+ return false;
+ }
+
+ // We have a hash, update it with the byte ranges.
+ for (const auto& rByteRange : aByteRanges)
+ {
+ rStream.Seek(rByteRange.first);
+
+ // And now hash this byte range.
+ const int nChunkLen = 4096;
+ std::vector<unsigned char> aBuffer(nChunkLen);
+ for (size_t nByte = 0; nByte < rByteRange.second;)
+ {
+ size_t nRemainingSize = rByteRange.second - nByte;
+ if (nRemainingSize < nChunkLen)
+ {
+ rStream.ReadBytes(aBuffer.data(), nRemainingSize);
+ HASH_Update(pHASHContext, aBuffer.data(), nRemainingSize);
+ nByte = rByteRange.second;
+ }
+ else
+ {
+ rStream.ReadBytes(aBuffer.data(), nChunkLen);
+ HASH_Update(pHASHContext, aBuffer.data(), nChunkLen);
+ nByte += nChunkLen;
+ }
+ }
+ }
+
+ // Find out what is the expected length of the hash.
+ unsigned int nMaxResultLen = 0;
+ switch (eOidTag)
+ {
+ case SEC_OID_SHA1:
+ nMaxResultLen = msfilter::SHA1_HASH_LENGTH;
+ rInformation.nDigestID = xml::crypto::DigestID::SHA1;
+ break;
+ case SEC_OID_SHA256:
+ nMaxResultLen = msfilter::SHA256_HASH_LENGTH;
+ rInformation.nDigestID = xml::crypto::DigestID::SHA256;
+ break;
+ case SEC_OID_SHA512:
+ nMaxResultLen = msfilter::SHA512_HASH_LENGTH;
+ break;
+ default:
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unrecognized algorithm");
+ return false;
+ }
+
+ auto pActualResultBuffer = static_cast<unsigned char*>(PORT_Alloc(nMaxResultLen));
+ unsigned int nActualResultLen;
+ HASH_End(pHASHContext, pActualResultBuffer, &nActualResultLen, nMaxResultLen);
+
+ CERTCertificate* pCertificate = NSS_CMSSignerInfo_GetSigningCertificate(pCMSSignerInfo, CERT_GetDefaultCertDB());
+ if (!pCertificate)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSSignerInfo_GetSigningCertificate() failed");
+ return false;
+ }
+ else
+ {
+ uno::Sequence<sal_Int8> aDerCert(pCertificate->derCert.len);
+ for (size_t i = 0; i < pCertificate->derCert.len; ++i)
+ aDerCert[i] = pCertificate->derCert.data[i];
+ OUStringBuffer aBuffer;
+ comphelper::Base64::encode(aBuffer, aDerCert);
+ rInformation.ouX509Certificate = aBuffer.makeStringAndClear();
+ }
+
+ PRTime nSigningTime;
+ // This may fail, in which case the date should be taken from the dictionary's "M" key.
+ if (NSS_CMSSignerInfo_GetSigningTime(pCMSSignerInfo, &nSigningTime) == SECSuccess)
+ {
+ // First convert the UTC UNIX timestamp to a tools::DateTime.
+ // nSigningTime is in microseconds.
+ DateTime aDateTime = DateTime::CreateFromUnixTime(static_cast<double>(nSigningTime) / 1000000);
+
+ // Then convert to a local UNO DateTime.
+ aDateTime.ConvertToLocalTime();
+ rInformation.stDateTime = aDateTime.GetUNODateTime();
+ }
+
+ // Check if we have a signing certificate attribute.
+ SECOidData aOidData;
+ aOidData.oid.data = nullptr;
+ /*
+ * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
+ * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+ * smime(16) id-aa(2) 47 }
+ */
+ if (StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "StringToOID() failed");
+ return false;
+ }
+ aOidData.offset = SEC_OID_UNKNOWN;
+ aOidData.desc = "id-aa-signingCertificateV2";
+ aOidData.mechanism = CKM_SHA_1;
+ aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION;
+ NSSCMSAttribute* pAttribute = CMSAttributeArray_FindAttrByOidData(pCMSSignerInfo->authAttr, &aOidData, PR_TRUE);
+ if (pAttribute)
+ rInformation.bHasSigningCertificate = true;
+
+ SECItem* pContentInfoContentData = pCMSSignedData->contentInfo.content.data;
+ if (bNonDetached && pContentInfoContentData && pContentInfoContentData->data)
+ {
+ // Not a detached signature.
+ if (!std::memcmp(pActualResultBuffer, pContentInfoContentData->data, nMaxResultLen) && nActualResultLen == pContentInfoContentData->len)
+ rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+ }
+ else
+ {
+ // Detached, the usual case.
+ SECItem aActualResultItem;
+ aActualResultItem.data = pActualResultBuffer;
+ aActualResultItem.len = nActualResultLen;
+ if (NSS_CMSSignerInfo_Verify(pCMSSignerInfo, &aActualResultItem, nullptr) == SECSuccess)
+ rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+ }
+
+ // Everything went fine
+ PORT_Free(pActualResultBuffer);
+ HASH_Destroy(pHASHContext);
+ NSS_CMSSignerInfo_Destroy(pCMSSignerInfo);
+ for (auto pDocumentCertificate : aDocumentCertificates)
+ CERT_DestroyCertificate(pDocumentCertificate);
+
+ return true;
+#elif defined SVL_CRYPTO_MSCRYPTO
+ // Open a message for decoding.
+ HCRYPTMSG hMsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ CMSG_DETACHED_FLAG,
+ 0,
+ NULL,
+ nullptr,
+ nullptr);
+ if (!hMsg)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgOpenToDecode() failed");
+ return false;
+ }
+
+ // Update the message with the encoded header blob.
+ if (!CryptMsgUpdate(hMsg, aSignature.data(), aSignature.size(), TRUE))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the header failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ // Update the message with the content blob.
+ for (const auto& rByteRange : aByteRanges)
+ {
+ rStream.Seek(rByteRange.first);
+
+ const int nChunkLen = 4096;
+ std::vector<unsigned char> aBuffer(nChunkLen);
+ for (size_t nByte = 0; nByte < rByteRange.second;)
+ {
+ size_t nRemainingSize = rByteRange.second - nByte;
+ if (nRemainingSize < nChunkLen)
+ {
+ rStream.ReadBytes(aBuffer.data(), nRemainingSize);
+ if (!CryptMsgUpdate(hMsg, aBuffer.data(), nRemainingSize, FALSE))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+ nByte = rByteRange.second;
+ }
+ else
+ {
+ rStream.ReadBytes(aBuffer.data(), nChunkLen);
+ if (!CryptMsgUpdate(hMsg, aBuffer.data(), nChunkLen, FALSE))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+ nByte += nChunkLen;
+ }
+ }
+ }
+ if (!CryptMsgUpdate(hMsg, nullptr, 0, TRUE))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the last content failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ // Get the CRYPT_ALGORITHM_IDENTIFIER from the message.
+ DWORD nDigestID = 0;
+ if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, nullptr, &nDigestID))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+ std::unique_ptr<BYTE[]> pDigestBytes(new BYTE[nDigestID]);
+ if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, pDigestBytes.get(), &nDigestID))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+ auto pDigestID = reinterpret_cast<CRYPT_ALGORITHM_IDENTIFIER*>(pDigestBytes.get());
+ if (OString(szOID_NIST_sha256) == pDigestID->pszObjId)
+ rInformation.nDigestID = xml::crypto::DigestID::SHA256;
+ else if (OString(szOID_RSA_SHA1RSA) == pDigestID->pszObjId || OString(szOID_OIWSEC_sha1) == pDigestID->pszObjId)
+ rInformation.nDigestID = xml::crypto::DigestID::SHA1;
+ else
+ // Don't error out here, we can still verify the message digest correctly, just the digest ID won't be set.
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unhandled algorithm identifier '"<<pDigestID->pszObjId<<"'");
+
+ // Get the signer CERT_INFO from the message.
+ DWORD nSignerCertInfo = 0;
+ if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, nullptr, &nSignerCertInfo))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
+ return false;
+ }
+ std::unique_ptr<BYTE[]> pSignerCertInfoBuf(new BYTE[nSignerCertInfo]);
+ if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, pSignerCertInfoBuf.get(), &nSignerCertInfo))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
+ return false;
+ }
+ PCERT_INFO pSignerCertInfo = reinterpret_cast<PCERT_INFO>(pSignerCertInfoBuf.get());
+
+ // Open a certificate store in memory using CERT_STORE_PROV_MSG, which
+ // initializes it with the certificates from the message.
+ HCERTSTORE hStoreHandle = CertOpenStore(CERT_STORE_PROV_MSG,
+ PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL,
+ 0,
+ hMsg);
+ if (!hStoreHandle)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CertOpenStore() failed");
+ return false;
+ }
+
+ // Find the signer's certificate in the store.
+ PCCERT_CONTEXT pSignerCertContext = CertGetSubjectCertificateFromStore(hStoreHandle,
+ PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ pSignerCertInfo);
+ if (!pSignerCertContext)
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CertGetSubjectCertificateFromStore() failed");
+ return false;
+ }
+ else
+ {
+ // Write rInformation.ouX509Certificate.
+ uno::Sequence<sal_Int8> aDerCert(pSignerCertContext->cbCertEncoded);
+ for (size_t i = 0; i < pSignerCertContext->cbCertEncoded; ++i)
+ aDerCert[i] = pSignerCertContext->pbCertEncoded[i];
+ OUStringBuffer aBuffer;
+ comphelper::Base64::encode(aBuffer, aDerCert);
+ rInformation.ouX509Certificate = aBuffer.makeStringAndClear();
+ }
+
+ if (bNonDetached)
+ {
+ // Not a detached signature.
+ DWORD nContentParam = 0;
+ if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, nullptr, &nContentParam))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
+ return false;
+ }
+
+ std::vector<BYTE> aContentParam(nContentParam);
+ if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, aContentParam.data(), &nContentParam))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
+ return false;
+ }
+
+ if (VerifyNonDetachedSignature(rStream, aByteRanges, aContentParam))
+ rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+ }
+ else
+ {
+ // Detached, the usual case.
+ // Use the CERT_INFO from the signer certificate to verify the signature.
+ if (CryptMsgControl(hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE, pSignerCertContext->pCertInfo))
+ rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+ }
+
+ // Check if we have a signing certificate attribute.
+ DWORD nSignedAttributes = 0;
+ if (CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, nullptr, &nSignedAttributes))
+ {
+ std::unique_ptr<BYTE[]> pSignedAttributesBuf(new BYTE[nSignedAttributes]);
+ if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, pSignedAttributesBuf.get(), &nSignedAttributes))
+ {
+ SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
+ return false;
+ }
+ auto pSignedAttributes = reinterpret_cast<PCRYPT_ATTRIBUTES>(pSignedAttributesBuf.get());
+ for (size_t nAttr = 0; nAttr < pSignedAttributes->cAttr; ++nAttr)
+ {
+ CRYPT_ATTRIBUTE& rAttr = pSignedAttributes->rgAttr[nAttr];
+ /*
+ * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
+ * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+ * smime(16) id-aa(2) 47 }
+ */
+ if (OString("1.2.840.113549.1.9.16.2.47") == rAttr.pszObjId)
+ {
+ rInformation.bHasSigningCertificate = true;
+ break;
+ }
+ }
+ }
+
+ CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
+ CryptMsgClose(hMsg);
+ return true;
+#else
+ // Not implemented.
+ (void)rStream;
+ (void)aByteRanges;
+ (void)bNonDetected;
+ (void)aSignature;
+ (void)rInformation;
+static_assert(false, "WHAT!!!");
+ return false;
+#endif
+}
+
}
}