desktop/source/lib/init.cxx    |   35 +++++++++++++++++++-
 include/sfx2/lokhelper.hxx     |    9 +++++
 sfx2/qa/cppunit/view.cxx       |   17 ++++++++++
 sfx2/source/view/lokhelper.cxx |   69 ++++++++++++++++++++++++++++++++++++++---
 4 files changed, 123 insertions(+), 7 deletions(-)

New commits:
commit 281c8fbfa7b04193c1abc6c380d7105ccda9de1f
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Sep 23 13:58:25 2024 +0200
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Sep 24 12:27:31 2024 +0200

    cool#9992 lok doc sign: handle .uno:SignatureCert/Key/Ca view options
    
    The desktop way to sign documents is to manually import a .p12 file into
    your Firefox user profile, and then the signing key is available in all
    views. The LOK case wants per-view signing certificates, set in a way
    similar to the name of the user.
    
    Start implementing this by:
    
    1) Extending initializeForRendering() to have JSON entries for the
       signing cert/key/ca chain.
    
    2) Importing the CA chain as trusted certificates, using a new
       SfxLokHelper::extractCertificates() + test for this.
    
    3) Marking a certificate as trusted is tricky, extract
       SfxLokHelper::addCertificate() from the existing doc_addCertificate()
       to do this.
    
    4) Parsing the signing certificate, but just warn if that fails, still
       need to connect that to the SfxViewShell later.
    
    (cherry picked from commit 90beea9a9a9ab1a5d4a154704acabadfc83870c9)
    
    Change-Id: I00e40b3cdd68dbe8994f28861dc7b0f578189643
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173851
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 13efa144ba57..b9542563f059 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -4732,8 +4732,37 @@ static void 
doc_initializeForRendering(LibreOfficeKitDocument* pThis,
     if (pDoc)
     {
         doc_iniUnoCommands();
-        pDoc->initializeForTiledRendering(
-                
comphelper::containerToSequence(jsonToPropertyValuesVector(pArguments)));
+        std::vector<beans::PropertyValue> aArgs = 
jsonToPropertyValuesVector(pArguments);
+        std::string aSignatureCert;
+        std::string aSignatureKey;
+        for (const auto& rArg : aArgs)
+        {
+            if (rArg.Name == ".uno:SignatureCert" && 
rArg.Value.has<OUString>())
+            {
+                aSignatureCert = rArg.Value.get<OUString>().toUtf8();
+            }
+            else if (rArg.Name == ".uno:SignatureKey" && 
rArg.Value.has<OUString>())
+            {
+                aSignatureKey = rArg.Value.get<OUString>().toUtf8();
+            }
+            else if (rArg.Name == ".uno:SignatureCa" && 
rArg.Value.has<OUString>())
+            {
+                std::string aSignatureCa;
+                aSignatureCa = rArg.Value.get<OUString>().toUtf8();
+                std::vector<std::string> aCerts = 
SfxLokHelper::extractCertificates(aSignatureCa);
+                SfxLokHelper::addCertificates(aCerts);
+            }
+        }
+        if (!aSignatureCert.empty() && !aSignatureKey.empty())
+        {
+            uno::Reference<security::XCertificate> xCertificate = 
SfxLokHelper::getSigningCertificate(aSignatureCert, aSignatureKey);
+            if (!xCertificate.is())
+            {
+                SAL_WARN("lok", "doc_initializeForRendering: cert/key didn't 
result in an XCertificate");
+            }
+        }
+
+        
pDoc->initializeForTiledRendering(comphelper::containerToSequence(aArgs));
     }
 }
 
@@ -7311,7 +7340,7 @@ static bool doc_addCertificate(LibreOfficeKitDocument* 
pThis,
         std::copy(pCertificateBinary, pCertificateBinary + 
nCertificateBinarySize, aCertificateSequence.getArray());
     }
 
-    uno::Reference<security::XCertificate> xCertificate = 
xCertificateCreator->addDERCertificateToTheDatabase(aCertificateSequence, 
u"TCu,Cu,Tu"_ustr);
+    uno::Reference<security::XCertificate> xCertificate = 
SfxLokHelper::addCertificate(xCertificateCreator, aCertificateSequence);
 
     if (!xCertificate.is())
         return false;
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index d6c514cd4f33..dc4c5efe28e1 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -12,6 +12,7 @@
 
 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
 #include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
 
 #include <vcl/IDialogRenderable.hxx>
 #include <vcl/ITiledRenderable.hxx>
@@ -238,7 +239,15 @@ public:
 
     static void notifyLog(const std::ostringstream& stream);
 
+    /// Extracts base64 data inside begin/end markers.
     static std::string extractCertificate(const std::string& rCert);
+    /// Extracts multiple certificates in base64 from inside begin/end markers.
+    static std::vector<std::string> extractCertificates(const std::string& 
rCerts);
+    /// Takes a single CA certificate to add them to the list of trusted 
certificates.
+    static css::uno::Reference<css::security::XCertificate> 
addCertificate(const 
css::uno::Reference<css::xml::crypto::XCertificateCreator>& 
xCertificateCreator, const css::uno::Sequence<sal_Int8>& rCert);
+    /// Takes a CA chain to add them to the list of trusted certificates.
+    static void addCertificates(const std::vector<std::string>& rCerts);
+    /// Parses a private key + certificate pair.
     static css::uno::Reference<css::security::XCertificate> 
getSigningCertificate(const std::string& rCert, const std::string& rKey);
 
 private:
diff --git a/sfx2/qa/cppunit/view.cxx b/sfx2/qa/cppunit/view.cxx
index 725cb5d02cea..f79b9fde4089 100644
--- a/sfx2/qa/cppunit/view.cxx
+++ b/sfx2/qa/cppunit/view.cxx
@@ -19,6 +19,7 @@
 #include <svl/intitem.hxx>
 #include <sfx2/request.hxx>
 #include <sfx2/bindings.hxx>
+#include <sfx2/lokhelper.hxx>
 
 using namespace com::sun::star;
 
@@ -59,6 +60,22 @@ CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testReloadPage)
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), nPage);
 }
 
+CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testLokHelperExtractCertificates)
+{
+    std::string signatureCa = R"(-----BEGIN CERTIFICATE-----
+foo
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+bar
+-----END CERTIFICATE-----)";
+
+    std::vector<std::string> aRet = 
SfxLokHelper::extractCertificates(signatureCa);
+
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aRet.size());
+    CPPUNIT_ASSERT_EQUAL(std::string("
foo
"), aRet[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("
bar
"), aRet[1]);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index eb1a42bfbad9..b93d7e39cce9 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -826,14 +826,16 @@ void SfxLokHelper::notifyLog(const std::ostringstream& 
stream)
     }
 }
 
-std::string SfxLokHelper::extractCertificate(const std::string & certificate)
+namespace
+{
+std::string extractCertificateWithOffset(const std::string& certificate, 
size_t& rOffset)
 {
     static constexpr std::string_view header("-----BEGIN CERTIFICATE-----");
     static constexpr std::string_view footer("-----END CERTIFICATE-----");
 
     std::string result;
 
-    size_t pos1 = certificate.find(header);
+    size_t pos1 = certificate.find(header, rOffset);
     if (pos1 == std::string::npos)
         return result;
 
@@ -842,9 +844,34 @@ std::string SfxLokHelper::extractCertificate(const 
std::string & certificate)
         return result;
 
     pos1 = pos1 + header.length();
-    pos2 = pos2 - pos1;
+    size_t len = pos2 - pos1;
 
-    return certificate.substr(pos1, pos2);
+    rOffset = pos2;
+    return certificate.substr(pos1, len);
+}
+}
+
+std::string SfxLokHelper::extractCertificate(const std::string & certificate)
+{
+    size_t nOffset = 0;
+    return extractCertificateWithOffset(certificate, nOffset);
+}
+
+std::vector<std::string> SfxLokHelper::extractCertificates(const std::string& 
rCerts)
+{
+    std::vector<std::string> aRet;
+    size_t nOffset = 0;
+    while (true)
+    {
+        std::string aNext = extractCertificateWithOffset(rCerts, nOffset);
+        if (aNext.empty())
+        {
+            break;
+        }
+
+        aRet.push_back(aNext);
+    }
+    return aRet;
 }
 
 namespace
@@ -920,6 +947,40 @@ css::uno::Reference<css::security::XCertificate> 
SfxLokHelper::getSigningCertifi
     return xCertificate;
 }
 
+uno::Reference<security::XCertificate> SfxLokHelper::addCertificate(
+    const css::uno::Reference<css::xml::crypto::XCertificateCreator>& 
xCertificateCreator,
+    const css::uno::Sequence<sal_Int8>& rCert)
+{
+    // Trust arg is handled by CERT_DecodeTrustString(), see 'man certutil'.
+    return xCertificateCreator->addDERCertificateToTheDatabase(rCert, 
u"TCu,Cu,Tu"_ustr);
+}
+
+void SfxLokHelper::addCertificates(const std::vector<std::string>& rCerts)
+{
+    uno::Reference<uno::XComponentContext> xContext = 
comphelper::getProcessComponentContext();
+    uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = 
xml::crypto::SEInitializer::create(xContext);
+    uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = 
xSEInitializer->createSecurityContext(OUString());
+    if (!xSecurityContext.is())
+    {
+        return;
+    }
+
+    uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = 
xSecurityContext->getSecurityEnvironment();
+    uno::Reference<xml::crypto::XCertificateCreator> 
xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
+    if (!xCertificateCreator.is())
+    {
+        return;
+    }
+
+    for (const auto& rCert : rCerts)
+    {
+        uno::Sequence<sal_Int8> aCertificateSequence;
+        OUString aBase64OUString = OUString::fromUtf8(rCert);
+        comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
+        addCertificate(xCertificateCreator, aCertificateSequence);
+    }
+}
+
 void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
 {
     if (DisableCallbacks::disabled())

Reply via email to