filter/CppunitTest_filter_pdf.mk |   47 ++++++++++++++++
 filter/Module_filter.mk          |    1 
 filter/qa/pdf.cxx                |  108 +++++++++++++++++++++++++++++++++++++++
 filter/source/pdf/pdfexport.cxx  |   28 ++++++++++
 filter/source/pdf/pdfexport.hxx  |    1 
 5 files changed, 185 insertions(+)

New commits:
commit 80723fccbb03c215bab84b10ac1eefaedef66b7c
Author:     Miklos Vajna <[email protected]>
AuthorDate: Wed Feb 2 20:46:03 2022 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Feb 3 21:06:20 2022 +0100

    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 <[email protected]>
    Tested-by: Jenkins

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:

Reply via email to