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:
47
filter/CppunitTest_filter_pdf.mk
Normal file
47
filter/CppunitTest_filter_pdf.mk
Normal 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:
|
@@ -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
108
filter/qa/pdf.cxx
Normal 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: */
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
Reference in New Issue
Block a user