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
This commit is contained in:
Miklos Vajna
2022-02-02 20:46:03 +01:00
parent 41acfefe1e
commit 80723fccbb
5 changed files with 185 additions and 0 deletions

View File

@@ -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:

View File

@@ -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)

108
filter/qa/pdf.cxx Normal file
View File

@@ -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: */

View File

@@ -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

View File

@@ -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: