summaryrefslogtreecommitdiff
path: root/filter
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2022-02-02 20:46:03 +0100
committerMiklos Vajna <vmiklos@collabora.com>2022-02-03 21:06:20 +0100
commit80723fccbb03c215bab84b10ac1eefaedef66b7c (patch)
treec14643c9345b81c0d4465c4e475797d95b485bbd /filter
parent41acfefe1ee1ba28ae38ad0cd3d9760e1e4d1765 (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.mk47
-rw-r--r--filter/Module_filter.mk1
-rw-r--r--filter/qa/pdf.cxx108
-rw-r--r--filter/source/pdf/pdfexport.cxx28
-rw-r--r--filter/source/pdf/pdfexport.hxx1
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: