cool#9992 lok doc sign, hash extract: initial getCommandValues('Signature')

The trouble with signing via ca/cert/key PEM files is that usually the
CA is not trusted by the received of the signature. 3rd-party services
are available to do generate trusted signatures, but then you need to
share your document with them, which can be also problematic.

A middle-ground here is to sign the hash of the document by a 3rd-party,
something that's supported by e.g.
<https://docs.eideasy.com/electronic-signatures/api-flow-with-file-hashes-pdf.html>
(which itself aggregates a number of providers).

As a first step, add LOK API to get what would be the signature time
during signing -- but instead of actually signing, just return this
information. Once the same is done with the doc hash, this is supposed
to provide the same info than what the reference
<https://github.com/eideasy/eideasy-external-pades-digital-signatures>
app does.

This is only a start: incrementally replace XCertificate with
SignatureContext, which allows aborting the signing right before calling
into NSS, and also later it'll allow injecting the PKCS#7 object we get
from the 3rd-party.

Change-Id: I108564f047fdb4fb796240c7d18a584cd9044313
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176279
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
This commit is contained in:
Miklos Vajna 2024-11-08 08:21:18 +01:00
parent e44f566a2c
commit 12e5082537
15 changed files with 107 additions and 29 deletions

View file

@ -74,6 +74,7 @@
#include <rtl/bootstrap.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/uri.hxx>
#include <svl/cryptosign.hxx>
#include <linguistic/misc.hxx>
#include <cppuhelper/bootstrap.hxx>
#include <comphelper/random.hxx>
@ -6836,6 +6837,12 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
pDoc->getCommandValues(aJsonWriter, aCommand);
return convertOString(aJsonWriter.finishAndGetAsOString());
}
else if (SfxLokHelper::supportsCommand(INetURLObject(OUString::fromUtf8(aCommand)).GetURLPath()))
{
tools::JsonWriter aJsonWriter;
SfxLokHelper::getCommandValues(aJsonWriter, aCommand);
return convertOString(aJsonWriter.finishAndGetAsOString());
}
else
{
SetLastExceptionMsg(OUString::fromUtf8(aCommand) + u" : Unknown command, no values returned"_ustr);
@ -7272,7 +7279,9 @@ static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
SolarMutexGuard aGuard;
return pObjectShell->SignDocumentContentUsingCertificate(xCertificate);
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCertificate;
return pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
}
static bool doc_addCertificate(LibreOfficeKitDocument* pThis,

View file

@ -19,6 +19,10 @@
#include <sal/types.h>
class SfxViewShell;
namespace svl::crypto
{
class SigningContext;
}
namespace sfx2
{
@ -27,11 +31,10 @@ class SAL_NO_VTABLE SAL_DLLPUBLIC_RTTI SAL_LOPLUGIN_ANNOTATE("crosscast") Digita
{
public:
/// Same as signDocumentWithCertificate(), but passes the xModel as well.
virtual bool
SignModelWithCertificate(const css::uno::Reference<css::frame::XModel>& xModel,
const css::uno::Reference<css::security::XCertificate>& xCertificate,
const css::uno::Reference<css::embed::XStorage>& xStorage,
const css::uno::Reference<css::io::XStream>& xStream)
virtual bool SignModelWithCertificate(const css::uno::Reference<css::frame::XModel>& xModel,
svl::crypto::SigningContext& rSigningContext,
const css::uno::Reference<css::embed::XStorage>& xStorage,
const css::uno::Reference<css::io::XStream>& xStream)
= 0;
/// Async replacement for signDocumentContent().

View file

@ -47,6 +47,7 @@ namespace com::sun::star::frame
class XModel;
}
namespace ucbhelper { class Content; }
namespace svl::crypto { class SigningContext; }
class SvKeyValueIterator;
class SfxFilter;
@ -289,7 +290,7 @@ public:
SAL_DLLPRIVATE bool SignDocumentContentUsingCertificate(
const css::uno::Reference<css::frame::XModel>& xModel, bool bHasValidDocumentSignature,
const css::uno::Reference<css::security::XCertificate>& xCertificate);
svl::crypto::SigningContext& rSigningContext);
// the following two methods must be used and make sense only during saving currently
// TODO/LATER: in future the signature state should be controlled by the medium not by the document

View file

@ -249,6 +249,10 @@ public:
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);
/// Decides if it's OK to call getCommandValues(rCommand).
static bool supportsCommand(std::u16string_view rCommand);
/// Returns information about a given command in JSON format.
static void getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand);
private:
static int createView(SfxViewFrame& rViewFrame, ViewShellDocId docId);

