desktop/source/lib/init.cxx | 11 ++++ include/sfx2/digitalsignatures.hxx | 13 +++-- include/sfx2/docfile.hxx | 3 - include/sfx2/lokhelper.hxx | 4 + include/sfx2/objsh.hxx | 3 - include/svl/cryptosign.hxx | 11 ++++ sfx2/source/doc/docfile.cxx | 9 ++-- sfx2/source/doc/objserv.cxx | 13 ++++- sfx2/source/view/lokhelper.cxx | 27 ++++++++++++ vcl/qa/cppunit/filter/ipdf/ipdf.cxx | 5 +- xmlsecurity/qa/unit/signing/signing.cxx | 5 +- xmlsecurity/source/component/documentdigitalsignatures.cxx | 29 ++++++++----- 12 files changed, 104 insertions(+), 29 deletions(-)
New commits: commit a5053d635f46859a8edceddf828caefb40368375 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Nov 8 08:21:18 2024 +0100 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Fri Nov 8 12:31:37 2024 +0100 cool#9992 lok doc sign, hash extract: initial getCommandValues('Signature') The trouble with signing via ca/cert/key PEM files is that usually the CA is not trusted by the received of the signature. 3rd-party services are available to do generate trusted signatures, but then you need to share your document with them, which can be also problematic. A middle-ground here is to sign the hash of the document by a 3rd-party, something that's supported by e.g. <https://docs.eideasy.com/electronic-signatures/api-flow-with-file-hashes-pdf.html> (which itself aggregates a number of providers). As a first step, add LOK API to get what would be the signature time during signing -- but instead of actually signing, just return this information. Once the same is done with the doc hash, this is supposed to provide the same info than what the reference <https://github.com/eideasy/eideasy-external-pades-digital-signatures> app does. This is only a start: incrementally replace XCertificate with SignatureContext, which allows aborting the signing right before calling into NSS, and also later it'll allow injecting the PKCS#7 object we get from the 3rd-party. Change-Id: I108564f047fdb4fb796240c7d18a584cd9044313 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176255 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/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 028f25402358..a58da03dab27 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -78,6 +78,7 @@ #include <rtl/strbuf.hxx> #include <rtl/uri.hxx> #include <svl/zforlist.hxx> +#include <svl/cryptosign.hxx> #include <linguistic/misc.hxx> #include <cppuhelper/bootstrap.hxx> #include <comphelper/random.hxx> @@ -6873,6 +6874,12 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo pDoc->getCommandValues(aJsonWriter, aCommand); return convertOString(aJsonWriter.finishAndGetAsOString()); } + else if (SfxLokHelper::supportsCommand(INetURLObject(OUString::fromUtf8(aCommand)).GetURLPath())) + { + tools::JsonWriter aJsonWriter; + SfxLokHelper::getCommandValues(aJsonWriter, aCommand); + return convertOString(aJsonWriter.finishAndGetAsOString()); + } else { SetLastExceptionMsg(OUString::fromUtf8(aCommand) + u" : Unknown command, no values returned"_ustr); @@ -7309,7 +7316,9 @@ static bool doc_insertCertificate(LibreOfficeKitDocument* pThis, SolarMutexGuard aGuard; - return pObjectShell->SignDocumentContentUsingCertificate(xCertificate); + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = xCertificate; + return pObjectShell->SignDocumentContentUsingCertificate(aSigningContext); } static bool doc_addCertificate(LibreOfficeKitDocument* pThis, diff --git a/include/sfx2/digitalsignatures.hxx b/include/sfx2/digitalsignatures.hxx index 84b77fd759dd..fe5f2bc97874 100644 --- a/include/sfx2/digitalsignatures.hxx +++ b/include/sfx2/digitalsignatures.hxx @@ -19,6 +19,10 @@ #include <sal/types.h> class SfxViewShell; +namespace svl::crypto +{ +class SigningContext; +} namespace sfx2 { @@ -27,11 +31,10 @@ class SAL_NO_VTABLE SAL_DLLPUBLIC_RTTI SAL_LOPLUGIN_ANNOTATE("crosscast") Digita { public: /// Same as signDocumentWithCertificate(), but passes the xModel as well. - virtual bool - SignModelWithCertificate(const css::uno::Reference<css::frame::XModel>& xModel, - const css::uno::Reference<css::security::XCertificate>& xCertificate, - const css::uno::Reference<css::embed::XStorage>& xStorage, - const css::uno::Reference<css::io::XStream>& xStream) + virtual bool SignModelWithCertificate(const css::uno::Reference<css::frame::XModel>& xModel, + svl::crypto::SigningContext& rSigningContext, + const css::uno::Reference<css::embed::XStorage>& xStorage, + const css::uno::Reference<css::io::XStream>& xStream) = 0; /// Async replacement for signDocumentContent(). diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx index 2eb59b654147..272d97572961 100644 --- a/include/sfx2/docfile.hxx +++ b/include/sfx2/docfile.hxx @@ -47,6 +47,7 @@ namespace com::sun::star::frame class XModel; } namespace ucbhelper { class Content; } +namespace svl::crypto { class SigningContext; } class SvKeyValueIterator; class SfxFilter; @@ -287,7 +288,7 @@ public: SAL_DLLPRIVATE bool SignDocumentContentUsingCertificate( const css::uno::Reference<css::frame::XModel>& xModel, bool bHasValidDocumentSignature, - const css::uno::Reference<css::security::XCertificate>& xCertificate); + svl::crypto::SigningContext& rSigningContext); // the following two methods must be used and make sense only during saving currently // TODO/LATER: in future the signature state should be controlled by the medium not by the document diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx index dc4c5efe28e1..526ad1a2aa53 100644 --- a/include/sfx2/lokhelper.hxx +++ b/include/sfx2/lokhelper.hxx @@ -249,6 +249,10 @@ public: static void addCertificates(const std::vector<std::string>& rCerts); /// Parses a private key + certificate pair. static css::uno::Reference<css::security::XCertificate> getSigningCertificate(const std::string& rCert, const std::string& rKey); + /// Decides if it's OK to call getCommandValues(rCommand). + static bool supportsCommand(std::u16string_view rCommand); + /// Returns information about a given command in JSON format. + static void getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand); private: static int createView(SfxViewFrame& rViewFrame, ViewShellDocId docId); diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx index 0b9a456b0b0c..a9db2f444e9f 100644 --- a/include/sfx2/objsh.hxx +++ b/include/sfx2/objsh.hxx @@ -145,6 +145,7 @@ namespace o3tl } namespace weld { class Window; } +namespace svl::crypto { class SigningContext; } enum class HiddenWarningFact { @@ -366,7 +367,7 @@ public: const css::uno::Reference<css::security::XDocumentDigitalSignatures>& xSigner = css::uno::Reference<css::security::XDocumentDigitalSignatures>()); - bool SignDocumentContentUsingCertificate(const css::uno::Reference<css::security::XCertificate>& xCertificate); + bool SignDocumentContentUsingCertificate(svl::crypto::SigningContext& rSigningContext); bool ResignDocument(css::uno::Sequence< css::security::DocumentSignatureInformation >& rSignaturesInfo); void SignSignatureLine(weld::Window* pDialogParent, const OUString& aSignatureLineId, diff --git a/include/svl/cryptosign.hxx b/include/svl/cryptosign.hxx index 3bb682916edc..a558690bbf48 100644 --- a/include/svl/cryptosign.hxx +++ b/include/svl/cryptosign.hxx @@ -92,6 +92,17 @@ private: OUString m_aSignPassword; }; +/// Wrapper around a certificate: allows either an actual signing or extracting enough info, so a +/// 3rd-party can sign our document. +class SVL_DLLPUBLIC SigningContext +{ +public: + /// If set, the certificate used for signing. + css::uno::Reference<css::security::XCertificate> m_xCertificate; + /// If m_xCertificate is not set, the time that would be used. + sal_Int64 m_nSignatureTime = 0; +}; + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 32a7721f7d6e..bbeff77e3b70 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -133,6 +133,7 @@ #include <sfx2/viewfrm.hxx> #include <comphelper/threadpool.hxx> #include <o3tl/string_view.hxx> +#include <svl/cryptosign.hxx> #include <condition_variable> #include <com/sun/star/io/WrongFormatException.hpp> @@ -4173,7 +4174,7 @@ void SfxMedium::CreateTempFileNoCopy() bool SfxMedium::SignDocumentContentUsingCertificate( const css::uno::Reference<css::frame::XModel>& xModel, bool bHasValidDocumentSignature, - const Reference<XCertificate>& xCertificate) + svl::crypto::SigningContext& rSigningContext) { bool bChanges = false; @@ -4241,7 +4242,7 @@ bool SfxMedium::SignDocumentContentUsingCertificate( xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW); bool bSuccess = xModelSigner->SignModelWithCertificate( - xModel, xCertificate, GetZipStorageToSign_Impl(), xStream); + xModel, rSigningContext, GetZipStorageToSign_Impl(), xStream); if (bSuccess) { @@ -4262,7 +4263,7 @@ bool SfxMedium::SignDocumentContentUsingCertificate( // We need read-write to be able to add the signature relation. bool bSuccess = xModelSigner->SignModelWithCertificate( - xModel, xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream); + xModel, rSigningContext, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream); if (bSuccess) { @@ -4280,7 +4281,7 @@ bool SfxMedium::SignDocumentContentUsingCertificate( std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE)); uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream)); if (xModelSigner->SignModelWithCertificate( - xModel, xCertificate, uno::Reference<embed::XStorage>(), xStream)) + xModel, rSigningContext, uno::Reference<embed::XStorage>(), xStream)) bChanges = true; } } diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx index 35efc6dcf60c..23b4503180df 100644 --- a/sfx2/source/doc/objserv.cxx +++ b/sfx2/source/doc/objserv.cxx @@ -65,6 +65,7 @@ #include <comphelper/lok.hxx> #include <LibreOfficeKit/LibreOfficeKitEnums.h> #include <tools/link.hxx> +#include <svl/cryptosign.hxx> #include <sfx2/signaturestate.hxx> #include <sfx2/sfxresid.hxx> @@ -589,7 +590,9 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq) if (xCertificate.is()) { - bHaveWeSigned |= SignDocumentContentUsingCertificate(xCertificate); + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = xCertificate; + bHaveWeSigned |= SignDocumentContentUsingCertificate(aSigningContext); // Reload to show how the PDF actually looks like after signing. This also // changes "finish signing" on the infobar back to "sign document" as a side @@ -2198,14 +2201,16 @@ bool SfxObjectShell::ResignDocument(uno::Sequence< security::DocumentSignatureIn auto xCert = rInfo.Signer; if (xCert.is()) { - bSignSuccess &= SignDocumentContentUsingCertificate(xCert); + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = xCert; + bSignSuccess &= SignDocumentContentUsingCertificate(aSigningContext); } } return bSignSuccess; } -bool SfxObjectShell::SignDocumentContentUsingCertificate(const Reference<XCertificate>& xCertificate) +bool SfxObjectShell::SignDocumentContentUsingCertificate(svl::crypto::SigningContext& rSigningContext) { // 1. PrepareForSigning @@ -2275,7 +2280,7 @@ bool SfxObjectShell::SignDocumentContentUsingCertificate(const Reference<XCertif // 3. Sign bool bSignSuccess = GetMedium()->SignDocumentContentUsingCertificate( - GetBaseModel(), HasValidSignatures(), xCertificate); + GetBaseModel(), HasValidSignatures(), rSigningContext); // 4. AfterSigning AfterSigning(bSignSuccess, false); diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx index 45d2ffe800ac..399a2e386b2b 100644 --- a/sfx2/source/view/lokhelper.cxx +++ b/sfx2/source/view/lokhelper.cxx @@ -41,6 +41,7 @@ #include <comphelper/scopeguard.hxx> #include <comphelper/base64.hxx> #include <tools/json_writer.hxx> +#include <svl/cryptosign.hxx> #include <boost/property_tree/json_parser.hpp> @@ -990,6 +991,32 @@ void SfxLokHelper::addCertificates(const std::vector<std::string>& rCerts) pObjectShell->RecheckSignature(false); } +bool SfxLokHelper::supportsCommand(std::u16string_view rCommand) +{ + static const std::initializer_list<std::u16string_view> vSupport = { u"Signature" }; + + return std::find(vSupport.begin(), vSupport.end(), rCommand) != vSupport.end(); +} + +void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand) +{ + static constexpr OStringLiteral aSignature(".uno:Signature"); + if (!o3tl::starts_with(rCommand, aSignature)) + { + return; + } + + SfxObjectShell* pObjectShell = SfxObjectShell::Current(); + if (!pObjectShell) + { + return; + } + + svl::crypto::SigningContext aSigningContext; + pObjectShell->SignDocumentContentUsingCertificate(aSigningContext); + rJsonWriter.put("signatureTime", aSigningContext.m_nSignatureTime); +} + void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType) { if (DisableCallbacks::disabled()) diff --git a/vcl/qa/cppunit/filter/ipdf/ipdf.cxx b/vcl/qa/cppunit/filter/ipdf/ipdf.cxx index ad2354b00eb8..14ac21c27c8b 100644 --- a/vcl/qa/cppunit/filter/ipdf/ipdf.cxx +++ b/vcl/qa/cppunit/filter/ipdf/ipdf.cxx @@ -23,6 +23,7 @@ #include <sfx2/objsh.hxx> #include <vcl/filter/PDFiumLibrary.hxx> #include <vcl/filter/pdfdocument.hxx> +#include <svl/cryptosign.hxx> using namespace ::com::sun::star; @@ -107,7 +108,9 @@ CPPUNIT_TEST_FIXTURE(VclFilterIpdfTest, testPDFAddVisibleSignatureLastPage) pObjectShell->SetModified(false); // When: do the actual signing. - pObjectShell->SignDocumentContentUsingCertificate(xCert); + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = xCert; + pObjectShell->SignDocumentContentUsingCertificate(aSigningContext); // Then: count the # of shapes on the signature widget/annotation. std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); diff --git a/xmlsecurity/qa/unit/signing/signing.cxx b/xmlsecurity/qa/unit/signing/signing.cxx index a6cdef19b039..cf33c8de030b 100644 --- a/xmlsecurity/qa/unit/signing/signing.cxx +++ b/xmlsecurity/qa/unit/signing/signing.cxx @@ -58,6 +58,7 @@ #include <comphelper/propertyvalue.hxx> #include <vcl/filter/PDFiumLibrary.hxx> #include <vcl/scheduler.hxx> +#include <svl/cryptosign.hxx> using namespace com::sun::star; @@ -764,7 +765,9 @@ CPPUNIT_TEST_FIXTURE(SigningTest, testPDFAddVisibleSignature) pObjectShell->SetModified(false); // When: do the actual signing. - pObjectShell->SignDocumentContentUsingCertificate(aCertificates[0]); + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = aCertificates[0]; + pObjectShell->SignDocumentContentUsingCertificate(aSigningContext); // Then: count the # of shapes on the signature widget/annotation. std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx index 5b5d29d37b8b..394cefe682ce 100644 --- a/xmlsecurity/source/component/documentdigitalsignatures.cxx +++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx @@ -60,6 +60,7 @@ #include <com/sun/star/security/XDocumentDigitalSignatures.hpp> #include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp> #include <sfx2/digitalsignatures.hxx> +#include <svl/cryptosign.hxx> #include <map> @@ -111,7 +112,7 @@ private: bool signWithCertificateImpl(const uno::Reference<frame::XModel>& /*xModel*/, - css::uno::Reference<css::security::XCertificate> const& xCertificate, + svl::crypto::SigningContext& rSigningContext, css::uno::Reference<css::embed::XStorage> const& xStorage, css::uno::Reference<css::io::XStream> const& xStream, DocumentSignatureMode eMode); @@ -217,7 +218,7 @@ public: /// See sfx2::DigitalSignatures::SignModelWithCertificate(). bool SignModelWithCertificate(const css::uno::Reference<css::frame::XModel>& xModel, - const css::uno::Reference<css::security::XCertificate>& xCertificate, + svl::crypto::SigningContext& rSigningContext, const css::uno::Reference<css::embed::XStorage>& xStorage, const css::uno::Reference<css::io::XStream>& xStream) override; /// See sfx2::DigitalSignatures::SignDocumentContentAsync(). @@ -829,17 +830,19 @@ sal_Bool DocumentDigitalSignatures::signDocumentWithCertificate( css::uno::Reference<css::io::XStream> const & xStream) { uno::Reference<frame::XModel> xModel; - return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream, + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = xCertificate; + return signWithCertificateImpl(xModel, aSigningContext, xStorage, xStream, DocumentSignatureMode::Content); } bool DocumentDigitalSignatures::SignModelWithCertificate( const uno::Reference<frame::XModel>& xModel, - const css::uno::Reference<css::security::XCertificate>& xCertificate, + svl::crypto::SigningContext& rSigningContext, const css::uno::Reference<css::embed::XStorage>& xStorage, const css::uno::Reference<css::io::XStream>& xStream) { - return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream, + return signWithCertificateImpl(xModel, rSigningContext, xStorage, xStream, DocumentSignatureMode::Content); } @@ -873,7 +876,9 @@ sal_Bool DocumentDigitalSignatures::signPackageWithCertificate( css::uno::Reference<css::io::XStream> const& xStream) { uno::Reference<frame::XModel> xModel; - return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream, + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = xCertificate; + return signWithCertificateImpl(xModel, aSigningContext, xStorage, xStream, DocumentSignatureMode::Package); } @@ -883,13 +888,15 @@ sal_Bool DocumentDigitalSignatures::signScriptingContentWithCertificate( css::uno::Reference<css::io::XStream> const& xStream) { uno::Reference<frame::XModel> xModel; - return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream, + svl::crypto::SigningContext aSigningContext; + aSigningContext.m_xCertificate = xCertificate; + return signWithCertificateImpl(xModel, aSigningContext, xStorage, xStream, DocumentSignatureMode::Macros); } bool DocumentDigitalSignatures::signWithCertificateImpl( const uno::Reference<frame::XModel>& xModel, - css::uno::Reference<css::security::XCertificate> const& xCertificate, + svl::crypto::SigningContext& rSigningContext, css::uno::Reference<css::embed::XStorage> const& xStorage, css::uno::Reference<css::io::XStream> const& xStream, DocumentSignatureMode eMode) { @@ -907,8 +914,8 @@ bool DocumentDigitalSignatures::signWithCertificateImpl( aSignatureManager.setModel(xModel); Reference<XXMLSecurityContext> xSecurityContext; - Reference<XServiceInfo> xServiceInfo(xCertificate, UNO_QUERY); - if (xServiceInfo->getImplementationName() + Reference<XServiceInfo> xServiceInfo(rSigningContext.m_xCertificate, UNO_QUERY); + if (xServiceInfo.is() && xServiceInfo->getImplementationName() == "com.sun.star.xml.security.gpg.XCertificate_GpgImpl") xSecurityContext = aSignatureManager.getGpgSecurityContext(); else @@ -916,7 +923,7 @@ bool DocumentDigitalSignatures::signWithCertificateImpl( sal_Int32 nSecurityId; - bool bSuccess = aSignatureManager.add(xCertificate, xSecurityContext, "", nSecurityId, true); + bool bSuccess = aSignatureManager.add(rSigningContext.m_xCertificate, xSecurityContext, "", nSecurityId, true); if (!bSuccess) return false;