include/svl/cryptosign.hxx | 11 +++++++++- sfx2/CppunitTest_sfx2_view.mk | 1 sfx2/qa/cppunit/data/signature.pkcs7 | 1 sfx2/qa/cppunit/view.cxx | 36 +++++++++++++++++++++++++++++++++ sfx2/sdi/sfx.sdi | 2 - sfx2/source/doc/objserv.cxx | 27 ++++++++++++++++++++++++ svl/source/crypto/cryptosign.cxx | 18 ++++++---------- vcl/source/filter/ipdf/pdfdocument.cxx | 26 +++++++++++++++++------ 8 files changed, 102 insertions(+), 20 deletions(-)
New commits: commit 60261557e5311a7b445fb2455aa534697697a9ec Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Nov 15 09:53:46 2024 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Nov 15 12:53:47 2024 +0100 cool#9992 lok doc sign: add initial serialization of external signatures Now that the hash extract part works, the other end of this external signature support is to be able to integrate an externally generated (e.g. qualified) signature into our PDF file. The problem is that we have SignDocumentContentUsingCertificate() for non-interactive signing and we have the interactive sign dialog, but we have no way to integrate an existing PKCS#7 blob. Fix the problem by extending vcl::filter::PDFDocument::Sign(): if a signature value is provided, then integrate that, instead of calling svl::crypto::Signing::Sign() to generate a new signature. Also extend the SigningContext documentation, since now it has 3 modes (normal sign, hash extract, sign serialize). Change-Id: I113fb536b1a83b8a4869a7064bb415bca6a91ae4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176623 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/include/svl/cryptosign.hxx b/include/svl/cryptosign.hxx index dce75dad8fcf..154292b5f3ce 100644 --- a/include/svl/cryptosign.hxx +++ b/include/svl/cryptosign.hxx @@ -82,6 +82,7 @@ public: const bool bNonDetached, const std::vector<unsigned char>& aSignature, SignatureInformation& rInformation); + static void appendHex(sal_Int8 nInt, OStringBuffer& rBuffer); private: /// The certificate to use for signing. @@ -95,6 +96,12 @@ private: /// Wrapper around a certificate: allows either an actual signing or extracting enough info, so a /// 3rd-party can sign our document. +/// +/// The following states are supported: +/// - actual signing: set m_xCertificate +/// - hash extract before external signing: don't set anything, m_nSignatureTime and m_aDigest will +/// be set +/// - signature serialization after external signing: set m_nSignatureTime and m_aSignatureValue class SVL_DLLPUBLIC SigningContext { public: @@ -103,8 +110,10 @@ public: /// If m_xCertificate is not set, the time that would be used, in milliseconds since the epoch /// (1970-01-01 UTC). sal_Int64 m_nSignatureTime = 0; - /// SHA-256 digest. + /// SHA-256 digest of the to-be signed document. std::vector<unsigned char> m_aDigest; + /// PKCS#7 data, produced externally. + std::vector<unsigned char> m_aSignatureValue; }; } diff --git a/sfx2/CppunitTest_sfx2_view.mk b/sfx2/CppunitTest_sfx2_view.mk index 70ca709fe4b6..d68b0ab7952e 100644 --- a/sfx2/CppunitTest_sfx2_view.mk +++ b/sfx2/CppunitTest_sfx2_view.mk @@ -31,6 +31,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sfx2_view, \ tl \ sfx \ svl \ + vcl \ )) $(eval $(call gb_CppunitTest_use_sdk_api,sfx2_view)) diff --git a/sfx2/qa/cppunit/data/signature.pkcs7 b/sfx2/qa/cppunit/data/signature.pkcs7 new file mode 100644 index 000000000000..08f32ab26381 --- /dev/null +++ b/sfx2/qa/cppunit/data/signature.pkcs7 @@ -0,0 +1 @@ +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 diff --git a/sfx2/qa/cppunit/view.cxx b/sfx2/qa/cppunit/view.cxx index 4068270350a6..220a913b2ca6 100644 --- a/sfx2/qa/cppunit/view.cxx +++ b/sfx2/qa/cppunit/view.cxx @@ -26,6 +26,7 @@ #include <tools/json_writer.hxx> #include <rtl/ustrbuf.hxx> #include <comphelper/base64.hxx> +#include <comphelper/propertyvalue.hxx> using namespace com::sun::star; @@ -175,6 +176,41 @@ CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testLokHelperCommandValuesSignatureHash) // calls, then this failed. CPPUNIT_ASSERT_EQUAL(aHash1, aHash2); } + +CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testSignatureSerialize) +{ + // Given an unsigned PDF file: + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + createTempCopy(u"unsigned.pdf"); + loadFromURL(maTempFile.GetURL()); + + // When signing by serializing an externally provided signature based on an earlier extracted + // timestamp & document hash: + OUString aSigUrl = createFileURL(u"signature.pkcs7"); + SvFileStream aSigStream(aSigUrl, StreamMode::READ); + auto aSigValue + = OUString::fromUtf8(read_uInt8s_ToOString(aSigStream, aSigStream.remainingSize())); + uno::Sequence<beans::PropertyValue> aArgs = { + comphelper::makePropertyValue(u"SignatureTime"_ustr, u"1643201995722"_ustr), + comphelper::makePropertyValue(u"SignatureValue"_ustr, aSigValue), + }; + dispatchCommand(mxComponent, u".uno:Signature"_ustr, aArgs); + + // Then make sure the document has a signature: + SvMemoryStream aStream; + aStream.WriteStream(*maTempFile.GetStream(StreamMode::READ)); + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); + CPPUNIT_ASSERT(pPdfDocument); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // i.e. no signature was added, since we tried to sign interactively instead of based on + // provided parameters. + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getSignatureCount()); +} #endif CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sfx2/sdi/sfx.sdi b/sfx2/sdi/sfx.sdi index 47780c70d666..3e7ebf9046be 100644 --- a/sfx2/sdi/sfx.sdi +++ b/sfx2/sdi/sfx.sdi @@ -4771,7 +4771,7 @@ SfxVoidItem VersionDialog SID_VERSION ] SfxUInt16Item Signature SID_SIGNATURE -(SfxStringItem SignatureCert FN_PARAM_1, SfxStringItem SignatureKey FN_PARAM_2) +(SfxStringItem SignatureCert FN_PARAM_1, SfxStringItem SignatureKey FN_PARAM_2, SfxStringItem SignatureTime FN_PARAM_3, SfxStringItem SignatureValue FN_PARAM_4) [ AutoUpdate = FALSE, FastCall = FALSE, diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx index 1af4ddbcb902..42a27a66106d 100644 --- a/sfx2/source/doc/objserv.cxx +++ b/sfx2/source/doc/objserv.cxx @@ -109,6 +109,7 @@ #include <unotools/streamwrap.hxx> #include <comphelper/sequenceashashmap.hxx> #include <editeng/unoprnms.hxx> +#include <comphelper/base64.hxx> #include <autoredactdialog.hxx> @@ -621,6 +622,32 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq) { aSignatureKey = pSignatureKey->GetValue().toUtf8(); } + + // See if an external signature time/value is provided: if so, sign with those + // instead of interactive signing via the dialog. + svl::crypto::SigningContext aSigningContext; + const SfxStringItem* pSignatureTime = rReq.GetArg<SfxStringItem>(FN_PARAM_3); + if (pSignatureTime) + { + sal_Int64 nSignatureTime = pSignatureTime->GetValue().toInt64(); + aSigningContext.m_nSignatureTime = nSignatureTime; + } + const SfxStringItem* pSignatureValue = rReq.GetArg<SfxStringItem>(FN_PARAM_4); + if (pSignatureValue) + { + OUString aSignatureValue = pSignatureValue->GetValue(); + uno::Sequence<sal_Int8> aBytes; + comphelper::Base64::decode(aBytes, aSignatureValue); + aSigningContext.m_aSignatureValue.assign( + aBytes.getArray(), aBytes.getArray() + aBytes.getLength()); + } + if (!aSigningContext.m_aSignatureValue.empty()) + { + SignDocumentContentUsingCertificate(aSigningContext); + rReq.Done(); + return; + } + SfxViewFrame* pFrame = GetFrame(); SfxViewShell* pViewShell = pFrame ? pFrame->GetViewShell() : nullptr; if (pViewShell) diff --git a/svl/source/crypto/cryptosign.cxx b/svl/source/crypto/cryptosign.cxx index a711a304c0fb..6a5f08077765 100644 --- a/svl/source/crypto/cryptosign.cxx +++ b/svl/source/crypto/cryptosign.cxx @@ -66,17 +66,6 @@ using namespace com::sun::star; namespace { - -#if USE_CRYPTO_ANY -void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer ) -{ - static const 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 - #if USE_CRYPTO_NSS char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg) { @@ -2407,6 +2396,13 @@ bool Signing::Verify(SvStream& rStream, #endif } +void Signing::appendHex(sal_Int8 nInt, OStringBuffer& rBuffer) +{ + static const 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 ] ); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx index 62ce923b3ecf..65faa176c8a8 100644 --- a/vcl/source/filter/ipdf/pdfdocument.cxx +++ b/vcl/source/filter/ipdf/pdfdocument.cxx @@ -946,16 +946,28 @@ bool PDFDocument::Sign(svl::crypto::SigningContext& rSigningContext, const OUStr m_aEditBuffer.ReadBytes(aBuffer2.get(), nBufferSize2); OStringBuffer aCMSHexBuffer; - svl::crypto::Signing aSigning(rSigningContext); - aSigning.AddDataRange(aBuffer1.get(), nBufferSize1); - aSigning.AddDataRange(aBuffer2.get(), nBufferSize2); - if (!aSigning.Sign(aCMSHexBuffer)) + if (rSigningContext.m_aSignatureValue.empty()) { - if (rSigningContext.m_xCertificate.is()) + svl::crypto::Signing aSigning(rSigningContext); + aSigning.AddDataRange(aBuffer1.get(), nBufferSize1); + aSigning.AddDataRange(aBuffer2.get(), nBufferSize2); + if (!aSigning.Sign(aCMSHexBuffer)) { - SAL_WARN("vcl.filter", "PDFDocument::Sign: PDFWriter::Sign() failed"); + if (rSigningContext.m_xCertificate.is()) + { + SAL_WARN("vcl.filter", "PDFDocument::Sign: PDFWriter::Sign() failed"); + } + return false; + } + } + else + { + // The signature value provided by the context: use that instead of building a new + // signature. + for (unsigned char ch : rSigningContext.m_aSignatureValue) + { + svl::crypto::Signing::appendHex(ch, aCMSHexBuffer); } - return false; } assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);