summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2018-12-31 12:27:39 +0100
committerMichael Meeks <michael.meeks@collabora.com>2019-08-05 21:06:36 -0400
commitc2321de7ae73ad40bb98bb8293323e5fb77a7638 (patch)
treee271abe66f4acd418be4566fbf83ae6aff7cc796
parentAdd SolarMutexGuard where needed (diff)
downloadcore-c2321de7ae73ad40bb98bb8293323e5fb77a7638.tar.gz
core-c2321de7ae73ad40bb98bb8293323e5fb77a7638.zip
lok: add signDocument to "Office" iface - to sign not opened docs.
LOKit function "signDocument" can sign document without the need to open them. This is useful to sign PDF documents after they are created from a currently opened (ODF, DOCX) document. This adds DocumentDigner to sfx2, which could also be used in desktop LibreOffice without opening them and in further tests. Change-Id: Id6f242e817f594aa672f94f9c6f9b34e4035d46a Reviewed-on: https://gerrit.libreoffice.org/65767 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r--desktop/qa/desktop_lib/test_desktop_lib.cxx83
-rw-r--r--desktop/source/lib/init.cxx78
-rw-r--r--include/LibreOfficeKit/LibreOfficeKit.h10
-rw-r--r--include/LibreOfficeKit/LibreOfficeKit.hxx12
-rw-r--r--include/sfx2/DocumentSigner.hxx41
-rw-r--r--sfx2/Library_sfx.mk1
-rw-r--r--sfx2/source/doc/DocumentSigner.cxx125
7 files changed, 348 insertions, 2 deletions
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 2ec64b6cc37f..581d673d2d1d 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -120,7 +120,10 @@ public:
void testExtractParameter();
void testGetSignatureState_NonSigned();
void testGetSignatureState_Signed();
- void testInsertCertificate();
+ void testInsertCertificate_DER_ODT();
+ void testInsertCertificate_PEM_ODT();
+ void testInsertCertificate_PEM_DOCX();
+ void testSignDocument_PEM_PDF();
void testABI();
CPPUNIT_TEST_SUITE(DesktopLOKTest);
@@ -166,7 +169,10 @@ public:
CPPUNIT_TEST(testExtractParameter);
CPPUNIT_TEST(testGetSignatureState_Signed);
CPPUNIT_TEST(testGetSignatureState_NonSigned);
- CPPUNIT_TEST(testInsertCertificate);
+ CPPUNIT_TEST(testInsertCertificate_DER_ODT);
+ CPPUNIT_TEST(testInsertCertificate_PEM_ODT);
+ CPPUNIT_TEST(testInsertCertificate_PEM_DOCX);
+ CPPUNIT_TEST(testSignDocument_PEM_PDF);
CPPUNIT_TEST(testABI);
CPPUNIT_TEST_SUITE_END();
@@ -2390,6 +2396,66 @@ void DesktopLOKTest::testInsertCertificate()
comphelper::LibreOfficeKit::setActive(false);
}
+void DesktopLOKTest::testSignDocument_PEM_PDF()
+{
+ comphelper::LibreOfficeKit::setActive();
+
+ // Load the document, save it into a temp file and load that file again
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(mxComponent.is());
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ Scheduler::ProcessEventsToIdle();
+
+ std::vector<unsigned char> aCertificate;
+ std::vector<unsigned char> aPrivateKey;
+
+ {
+ readFileIntoByteVector("test-cert-chain-1.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-2.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-3.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ CPPUNIT_ASSERT(pDocument->pClass->saveAs(pDocument, aTempFile.GetURL().toUtf8().getStr(), "pdf", nullptr));
+
+ closeDoc();
+
+ Scheduler::ProcessEventsToIdle();
+
+ readFileIntoByteVector("test-cert-signing.pem", aCertificate);
+ readFileIntoByteVector("test-PK-signing.pem", aPrivateKey);
+
+ LibLibreOffice_Impl aOffice;
+ bool bResult = aOffice.m_pOfficeClass->signDocument(&aOffice, aTempFile.GetURL().toUtf8().getStr(),
+ aCertificate.data(), int(aCertificate.size()),
+ aPrivateKey.data(), int(aPrivateKey.size()));
+
+ CPPUNIT_ASSERT(bResult);
+
+ comphelper::LibreOfficeKit::setActive(false);
+}
+
namespace {
constexpr size_t documentClassOffset(int i)
@@ -2402,6 +2468,19 @@ constexpr size_t documentClassOffset(int i)
void DesktopLOKTest::testABI()
{
// STABLE ABI, NEVER CHANGE (unless there's a very good reason, agreed by ESC, etc.)
+ CPPUNIT_ASSERT_EQUAL(classOffset(0), offsetof(struct _LibreOfficeKitClass, destroy));
+ CPPUNIT_ASSERT_EQUAL(classOffset(1), offsetof(struct _LibreOfficeKitClass, documentLoad));
+ CPPUNIT_ASSERT_EQUAL(classOffset(2), offsetof(struct _LibreOfficeKitClass, getError));
+ CPPUNIT_ASSERT_EQUAL(classOffset(3), offsetof(struct _LibreOfficeKitClass, documentLoadWithOptions));
+ CPPUNIT_ASSERT_EQUAL(classOffset(4), offsetof(struct _LibreOfficeKitClass, freeError));
+ CPPUNIT_ASSERT_EQUAL(classOffset(5), offsetof(struct _LibreOfficeKitClass, registerCallback));
+ CPPUNIT_ASSERT_EQUAL(classOffset(6), offsetof(struct _LibreOfficeKitClass, getFilterTypes));
+ CPPUNIT_ASSERT_EQUAL(classOffset(7), offsetof(struct _LibreOfficeKitClass, setOptionalFeatures));
+ CPPUNIT_ASSERT_EQUAL(classOffset(8), offsetof(struct _LibreOfficeKitClass, setDocumentPassword));
+ CPPUNIT_ASSERT_EQUAL(classOffset(9), offsetof(struct _LibreOfficeKitClass, getVersionInfo));
+ CPPUNIT_ASSERT_EQUAL(classOffset(10), offsetof(struct _LibreOfficeKitClass, runMacro));
+ CPPUNIT_ASSERT_EQUAL(classOffset(11), offsetof(struct _LibreOfficeKitClass, signDocument));
+
CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass, destroy));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass, saveAs));
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index f6462057cd9d..aa8dfe85c88f 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -95,6 +95,7 @@
#include <sfx2/msgpool.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/lokhelper.hxx>
+#include <sfx2/DocumentSigner.hxx>
#include <svx/dialmgr.hxx>
#include <svx/dialogs.hrc>
#include <svx/strings.hrc>
@@ -1445,6 +1446,13 @@ static void lo_setDocumentPassword(LibreOfficeKit* pThis,
static char* lo_getVersionInfo(LibreOfficeKit* pThis);
static int lo_runMacro (LibreOfficeKit* pThis, const char* pURL);
+static bool lo_signDocument(LibreOfficeKit* pThis,
+ const char* pUrl,
+ const unsigned char* pCertificateBinary,
+ const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary,
+ const int nPrivateKeyBinarySize);
+
LibLibreOffice_Impl::LibLibreOffice_Impl()
: m_pOfficeClass( gOfficeClass.lock() )
, maThread(nullptr)
@@ -1467,6 +1475,7 @@ LibLibreOffice_Impl::LibLibreOffice_Impl()
m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword;
m_pOfficeClass->getVersionInfo = lo_getVersionInfo;
m_pOfficeClass->runMacro = lo_runMacro;
+ m_pOfficeClass->signDocument = lo_signDocument;
gOfficeClass = m_pOfficeClass;
}
@@ -1699,6 +1708,75 @@ static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL)
return false;
}
+static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
+ const char* pURL,
+ const unsigned char* pCertificateBinary,
+ const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary,
+ const int nPrivateKeyBinarySize)
+{
+ OUString aURL(getAbsoluteURL(pURL));
+ if (aURL.isEmpty())
+ return false;
+
+ if (!xContext.is())
+ return false;
+
+ uno::Sequence<sal_Int8> aCertificateSequence;
+
+ std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
+ std::string aCertificateBase64String = extractCertificate(aCertificateString);
+ if (!aCertificateBase64String.empty())
+ {
+ OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
+ comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
+ }
+ else
+ {
+ aCertificateSequence.realloc(nCertificateBinarySize);
+ std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
+ }
+
+ uno::Sequence<sal_Int8> aPrivateKeySequence;
+ std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize);
+ std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
+ if (!aPrivateKeyBase64String.empty())
+ {
+ OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
+ comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
+ }
+ else
+ {
+ aPrivateKeySequence.realloc(nPrivateKeyBinarySize);
+ std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.begin());
+ }
+
+ uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
+ uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext;
+ xSecurityContext = xSEInitializer->createSecurityContext(OUString());
+ if (!xSecurityContext.is())
+ return false;
+
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment;
+ xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
+ uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
+
+ if (!xCertificateCreator.is())
+ return false;
+
+ uno::Reference<security::XCertificate> xCertificate;
+ xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
+
+ if (!xCertificate.is())
+ return false;
+
+ sfx2::DocumentSigner aDocumentSigner(aURL);
+ if (!aDocumentSigner.signDocument(xCertificate))
+ return false;
+
+ return true;
+}
+
static void lo_registerCallback (LibreOfficeKit* pThis,
LibreOfficeKitCallback pCallback,
void* pData)
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h
index 952f023cd26c..7a63fa2030e3 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -94,6 +94,16 @@ struct _LibreOfficeKitClass
@since LibreOffice 6.0
*/
int (*runMacro) (LibreOfficeKit *pThis, const char* pURL);
+
+ /** @see lok::Office::signDocument().
+ @since LibreOffice 6.2
+ */
+ bool (*signDocument) (LibreOfficeKit* pThis,
+ const char* pUrl,
+ const unsigned char* pCertificateBinary,
+ const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary,
+ const int nPrivateKeyBinarySize);
};
#define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize)
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 863f1ac48ba0..4f9ef60d0a3f 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -826,6 +826,18 @@ public:
{
return mpThis->pClass->runMacro( mpThis, pURL );
}
+
+ /**
+ * Exports the document and signes its content.
+ */
+ bool signDocument(const char* pURL,
+ const unsigned char* pCertificateBinary, const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary, const int nPrivateKeyBinarySize)
+ {
+ return mpThis->pClass->signDocument(mpThis, pURL,
+ pCertificateBinary, nCertificateBinarySize,
+ pPrivateKeyBinary, nPrivateKeyBinarySize);
+ }
};
/// Factory method to create a lok::Office instance.
diff --git a/include/sfx2/DocumentSigner.hxx b/include/sfx2/DocumentSigner.hxx
new file mode 100644
index 000000000000..1f4326ef3976
--- /dev/null
+++ b/include/sfx2/DocumentSigner.hxx
@@ -0,0 +1,41 @@
+/* -*- 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/.
+ *
+ */
+
+#ifndef INCLUDED_SFX2_DOCUMENTSIGNER_HXX
+#define INCLUDED_SFX2_DOCUMENTSIGNER_HXX
+
+#include <sal/config.h>
+#include <sfx2/dllapi.h>
+
+#include <memory>
+
+#include <com/sun/star/security/XCertificate.hpp>
+
+namespace sfx2
+{
+class SFX2_DLLPUBLIC DocumentSigner
+{
+private:
+ OUString m_aUrl;
+
+public:
+ DocumentSigner(OUString const& rUrl)
+ : m_aUrl(rUrl)
+ {
+ }
+
+ bool signDocument(css::uno::Reference<css::security::XCertificate> const& rxCertificate);
+};
+
+} // namespace sfx2
+
+#endif // INCLUDED_SFX2_DOCUMENTSIGNER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/Library_sfx.mk b/sfx2/Library_sfx.mk
index 50a1d4bfd778..e7f57369b048 100644
--- a/sfx2/Library_sfx.mk
+++ b/sfx2/Library_sfx.mk
@@ -195,6 +195,7 @@ $(eval $(call gb_Library_add_exception_objects,sfx,\
sfx2/source/dialog/tplpitem \
sfx2/source/dialog/versdlg \
sfx2/source/doc/DocumentMetadataAccess \
+ sfx2/source/doc/DocumentSigner \
sfx2/source/doc/Metadatable \
sfx2/source/doc/QuerySaveDocument \
sfx2/source/doc/SfxDocumentMetaData \
diff --git a/sfx2/source/doc/DocumentSigner.cxx b/sfx2/source/doc/DocumentSigner.cxx
new file mode 100644
index 000000000000..7e29b02b97b4
--- /dev/null
+++ b/sfx2/source/doc/DocumentSigner.cxx
@@ -0,0 +1,125 @@
+/* -*- 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 <sfx2/DocumentSigner.hxx>
+
+#include <tools/stream.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/security/DocumentSignatureInformation.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+
+using namespace css;
+
+namespace sfx2
+{
+bool DocumentSigner::signDocument(uno::Reference<security::XCertificate> const& rxCertificate)
+{
+ std::unique_ptr<SvStream> pStream(
+ utl::UcbStreamHelper::CreateStream(m_aUrl, StreamMode::READ | StreamMode::WRITE));
+ uno::Reference<io::XStream> xInputStream(new utl::OStreamWrapper(std::move(pStream)));
+
+ bool bHasValidDocumentSignature = true;
+
+ bool bResult = false;
+ uno::Reference<embed::XStorage> xWriteableZipStore;
+ try
+ {
+ xWriteableZipStore = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, xInputStream);
+ }
+ catch (const io::IOException&)
+ {
+ }
+
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(xWriteableZipStore));
+
+ uno::Reference<security::XDocumentDigitalSignatures> xSigner(
+ security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
+ comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature));
+
+ try
+ {
+ uno::Reference<embed::XStorage> xMetaInf;
+ uno::Reference<container::XNameAccess> xNameAccess(xWriteableZipStore, uno::UNO_QUERY);
+ if (xNameAccess.is() && xNameAccess->hasByName("META-INF"))
+ {
+ xMetaInf = xWriteableZipStore->openStorageElement("META-INF",
+ embed::ElementModes::READWRITE);
+ if (!xMetaInf.is())
+ throw uno::RuntimeException();
+ }
+ if (xMetaInf.is())
+ {
+ uno::Reference<embed::XStorage> xStorage;
+ xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, xInputStream);
+
+ // ODF.
+ uno::Reference<io::XStream> xStream;
+ xStream.set(
+ xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(),
+ embed::ElementModes::READWRITE),
+ uno::UNO_SET_THROW);
+ bool bSuccess = xSigner->signDocumentWithCertificate(rxCertificate, xStorage, xStream);
+ if (bSuccess)
+ {
+ uno::Reference<embed::XTransactedObject> xTransact(xMetaInf, uno::UNO_QUERY_THROW);
+ xTransact->commit();
+ xTransact.set(xWriteableZipStore, uno::UNO_QUERY_THROW);
+ xTransact->commit();
+ bResult = true;
+ }
+ }
+ else if (xWriteableZipStore.is())
+ {
+ uno::Reference<embed::XStorage> xStorage;
+ xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, xInputStream);
+
+ // OOXML.
+ uno::Reference<io::XStream> xStream;
+
+ // We need read-write to be able to add the signature relation.
+ bool bSuccess = xSigner->signDocumentWithCertificate(rxCertificate, xStorage, xStream);
+
+ if (bSuccess)
+ {
+ uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStore,
+ uno::UNO_QUERY_THROW);
+ xTransact->commit();
+ bResult = true;
+ }
+ }
+ else
+ {
+ // Something not ZIP based: e.g. PDF.
+ bResult = xSigner->signDocumentWithCertificate(
+ rxCertificate, uno::Reference<embed::XStorage>(), xInputStream);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return bResult;
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */