From a8cb7cf68ff661b502e7c006fe4330098b5b0944 Mon Sep 17 00:00:00 2001 From: Tomaž Vajngerl Date: Mon, 31 Dec 2018 12:27:39 +0100 Subject: lok: add signDocument to "Office" iface - to sign not opened docs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- desktop/qa/desktop_lib/test_desktop_lib.cxx | 63 +++++++++++++++++++++++ desktop/source/lib/init.cxx | 78 +++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) (limited to 'desktop') 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 aCertificate; + std::vector 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 #include #include +#include #include #include #include @@ -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 aCertificateSequence; + + std::string aCertificateString(reinterpret_cast(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 aPrivateKeySequence; + std::string aPrivateKeyString(reinterpret_cast(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 xSEInitializer = xml::crypto::SEInitializer::create(xContext); + uno::Reference xSecurityContext; + xSecurityContext = xSEInitializer->createSecurityContext(OUString()); + if (!xSecurityContext.is()) + return false; + + uno::Reference xSecurityEnvironment; + xSecurityEnvironment = xSecurityContext->getSecurityEnvironment(); + uno::Reference xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY); + + if (!xCertificateCreator.is()) + return false; + + uno::Reference 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) -- cgit