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 a5e8897ab495fd8173470f076c59629a00fb0a93 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Nov 15 09:53:46 2024 +0100 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Fri Nov 15 11:33:59 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/+/176618 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com> Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/include/svl/cryptosign.hxx b/include/svl/cryptosign.hxx index 9a245573c1e6..8c8e48f0da54 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 ee595fc4bbd6..17d9d2bd3e00 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; @@ -173,6 +174,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"); + load(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 eb39cafd5b30..03a4e25843ea 100644 --- a/sfx2/sdi/sfx.sdi +++ b/sfx2/sdi/sfx.sdi @@ -4754,7 +4754,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 23b4503180df..65103489bc97 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> @@ -631,6 +632,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 3054871baf45..79d9fb4aaea8 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) { @@ -2404,6 +2393,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 3e4ce6e7c696..41eaca7aff34 100644 --- a/vcl/source/filter/ipdf/pdfdocument.cxx +++ b/vcl/source/filter/ipdf/pdfdocument.cxx @@ -945,16 +945,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);