diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2022-02-02 20:46:03 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-02-03 21:06:20 +0100 |
commit | 80723fccbb03c215bab84b10ac1eefaedef66b7c (patch) | |
tree | c14643c9345b81c0d4465c4e475797d95b485bbd /filter | |
parent | 41acfefe1ee1ba28ae38ad0cd3d9760e1e4d1765 (diff) |
filter: allow PDF export to sign from the cmdline
Sample soffice invocation:
soffice --convert-to 'pdf:draw_pdf_Export:{"SignPDF":{"type":"boolean","value":"true"},"SignCertificateSubjectName":{"type":"string","value":"CN=..."}}' test.odg
You can copy the subject name from the PDF export dialog. This works
only in case the signing certificate already appears in the certificate
list.
Change-Id: I8670f9a410c6e80497a4d6223d1438938bc949e8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129387
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
Diffstat (limited to 'filter')
-rw-r--r-- | filter/CppunitTest_filter_pdf.mk | 47 | ||||
-rw-r--r-- | filter/Module_filter.mk | 1 | ||||
-rw-r--r-- | filter/qa/pdf.cxx | 108 | ||||
-rw-r--r-- | filter/source/pdf/pdfexport.cxx | 28 | ||||
-rw-r--r-- | filter/source/pdf/pdfexport.hxx | 1 |
5 files changed, 185 insertions, 0 deletions
diff --git a/filter/CppunitTest_filter_pdf.mk b/filter/CppunitTest_filter_pdf.mk new file mode 100644 index 000000000000..b85bbf45437c --- /dev/null +++ b/filter/CppunitTest_filter_pdf.mk @@ -0,0 +1,47 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,filter_pdf)) + +$(eval $(call gb_CppunitTest_use_externals,filter_pdf,\ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,filter_pdf, \ + filter/qa/pdf \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,filter_pdf, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + test \ + tl \ + unotest \ + utl \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,filter_pdf)) + +$(eval $(call gb_CppunitTest_use_ure,filter_pdf)) +$(eval $(call gb_CppunitTest_use_vcl,filter_pdf)) + +$(eval $(call gb_CppunitTest_use_rdb,filter_pdf,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,filter_pdf,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,filter_pdf)) + +# vim: set noet sw=4 ts=4: diff --git a/filter/Module_filter.mk b/filter/Module_filter.mk index 42a82bacf5c0..c28c72705cec 100644 --- a/filter/Module_filter.mk +++ b/filter/Module_filter.mk @@ -50,6 +50,7 @@ $(eval $(call gb_Module_add_check_targets,filter,\ CppunitTest_filter_priority \ CppunitTest_filter_msfilter \ CppunitTest_filter_textfilterdetect \ + CppunitTest_filter_pdf \ )) ifneq ($(DISABLE_CVE_TESTS),TRUE) diff --git a/filter/qa/pdf.cxx b/filter/qa/pdf.cxx new file mode 100644 index 000000000000..2354baef89ca --- /dev/null +++ b/filter/qa/pdf.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> + +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/xml/crypto/SEInitializer.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <tools/stream.hxx> +#include <unotools/streamwrap.hxx> +#include <vcl/filter/PDFiumLibrary.hxx> + +using namespace ::com::sun::star; + +namespace +{ +/// Covers filter/source/pdf/ fixes. +class Test : public test::BootstrapFixture, public unotest::MacrosTest +{ +private: + uno::Reference<lang::XComponent> mxComponent; + +public: + void setUp() override; + void tearDown() override; + uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } +}; + +void Test::setUp() +{ + test::BootstrapFixture::setUp(); + MacrosTest::setUpNssGpg(m_directories, "filter_pdf"); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void Test::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} + +CPPUNIT_TEST_FIXTURE(Test, testSignCertificateSubjectName) +{ + uno::Reference<xml::crypto::XSEInitializer> xSEInitializer + = xml::crypto::SEInitializer::create(mxComponentContext); + uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext + = xSEInitializer->createSecurityContext(OUString()); + uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment + = xSecurityContext->getSecurityEnvironment(); + if (!xSecurityEnvironment->getPersonalCertificates().hasElements()) + { + return; + } + + // Given an empty document: + getComponent().set( + loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When exporting to PDF, and referring to a certficicate using a subject name: + uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory(); + uno::Reference<document::XFilter> xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(getComponent()); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + + uno::Sequence<beans::PropertyValue> aFilterData{ + comphelper::makePropertyValue("SignPDF", true), + comphelper::makePropertyValue( + "SignCertificateSubjectName", + OUString( + "CN=Xmlsecurity RSA Test example Alice,O=Xmlsecurity RSA Test,ST=England,C=UK")), + }; + uno::Sequence<beans::PropertyValue> aDescriptor{ + comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xOutputStream), + }; + xFilter->filter(aDescriptor); + + // Then make sure the resulting PDF has a signature: + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPDFium->openDocument(aStream.GetData(), aStream.GetSize()); + // Without the accompanying fix in place, this test would have failed, as signing was enabled + // without configuring a certificate, so the whole export failed. + CPPUNIT_ASSERT(pPdfDocument); + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getSignatureCount()); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx index 604326b4bdbf..d6f451f8392d 100644 --- a/filter/source/pdf/pdfexport.cxx +++ b/filter/source/pdf/pdfexport.cxx @@ -60,6 +60,7 @@ #include <com/sun/star/drawing/XShapes.hpp> #include <com/sun/star/security/XCertificate.hpp> #include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/xml/crypto/SEInitializer.hpp> #include <memory> @@ -398,6 +399,25 @@ static OUString getMimetypeForDocument( const Reference< XComponentContext >& xC return aDocMimetype; } +uno::Reference<security::XCertificate> +PDFExport::GetCertificateFromSubjectName(const std::u16string_view& rSubjectName) const +{ + uno::Reference<xml::crypto::XSEInitializer> xSEInitializer + = xml::crypto::SEInitializer::create(mxContext); + uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext + = xSEInitializer->createSecurityContext(OUString()); + uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment + = xSecurityContext->getSecurityEnvironment(); + for (const auto& xCertificate : xSecurityEnvironment->getPersonalCertificates()) + { + if (xCertificate->getSubjectName() == rSubjectName) + { + return xCertificate; + } + } + + return {}; +} bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& rFilterData ) { @@ -465,6 +485,7 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& utl::ConfigManager::getProductVersion(); aContext.DocumentInfo.Creator = aCreator; + OUString aSignCertificateSubjectName; for ( const beans::PropertyValue& rProp : rFilterData ) { if ( rProp.Name == "PageRange" ) @@ -584,6 +605,8 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& rProp.Value >>= msSignPassword; else if ( rProp.Name == "SignatureCertificate" ) rProp.Value >>= maSignCertificate; + else if (rProp.Name == "SignCertificateSubjectName") + rProp.Value >>= aSignCertificateSubjectName; else if ( rProp.Name == "SignatureTSA" ) rProp.Value >>= msSignTSA; else if ( rProp.Name == "ExportPlaceholders" ) @@ -595,6 +618,11 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& rProp.Value >>= mbIsRedactMode; } + if (!maSignCertificate.is() && !aSignCertificateSubjectName.isEmpty()) + { + maSignCertificate = GetCertificateFromSubjectName(aSignCertificateSubjectName); + } + aContext.URL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri); // set the correct version, depending on user request diff --git a/filter/source/pdf/pdfexport.hxx b/filter/source/pdf/pdfexport.hxx index 967668e7421b..031547beeb45 100644 --- a/filter/source/pdf/pdfexport.hxx +++ b/filter/source/pdf/pdfexport.hxx @@ -120,6 +120,7 @@ private: void ImplWriteWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize ); void ImplWriteTiledWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize ); + css::uno::Reference<css::security::XCertificate> GetCertificateFromSubjectName(const std::u16string_view& rSubjectName) const; public: |