[Libreoffice-commits] core.git: desktop/qa desktop/source include/LibreOfficeKit include/sfx2 sfx2/Library_sfx.mk sfx2/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Tue Jan 1 08:53:41 UTC 2019


 desktop/qa/desktop_lib/test_desktop_lib.cxx |   63 ++++++++++++++
 desktop/source/lib/init.cxx                 |   78 +++++++++++++++++
 include/LibreOfficeKit/LibreOfficeKit.h     |   10 ++
 include/LibreOfficeKit/LibreOfficeKit.hxx   |   12 ++
 include/sfx2/DocumentSigner.hxx             |   41 +++++++++
 sfx2/Library_sfx.mk                         |    1 
 sfx2/source/doc/DocumentSigner.cxx          |  125 ++++++++++++++++++++++++++++
 7 files changed, 330 insertions(+)

New commits:
commit a8cb7cf68ff661b502e7c006fe4330098b5b0944
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Mon Dec 31 12:27:39 2018 +0100
Commit:     Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Tue Jan 1 09:53:20 2019 +0100

    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 at gmail.com>

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 740e2d4fdecf..55d8c6761420 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -126,6 +126,7 @@ public:
     void testInsertCertificate_DER_ODT();
     void testInsertCertificate_PEM_ODT();
     void testInsertCertificate_PEM_DOCX();
+    void testSignDocument_PEM_PDF();
     void testABI();
 
     CPPUNIT_TEST_SUITE(DesktopLOKTest);
@@ -174,6 +175,7 @@ public:
     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();
 
@@ -2485,6 +2487,66 @@ void DesktopLOKTest::testInsertCertificate_PEM_DOCX()
     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 classOffset(int i)
@@ -2513,6 +2575,7 @@ void DesktopLOKTest::testABI()
     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 0537d3808d61..0ec67bc6618f 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -94,6 +94,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>
@@ -1423,6 +1424,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)
@@ -1445,6 +1453,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;
     }
@@ -1672,6 +1681,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 4dd23a2cbc7a..2df1cea6dd31 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 5d7771cf80b0..31e95a09c0ec 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -791,6 +791,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 97f6f9706e76..18cf9396794d 100644
--- a/sfx2/Library_sfx.mk
+++ b/sfx2/Library_sfx.mk
@@ -194,6 +194,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: */


More information about the Libreoffice-commits mailing list