diff options
-rw-r--r-- | xmlsecurity/Library_xmlsecurity.mk | 1 | ||||
-rw-r--r-- | xmlsecurity/source/pdfio/pdfdocument.cxx | 206 |
2 files changed, 204 insertions, 3 deletions
diff --git a/xmlsecurity/Library_xmlsecurity.mk b/xmlsecurity/Library_xmlsecurity.mk index c5e8d68d9483..e211c61d3e10 100644 --- a/xmlsecurity/Library_xmlsecurity.mk +++ b/xmlsecurity/Library_xmlsecurity.mk @@ -78,6 +78,7 @@ $(eval $(call gb_Library_add_defs,xmlsecurity,\ )) $(eval $(call gb_Library_use_externals,xmlsecurity,\ nss3 \ + plc4 \ )) endif # BUILD_TYPE=DESKTOP endif diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx index 95660839ec28..3b01578ec777 100644 --- a/xmlsecurity/source/pdfio/pdfdocument.cxx +++ b/xmlsecurity/source/pdfio/pdfdocument.cxx @@ -232,7 +232,102 @@ PDFDocument::PDFDocument() { } -bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& /*xCertificate*/) +#ifdef XMLSEC_CRYPTO_NSS +static NSSCMSMessage* CreateCMSMessage(PRTime nTime, + NSSCMSSignedData** ppCMSSignedData, + NSSCMSSignerInfo** ppCMSSigner, + CERTCertificate* pCertificate, + SECItem* pDigest) +{ + NSSCMSMessage* pResult = NSS_CMSMessage_Create(nullptr); + if (!pResult) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSMessage_Create() failed"); + return nullptr; + } + + *ppCMSSignedData = NSS_CMSSignedData_Create(pResult); + if (!*ppCMSSignedData) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_Create() failed"); + return nullptr; + } + + NSSCMSContentInfo* pCMSContentInfo = NSS_CMSMessage_GetContentInfo(pResult); + if (NSS_CMSContentInfo_SetContent_SignedData(pResult, pCMSContentInfo, *ppCMSSignedData) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSContentInfo_SetContent_SignedData() failed"); + return nullptr; + } + + pCMSContentInfo = NSS_CMSSignedData_GetContentInfo(*ppCMSSignedData); + + // No detached data. + if (NSS_CMSContentInfo_SetContent_Data(pResult, pCMSContentInfo, nullptr, PR_TRUE) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSContentInfo_SetContent_Data() failed"); + return nullptr; + } + + *ppCMSSigner = NSS_CMSSignerInfo_Create(pResult, pCertificate, SEC_OID_SHA1); + if (!*ppCMSSigner) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignerInfo_Create() failed"); + return nullptr; + } + + if (NSS_CMSSignerInfo_AddSigningTime(*ppCMSSigner, nTime) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignerInfo_AddSigningTime() failed"); + return nullptr; + } + + if (NSS_CMSSignerInfo_IncludeCerts(*ppCMSSigner, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignerInfo_IncludeCerts() failed"); + return nullptr; + } + + if (NSS_CMSSignedData_AddCertificate(*ppCMSSignedData, pCertificate) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_AddCertificate() failed"); + return nullptr; + } + + if (NSS_CMSSignedData_AddSignerInfo(*ppCMSSignedData, *ppCMSSigner) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_AddSignerInfo() failed"); + return nullptr; + } + + if (NSS_CMSSignedData_SetDigestValue(*ppCMSSignedData, SEC_OID_SHA1, pDigest) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_SetDigestValue() failed"); + return nullptr; + } + + return pResult; +} + +static char* PasswordCallback(PK11SlotInfo* /*pSlot*/, PRBool /*bRetry*/, void* pArg) +{ + return PL_strdup(static_cast<char*>(pArg)); +} + +static void AppendHex(sal_Int8 nInt, OStringBuffer& rBuffer) +{ + static const sal_Char pHexDigits[] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + rBuffer.append(pHexDigits[(nInt >> 4) & 15]); + rBuffer.append(pHexDigits[nInt & 15]); +} + +#endif + +bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificate) { m_aEditBuffer.WriteCharPtr("\n"); @@ -357,7 +452,7 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& /*xCertific // Write the xref table. sal_uInt64 nXRefOffset = m_aEditBuffer.Tell(); - m_aEditBuffer.WriteCharPtr("\nxref\n0 "); + m_aEditBuffer.WriteCharPtr("xref\n0 "); m_aEditBuffer.WriteUInt32AsString(m_aXRef.size()); m_aEditBuffer.WriteCharPtr("\n"); for (size_t nObject = 0; nObject < m_aXRef.size(); ++nObject) @@ -423,7 +518,109 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& /*xCertific aByteRangeBuffer.append(" ]"); m_aEditBuffer.WriteOString(aByteRangeBuffer.toString()); + // Create the PKCS#7 object. + css::uno::Sequence<sal_Int8> aDerEncoded = xCertificate->getEncoded(); + if (!aDerEncoded.hasElements()) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: empty certificate"); + return false; + } + + sal_Int8* pDerEncoded = aDerEncoded.getArray(); + sal_Int32 nDerEncoded = aDerEncoded.getLength(); + +#ifdef XMLSEC_CRYPTO_NSS + CERTCertificate* pCertificate = CERT_DecodeCertFromPackage(reinterpret_cast<char*>(pDerEncoded), nDerEncoded); + if (!pCertificate) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: CERT_DecodeCertFromPackage() failed"); + return false; + } + + HASHContext* pHASHContext = HASH_Create(HASH_AlgSHA1); + if (!pHASHContext) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: HASH_Create() failed"); + return false; + } + + HASH_Begin(pHASHContext); + + m_aEditBuffer.Seek(0); + sal_uInt64 nBufferSize = nSignatureContentOffset - 1; + std::unique_ptr<char[]> aBuffer(new char[nBufferSize]); + m_aEditBuffer.ReadBytes(aBuffer.get(), nBufferSize); + HASH_Update(pHASHContext, reinterpret_cast<const unsigned char*>(aBuffer.get()), nBufferSize); + + m_aEditBuffer.Seek(nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1); + nBufferSize = nLastByteRangeLength; + aBuffer.reset(new char[nBufferSize]); + m_aEditBuffer.ReadBytes(aBuffer.get(), nBufferSize); + HASH_Update(pHASHContext, reinterpret_cast<const unsigned char*>(aBuffer.get()), nBufferSize); + + SECItem aDigestItem; + unsigned char aDigest[SHA1_LENGTH]; + aDigestItem.data = aDigest; + HASH_End(pHASHContext, aDigestItem.data, &aDigestItem.len, SHA1_LENGTH); + HASH_Destroy(pHASHContext); + + PRTime nNow = PR_Now(); + NSSCMSSignedData* pCMSSignedData; + NSSCMSSignerInfo* pCMSSignerInfo; + NSSCMSMessage* pCMSMessage = CreateCMSMessage(nNow, &pCMSSignedData, &pCMSSignerInfo, pCertificate, &aDigestItem); + if (!pCMSMessage) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: CreateCMSMessage() failed"); + return false; + } + + char* pPass = strdup(""); + SECItem aCMSOutputItem; + aCMSOutputItem.data = nullptr; + aCMSOutputItem.len = 0; + PLArenaPool* pAreanaPool = PORT_NewArena(10000); + NSSCMSEncoderContext* pCMSEncoderContext; + + pCMSEncoderContext = NSS_CMSEncoder_Start(pCMSMessage, nullptr, nullptr, &aCMSOutputItem, pAreanaPool, PasswordCallback, pPass, nullptr, nullptr, nullptr, nullptr); + + if (!pCMSEncoderContext) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: NSS_CMSEncoder_Start() failed"); + return false; + } + + if (NSS_CMSEncoder_Finish(pCMSEncoderContext) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: NSS_CMSEncoder_Finish() failed"); + return false; + } + + free(pPass); + + if (aCMSOutputItem.len * 2 > MAX_SIGNATURE_CONTENT_LENGTH) + { + SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: not enough space to write the signature"); + return false; + } + + OStringBuffer aCMSHexBuffer; + for (unsigned int i = 0; i < aCMSOutputItem.len; ++i) + AppendHex(aCMSOutputItem.data[i], aCMSHexBuffer); + assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH); + + m_aEditBuffer.Seek(nSignatureContentOffset); + m_aEditBuffer.WriteOString(aCMSHexBuffer.toString()); + + NSS_CMSMessage_Destroy(pCMSMessage); + return true; +#endif + + // Not implemented. + (void)pDerEncoded; + (void)nDerEncoded; + + return false; } bool PDFDocument::Write(SvStream& rStream) @@ -1667,8 +1864,11 @@ double PDFReferenceElement::LookupNumber(SvStream& rStream) const PDFObjectElement* PDFReferenceElement::LookupObject() const { const std::vector< std::unique_ptr<PDFElement> >& rElements = m_rDoc.GetElements(); - for (const auto& rElement : rElements) + // Iterate in reverse order, so in case an incremental update adds a newer + // version, we find it. + for (int i = rElements.size() - 1; i >= 0; --i) { + const std::unique_ptr<PDFElement>& rElement = rElements[i]; auto* pObjectElement = dynamic_cast<PDFObjectElement*>(rElement.get()); if (!pObjectElement) continue; |