View file

@ -147,6 +147,7 @@ namespace o3tl
}
namespace weld { class Window; }
namespace svl::crypto { class SigningContext; }
enum class HiddenWarningFact
{
@ -368,7 +369,7 @@ public:
const css::uno::Reference<css::security::XDocumentDigitalSignatures>& xSigner
= css::uno::Reference<css::security::XDocumentDigitalSignatures>());
bool SignDocumentContentUsingCertificate(const css::uno::Reference<css::security::XCertificate>& xCertificate);
bool SignDocumentContentUsingCertificate(svl::crypto::SigningContext& rSigningContext);
bool ResignDocument(css::uno::Sequence< css::security::DocumentSignatureInformation >& rSignaturesInfo);
void SignSignatureLine(weld::Window* pDialogParent, const OUString& aSignatureLineId,

View file

@ -92,6 +92,17 @@ private:
OUString m_aSignPassword;
};
/// Wrapper around a certificate: allows either an actual signing or extracting enough info, so a
/// 3rd-party can sign our document.
class SVL_DLLPUBLIC SigningContext
{
public:
/// If set, the certificate used for signing.
css::uno::Reference<css::security::XCertificate> m_xCertificate;
/// If m_xCertificate is not set, the time that would be used.
sal_Int64 m_nSignatureTime = 0;
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -132,6 +132,7 @@
#include <sfx2/viewfrm.hxx>
#include <comphelper/threadpool.hxx>
#include <o3tl/string_view.hxx>
#include <svl/cryptosign.hxx>
#include <condition_variable>
#include <com/sun/star/io/WrongFormatException.hpp>
@ -4184,7 +4185,7 @@ void SfxMedium::CreateTempFileNoCopy()
bool SfxMedium::SignDocumentContentUsingCertificate(
const css::uno::Reference<css::frame::XModel>& xModel, bool bHasValidDocumentSignature,
const Reference<XCertificate>& xCertificate)
svl::crypto::SigningContext& rSigningContext)
{
bool bChanges = false;
@ -4252,7 +4253,7 @@ bool SfxMedium::SignDocumentContentUsingCertificate(
xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
bool bSuccess = xModelSigner->SignModelWithCertificate(
xModel, xCertificate, GetZipStorageToSign_Impl(), xStream);
xModel, rSigningContext, GetZipStorageToSign_Impl(), xStream);
if (bSuccess)
{
@ -4273,7 +4274,7 @@ bool SfxMedium::SignDocumentContentUsingCertificate(
// We need read-write to be able to add the signature relation.
bool bSuccess = xModelSigner->SignModelWithCertificate(
xModel, xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
xModel, rSigningContext, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
if (bSuccess)
{
@ -4291,7 +4292,7 @@ bool SfxMedium::SignDocumentContentUsingCertificate(
std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
if (xModelSigner->SignModelWithCertificate(
xModel, xCertificate, uno::Reference<embed::XStorage>(), xStream))
xModel, rSigningContext, uno::Reference<embed::XStorage>(), xStream))
bChanges = true;
}
}

View file

@ -101,6 +101,7 @@
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <osl/file.hxx>
#include <svl/cryptosign.hxx>
#ifdef _WIN32
#include <Shlobj.h>
@ -1922,7 +1923,9 @@ bool SfxStoringHelper::FinishGUIStoreModel(::comphelper::SequenceAsHashMap::cons
{
bFoundCert = true;
SfxObjectShell* pDocShell = SfxViewShell::Current()->GetObjectShell();
bool bSigned = pDocShell->SignDocumentContentUsingCertificate(xCert);
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCert;
bool bSigned = pDocShell->SignDocumentContentUsingCertificate(aSigningContext);
if (bSigned && pDocShell->HasValidSignatures())
{
std::unique_ptr<weld::MessageDialog> xBox(

View file

@ -65,6 +65,7 @@
#include <comphelper/lok.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <tools/link.hxx>
#include <svl/cryptosign.hxx>
#include <sfx2/signaturestate.hxx>
#include <sfx2/sfxresid.hxx>
@ -579,7 +580,9 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)
if (xCertificate.is())
{
bHaveWeSigned |= SignDocumentContentUsingCertificate(xCertificate);
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCertificate;
bHaveWeSigned |= SignDocumentContentUsingCertificate(aSigningContext);
// Reload to show how the PDF actually looks like after signing. This also
// changes "finish signing" on the infobar back to "sign document" as a side
@ -2194,14 +2197,16 @@ bool SfxObjectShell::ResignDocument(uno::Sequence< security::DocumentSignatureIn
auto xCert = rInfo.Signer;
if (xCert.is())
{
bSignSuccess &= SignDocumentContentUsingCertificate(xCert);
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCert;
bSignSuccess &= SignDocumentContentUsingCertificate(aSigningContext);
}
}
return bSignSuccess;
}
bool SfxObjectShell::SignDocumentContentUsingCertificate(const Reference<XCertificate>& xCertificate)
bool SfxObjectShell::SignDocumentContentUsingCertificate(svl::crypto::SigningContext& rSigningContext)
{
// 1. PrepareForSigning
@ -2271,7 +2276,7 @@ bool SfxObjectShell::SignDocumentContentUsingCertificate(const Reference<XCertif
// 3. Sign
bool bSignSuccess = GetMedium()->SignDocumentContentUsingCertificate(
GetBaseModel(), HasValidSignatures(), xCertificate);
GetBaseModel(), HasValidSignatures(), rSigningContext);
// 4. AfterSigning
AfterSigning(bSignSuccess, false);

View file

@ -42,6 +42,7 @@
#include <comphelper/scopeguard.hxx>
#include <comphelper/base64.hxx>
#include <tools/json_writer.hxx>
#include <svl/cryptosign.hxx>
#include <boost/property_tree/json_parser.hpp>
@ -992,6 +993,32 @@ void SfxLokHelper::addCertificates(const std::vector<std::string>& rCerts)
pObjectShell->RecheckSignature(false);
}
bool SfxLokHelper::supportsCommand(std::u16string_view rCommand)
{
static const std::initializer_list<std::u16string_view> vSupport = { u"Signature" };
return std::find(vSupport.begin(), vSupport.end(), rCommand) != vSupport.end();
}
void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand)
{
static constexpr OStringLiteral aSignature(".uno:Signature");
if (!o3tl::starts_with(rCommand, aSignature))
{
return;
}
SfxObjectShell* pObjectShell = SfxObjectShell::Current();
if (!pObjectShell)
{
return;
}
svl::crypto::SigningContext aSigningContext;
pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
rJsonWriter.put("signatureTime", aSigningContext.m_nSignatureTime);
}
void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
{
if (DisableCallbacks::disabled())

View file

@ -27,6 +27,7 @@ $(eval $(call gb_CppunitTest_use_libraries,vcl_filter_ipdf, \
sal \
sfx \
subsequenttest \
svl \
svx \
test \
tl \

View file

@ -23,6 +23,7 @@
#include <sfx2/objsh.hxx>
#include <vcl/filter/PDFiumLibrary.hxx>
#include <vcl/filter/pdfdocument.hxx>
#include <svl/cryptosign.hxx>
using namespace ::com::sun::star;
@ -109,7 +110,9 @@ CPPUNIT_TEST_FIXTURE(VclFilterIpdfTest, testPDFAddVisibleSignatureLastPage)
pObjectShell->SetModified(false);
// When: do the actual signing.
pObjectShell->SignDocumentContentUsingCertificate(xCert);
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCert;
pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
// Then: count the # of shapes on the signature widget/annotation.
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();

View file

@ -22,6 +22,7 @@ $(eval $(call gb_CppunitTest_use_libraries,xmlsecurity_signing, \
sal \
sax \
sfx \
svl \
svx \
subsequenttest \
test \

View file

@ -57,6 +57,7 @@
#include <comphelper/propertyvalue.hxx>
#include <vcl/filter/PDFiumLibrary.hxx>
#include <vcl/scheduler.hxx>
#include <svl/cryptosign.hxx>
using namespace com::sun::star;
@ -765,7 +766,9 @@ CPPUNIT_TEST_FIXTURE(SigningTest, testPDFAddVisibleSignature)
pObjectShell->SetModified(false);
// When: do the actual signing.
pObjectShell->SignDocumentContentUsingCertificate(xCert);
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCert;
pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
// Then: count the # of shapes on the signature widget/annotation.
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();

View file

@ -52,6 +52,7 @@
#include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
#include <sfx2/digitalsignatures.hxx>
#include <svl/cryptosign.hxx>
#include <map>
@ -103,7 +104,7 @@ private:
bool
signWithCertificateImpl(const uno::Reference<frame::XModel>& /*xModel*/,
css::uno::Reference<css::security::XCertificate> const& xCertificate,
svl::crypto::SigningContext& rSigningContext,
css::uno::Reference<css::embed::XStorage> const& xStorage,
css::uno::Reference<css::io::XStream> const& xStream,
DocumentSignatureMode eMode);
@ -190,7 +191,7 @@ public:
/// See sfx2::DigitalSignatures::SignModelWithCertificate().
bool
SignModelWithCertificate(const css::uno::Reference<css::frame::XModel>& xModel,
const css::uno::Reference<css::security::XCertificate>& xCertificate,
svl::crypto::SigningContext& rSigningContext,
const css::uno::Reference<css::embed::XStorage>& xStorage,
const css::uno::Reference<css::io::XStream>& xStream) override;
/// See sfx2::DigitalSignatures::SignDocumentContentAsync().
@ -759,17 +760,19 @@ sal_Bool DocumentDigitalSignatures::signDocumentWithCertificate(
css::uno::Reference<css::io::XStream> const & xStream)
{
uno::Reference<frame::XModel> xModel;
return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream,
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCertificate;
return signWithCertificateImpl(xModel, aSigningContext, xStorage, xStream,
DocumentSignatureMode::Content);
}
bool DocumentDigitalSignatures::SignModelWithCertificate(
const uno::Reference<frame::XModel>& xModel,
const css::uno::Reference<css::security::XCertificate>& xCertificate,
svl::crypto::SigningContext& rSigningContext,
const css::uno::Reference<css::embed::XStorage>& xStorage,
const css::uno::Reference<css::io::XStream>& xStream)
{
return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream,
return signWithCertificateImpl(xModel, rSigningContext, xStorage, xStream,
DocumentSignatureMode::Content);
}
@ -814,13 +817,15 @@ sal_Bool DocumentDigitalSignatures::signScriptingContentWithCertificate(
css::uno::Reference<css::io::XStream> const& xStream)
{
uno::Reference<frame::XModel> xModel;
return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream,
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = xCertificate;
return signWithCertificateImpl(xModel, aSigningContext, xStorage, xStream,
DocumentSignatureMode::Macros);
}
bool DocumentDigitalSignatures::signWithCertificateImpl(
const uno::Reference<frame::XModel>& xModel,
css::uno::Reference<css::security::XCertificate> const& xCertificate,
svl::crypto::SigningContext& rSigningContext,
css::uno::Reference<css::embed::XStorage> const& xStorage,
css::uno::Reference<css::io::XStream> const& xStream, DocumentSignatureMode eMode)
{
@ -838,8 +843,8 @@ bool DocumentDigitalSignatures::signWithCertificateImpl(
aSignatureManager.setModel(xModel);
Reference<XXMLSecurityContext> xSecurityContext;
Reference<XServiceInfo> xServiceInfo(xCertificate, UNO_QUERY);
if (xServiceInfo->getImplementationName()
Reference<XServiceInfo> xServiceInfo(rSigningContext.m_xCertificate, UNO_QUERY);
if (xServiceInfo.is() && xServiceInfo->getImplementationName()
== "com.sun.star.xml.security.gpg.XCertificate_GpgImpl")
xSecurityContext = aSignatureManager.getGpgSecurityContext();
else
@ -847,7 +852,7 @@ bool DocumentDigitalSignatures::signWithCertificateImpl(
sal_Int32 nSecurityId;
bool bSuccess = aSignatureManager.add(xCertificate, xSecurityContext, u""_ustr, nSecurityId, true);
bool bSuccess = aSignatureManager.add(rSigningContext.m_xCertificate, xSecurityContext, u""_ustr, nSecurityId, true);
if (!bSuccess)
return false;