include/svl/cryptosign.hxx | 15 svl/Library_svl.mk | 19 svl/source/crypto/cryptosign.cxx | 637 +++++++++++++++++++++++ xmlsecurity/inc/certificatechooser.hxx | 2 xmlsecurity/inc/documentsignaturehelper.hxx | 2 xmlsecurity/inc/documentsignaturemanager.hxx | 2 xmlsecurity/inc/pch/precompiled_xmlsecurity.hxx | 1 xmlsecurity/inc/pdfsignaturehelper.hxx | 2 xmlsecurity/inc/xmlsignaturehelper.hxx | 2 xmlsecurity/inc/xsecctl.hxx | 2 xmlsecurity/source/helper/ooxmlsecexporter.hxx | 2 xmlsecurity/source/pdfio/pdfdocument.cxx | 641 ------------------------ xmlsecurity/workben/pdfverify.cxx | 2 13 files changed, 680 insertions(+), 649 deletions(-)
New commits: commit 4f17445c12dc26c4881c4e486215b58d26515f8d Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> Date: Sun Jul 9 09:42:01 2017 -0400 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 <c...@libreoffice.org> Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> diff --git a/include/svl/cryptosign.hxx b/include/svl/cryptosign.hxx index db0abc9f1480..ae82a59b33a4 100644 --- a/include/svl/cryptosign.hxx +++ b/include/svl/cryptosign.hxx @@ -14,8 +14,12 @@ #include <vector> #include <rtl/strbuf.hxx> +#include <rtl/ustring.hxx> + +#include <com/sun/star/uno/Reference.hxx> + #include <svl/svldllapi.h> -#include "com/sun/star/uno/Reference.hxx" +#include <svl/sigstruct.hxx> namespace com { namespace sun { @@ -24,6 +28,8 @@ namespace security { class XCertificate; } }}} +class SvStream; + namespace svl { namespace crypto { @@ -54,6 +60,13 @@ public: /// Returns the signature (in PKCS#7 format) as string (hex). bool Sign(OStringBuffer& rCMSHexBuffer); + /// Verify and get Signature Information given a signature and stream. + static bool Verify(SvStream& rStream, + const std::vector<std::pair<size_t, size_t>>& aByteRanges, + const bool bNonDetached, + const std::vector<unsigned char>& aSignature, + SignatureInformation& rInformation); + private: /// The certificate to use for signing. const css::uno::Reference<css::security::XCertificate> m_xCertificate; diff --git a/xmlsecurity/inc/sigstruct.hxx b/include/svl/sigstruct.hxx similarity index 100% rename from xmlsecurity/inc/sigstruct.hxx rename to include/svl/sigstruct.hxx 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 +} + } } diff --git a/xmlsecurity/inc/certificatechooser.hxx b/xmlsecurity/inc/certificatechooser.hxx index 264b740dd448..009d20e4ea2f 100644 --- a/xmlsecurity/inc/certificatechooser.hxx +++ b/xmlsecurity/inc/certificatechooser.hxx @@ -28,7 +28,7 @@ #include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp> #include <com/sun/star/uno/XComponentContext.hpp> #include <com/sun/star/uno/Sequence.hxx> -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> namespace com { diff --git a/xmlsecurity/inc/documentsignaturehelper.hxx b/xmlsecurity/inc/documentsignaturehelper.hxx index a8f3cb1f5d40..3997ae47260d 100644 --- a/xmlsecurity/inc/documentsignaturehelper.hxx +++ b/xmlsecurity/inc/documentsignaturehelper.hxx @@ -23,7 +23,7 @@ #include <com/sun/star/uno/Reference.h> #include <com/sun/star/xml/sax/XDocumentHandler.hpp> #include <rtl/ustring.hxx> -#include "sigstruct.hxx" +#include <svl/sigstruct.hxx> #include "xmlsecurity/xmlsecuritydllapi.h" #include <vector> diff --git a/xmlsecurity/inc/documentsignaturemanager.hxx b/xmlsecurity/inc/documentsignaturemanager.hxx index f354ad8c8cbb..0ea708c5d241 100644 --- a/xmlsecurity/inc/documentsignaturemanager.hxx +++ b/xmlsecurity/inc/documentsignaturemanager.hxx @@ -24,7 +24,7 @@ #include <memory> -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> #include <xmlsignaturehelper.hxx> #include <pdfsignaturehelper.hxx> #include <com/sun/star/uno/XComponentContext.hpp> diff --git a/xmlsecurity/inc/pch/precompiled_xmlsecurity.hxx b/xmlsecurity/inc/pch/precompiled_xmlsecurity.hxx index cfb26d8593ae..07008a0b5181 100644 --- a/xmlsecurity/inc/pch/precompiled_xmlsecurity.hxx +++ b/xmlsecurity/inc/pch/precompiled_xmlsecurity.hxx @@ -302,6 +302,5 @@ #include <unotools/readwritemutexguard.hxx> #include <unotools/syslocale.hxx> #include <unotools/unotoolsdllapi.h> -#include <sigstruct.hxx> /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlsecurity/inc/pdfsignaturehelper.hxx b/xmlsecurity/inc/pdfsignaturehelper.hxx index 74b89fe44e02..7da417c46e4f 100644 --- a/xmlsecurity/inc/pdfsignaturehelper.hxx +++ b/xmlsecurity/inc/pdfsignaturehelper.hxx @@ -19,7 +19,7 @@ #include <com/sun/star/xml/crypto/XSEInitializer.hpp> #include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp> -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> /// Handles signatures of a PDF file. class XMLSECURITY_DLLPUBLIC PDFSignatureHelper diff --git a/xmlsecurity/inc/xmlsignaturehelper.hxx b/xmlsecurity/inc/xmlsignaturehelper.hxx index 209c93c15a09..d527942f50f2 100644 --- a/xmlsecurity/inc/xmlsignaturehelper.hxx +++ b/xmlsecurity/inc/xmlsignaturehelper.hxx @@ -25,7 +25,7 @@ #include <tools/link.hxx> #include <rtl/ustring.hxx> #include <rtl/ref.hxx> -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> #include <xsecctl.hxx> #include <xmlsecurity/xmlsecuritydllapi.h> diff --git a/xmlsecurity/inc/xsecctl.hxx b/xmlsecurity/inc/xsecctl.hxx index 9ddc22affbae..27cb60e54dc0 100644 --- a/xmlsecurity/inc/xsecctl.hxx +++ b/xmlsecurity/inc/xsecctl.hxx @@ -20,7 +20,7 @@ #ifndef INCLUDED_XMLSECURITY_SOURCE_HELPER_XSECCTL_HXX #define INCLUDED_XMLSECURITY_SOURCE_HELPER_XSECCTL_HXX -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> #include <com/sun/star/uno/XComponentContext.hpp> #include <com/sun/star/xml/sax/XParser.hpp> diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.hxx b/xmlsecurity/source/helper/ooxmlsecexporter.hxx index 1a96430bba9a..12c7c197047c 100644 --- a/xmlsecurity/source/helper/ooxmlsecexporter.hxx +++ b/xmlsecurity/source/helper/ooxmlsecexporter.hxx @@ -15,7 +15,7 @@ #include <com/sun/star/uno/XComponentContext.hpp> #include <com/sun/star/embed/XStorage.hpp> #include <com/sun/star/xml/sax/XDocumentHandler.hpp> -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> /// Writes a single OOXML digital signature. class OOXMLSecExporter diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx index 6925b41a1960..d0d56f4bd409 100644 --- a/xmlsecurity/source/pdfio/pdfdocument.cxx +++ b/xmlsecurity/source/pdfio/pdfdocument.cxx @@ -32,7 +32,8 @@ #include <xmloff/xmluconv.hxx> #include <o3tl/make_unique.hxx> -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> +#include <svl/cryptosign.hxx> #ifdef XMLSEC_CRYPTO_NSS #include <cert.h> @@ -56,232 +57,6 @@ namespace xmlsecurity namespace pdfio { -namespace -{ -#ifdef XMLSEC_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 XMLSEC_CRYPTO_MSCRYPTO -/// Verifies a non-detached signature using CryptoAPI. -bool VerifyNonDetachedSignature(SvStream& rStream, 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 ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature, SignatureInformation& rInformation, bool bLast) { vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V"); @@ -306,7 +81,7 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat } auto pSubFilter = dynamic_cast<vcl::filter::PDFNameElement*>(pValue->Lookup("SubFilter")); - bool bNonDetached = pSubFilter && pSubFilter->GetValue() == "adbe.pkcs7.sha1"; + const bool bNonDetached = pSubFilter && pSubFilter->GetValue() == "adbe.pkcs7.sha1"; if (!pSubFilter || (pSubFilter->GetValue() != "adbe.pkcs7.detached" && !bNonDetached && pSubFilter->GetValue() != "ETSI.CAdES.detached")) { if (!pSubFilter) @@ -414,415 +189,7 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat return false; } -#ifdef XMLSEC_CRYPTO_NSS - // Validate the signature. No need to call NSS_Init() here, assume that the - // caller did that already. - - SECItem aSignatureItem; - aSignatureItem.data = 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; - sax::Converter::encodeBase64(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 UNIX timestamp to an ISO8601 string. - OUStringBuffer aBuffer; - uno::Reference<uno::XComponentContext> xComponentContext = comphelper::getProcessComponentContext(); - CalendarWrapper aCalendarWrapper(xComponentContext); - // nSigningTime is in microseconds. - SvXMLUnitConverter::convertDateTime(aBuffer, static_cast<double>(nSigningTime) / 1000000 / tools::Time::secondPerDay, aCalendarWrapper.getEpochStart().GetUNODate()); - - // Then convert this string to a local UNO DateTime. - util::DateTime aUNODateTime; - try - { - utl::ISO8601parseDateTime(aBuffer.toString(), aUNODateTime); - } - catch (const std::length_error&) - { - SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: failed to parse signature date string"); - return false; - } - DateTime aDateTime(aUNODateTime); - 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 XMLSEC_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; - sax::Converter::encodeBase64(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)rInformation; - - return false; -#endif + return svl::crypto::Signing::Verify(rStream, aByteRanges, bNonDetached, aSignature, rInformation); } } // namespace pdfio diff --git a/xmlsecurity/workben/pdfverify.cxx b/xmlsecurity/workben/pdfverify.cxx index 8a14f7bf3402..673eb0171050 100644 --- a/xmlsecurity/workben/pdfverify.cxx +++ b/xmlsecurity/workben/pdfverify.cxx @@ -23,7 +23,7 @@ #include <xmlsecurity/pdfio/pdfdocument.hxx> -#include <sigstruct.hxx> +#include <svl/sigstruct.hxx> using namespace com::sun::star; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits