summaryrefslogtreecommitdiffstats
path: root/xmlsecurity/source
diff options
context:
space:
mode:
Diffstat (limited to 'xmlsecurity/source')
-rw-r--r--xmlsecurity/source/component/certificatecontainer.cxx152
-rw-r--r--xmlsecurity/source/component/documentdigitalsignatures.cxx900
-rw-r--r--xmlsecurity/source/dialogs/certificatechooser.cxx332
-rw-r--r--xmlsecurity/source/dialogs/certificateviewer.cxx377
-rw-r--r--xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx801
-rw-r--r--xmlsecurity/source/dialogs/macrosecurity.cxx446
-rw-r--r--xmlsecurity/source/framework/buffernode.cxx886
-rw-r--r--xmlsecurity/source/framework/buffernode.hxx119
-rw-r--r--xmlsecurity/source/framework/elementcollector.cxx137
-rw-r--r--xmlsecurity/source/framework/elementcollector.hxx80
-rw-r--r--xmlsecurity/source/framework/elementmark.cxx64
-rw-r--r--xmlsecurity/source/framework/elementmark.hxx67
-rw-r--r--xmlsecurity/source/framework/saxeventkeeperimpl.cxx1152
-rw-r--r--xmlsecurity/source/framework/securityengine.cxx67
-rw-r--r--xmlsecurity/source/framework/signaturecreatorimpl.cxx175
-rw-r--r--xmlsecurity/source/framework/signatureengine.cxx197
-rw-r--r--xmlsecurity/source/framework/signatureverifierimpl.cxx130
-rw-r--r--xmlsecurity/source/framework/xmlsignaturetemplateimpl.cxx113
-rw-r--r--xmlsecurity/source/gpg/CertificateImpl.cxx261
-rw-r--r--xmlsecurity/source/gpg/CertificateImpl.hxx100
-rw-r--r--xmlsecurity/source/gpg/CipherContext.cxx27
-rw-r--r--xmlsecurity/source/gpg/CipherContext.hxx26
-rw-r--r--xmlsecurity/source/gpg/DigestContext.cxx23
-rw-r--r--xmlsecurity/source/gpg/DigestContext.hxx24
-rw-r--r--xmlsecurity/source/gpg/SEInitializer.cxx80
-rw-r--r--xmlsecurity/source/gpg/SecurityEnvironment.cxx232
-rw-r--r--xmlsecurity/source/gpg/SecurityEnvironment.hxx72
-rw-r--r--xmlsecurity/source/gpg/XMLEncryption.cxx37
-rw-r--r--xmlsecurity/source/gpg/XMLEncryption.hxx37
-rw-r--r--xmlsecurity/source/gpg/XMLSecurityContext.cxx87
-rw-r--r--xmlsecurity/source/gpg/XMLSecurityContext.hxx58
-rw-r--r--xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx527
-rw-r--r--xmlsecurity/source/helper/UriBindingHelper.cxx113
-rw-r--r--xmlsecurity/source/helper/documentsignaturehelper.cxx650
-rw-r--r--xmlsecurity/source/helper/documentsignaturemanager.cxx705
-rw-r--r--xmlsecurity/source/helper/ooxmlsecexporter.cxx558
-rw-r--r--xmlsecurity/source/helper/ooxmlsecexporter.hxx47
-rw-r--r--xmlsecurity/source/helper/ooxmlsecparser.cxx1352
-rw-r--r--xmlsecurity/source/helper/ooxmlsecparser.hxx110
-rw-r--r--xmlsecurity/source/helper/pdfsignaturehelper.cxx626
-rw-r--r--xmlsecurity/source/helper/xmlsignaturehelper.cxx714
-rw-r--r--xmlsecurity/source/helper/xsecctl.cxx1004
-rw-r--r--xmlsecurity/source/helper/xsecparser.cxx1632
-rw-r--r--xmlsecurity/source/helper/xsecparser.hxx159
-rw-r--r--xmlsecurity/source/helper/xsecsign.cxx460
-rw-r--r--xmlsecurity/source/helper/xsecverify.cxx630
-rw-r--r--xmlsecurity/source/xmlsec/biginteger.cxx106
-rw-r--r--xmlsecurity/source/xmlsec/certificateextension_certextn.cxx47
-rw-r--r--xmlsecurity/source/xmlsec/certificateextension_certextn.hxx36
-rw-r--r--xmlsecurity/source/xmlsec/certificateextension_xmlsecimpl.hxx56
-rw-r--r--xmlsecurity/source/xmlsec/errorcallback.cxx68
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/akmngr.cxx229
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/akmngr.hxx56
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/oid.hxx153
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.cxx131
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.hxx66
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx1118
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.hxx173
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.cxx173
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.hxx71
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx786
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.hxx96
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/xmlsecuritycontext_mscryptimpl.cxx155
-rw-r--r--xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx312
-rw-r--r--xmlsecurity/source/xmlsec/nss/certerrors.h385
-rw-r--r--xmlsecurity/source/xmlsec/nss/ciphercontext.cxx265
-rw-r--r--xmlsecurity/source/xmlsec/nss/ciphercontext.hxx80
-rw-r--r--xmlsecurity/source/xmlsec/nss/digestcontext.cxx94
-rw-r--r--xmlsecurity/source/xmlsec/nss/digestcontext.hxx59
-rw-r--r--xmlsecurity/source/xmlsec/nss/nssinitializer.cxx638
-rw-r--r--xmlsecurity/source/xmlsec/nss/nssinitializer.hxx68
-rw-r--r--xmlsecurity/source/xmlsec/nss/nssrenam.h41
-rw-r--r--xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.cxx164
-rw-r--r--xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.hxx65
-rw-r--r--xmlsecurity/source/xmlsec/nss/secerror.cxx152
-rw-r--r--xmlsecurity/source/xmlsec/nss/secerror.hxx31
-rw-r--r--xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx890
-rw-r--r--xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.hxx146
-rw-r--r--xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.cxx144
-rw-r--r--xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.hxx55
-rw-r--r--xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx614
-rw-r--r--xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.hxx104
-rw-r--r--xmlsecurity/source/xmlsec/nss/xmlsecuritycontext_nssimpl.cxx150
-rw-r--r--xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx325
-rw-r--r--xmlsecurity/source/xmlsec/saxhelper.cxx364
-rw-r--r--xmlsecurity/source/xmlsec/xmldocumentwrapper_xmlsecimpl.cxx902
-rw-r--r--xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.cxx70
-rw-r--r--xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.hxx79
-rw-r--r--xmlsecurity/source/xmlsec/xmlsec_init.cxx73
-rw-r--r--xmlsecurity/source/xmlsec/xmlstreamio.cxx251
90 files changed, 26587 insertions, 0 deletions
diff --git a/xmlsecurity/source/component/certificatecontainer.cxx b/xmlsecurity/source/component/certificatecontainer.cxx
new file mode 100644
index 000000000..e230c7b2c
--- /dev/null
+++ b/xmlsecurity/source/component/certificatecontainer.cxx
@@ -0,0 +1,152 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <map>
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/security/XCertificateContainer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+
+#include <sal/config.h>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+class CertificateContainer
+ : public ::cppu::WeakImplHelper<css::lang::XServiceInfo, css::security::XCertificateContainer>
+{
+private:
+ typedef std::map<OUString, OUString> Map;
+ Map certMap;
+ Map certTrustMap;
+
+ static bool searchMap(const OUString& url, std::u16string_view certificate_name, Map& _certMap);
+ /// @throws css::uno::RuntimeException
+ bool isTemporaryCertificate(const OUString& url, std::u16string_view certificate_name);
+ /// @throws css::uno::RuntimeException
+ bool isCertificateTrust(const OUString& url, std::u16string_view certificate_name);
+
+public:
+ explicit CertificateContainer(const uno::Reference<uno::XComponentContext>&) {}
+ virtual sal_Bool SAL_CALL addCertificate(const OUString& url, const OUString& certificate_name,
+ sal_Bool trust) override;
+ virtual css::security::CertificateContainerStatus SAL_CALL
+ hasCertificate(const OUString& url, const OUString& certificate_name) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+bool
+CertificateContainer::searchMap( const OUString & url, std::u16string_view certificate_name, Map &_certMap )
+{
+ Map::iterator p = _certMap.find(url);
+
+ bool ret = false;
+
+ while( p != _certMap.end() )
+ {
+ ret = (*p).second == certificate_name;
+ if( ret )
+ break;
+ ++p;
+ }
+
+ return ret;
+}
+
+bool
+CertificateContainer::isTemporaryCertificate ( const OUString & url, std::u16string_view certificate_name )
+{
+ return searchMap( url, certificate_name, certMap);
+}
+
+bool
+CertificateContainer::isCertificateTrust ( const OUString & url, std::u16string_view certificate_name )
+{
+ return searchMap( url, certificate_name, certTrustMap);
+}
+
+sal_Bool
+CertificateContainer::addCertificate( const OUString & url, const OUString & certificate_name, sal_Bool trust )
+{
+ certMap.emplace( url, certificate_name );
+
+ //remember that the cert is trusted
+ if (trust)
+ certTrustMap.emplace( url, certificate_name );
+
+ return true;
+}
+
+::security::CertificateContainerStatus
+CertificateContainer::hasCertificate( const OUString & url, const OUString & certificate_name )
+{
+ if ( isTemporaryCertificate( url, certificate_name ) )
+ {
+ if ( isCertificateTrust( url, certificate_name ) )
+ return security::CertificateContainerStatus_TRUSTED;
+ else
+ return security::CertificateContainerStatus_UNTRUSTED;
+ } else
+ {
+ return security::CertificateContainerStatus_NOCERT;
+ }
+}
+
+OUString SAL_CALL
+CertificateContainer::getImplementationName( )
+{
+ return "com.sun.star.security.CertificateContainer";
+}
+
+sal_Bool SAL_CALL
+CertificateContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL
+CertificateContainer::getSupportedServiceNames( )
+{
+ return { "com.sun.star.security.CertificateContainer" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_security_CertificateContainer_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ static rtl::Reference<CertificateContainer> gContainer = new CertificateContainer(context);
+ return cppu::acquire(gContainer.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx
new file mode 100644
index 000000000..990fa5dea
--- /dev/null
+++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx
@@ -0,0 +1,900 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <resourcemanager.hxx>
+
+#include <certificate.hxx>
+#include <certificatechooser.hxx>
+#include <certificateviewer.hxx>
+#include <digitalsignaturesdialog.hxx>
+#include <macrosecurity.hxx>
+#include <biginteger.hxx>
+#include <strings.hrc>
+#include <pdfsignaturehelper.hxx>
+#include <sax/tools/converter.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/embed/ElementModes.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/ucb/XContentIdentifierFactory.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XCommandProcessor.hpp>
+#include <com/sun/star/ucb/Command.hpp>
+#include <com/sun/star/uno/SecurityException.hpp>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <unotools/securityoptions.hxx>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/security/CertificateKind.hpp>
+#include <comphelper/base64.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/xmlsechelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
+#include <sfx2/digitalsignatures.hxx>
+
+#include <map>
+
+using namespace css;
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::security;
+using namespace css::xml::crypto;
+
+namespace {
+class DocumentDigitalSignatures
+ : public cppu::WeakImplHelper<css::security::XDocumentDigitalSignatures,
+ css::lang::XInitialization, css::lang::XServiceInfo>,
+ public sfx2::DigitalSignatures
+{
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxCtx;
+ css::uno::Reference<css::awt::XWindow> mxParentWindow;
+
+ /// will be set by XInitialization. If not we assume true. false means an earlier version (whatever that means,
+ /// this is a string, not a boolean).
+ /// Note that the code talks about "ODF version" even if this class is also used to sign OOXML.
+ OUString m_sODFVersion;
+ /// The number of arguments which were passed in XInitialization::initialize
+ int m_nArgumentsCount;
+ /// Indicates if the document already contains a document signature
+ bool m_bHasDocumentSignature;
+
+ /// @throws css::uno::RuntimeException
+ bool ImplViewSignatures(const css::uno::Reference<css::embed::XStorage>& rxStorage,
+ const css::uno::Reference<css::io::XStream>& xSignStream,
+ DocumentSignatureMode eMode, bool bReadOnly);
+ /// @throws css::uno::RuntimeException
+ void ImplViewSignatures(const css::uno::Reference<css::embed::XStorage>& rxStorage,
+ const css::uno::Reference<css::io::XInputStream>& xSignStream,
+ DocumentSignatureMode eMode, bool bReadOnly);
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence<css::security::DocumentSignatureInformation>
+ ImplVerifySignatures(const css::uno::Reference<css::embed::XStorage>& rxStorage,
+ const ::com::sun::star::uno::Reference<css::io::XInputStream>& xSignStream,
+ DocumentSignatureMode eMode);
+
+ css::uno::Sequence<css::uno::Reference<css::security::XCertificate>>
+ chooseCertificatesImpl(std::map<OUString, OUString>& rProperties, const UserAction eAction,
+ const CertificateKind certificateKind=CertificateKind_NONE);
+
+ bool
+ signWithCertificateImpl(const uno::Reference<frame::XModel>& /*xModel*/,
+ css::uno::Reference<css::security::XCertificate> const& xCertificate,
+ css::uno::Reference<css::embed::XStorage> const& xStorage,
+ css::uno::Reference<css::io::XStream> const& xStream,
+ DocumentSignatureMode eMode);
+
+public:
+ explicit DocumentDigitalSignatures(
+ const css::uno::Reference<css::uno::XComponentContext>& rxCtx);
+
+ //XInitialization
+ void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& aArguments) override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XDocumentDigitalSignatures
+ sal_Bool SAL_CALL
+ signDocumentContent(const css::uno::Reference<css::embed::XStorage>& xStorage,
+ const css::uno::Reference<css::io::XStream>& xSignStream) override;
+ sal_Bool SAL_CALL signSignatureLine(
+ const css::uno::Reference<css::embed::XStorage>& Storage,
+ const css::uno::Reference<css::io::XStream>& xSignStream, const OUString& aSignatureLineId,
+ const Reference<css::security::XCertificate>& xCertificate,
+ const Reference<css::graphic::XGraphic>& xValidGraphic,
+ const Reference<css::graphic::XGraphic>& xInvalidGraphic,
+ const OUString& aComment) override;
+ css::uno::Sequence<css::security::DocumentSignatureInformation>
+ SAL_CALL verifyDocumentContentSignatures(
+ const css::uno::Reference<css::embed::XStorage>& xStorage,
+ const css::uno::Reference<css::io::XInputStream>& xSignInStream) override;
+ void SAL_CALL showDocumentContentSignatures(
+ const css::uno::Reference<css::embed::XStorage>& xStorage,
+ const css::uno::Reference<css::io::XInputStream>& xSignInStream) override;
+ OUString SAL_CALL getDocumentContentSignatureDefaultStreamName() override;
+ sal_Bool SAL_CALL
+ signScriptingContent(const css::uno::Reference<css::embed::XStorage>& xStorage,
+ const css::uno::Reference<css::io::XStream>& xSignStream) override;
+ css::uno::Sequence<css::security::DocumentSignatureInformation>
+ SAL_CALL verifyScriptingContentSignatures(
+ const css::uno::Reference<css::embed::XStorage>& xStorage,
+ const css::uno::Reference<css::io::XInputStream>& xSignInStream) override;
+ void SAL_CALL showScriptingContentSignatures(
+ const css::uno::Reference<css::embed::XStorage>& xStorage,
+ const css::uno::Reference<css::io::XInputStream>& xSignInStream) override;
+ OUString SAL_CALL getScriptingContentSignatureDefaultStreamName() override;
+ sal_Bool SAL_CALL
+ signPackage(const css::uno::Reference<css::embed::XStorage>& Storage,
+ const css::uno::Reference<css::io::XStream>& xSignStream) override;
+ css::uno::Sequence<css::security::DocumentSignatureInformation>
+ SAL_CALL verifyPackageSignatures(
+ const css::uno::Reference<css::embed::XStorage>& Storage,
+ const css::uno::Reference<css::io::XInputStream>& xSignInStream) override;
+ void SAL_CALL
+ showPackageSignatures(const css::uno::Reference<css::embed::XStorage>& xStorage,
+ const css::uno::Reference<css::io::XInputStream>& xSignInStream) override;
+ OUString SAL_CALL getPackageSignatureDefaultStreamName() override;
+ void SAL_CALL
+ showCertificate(const css::uno::Reference<css::security::XCertificate>& Certificate) override;
+ void SAL_CALL manageTrustedSources() override;
+ sal_Bool SAL_CALL
+ isAuthorTrusted(const css::uno::Reference<css::security::XCertificate>& Author) override;
+ sal_Bool SAL_CALL isLocationTrusted(const OUString& Location) override;
+ void SAL_CALL addAuthorToTrustedSources(
+ const css::uno::Reference<css::security::XCertificate>& Author) override;
+ void SAL_CALL addLocationToTrustedSources(const OUString& Location) override;
+
+ css::uno::Reference<css::security::XCertificate>
+ SAL_CALL chooseCertificate(OUString& rDescription) override;
+ css::uno::Reference<css::security::XCertificate>
+ SAL_CALL chooseSigningCertificate(OUString& rDescription) override;
+ css::uno::Reference<css::security::XCertificate>
+ SAL_CALL selectSigningCertificate(OUString& rDescription) override;
+ css::uno::Reference<css::security::XCertificate>
+ SAL_CALL selectSigningCertificateWithType(const CertificateKind certificateKind,
+ OUString& rDescription) override;
+ css::uno::Sequence<css::uno::Reference<css::security::XCertificate>>
+ SAL_CALL chooseEncryptionCertificate() override;
+ css::uno::Reference<css::security::XCertificate> SAL_CALL chooseCertificateWithProps(
+ css::uno::Sequence<::com::sun::star::beans::PropertyValue>& Properties) override;
+
+ sal_Bool SAL_CALL signDocumentWithCertificate(
+ css::uno::Reference<css::security::XCertificate> const & xCertificate,
+ css::uno::Reference<css::embed::XStorage> const & xStoragexStorage,
+ css::uno::Reference<css::io::XStream> const & xStream) override;
+
+ sal_Bool SAL_CALL signPackageWithCertificate(
+ css::uno::Reference<css::security::XCertificate> const& xCertificate,
+ css::uno::Reference<css::embed::XStorage> const& xStoragexStorage,
+ css::uno::Reference<css::io::XStream> const& xStream) override;
+
+ sal_Bool SAL_CALL signScriptingContentWithCertificate(
+ css::uno::Reference<css::security::XCertificate> const& xCertificate,
+ css::uno::Reference<css::embed::XStorage> const& xStoragexStorage,
+ css::uno::Reference<css::io::XStream> const& xStream) override;
+
+ void SAL_CALL setParentWindow(const css::uno::Reference<css::awt::XWindow>& rParentwindow) override
+ {
+ mxParentWindow = rParentwindow;
+ }
+
+ /// See sfx2::DigitalSignatures::SignModelWithCertificate().
+ 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) override;
+};
+
+}
+
+DocumentDigitalSignatures::DocumentDigitalSignatures( const Reference< XComponentContext >& rxCtx ):
+ mxCtx(rxCtx),
+ m_sODFVersion(ODFVER_013_TEXT),
+ m_nArgumentsCount(0),
+ m_bHasDocumentSignature(false)
+{
+}
+
+void DocumentDigitalSignatures::initialize( const Sequence< Any >& aArguments)
+{
+ if (aArguments.getLength() > 2)
+ throw css::lang::IllegalArgumentException(
+ "DocumentDigitalSignatures::initialize requires zero, one, or two arguments",
+ static_cast<XInitialization*>(this), 0);
+
+ m_nArgumentsCount = aArguments.getLength();
+
+ if (!aArguments.hasElements())
+ return;
+
+ if (!(aArguments[0] >>= m_sODFVersion))
+ throw css::lang::IllegalArgumentException(
+ "DocumentDigitalSignatures::initialize: the first arguments must be a string",
+ static_cast<XInitialization*>(this), 0);
+
+ if (aArguments.getLength() == 2
+ && !(aArguments[1] >>= m_bHasDocumentSignature))
+ throw css::lang::IllegalArgumentException(
+ "DocumentDigitalSignatures::initialize: the second arguments must be a bool",
+ static_cast<XInitialization*>(this), 1);
+
+ //the Version is supported as of ODF1.2, so for and 1.1 document or older we will receive the
+ //an empty string. In this case we set it to ODFVER_010_TEXT. Then we can later check easily
+ //if initialize was called. Only then m_sODFVersion.getLength() is greater than 0
+ if (m_sODFVersion.isEmpty())
+ m_sODFVersion = ODFVER_010_TEXT;
+}
+
+OUString DocumentDigitalSignatures::getImplementationName()
+{
+ return "com.sun.star.security.DocumentDigitalSignatures";
+}
+
+sal_Bool DocumentDigitalSignatures::supportsService(
+ OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString>
+DocumentDigitalSignatures::getSupportedServiceNames()
+{
+ Sequence<OUString> aRet{ "com.sun.star.security.DocumentDigitalSignatures" };
+ return aRet;
+}
+
+sal_Bool DocumentDigitalSignatures::signDocumentContent(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XStream >& xSignStream)
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(), "DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ return ImplViewSignatures( rxStorage, xSignStream, DocumentSignatureMode::Content, false );
+}
+
+sal_Bool DocumentDigitalSignatures::signSignatureLine(
+ const Reference<css::embed::XStorage>& rxStorage,
+ const Reference<css::io::XStream>& xSignStream,
+ const OUString& aSignatureLineId,
+ const Reference<css::security::XCertificate>& xCertificate,
+ const Reference<css::graphic::XGraphic>& xValidGraphic,
+ const Reference<css::graphic::XGraphic>& xInvalidGraphic,
+ const OUString& aComment)
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),
+ "DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+
+ DocumentSignatureManager aSignatureManager(mxCtx, DocumentSignatureMode::Content);
+
+ if (!aSignatureManager.init())
+ return false;
+
+ aSignatureManager.setStore(rxStorage);
+ aSignatureManager.getSignatureHelper().SetStorage(rxStorage, m_sODFVersion);
+ aSignatureManager.setSignatureStream(xSignStream);
+
+ Reference<XXMLSecurityContext> xSecurityContext;
+ Reference<XServiceInfo> xServiceInfo(xCertificate, UNO_QUERY);
+ if (xServiceInfo->getImplementationName()
+ == "com.sun.star.xml.security.gpg.XCertificate_GpgImpl")
+ xSecurityContext = aSignatureManager.getGpgSecurityContext();
+ else
+ xSecurityContext = aSignatureManager.getSecurityContext();
+
+ sal_Int32 nSecurityId;
+ bool bSuccess = aSignatureManager.add(xCertificate, xSecurityContext, aComment, nSecurityId,
+ true, aSignatureLineId, xValidGraphic, xInvalidGraphic);
+ if (!bSuccess)
+ return false;
+
+ // Need to have this to verify the signature
+ aSignatureManager.read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
+ aSignatureManager.write(true);
+
+ if (rxStorage.is() && !xSignStream.is())
+ {
+ uno::Reference<embed::XTransactedObject> xTrans(rxStorage, uno::UNO_QUERY);
+ xTrans->commit();
+ }
+
+ return true;
+}
+
+Sequence< css::security::DocumentSignatureInformation >
+DocumentDigitalSignatures::verifyDocumentContentSignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignInStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ return ImplVerifySignatures( rxStorage, xSignInStream, DocumentSignatureMode::Content );
+}
+
+void DocumentDigitalSignatures::showDocumentContentSignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignInStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ ImplViewSignatures( rxStorage, xSignInStream, DocumentSignatureMode::Content, true );
+}
+
+OUString DocumentDigitalSignatures::getDocumentContentSignatureDefaultStreamName()
+{
+ return DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName();
+}
+
+sal_Bool DocumentDigitalSignatures::signScriptingContent(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XStream >& xSignStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ OSL_ENSURE(m_nArgumentsCount == 2, "DocumentDigitalSignatures: Service was not initialized properly");
+ return ImplViewSignatures( rxStorage, xSignStream, DocumentSignatureMode::Macros, false );
+}
+
+Sequence< css::security::DocumentSignatureInformation >
+DocumentDigitalSignatures::verifyScriptingContentSignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignInStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ return ImplVerifySignatures( rxStorage, xSignInStream, DocumentSignatureMode::Macros );
+}
+
+void DocumentDigitalSignatures::showScriptingContentSignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignInStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ ImplViewSignatures( rxStorage, xSignInStream, DocumentSignatureMode::Macros, true );
+}
+
+OUString DocumentDigitalSignatures::getScriptingContentSignatureDefaultStreamName()
+{
+ return DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName();
+}
+
+
+sal_Bool DocumentDigitalSignatures::signPackage(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XStream >& xSignStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ return ImplViewSignatures( rxStorage, xSignStream, DocumentSignatureMode::Package, false );
+}
+
+Sequence< css::security::DocumentSignatureInformation >
+DocumentDigitalSignatures::verifyPackageSignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignInStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ return ImplVerifySignatures( rxStorage, xSignInStream, DocumentSignatureMode::Package );
+}
+
+void DocumentDigitalSignatures::showPackageSignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignInStream )
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),"DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+ ImplViewSignatures( rxStorage, xSignInStream, DocumentSignatureMode::Package, true );
+}
+
+OUString DocumentDigitalSignatures::getPackageSignatureDefaultStreamName( )
+{
+ return DocumentSignatureHelper::GetPackageSignatureDefaultStreamName();
+}
+
+
+void DocumentDigitalSignatures::ImplViewSignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignStream,
+ DocumentSignatureMode eMode, bool bReadOnly )
+{
+ Reference< io::XStream > xStream;
+ if ( xSignStream.is() )
+ xStream.set( xSignStream, UNO_QUERY );
+ ImplViewSignatures( rxStorage, xStream, eMode, bReadOnly );
+}
+
+bool DocumentDigitalSignatures::ImplViewSignatures(
+ const Reference< css::embed::XStorage >& rxStorage, const Reference< css::io::XStream >& xSignStream,
+ DocumentSignatureMode eMode, bool bReadOnly )
+{
+ bool bChanges = false;
+ auto xSignaturesDialog = std::make_shared<DigitalSignaturesDialog>(
+ Application::GetFrameWeld(mxParentWindow), mxCtx, eMode, bReadOnly, m_sODFVersion,
+ m_bHasDocumentSignature);
+ bool bInit = xSignaturesDialog->Init();
+ SAL_WARN_IF( !bInit, "xmlsecurity.comp", "Error initializing security context!" );
+ if ( bInit )
+ {
+ xSignaturesDialog->SetStorage(rxStorage);
+
+ xSignaturesDialog->SetSignatureStream( xSignStream );
+
+ if (bReadOnly)
+ {
+ xSignaturesDialog->beforeRun();
+ weld::DialogController::runAsync(xSignaturesDialog, [] (sal_Int32) {});
+ return false;
+ }
+ else if (xSignaturesDialog->run() == RET_OK)
+ {
+ if (xSignaturesDialog->SignaturesChanged())
+ {
+ bChanges = true;
+ // If we have a storage and no stream, we are responsible for commit
+ if ( rxStorage.is() && !xSignStream.is() )
+ {
+ uno::Reference< embed::XTransactedObject > xTrans( rxStorage, uno::UNO_QUERY );
+ xTrans->commit();
+ }
+ }
+ }
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(Application::GetFrameWeld(mxParentWindow),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ XsResId(RID_XMLSECWB_NO_MOZILLA_PROFILE)));
+ xBox->run();
+ }
+
+ return bChanges;
+}
+
+Sequence< css::security::DocumentSignatureInformation >
+DocumentDigitalSignatures::ImplVerifySignatures(
+ const Reference< css::embed::XStorage >& rxStorage,
+ const Reference< css::io::XInputStream >& xSignStream, DocumentSignatureMode eMode )
+{
+ DocumentSignatureManager aSignatureManager(mxCtx, eMode);
+
+ bool bInit = aSignatureManager.init();
+
+ SAL_WARN_IF(!bInit, "xmlsecurity.comp", "Error initializing security context!");
+
+ if (!bInit)
+ return {};
+
+ if (!rxStorage.is())
+ {
+ if (xSignStream.is())
+ {
+ // Something not ZIP-based, try PDF.
+ PDFSignatureHelper& rSignatureHelper = aSignatureManager.getPDFSignatureHelper();
+ if (rSignatureHelper.ReadAndVerifySignature(xSignStream))
+ return rSignatureHelper.GetDocumentSignatureInformations(aSignatureManager.getSecurityEnvironment());
+ }
+
+ SAL_WARN( "xmlsecurity.comp", "Error, no XStorage provided");
+ return Sequence<css::security::DocumentSignatureInformation>();
+ }
+ // First check for the InputStream, to avoid unnecessary initialization of the security environment...
+ SignatureStreamHelper aStreamHelper;
+ Reference< io::XInputStream > xInputStream = xSignStream;
+
+ if ( !xInputStream.is() )
+ {
+ aStreamHelper = DocumentSignatureHelper::OpenSignatureStream( rxStorage, embed::ElementModes::READ, eMode );
+ if ( aStreamHelper.xSignatureStream.is() )
+ xInputStream.set( aStreamHelper.xSignatureStream, UNO_QUERY );
+ }
+
+ if (!xInputStream.is() && aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ return {};
+
+
+ XMLSignatureHelper& rSignatureHelper = aSignatureManager.getSignatureHelper();
+ rSignatureHelper.SetStorage(rxStorage, m_sODFVersion);
+
+ rSignatureHelper.StartMission(aSignatureManager.getSecurityContext());
+
+ if (xInputStream.is())
+ rSignatureHelper.ReadAndVerifySignature(xInputStream);
+ else if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ rSignatureHelper.ReadAndVerifySignatureStorage(aStreamHelper.xSignatureStorage);
+
+ rSignatureHelper.EndMission();
+
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = aSignatureManager.getSecurityEnvironment();
+ uno::Reference<xml::crypto::XSecurityEnvironment> xGpgSecEnv = aSignatureManager.getGpgSecurityEnvironment();
+
+ SignatureInformations aSignInfos = rSignatureHelper.GetSignatureInformations();
+ int nInfos = aSignInfos.size();
+ Sequence< css::security::DocumentSignatureInformation > aInfos(nInfos);
+ css::security::DocumentSignatureInformation* arInfos = aInfos.getArray();
+
+ for (int n = 0; n < nInfos; ++n)
+ {
+ DocumentSignatureAlgorithm mode
+ = DocumentSignatureHelper::getDocumentAlgorithm(m_sODFVersion, aSignInfos[n]);
+ const std::vector<OUString> aElementsToBeVerified
+ = DocumentSignatureHelper::CreateElementList(rxStorage, eMode, mode);
+
+ const SignatureInformation& rInfo = aSignInfos[n];
+ css::security::DocumentSignatureInformation& rSigInfo = arInfos[n];
+
+ if (!rInfo.X509Datas.empty()) // X.509
+ {
+ std::vector<uno::Reference<XCertificate>> certs(
+ rSignatureHelper.CheckAndUpdateSignatureInformation(
+ xSecEnv, rInfo));
+ if (certs.empty())
+ {
+ rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID;
+ }
+ else
+ {
+ rSigInfo.Signer = certs.back();
+ // get only intermediates
+ certs.pop_back();
+ // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name)
+ // to find the parent certificate. It does not take into account that there can be several certificates
+ // with the same subject name.
+ try
+ {
+ rSigInfo.CertificateStatus = xSecEnv->verifyCertificate(
+ rSigInfo.Signer, comphelper::containerToSequence(certs));
+ }
+ catch (SecurityException&)
+ {
+ SAL_WARN("xmlsecurity.comp", "Verification of certificate failed");
+ rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID;
+ }
+ }
+ }
+ else if (!rInfo.ouGpgCertificate.isEmpty() && xGpgSecEnv.is()) // GPG
+ {
+ // TODO not ideal to retrieve cert by keyID, might
+ // collide, or PGPKeyID format might change - can't we
+ // keep the xCert itself in rInfo?
+ rSigInfo.Signer = xGpgSecEnv->getCertificate(
+ rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger(u""));
+ rSigInfo.CertificateStatus = xGpgSecEnv->verifyCertificate(
+ rSigInfo.Signer, Sequence<Reference<css::security::XCertificate>>());
+ }
+
+ // Time support again (#i38744#)
+ Date aDate(rInfo.stDateTime.Day, rInfo.stDateTime.Month, rInfo.stDateTime.Year);
+ tools::Time aTime(rInfo.stDateTime.Hours, rInfo.stDateTime.Minutes,
+ rInfo.stDateTime.Seconds, rInfo.stDateTime.NanoSeconds);
+ rSigInfo.SignatureDate = aDate.GetDate();
+ rSigInfo.SignatureTime = aTime.GetTime() / tools::Time::nanoPerCenti;
+
+ rSigInfo.SignatureIsValid
+ = (rInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED);
+
+ // Signature line info (ID + Images)
+ if (!rInfo.ouSignatureLineId.isEmpty())
+ rSigInfo.SignatureLineId = rInfo.ouSignatureLineId;
+
+ if (rInfo.aValidSignatureImage.is())
+ rSigInfo.ValidSignatureLineImage = rInfo.aValidSignatureImage;
+
+ if (rInfo.aInvalidSignatureImage.is())
+ rSigInfo.InvalidSignatureLineImage = rInfo.aInvalidSignatureImage;
+
+ // OOXML intentionally doesn't sign metadata.
+ if (rSigInfo.SignatureIsValid
+ && aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ rSigInfo.SignatureIsValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
+ aElementsToBeVerified, rInfo, mode);
+ }
+ if (eMode == DocumentSignatureMode::Content)
+ {
+ if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ rSigInfo.PartialDocumentSignature = true;
+ else
+ rSigInfo.PartialDocumentSignature
+ = !DocumentSignatureHelper::isOOo3_2_Signature(aSignInfos[n]);
+ }
+ }
+
+ return aInfos;
+
+}
+
+void DocumentDigitalSignatures::manageTrustedSources( )
+{
+ // MT: i45295
+ // SecEnv is only needed to display certificate information from trusted sources.
+ // Macro Security also has some options where no security environment is needed, so raise dialog anyway.
+ // Later I should change the code so the Dialog creates the SecEnv on demand...
+
+ Reference< css::xml::crypto::XSecurityEnvironment > xSecEnv;
+
+ DocumentSignatureManager aSignatureManager(mxCtx, {});
+ if (aSignatureManager.init())
+ xSecEnv = aSignatureManager.getSecurityEnvironment();
+
+ MacroSecurity aDlg(Application::GetFrameWeld(mxParentWindow), xSecEnv);
+ aDlg.run();
+}
+
+void DocumentDigitalSignatures::showCertificate(
+ const Reference< css::security::XCertificate >& Certificate )
+{
+ DocumentSignatureManager aSignatureManager(mxCtx, {});
+
+ bool bInit = aSignatureManager.init();
+
+ SAL_WARN_IF( !bInit, "xmlsecurity.comp", "Error initializing security context!" );
+
+ if ( bInit )
+ {
+ CertificateViewer aViewer(Application::GetFrameWeld(mxParentWindow), aSignatureManager.getSecurityEnvironment(), Certificate, false, nullptr);
+ aViewer.run();
+ }
+}
+
+sal_Bool DocumentDigitalSignatures::isAuthorTrusted(
+ const Reference<css::security::XCertificate>& xAuthor)
+{
+ if (!xAuthor.is())
+ {
+ return false;
+ }
+ OUString sSerialNum = xmlsecurity::bigIntegerToNumericString(xAuthor->getSerialNumber());
+
+ std::vector< SvtSecurityOptions::Certificate > aTrustedAuthors = SvtSecurityOptions::GetTrustedAuthors();
+
+ return std::any_of(aTrustedAuthors.begin(), aTrustedAuthors.end(),
+ [this, &xAuthor, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) {
+ if (!xmlsecurity::EqualDistinguishedNames(rAuthor.SubjectName, xAuthor->getIssuerName(), xmlsecurity::NOCOMPAT))
+ return false;
+ if (rAuthor.SerialNumber != sSerialNum)
+ return false;
+
+ DocumentSignatureManager aSignatureManager(mxCtx, {});
+ if (!aSignatureManager.init())
+ return false;
+ uno::Reference<css::security::XCertificate> xCert = aSignatureManager.getSecurityEnvironment()->createCertificateFromAscii(rAuthor.RawData);
+
+ auto pAuthor = dynamic_cast<xmlsecurity::Certificate*>(xAuthor.get());
+ auto pCert = dynamic_cast<xmlsecurity::Certificate*>(xCert.get());
+ if (pAuthor && pCert)
+ return pCert->getSHA256Thumbprint() == pAuthor->getSHA256Thumbprint();
+
+ return xCert->getSHA1Thumbprint() == xAuthor->getSHA1Thumbprint();
+ });
+}
+
+uno::Sequence<Reference<css::security::XCertificate>>
+DocumentDigitalSignatures::chooseCertificatesImpl(std::map<OUString, OUString>& rProperties,
+ const UserAction eAction,
+ const CertificateKind certificateKind)
+{
+ std::vector< Reference< css::xml::crypto::XXMLSecurityContext > > xSecContexts;
+
+ DocumentSignatureManager aSignatureManager(mxCtx, {});
+ if (aSignatureManager.init()) {
+ xSecContexts.push_back(aSignatureManager.getSecurityContext());
+ // Don't include OpenPGP if only X.509 certs are requested
+ if (certificateKind == CertificateKind_NONE || certificateKind == CertificateKind_OPENPGP)
+ xSecContexts.push_back(aSignatureManager.getGpgSecurityContext());
+ }
+
+ CertificateChooser aChooser(Application::GetFrameWeld(mxParentWindow), std::move(xSecContexts), eAction);
+
+ if (aChooser.run() != RET_OK)
+ return { Reference< css::security::XCertificate >(nullptr) };
+
+ uno::Sequence< Reference< css::security::XCertificate > > xCerts = aChooser.GetSelectedCertificates();
+ rProperties["Description"] = aChooser.GetDescription();
+ rProperties["Usage"] = aChooser.GetUsageText();
+
+ return xCerts;
+}
+
+Reference< css::security::XCertificate > DocumentDigitalSignatures::chooseCertificate(OUString& rDescription)
+{
+ return chooseSigningCertificate( rDescription );
+}
+
+Reference< css::security::XCertificate > DocumentDigitalSignatures::chooseSigningCertificate(OUString& rDescription)
+{
+ std::map<OUString, OUString> aProperties;
+ Reference< css::security::XCertificate > xCert = chooseCertificatesImpl( aProperties, UserAction::Sign )[0];
+ rDescription = aProperties["Description"];
+ return xCert;
+}
+
+Reference< css::security::XCertificate > DocumentDigitalSignatures::selectSigningCertificate(OUString& rDescription)
+{
+ std::map<OUString, OUString> aProperties;
+ Reference< css::security::XCertificate > xCert = chooseCertificatesImpl( aProperties, UserAction::SelectSign )[0];
+ rDescription = aProperties["Description"];
+ return xCert;
+}
+
+Reference<css::security::XCertificate>
+DocumentDigitalSignatures::selectSigningCertificateWithType(const CertificateKind certificateKind,
+ OUString& rDescription)
+{
+ std::map<OUString, OUString> aProperties;
+ Reference<css::security::XCertificate> xCert
+ = chooseCertificatesImpl(aProperties, UserAction::SelectSign, certificateKind)[0];
+ rDescription = aProperties["Description"];
+ return xCert;
+}
+
+css::uno::Sequence< Reference< css::security::XCertificate > > DocumentDigitalSignatures::chooseEncryptionCertificate()
+{
+ std::map<OUString, OUString> aProperties;
+ uno::Sequence< Reference< css::security::XCertificate > > aCerts=
+ chooseCertificatesImpl( aProperties, UserAction::Encrypt );
+ if (aCerts.getLength() == 1 && !aCerts[0].is())
+ // our error case contract is: empty sequence, so map that!
+ return uno::Sequence< Reference< css::security::XCertificate > >();
+ else
+ return aCerts;
+}
+
+css::uno::Reference< css::security::XCertificate > DocumentDigitalSignatures::chooseCertificateWithProps(Sequence<::com::sun::star::beans::PropertyValue>& rProperties)
+{
+ std::map<OUString, OUString> aProperties;
+ auto xCert = chooseCertificatesImpl( aProperties, UserAction::Sign )[0];
+
+ std::vector<css::beans::PropertyValue> vec;
+ vec.reserve(aProperties.size());
+ for (const auto& pair : aProperties)
+ {
+ vec.emplace_back(comphelper::makePropertyValue(pair.first, pair.second));
+ }
+
+ rProperties = comphelper::containerToSequence(vec);
+ return xCert;
+}
+
+sal_Bool DocumentDigitalSignatures::isLocationTrusted( const OUString& Location )
+{
+ return SvtSecurityOptions::isTrustedLocationUri(Location);
+}
+
+void DocumentDigitalSignatures::addAuthorToTrustedSources(
+ const Reference< css::security::XCertificate >& Author )
+{
+ SvtSecurityOptions::Certificate aNewCert;
+ aNewCert.SubjectName = Author->getIssuerName();
+ aNewCert.SerialNumber = xmlsecurity::bigIntegerToNumericString( Author->getSerialNumber() );
+
+ OUStringBuffer aStrBuffer;
+ ::comphelper::Base64::encode(aStrBuffer, Author->getEncoded());
+ aNewCert.RawData = aStrBuffer.makeStringAndClear();
+
+ std::vector< SvtSecurityOptions::Certificate > aTrustedAuthors = SvtSecurityOptions::GetTrustedAuthors();
+ aTrustedAuthors.push_back( aNewCert );
+ SvtSecurityOptions::SetTrustedAuthors( aTrustedAuthors );
+}
+
+void DocumentDigitalSignatures::addLocationToTrustedSources( const OUString& Location )
+{
+ std::vector< OUString > aSecURLs = SvtSecurityOptions::GetSecureURLs();
+ aSecURLs.push_back(Location);
+
+ SvtSecurityOptions::SetSecureURLs( std::move(aSecURLs) );
+}
+
+sal_Bool DocumentDigitalSignatures::signDocumentWithCertificate(
+ css::uno::Reference<css::security::XCertificate> const & xCertificate,
+ css::uno::Reference<css::embed::XStorage> const & xStorage,
+ css::uno::Reference<css::io::XStream> const & xStream)
+{
+ uno::Reference<frame::XModel> xModel;
+ return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream,
+ DocumentSignatureMode::Content);
+}
+
+bool DocumentDigitalSignatures::SignModelWithCertificate(
+ const uno::Reference<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)
+{
+ return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream,
+ DocumentSignatureMode::Content);
+}
+
+sal_Bool DocumentDigitalSignatures::signPackageWithCertificate(
+ css::uno::Reference<css::security::XCertificate> const& xCertificate,
+ css::uno::Reference<css::embed::XStorage> const& xStorage,
+ css::uno::Reference<css::io::XStream> const& xStream)
+{
+ uno::Reference<frame::XModel> xModel;
+ return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream,
+ DocumentSignatureMode::Package);
+}
+
+sal_Bool DocumentDigitalSignatures::signScriptingContentWithCertificate(
+ css::uno::Reference<css::security::XCertificate> const& xCertificate,
+ css::uno::Reference<css::embed::XStorage> const& xStorage,
+ css::uno::Reference<css::io::XStream> const& xStream)
+{
+ uno::Reference<frame::XModel> xModel;
+ return signWithCertificateImpl(xModel, xCertificate, xStorage, xStream,
+ DocumentSignatureMode::Macros);
+}
+
+bool DocumentDigitalSignatures::signWithCertificateImpl(
+ const uno::Reference<frame::XModel>& xModel,
+ css::uno::Reference<css::security::XCertificate> const& xCertificate,
+ css::uno::Reference<css::embed::XStorage> const& xStorage,
+ css::uno::Reference<css::io::XStream> const& xStream, DocumentSignatureMode eMode)
+{
+ OSL_ENSURE(!m_sODFVersion.isEmpty(),
+ "DocumentDigitalSignatures: ODF Version not set, assuming minimum 1.2");
+
+ DocumentSignatureManager aSignatureManager(mxCtx, eMode);
+
+ if (!aSignatureManager.init())
+ return false;
+
+ aSignatureManager.setStore(xStorage);
+ aSignatureManager.getSignatureHelper().SetStorage(xStorage, m_sODFVersion);
+ aSignatureManager.setSignatureStream(xStream);
+ aSignatureManager.setModel(xModel);
+
+ Reference<XXMLSecurityContext> xSecurityContext = aSignatureManager.getSecurityContext();
+
+ sal_Int32 nSecurityId;
+
+ bool bSuccess = aSignatureManager.add(xCertificate, xSecurityContext, "", nSecurityId, true);
+ if (!bSuccess)
+ return false;
+
+ aSignatureManager.read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
+ aSignatureManager.write(true);
+
+ if (xStorage.is() && !xStream.is())
+ {
+ uno::Reference<embed::XTransactedObject> xTransaction(xStorage, uno::UNO_QUERY);
+ xTransaction->commit();
+ }
+
+ return true;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_security_DocumentDigitalSignatures_get_implementation(
+ uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(
+ new DocumentDigitalSignatures(uno::Reference<uno::XComponentContext>(pCtx)));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/dialogs/certificatechooser.cxx b/xmlsecurity/source/dialogs/certificatechooser.cxx
new file mode 100644
index 000000000..3ac503521
--- /dev/null
+++ b/xmlsecurity/source/dialogs/certificatechooser.cxx
@@ -0,0 +1,332 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_gpgme.h>
+#include <certificatechooser.hxx>
+#include <certificateviewer.hxx>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/xmlsechelper.hxx>
+
+#include <com/sun/star/security/NoPasswordException.hpp>
+#include <com/sun/star/security/CertificateCharacters.hpp>
+
+#include <o3tl/safeint.hxx>
+#include <unotools/datetime.hxx>
+#include <unotools/useroptions.hxx>
+
+#include <resourcemanager.hxx>
+#include <strings.hrc>
+
+using namespace comphelper;
+using namespace css;
+
+CertificateChooser::CertificateChooser(weld::Window* _pParent,
+ std::vector< css::uno::Reference< css::xml::crypto::XXMLSecurityContext > > && rxSecurityContexts,
+ UserAction eAction)
+ : GenericDialogController(_pParent, "xmlsec/ui/selectcertificatedialog.ui", "SelectCertificateDialog")
+ , meAction(eAction)
+ , m_xFTSign(m_xBuilder->weld_label("sign"))
+ , m_xFTEncrypt(m_xBuilder->weld_label("encrypt"))
+ , m_xCertLB(m_xBuilder->weld_tree_view("signatures"))
+ , m_xViewBtn(m_xBuilder->weld_button("viewcert"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ , m_xFTDescription(m_xBuilder->weld_label("description-label"))
+ , m_xDescriptionED(m_xBuilder->weld_entry("description"))
+{
+ auto nControlWidth = m_xCertLB->get_approximate_digit_width() * 105;
+ m_xCertLB->set_size_request(nControlWidth, m_xCertLB->get_height_rows(12));
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(30*nControlWidth/100),
+ o3tl::narrowing<int>(30*nControlWidth/100),
+ o3tl::narrowing<int>(10*nControlWidth/100),
+ o3tl::narrowing<int>(20*nControlWidth/100)
+ };
+ m_xCertLB->set_column_fixed_widths(aWidths);
+ m_xCertLB->connect_changed( LINK( this, CertificateChooser, CertificateHighlightHdl ) );
+ m_xCertLB->connect_row_activated( LINK( this, CertificateChooser, CertificateSelectHdl ) );
+ m_xViewBtn->connect_clicked( LINK( this, CertificateChooser, ViewButtonHdl ) );
+
+ mxSecurityContexts = std::move(rxSecurityContexts);
+ mbInitialized = false;
+
+ // disable buttons
+ CertificateHighlightHdl(*m_xCertLB);
+}
+
+CertificateChooser::~CertificateChooser()
+{
+}
+
+short CertificateChooser::run()
+{
+ // #i48432#
+ // We can't check for personal certificates before raising this dialog,
+ // because the mozilla implementation throws a NoPassword exception,
+ // if the user pressed cancel, and also if the database does not exist!
+ // But in the later case, the is no password query, and the user is confused
+ // that nothing happens when pressing "Add..." in the SignatureDialog.
+
+ // PostUserEvent( LINK( this, CertificateChooser, Initialize ) );
+
+ // PostUserLink behavior is too slow, so do it directly before Execute().
+ // Problem: This Dialog should be visible right now, and the parent should not be accessible.
+ // Show, Update, DisableInput...
+
+ m_xDialog->show();
+ ImplInitialize();
+ return GenericDialogController::run();
+}
+
+void CertificateChooser::HandleOneUsageBit(OUString& string, int& bits, int bit, TranslateId pResId)
+{
+ if (bits & bit)
+ {
+ if (!string.isEmpty())
+ string += ", ";
+ string += XsResId(pResId);
+ bits &= ~bit;
+ }
+}
+
+OUString CertificateChooser::UsageInClearText(int bits)
+{
+ OUString result;
+
+ HandleOneUsageBit(result, bits, 0x80, STR_DIGITAL_SIGNATURE);
+ HandleOneUsageBit(result, bits, 0x40, STR_NON_REPUDIATION);
+ HandleOneUsageBit(result, bits, 0x20, STR_KEY_ENCIPHERMENT);
+ HandleOneUsageBit(result, bits, 0x10, STR_DATA_ENCIPHERMENT);
+ HandleOneUsageBit(result, bits, 0x08, STR_KEY_AGREEMENT);
+ HandleOneUsageBit(result, bits, 0x04, STR_KEY_CERT_SIGN);
+ HandleOneUsageBit(result, bits, 0x02, STR_CRL_SIGN);
+ HandleOneUsageBit(result, bits, 0x01, STR_ENCIPHER_ONLY);
+
+ // Check for mystery leftover bits
+ if (bits != 0)
+ {
+ if (!result.isEmpty())
+ result += ", ";
+ result += "0x" + OUString::number(bits, 16);
+ }
+
+ return result;
+}
+
+void CertificateChooser::ImplInitialize()
+{
+ if ( mbInitialized )
+ return;
+
+ SvtUserOptions aUserOpts;
+
+ switch (meAction)
+ {
+ case UserAction::Sign:
+ m_xFTSign->show();
+ m_xOKBtn->set_label(XsResId(STR_SIGN));
+ msPreferredKey = aUserOpts.GetSigningKey();
+ break;
+
+ case UserAction::SelectSign:
+ m_xFTSign->show();
+ m_xOKBtn->set_label(XsResId(STR_SELECTSIGN));
+ msPreferredKey = aUserOpts.GetSigningKey();
+ break;
+
+ case UserAction::Encrypt:
+ m_xFTEncrypt->show();
+ m_xFTDescription->hide();
+ m_xDescriptionED->hide();
+ m_xCertLB->set_selection_mode(SelectionMode::Multiple);
+ m_xOKBtn->set_label(XsResId(STR_ENCRYPT));
+ msPreferredKey = aUserOpts.GetEncryptionKey();
+ break;
+
+ }
+
+ for (auto &secContext : mxSecurityContexts)
+ {
+ if (!secContext.is())
+ continue;
+ auto secEnvironment = secContext->getSecurityEnvironment();
+ if (!secEnvironment.is())
+ continue;
+
+ uno::Sequence< uno::Reference< security::XCertificate > > xCerts;
+ try
+ {
+ if ( meAction == UserAction::Sign || meAction == UserAction::SelectSign)
+ xCerts = secEnvironment->getPersonalCertificates();
+ else
+ xCerts = secEnvironment->getAllCertificates();
+ }
+ catch (security::NoPasswordException&)
+ {
+ }
+
+ for( sal_Int32 nCert = xCerts.getLength(); nCert; )
+ {
+ uno::Reference< security::XCertificate > xCert = xCerts[ --nCert ];
+ // Check if we have a private key for this...
+ tools::Long nCertificateCharacters = secEnvironment->getCertificateCharacters(xCert);
+
+ if (!(nCertificateCharacters & security::CertificateCharacters::HAS_PRIVATE_KEY))
+ {
+ ::comphelper::removeElementAt( xCerts, nCert );
+ }
+ }
+
+
+ // fill list of certificates; the first entry will be selected
+ for ( const auto& xCert : std::as_const(xCerts) )
+ {
+ std::shared_ptr<UserData> userData = std::make_shared<UserData>();
+ userData->xCertificate = xCert;
+ userData->xSecurityContext = secContext;
+ userData->xSecurityEnvironment = secEnvironment;
+ mvUserData.push_back(userData);
+
+ OUString sIssuer = xmlsec::GetContentPart( xCert->getIssuerName(), xCert->getCertificateKind());
+
+ m_xCertLB->append();
+ int nRow = m_xCertLB->n_children() - 1;
+ m_xCertLB->set_text(nRow, xmlsec::GetContentPart(xCert->getSubjectName(), xCert->getCertificateKind()), 0);
+ m_xCertLB->set_text(nRow, sIssuer, 1);
+ m_xCertLB->set_text(nRow, xmlsec::GetCertificateKind(xCert->getCertificateKind()), 2);
+ m_xCertLB->set_text(nRow, utl::GetDateString(xCert->getNotValidAfter()), 3);
+ m_xCertLB->set_text(nRow, UsageInClearText(xCert->getCertificateUsage()), 4);
+ OUString sId(weld::toId(userData.get()));
+ m_xCertLB->set_id(nRow, sId);
+
+#if HAVE_FEATURE_GPGME
+ // only GPG has preferred keys
+ if ( !sIssuer.isEmpty() && !msPreferredKey.isEmpty() ) {
+ if ( sIssuer == msPreferredKey )
+ {
+ if ( meAction == UserAction::Sign || meAction == UserAction::SelectSign )
+ m_xCertLB->select(nRow);
+ else if ( meAction == UserAction::Encrypt &&
+ aUserOpts.GetEncryptToSelf() )
+ mxEncryptToSelf = xCert;
+ }
+ }
+#endif
+ }
+ }
+
+ // enable/disable buttons
+ CertificateHighlightHdl(*m_xCertLB);
+ mbInitialized = true;
+}
+
+uno::Sequence<uno::Reference< css::security::XCertificate > > CertificateChooser::GetSelectedCertificates()
+{
+ std::vector< uno::Reference< css::security::XCertificate > > aRet;
+ if (meAction == UserAction::Encrypt)
+ {
+ // for encryption, multiselection is enabled
+ m_xCertLB->selected_foreach([this, &aRet](weld::TreeIter& rEntry){
+ UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(rEntry));
+ aRet.push_back( userData->xCertificate );
+ return false;
+ });
+ }
+ else
+ {
+ uno::Reference< css::security::XCertificate > xCert;
+ int nSel = m_xCertLB->get_selected_index();
+ if (nSel != -1)
+ {
+ UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(nSel));
+ xCert = userData->xCertificate;
+ }
+ aRet.push_back( xCert );
+ }
+
+#if HAVE_FEATURE_GPGME
+ if ( mxEncryptToSelf.is())
+ aRet.push_back( mxEncryptToSelf );
+#endif
+
+ return comphelper::containerToSequence(aRet);
+}
+
+uno::Reference<xml::crypto::XXMLSecurityContext> CertificateChooser::GetSelectedSecurityContext() const
+{
+ int nSel = m_xCertLB->get_selected_index();
+ if (nSel == -1)
+ return uno::Reference<xml::crypto::XXMLSecurityContext>();
+
+ UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(nSel));
+ uno::Reference<xml::crypto::XXMLSecurityContext> xCert = userData->xSecurityContext;
+ return xCert;
+}
+
+OUString CertificateChooser::GetDescription() const
+{
+ return m_xDescriptionED->get_text();
+}
+
+OUString CertificateChooser::GetUsageText()
+{
+ uno::Sequence< uno::Reference<css::security::XCertificate> > xCerts =
+ GetSelectedCertificates();
+ return (xCerts.hasElements() && xCerts[0].is()) ?
+ UsageInClearText(xCerts[0]->getCertificateUsage()) : OUString();
+}
+
+IMPL_LINK_NOARG(CertificateChooser, CertificateHighlightHdl, weld::TreeView&, void)
+{
+ bool bEnable = m_xCertLB->get_selected_index() != -1;
+ m_xViewBtn->set_sensitive(bEnable);
+ m_xOKBtn->set_sensitive(bEnable);
+ m_xDescriptionED->set_sensitive(bEnable);
+}
+
+IMPL_LINK_NOARG(CertificateChooser, CertificateSelectHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+IMPL_LINK_NOARG(CertificateChooser, ViewButtonHdl, weld::Button&, void)
+{
+ ImplShowCertificateDetails();
+}
+
+void CertificateChooser::ImplShowCertificateDetails()
+{
+ int nSel = m_xCertLB->get_selected_index();
+ if (nSel == -1)
+ return;
+
+ UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(nSel));
+
+ if (!userData->xSecurityEnvironment.is() || !userData->xCertificate.is())
+ return;
+
+ CertificateViewer aViewer(m_xDialog.get(), userData->xSecurityEnvironment, userData->xCertificate, true, this);
+ aViewer.run();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/dialogs/certificateviewer.cxx b/xmlsecurity/source/dialogs/certificateviewer.cxx
new file mode 100644
index 000000000..1801cdf53
--- /dev/null
+++ b/xmlsecurity/source/dialogs/certificateviewer.cxx
@@ -0,0 +1,377 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <certificatechooser.hxx>
+#include <certificateviewer.hxx>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#include <com/sun/star/security/CertificateCharacters.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/datetime.hxx>
+
+#include <strings.hrc>
+#include <resourcemanager.hxx>
+#include <comphelper/xmlsechelper.hxx>
+#include <tools/datetime.hxx>
+#include <bitmaps.hlst>
+
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+CertificateViewer::CertificateViewer(weld::Window* _pParent,
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& _rxSecurityEnvironment,
+ const css::uno::Reference< css::security::XCertificate >& _rXCert, bool bCheckForPrivateKey,
+ CertificateChooser* pParentChooser)
+ : GenericDialogController(_pParent, "xmlsec/ui/viewcertdialog.ui", "ViewCertDialog")
+ , mbCheckForPrivateKey(bCheckForPrivateKey)
+ , mpParentChooser(pParentChooser)
+ , mxTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
+{
+ mxTabCtrl->connect_enter_page(LINK(this, CertificateViewer, ActivatePageHdl));
+
+ mxSecurityEnvironment = _rxSecurityEnvironment;
+ mxCert = _rXCert;
+
+ mxGeneralPage.reset(new CertificateViewerGeneralTP(mxTabCtrl->get_page("general"), this));
+ mxDetailsPage.reset(new CertificateViewerDetailsTP(mxTabCtrl->get_page("details"), this));
+ if (!mxSecurityEnvironment->buildCertificatePath(mxCert).hasElements())
+ mxTabCtrl->remove_page("path");
+ else
+ mxPathId.reset(new CertificateViewerCertPathTP(mxTabCtrl->get_page("path"), this));
+ mxTabCtrl->set_current_page("general");
+}
+
+IMPL_LINK(CertificateViewer, ActivatePageHdl, const OString&, rPage, void)
+{
+ if (rPage == "path")
+ mxPathId->ActivatePage();
+}
+
+CertificateViewerTP::CertificateViewerTP(weld::Container* pParent, const OUString& rUIXMLDescription,
+ const OString& rID, CertificateViewer* pDlg)
+ : mxBuilder(Application::CreateBuilder(pParent, rUIXMLDescription))
+ , mxContainer(mxBuilder->weld_container(rID))
+ , mpDlg(pDlg)
+{
+}
+
+CertificateViewerGeneralTP::CertificateViewerGeneralTP(weld::Container* pParent, CertificateViewer* pDlg)
+ : CertificateViewerTP(pParent, "xmlsec/ui/certgeneral.ui", "CertGeneral", pDlg)
+ , m_xCertImg(mxBuilder->weld_image("certimage"))
+ , m_xHintNotTrustedFT(mxBuilder->weld_label("hintnotrust"))
+ , m_xIssuedToLabelFT(mxBuilder->weld_label("issued_to"))
+ , m_xIssuedToFT(mxBuilder->weld_label("issued_to_value"))
+ , m_xIssuedByLabelFT(mxBuilder->weld_label("issued_by"))
+ , m_xIssuedByFT(mxBuilder->weld_label("issued_by_value"))
+ , m_xValidFromDateFT(mxBuilder->weld_label("valid_from_value"))
+ , m_xValidToDateFT(mxBuilder->weld_label("valid_to_value"))
+ , m_xKeyImg(mxBuilder->weld_image("keyimage"))
+ , m_xHintCorrespPrivKeyFT(mxBuilder->weld_label("privatekey"))
+{
+ //Verify the certificate
+ sal_Int32 certStatus = mpDlg->mxSecurityEnvironment->verifyCertificate(mpDlg->mxCert,
+ Sequence<Reference<css::security::XCertificate> >());
+
+ bool bCertValid = certStatus == css::security::CertificateValidity::VALID;
+
+ if ( !bCertValid )
+ {
+ m_xCertImg->set_from_icon_name(BMP_STATE_NOT_VALIDATED);
+ m_xHintNotTrustedFT->set_label(XsResId(STR_CERTIFICATE_NOT_VALIDATED));
+ }
+
+ // insert data
+ css::uno::Reference< css::security::XCertificate > xCert = mpDlg->mxCert;
+
+ OUString sSubjectName(xmlsec::GetContentPart(xCert->getSubjectName(), xCert->getCertificateKind()));
+ if (!sSubjectName.isEmpty())
+ m_xIssuedToFT->set_label(sSubjectName);
+ else
+ m_xIssuedToLabelFT->hide();
+ OUString sIssuerName(xmlsec::GetContentPart(xCert->getIssuerName(), xCert->getCertificateKind()));
+ if (!sIssuerName.isEmpty())
+ m_xIssuedByFT->set_label(sIssuerName);
+ else
+ m_xIssuedByLabelFT->hide();
+
+ DateTime aDateTimeStart( DateTime::EMPTY );
+ DateTime aDateTimeEnd( DateTime::EMPTY );
+ utl::typeConvert( xCert->getNotValidBefore(), aDateTimeStart );
+ utl::typeConvert( xCert->getNotValidAfter(), aDateTimeEnd );
+
+ OUString sValidFromDate = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTimeStart.GetDate()));
+ OUString sValidToDate = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTimeEnd.GetDate()));
+
+ m_xValidFromDateFT->set_label(sValidFromDate);
+ m_xValidToDateFT->set_label(sValidToDate);
+
+ // Check if we have the private key...
+ bool bHasPrivateKey = false;
+ // #i41270# Check only if we have that certificate in our security environment
+ if (pDlg->mbCheckForPrivateKey)
+ {
+ tools::Long nCertificateCharacters = pDlg->mxSecurityEnvironment->getCertificateCharacters(xCert);
+ bHasPrivateKey = (nCertificateCharacters & security::CertificateCharacters::HAS_PRIVATE_KEY);
+ }
+ if (!bHasPrivateKey)
+ {
+ m_xKeyImg->hide();
+ m_xHintCorrespPrivKeyFT->hide();
+ }
+}
+
+void CertificateViewerDetailsTP::InsertElement(const OUString& rField, const OUString& rValue,
+ const OUString& rDetails, bool bFixedWidthFont)
+{
+ m_aUserData.emplace_back(std::make_unique<Details_UserDatat>(rDetails, bFixedWidthFont));
+ OUString sId(weld::toId(m_aUserData.back().get()));
+ m_xElementsLB->append(sId, rField);
+ m_xElementsLB->set_text(m_xElementsLB->n_children() -1, rValue, 1);
+}
+
+CertificateViewerDetailsTP::CertificateViewerDetailsTP(weld::Container* pParent, CertificateViewer* pDlg)
+ : CertificateViewerTP(pParent, "xmlsec/ui/certdetails.ui", "CertDetails", pDlg)
+ , m_xElementsLB(mxBuilder->weld_tree_view("tablecontainer"))
+ , m_xValueDetails(mxBuilder->weld_text_view("valuedetails"))
+{
+ const int nWidth = m_xElementsLB->get_approximate_digit_width() * 60;
+ const int nHeight = m_xElementsLB->get_height_rows(8);
+ m_xElementsLB->set_size_request(nWidth, nHeight);
+ m_xValueDetails->set_size_request(nWidth, nHeight);
+ m_xElementsLB->set_column_fixed_widths( { nWidth / 2 } );
+
+ // fill list box
+ Reference< security::XCertificate > xCert = mpDlg->mxCert;
+ sal_uInt16 nLineBreak = 16;
+ const char* const pHexSep = " ";
+ OUString aLBEntry;
+ OUString aDetails;
+ // Certificate Versions are reported wrong (#i35107#) - 0 == "V1", 1 == "V2", ..., n = "V(n+1)"
+ aLBEntry = "V" + OUString::number( xCert->getVersion() + 1 );
+ InsertElement( XsResId( STR_VERSION ), aLBEntry, aLBEntry );
+ Sequence< sal_Int8 > aSeq = xCert->getSerialNumber();
+ aLBEntry = xmlsec::GetHexString( aSeq, pHexSep );
+ aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak );
+ InsertElement( XsResId( STR_SERIALNUM ), aLBEntry, aDetails, true );
+
+ std::pair< OUString, OUString> pairIssuer =
+ xmlsec::GetDNForCertDetailsView(xCert->getIssuerName());
+ aLBEntry = pairIssuer.first;
+ aDetails = pairIssuer.second;
+ InsertElement( XsResId( STR_ISSUER ), aLBEntry, aDetails );
+
+ DateTime aDateTime( DateTime::EMPTY );
+ utl::typeConvert( xCert->getNotValidBefore(), aDateTime );
+ aLBEntry = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTime.GetDate())) + " ";
+ aLBEntry += Application::GetSettings().GetUILocaleDataWrapper().getTime(tools::Time(aDateTime.GetTime()));
+ InsertElement( XsResId( STR_VALIDFROM ), aLBEntry, aLBEntry );
+ utl::typeConvert( xCert->getNotValidAfter(), aDateTime );
+ aLBEntry = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTime.GetDate()) ) + " ";
+ aLBEntry += Application::GetSettings().GetUILocaleDataWrapper().getTime(tools::Time(aDateTime.GetTime()));
+ InsertElement( XsResId( STR_VALIDTO ), aLBEntry, aLBEntry );
+
+ std::pair< OUString, OUString > pairSubject =
+ xmlsec::GetDNForCertDetailsView(xCert->getSubjectName());
+ aLBEntry = pairSubject.first;
+ aDetails = pairSubject.second;
+ InsertElement( XsResId( STR_SUBJECT ), aLBEntry, aDetails );
+
+ aLBEntry = aDetails = xCert->getSubjectPublicKeyAlgorithm();
+ InsertElement( XsResId( STR_SUBJECT_PUBKEY_ALGO ), aLBEntry, aDetails );
+ aSeq = xCert->getSubjectPublicKeyValue();
+ aLBEntry = xmlsec::GetHexString( aSeq, pHexSep );
+ aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak );
+ InsertElement( XsResId( STR_SUBJECT_PUBKEY_VAL ), aLBEntry, aDetails, true );
+
+ aLBEntry = aDetails = xCert->getSignatureAlgorithm();
+ InsertElement( XsResId( STR_SIGNATURE_ALGO ), aLBEntry, aDetails );
+
+ CertificateChooser* pChooser = mpDlg->GetParentChooser();
+ if (pChooser)
+ {
+ aLBEntry = CertificateChooser::UsageInClearText( mpDlg->mxCert->getCertificateUsage() );
+ InsertElement( XsResId( STR_USE ), aLBEntry, aLBEntry );
+ }
+
+ aSeq = xCert->getSHA1Thumbprint();
+ aLBEntry = xmlsec::GetHexString( aSeq, pHexSep );
+ aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak );
+ InsertElement( XsResId( STR_THUMBPRINT_SHA1 ), aLBEntry, aDetails, true );
+
+ aSeq = xCert->getMD5Thumbprint();
+ aLBEntry = xmlsec::GetHexString( aSeq, pHexSep );
+ aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak );
+ InsertElement( XsResId( STR_THUMBPRINT_MD5 ), aLBEntry, aDetails, true );
+
+ m_xElementsLB->connect_changed(LINK(this, CertificateViewerDetailsTP, ElementSelectHdl));
+}
+
+IMPL_LINK_NOARG(CertificateViewerDetailsTP, ElementSelectHdl, weld::TreeView&, void)
+{
+ int nEntry = m_xElementsLB->get_selected_index();
+ OUString aElementText;
+ bool bFixedWidthFont;
+ if (nEntry != -1)
+ {
+ const Details_UserDatat* p = weld::fromId<Details_UserDatat*>(m_xElementsLB->get_id(nEntry));
+ aElementText = p->maTxt;
+ bFixedWidthFont = p->mbFixedWidthFont;
+ }
+ else
+ bFixedWidthFont = false;
+
+ m_xValueDetails->set_monospace(bFixedWidthFont);
+ m_xValueDetails->set_text(aElementText);
+}
+
+CertificateViewerCertPathTP::CertificateViewerCertPathTP(weld::Container* pParent, CertificateViewer* pDlg)
+ : CertificateViewerTP(pParent, "xmlsec/ui/certpage.ui", "CertPage", pDlg)
+ , mpParent(pDlg)
+ , mbFirstActivateDone(false)
+ , mxCertPathLB(mxBuilder->weld_tree_view("signatures"))
+ , mxScratchIter(mxCertPathLB->make_iterator())
+ , mxViewCertPB(mxBuilder->weld_button("viewcert"))
+ , mxCertStatusML(mxBuilder->weld_text_view("status"))
+ , mxCertOK(mxBuilder->weld_label("certok"))
+ , mxCertNotValidated(mxBuilder->weld_label("certnotok"))
+{
+ const int nWidth = mxCertPathLB->get_approximate_digit_width() * 60;
+ const int nHeight = mxCertPathLB->get_height_rows(6);
+ mxCertPathLB->set_size_request(nWidth, nHeight);
+ mxCertStatusML->set_size_request(nWidth, nHeight);
+
+ mxCertPathLB->connect_changed( LINK( this, CertificateViewerCertPathTP, CertSelectHdl ) );
+ mxViewCertPB->connect_clicked( LINK( this, CertificateViewerCertPathTP, ViewCertHdl ) );
+}
+
+CertificateViewerCertPathTP::~CertificateViewerCertPathTP()
+{
+ if (mxCertificateViewer)
+ mxCertificateViewer->response(RET_OK);
+}
+
+void CertificateViewerCertPathTP::ActivatePage()
+{
+ if ( mbFirstActivateDone )
+ return;
+
+ mbFirstActivateDone = true;
+ Sequence< Reference< security::XCertificate > > aCertPath =
+ mpParent->mxSecurityEnvironment->buildCertificatePath( mpParent->mxCert );
+ const Reference< security::XCertificate >* pCertPath = aCertPath.getConstArray();
+
+ sal_Int32 i, nCnt = aCertPath.getLength();
+ std::unique_ptr<weld::TreeIter> xParent;
+ for (i = nCnt-1; i >= 0; i--)
+ {
+ const Reference< security::XCertificate > rCert = pCertPath[ i ];
+ OUString sName = xmlsec::GetContentPart( rCert->getSubjectName(), rCert->getCertificateKind() );
+ //Verify the certificate
+ sal_Int32 certStatus = mpDlg->mxSecurityEnvironment->verifyCertificate(rCert,
+ Sequence<Reference<css::security::XCertificate> >());
+ bool bCertValid = certStatus == css::security::CertificateValidity::VALID;
+ InsertCert(xParent.get(), sName, rCert, bCertValid);
+ if (!xParent)
+ {
+ xParent = mxCertPathLB->make_iterator();
+ (void)mxCertPathLB->get_iter_first(*xParent);
+ }
+ else
+ {
+ (void)mxCertPathLB->iter_children(*xParent);
+ }
+ }
+
+ if (xParent)
+ mxCertPathLB->select(*xParent);
+ mxViewCertPB->set_sensitive(false); // Own certificate selected
+
+ while (xParent)
+ {
+ mxCertPathLB->expand_row(*xParent);
+ if (!mxCertPathLB->iter_parent(*xParent))
+ xParent.reset();
+ }
+
+ CertSelectHdl(*mxCertPathLB);
+}
+
+IMPL_LINK_NOARG(CertificateViewerCertPathTP, ViewCertHdl, weld::Button&, void)
+{
+ std::unique_ptr<weld::TreeIter> xIter = mxCertPathLB->make_iterator();
+ if (mxCertPathLB->get_selected(xIter.get()))
+ {
+ if (mxCertificateViewer)
+ mxCertificateViewer->response(RET_OK);
+
+ CertPath_UserData* pData = weld::fromId<CertPath_UserData*>(mxCertPathLB->get_id(*xIter));
+ mxCertificateViewer = std::make_shared<CertificateViewer>(mpDlg->getDialog(), mpDlg->mxSecurityEnvironment,
+ pData->mxCert, false, nullptr);
+ weld::DialogController::runAsync(mxCertificateViewer, [this] (sal_Int32) { mxCertificateViewer = nullptr; });
+ }
+}
+
+IMPL_LINK_NOARG(CertificateViewerCertPathTP, CertSelectHdl, weld::TreeView&, void)
+{
+ OUString sStatus;
+
+ std::unique_ptr<weld::TreeIter> xIter = mxCertPathLB->make_iterator();
+ bool bEntry = mxCertPathLB->get_selected(xIter.get());
+ if (bEntry)
+ {
+ CertPath_UserData* pData = weld::fromId<CertPath_UserData*>(mxCertPathLB->get_id(*xIter));
+ if (pData)
+ sStatus = pData->mbValid ? mxCertOK->get_label() : mxCertNotValidated->get_label();
+ }
+
+ mxCertStatusML->set_text(sStatus);
+
+ bool bSensitive = false;
+ if (bEntry)
+ {
+ // if has children, so not the last one in the chain
+ if (mxCertPathLB->iter_children(*xIter))
+ bSensitive = true;
+ }
+ mxViewCertPB->set_sensitive(bSensitive);
+}
+
+void CertificateViewerCertPathTP::InsertCert(const weld::TreeIter* pParent, const OUString& rName,
+ const css::uno::Reference< css::security::XCertificate >& rxCert,
+ bool bValid)
+{
+ auto const sImage = bValid ? std::u16string_view(u"" BMP_CERT_OK) : std::u16string_view(u"" BMP_CERT_NOT_OK);
+ maUserData.emplace_back(std::make_unique<CertPath_UserData>(rxCert, bValid));
+ OUString sId(weld::toId(maUserData.back().get()));
+ mxCertPathLB->insert(pParent, -1, &rName, &sId, nullptr, nullptr, false, mxScratchIter.get());
+ mxCertPathLB->set_image(*mxScratchIter, OUString(sImage));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
new file mode 100644
index 000000000..1d40e7562
--- /dev/null
+++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
@@ -0,0 +1,801 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <digitalsignaturesdialog.hxx>
+#include <certificatechooser.hxx>
+#include <certificateviewer.hxx>
+#include <biginteger.hxx>
+#include <sax/tools/converter.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/security/NoPasswordException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/security/CertificateKind.hpp>
+#include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+
+#include <osl/file.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <unotools/datetime.hxx>
+
+#include <bitmaps.hlst>
+#include <strings.hrc>
+#include <resourcemanager.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/xmlsechelper.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/configitem.hxx>
+
+#ifdef _WIN32
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <prewin.h>
+#include <Shlobj.h>
+#endif
+
+using namespace comphelper;
+using namespace css::security;
+using namespace css::uno;
+using namespace css;
+
+namespace
+{
+ class SaveODFItem: public utl::ConfigItem
+ {
+ private:
+ sal_Int16 m_nODF;
+
+ virtual void ImplCommit() override;
+
+ public:
+ virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ SaveODFItem();
+ //See group ODF in Common.xcs
+ bool isLessODF1_2() const
+ {
+ return m_nODF < 3;
+ }
+ };
+
+ void SaveODFItem::ImplCommit() {}
+ void SaveODFItem::Notify( const css::uno::Sequence< OUString >& ) {}
+
+ SaveODFItem::SaveODFItem(): utl::ConfigItem("Office.Common/Save"), m_nODF(0)
+ {
+ OUString sDef("ODF/DefaultVersion");
+ Sequence< css::uno::Any > aValues = GetProperties( Sequence<OUString>(&sDef,1) );
+ if ( aValues.getLength() != 1)
+ throw uno::RuntimeException(
+ "[xmlsecurity] Could not open property Office.Common/Save/ODF/DefaultVersion",
+ nullptr);
+
+ sal_Int16 nTmp = 0;
+ if ( !(aValues[0] >>= nTmp) )
+ throw uno::RuntimeException(
+ "[xmlsecurity]SaveODFItem::SaveODFItem(): Wrong Type!",
+ nullptr );
+
+ m_nODF = nTmp;
+ }
+}
+
+DigitalSignaturesDialog::DigitalSignaturesDialog(
+ weld::Window* pParent,
+ const uno::Reference< uno::XComponentContext >& rxCtx, DocumentSignatureMode eMode,
+ bool bReadOnly, const OUString& sODFVersion, bool bHasDocumentSignature)
+ : GenericDialogController(pParent, "xmlsec/ui/digitalsignaturesdialog.ui", "DigitalSignaturesDialog")
+ , maSignatureManager(rxCtx, eMode)
+ , m_sODFVersion (sODFVersion)
+ , m_bHasDocumentSignature(bHasDocumentSignature)
+ , m_bWarningShowSignMacro(false)
+ , m_xHintDocFT(m_xBuilder->weld_label("dochint"))
+ , m_xHintBasicFT(m_xBuilder->weld_label("macrohint"))
+ , m_xHintPackageFT(m_xBuilder->weld_label("packagehint"))
+ , m_xSignaturesLB(m_xBuilder->weld_tree_view("signatures"))
+ , m_xSigsValidImg(m_xBuilder->weld_image("validimg"))
+ , m_xSigsValidFI(m_xBuilder->weld_label("validft"))
+ , m_xSigsInvalidImg(m_xBuilder->weld_image("invalidimg"))
+ , m_xSigsInvalidFI(m_xBuilder->weld_label("invalidft"))
+ , m_xSigsNotvalidatedImg(m_xBuilder->weld_image("notvalidatedimg"))
+ , m_xSigsNotvalidatedFI(m_xBuilder->weld_label("notvalidatedft"))
+ , m_xSigsOldSignatureImg(m_xBuilder->weld_image("oldsignatureimg"))
+ , m_xSigsOldSignatureFI(m_xBuilder->weld_label("oldsignatureft"))
+ , m_xAdESCompliantCB(m_xBuilder->weld_check_button("adescompliant"))
+ , m_xViewBtn(m_xBuilder->weld_button("view"))
+ , m_xAddBtn(m_xBuilder->weld_button("sign"))
+ , m_xRemoveBtn(m_xBuilder->weld_button("remove"))
+ , m_xStartCertMgrBtn(m_xBuilder->weld_button("start_certmanager"))
+ , m_xCloseBtn(m_xBuilder->weld_button("close"))
+{
+ m_bAdESCompliant = !DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
+
+ auto nControlWidth = m_xSignaturesLB->get_approximate_digit_width() * 105;
+ m_xSignaturesLB->set_size_request(nControlWidth, m_xSignaturesLB->get_height_rows(10));
+
+ // Give the first column 6 percent, try to distribute the rest equally.
+ std::vector<int> aWidths;
+ aWidths.push_back(6*nControlWidth/100);
+ auto nColWidth = (nControlWidth - aWidths[0]) / 4;
+ aWidths.push_back(nColWidth);
+ aWidths.push_back(nColWidth);
+ aWidths.push_back(nColWidth);
+ m_xSignaturesLB->set_column_fixed_widths(aWidths);
+
+ mbVerifySignatures = true;
+ mbSignaturesChanged = false;
+
+ m_xSignaturesLB->connect_changed( LINK( this, DigitalSignaturesDialog, SignatureHighlightHdl ) );
+ m_xSignaturesLB->connect_row_activated( LINK( this, DigitalSignaturesDialog, SignatureSelectHdl ) );
+
+ m_xAdESCompliantCB->connect_toggled( LINK( this, DigitalSignaturesDialog, AdESCompliantCheckBoxHdl ) );
+ m_xAdESCompliantCB->set_active(m_bAdESCompliant);
+
+ m_xViewBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, ViewButtonHdl ) );
+ m_xViewBtn->set_sensitive(false);
+
+ m_xAddBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, AddButtonHdl ) );
+ if ( bReadOnly )
+ m_xAddBtn->set_sensitive(false);
+
+ m_xRemoveBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, RemoveButtonHdl ) );
+ m_xRemoveBtn->set_sensitive(false);
+
+ m_xStartCertMgrBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, CertMgrButtonHdl ) );
+
+ m_xCloseBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, OKButtonHdl) );
+
+ switch( maSignatureManager.getSignatureMode() )
+ {
+ case DocumentSignatureMode::Content:
+ m_xHintDocFT->show();
+ break;
+ case DocumentSignatureMode::Macros:
+ m_xHintBasicFT->show();
+ break;
+ case DocumentSignatureMode::Package:
+ m_xHintPackageFT->show();
+ break;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ m_xAddBtn->hide();
+ m_xRemoveBtn->hide();
+ m_xStartCertMgrBtn->hide();
+ }
+}
+
+DigitalSignaturesDialog::~DigitalSignaturesDialog()
+{
+ if (m_xViewer)
+ m_xViewer->response(RET_OK);
+
+ if (m_xInfoBox)
+ m_xInfoBox->response(RET_OK);
+}
+
+bool DigitalSignaturesDialog::Init()
+{
+ bool bInit = maSignatureManager.init();
+
+ SAL_WARN_IF( !bInit, "xmlsecurity.dialogs", "Error initializing security context!" );
+
+ if ( bInit )
+ {
+ maSignatureManager.getSignatureHelper().SetStartVerifySignatureHdl( LINK( this, DigitalSignaturesDialog, StartVerifySignatureHdl ) );
+ }
+
+ return bInit;
+}
+
+void DigitalSignaturesDialog::SetStorage( const css::uno::Reference < css::embed::XStorage >& rxStore )
+{
+ if (!rxStore.is())
+ {
+ // PDF supports AdES.
+ m_bAdESCompliant = true;
+ m_xAdESCompliantCB->set_active(m_bAdESCompliant);
+ return;
+ }
+
+ maSignatureManager.setStore(rxStore);
+ maSignatureManager.getSignatureHelper().SetStorage( maSignatureManager.getStore(), m_sODFVersion);
+}
+
+void DigitalSignaturesDialog::SetSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream )
+{
+ maSignatureManager.setSignatureStream(rxStream);
+}
+
+bool DigitalSignaturesDialog::canAddRemove()
+{
+ //FIXME: this func needs some cleanup, such as real split between
+ //'canAdd' and 'canRemove' case
+ bool ret = true;
+
+ uno::Reference<container::XNameAccess> xNameAccess = maSignatureManager.getStore();
+ if (xNameAccess.is() && xNameAccess->hasByName("[Content_Types].xml"))
+ // It's always possible to append an OOXML signature.
+ return ret;
+
+ if (!maSignatureManager.getStore().is())
+ // It's always possible to append a PDF signature.
+ return ret;
+
+ OSL_ASSERT(maSignatureManager.getStore().is());
+ bool bDoc1_1 = DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
+ SaveODFItem item;
+ bool bSave1_1 = item.isLessODF1_2();
+
+ // see specification
+ //cvs: specs/www/appwide/security/Electronic_Signatures_and_Security.sxw
+ //Paragraph 'Behavior with regard to ODF 1.2'
+ //For both, macro and document
+ if ( (!bSave1_1 && bDoc1_1) || (bSave1_1 && bDoc1_1) )
+ {
+ //#4
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ XsResId(STR_XMLSECDLG_OLD_ODF_FORMAT)));
+ xBox->run();
+ ret = false;
+ }
+
+ //As of OOo 3.2 the document signature includes in macrosignatures.xml. That is
+ //adding a macro signature will break an existing document signature.
+ //The sfx2 will remove the documentsignature when the user adds a macro signature
+ if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Macros
+ && ret)
+ {
+ if (m_bHasDocumentSignature && !m_bWarningShowSignMacro)
+ {
+ //The warning says that the document signatures will be removed if the user
+ //continues. He can then either press 'OK' or 'NO'
+ //It the user presses 'Add' or 'Remove' several times then, then the warning
+ //is shown every time until the user presses 'OK'. From then on, the warning
+ //is not displayed anymore as long as the signatures dialog is alive.
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ XsResId(STR_XMLSECDLG_QUERY_REMOVEDOCSIGNBEFORESIGN)));
+ if (xBox->run() == RET_NO)
+ ret = false;
+ else
+ m_bWarningShowSignMacro = true;
+
+ }
+ }
+ return ret;
+}
+
+bool DigitalSignaturesDialog::canAdd()
+{
+ return canAddRemove();
+}
+
+bool DigitalSignaturesDialog::canRemove()
+{
+ bool bRet = true;
+
+ if ( maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ XsResId(STR_XMLSECDLG_QUERY_REALLYREMOVE)));
+ short nDlgRet = xBox->run();
+ bRet = ( nDlgRet == RET_YES );
+ }
+
+ return (bRet && canAddRemove());
+}
+
+void DigitalSignaturesDialog::beforeRun()
+{
+ // Verify Signatures and add certificates to ListBox...
+ mbVerifySignatures = true;
+ ImplGetSignatureInformations(/*bUseTempStream=*/false, /*bCacheLastSignature=*/true);
+ ImplFillSignaturesBox();
+
+ // FIXME: Disable the "Use XAdES compliant signatures" checkbox if it is irrelevant. If it is
+ // enabled, set its initial state based on existing signatures, if any.
+
+ // If it is OOXML, the checkbox is irrelevant.
+
+ // How to find out here whether it is OOXML? I don't want to create a SignatureStreamHelper and
+ // check its nStorageFormat as that seems overly complicated and seems to have weird indirect
+ // consequences, as I noticed when I tried to use DocumentSignatureManager::IsXAdESRelevant()
+ // (which now is in #if 0).
+
+ if (!maSignatureManager.getCurrentSignatureInformations().empty())
+ {
+ // If the document has only SHA-1 signatures we probably want it to stay that way?
+ }
+
+ // Only verify once, content will not change.
+ // But for refreshing signature information, StartVerifySignatureHdl will be called after each add/remove
+ mbVerifySignatures = false;
+}
+
+short DigitalSignaturesDialog::run()
+{
+ beforeRun();
+ return GenericDialogController::run();
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureHighlightHdl, weld::TreeView&, void)
+{
+ bool bSel = m_xSignaturesLB->get_selected_index() != -1;
+ m_xViewBtn->set_sensitive( bSel );
+ if ( m_xAddBtn->get_sensitive() ) // not read only
+ m_xRemoveBtn->set_sensitive( bSel );
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, OKButtonHdl, weld::Button&, void)
+{
+ if (mbSignaturesChanged)
+ maSignatureManager.write(m_bAdESCompliant);
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureSelectHdl, weld::TreeView&, bool)
+{
+ ImplShowSignaturesDetails();
+ return true;
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, AdESCompliantCheckBoxHdl, weld::Toggleable&, void)
+{
+ m_bAdESCompliant = m_xAdESCompliantCB->get_active();
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, ViewButtonHdl, weld::Button&, void)
+{
+ ImplShowSignaturesDetails();
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, AddButtonHdl, weld::Button&, void)
+{
+ if( ! canAdd())
+ return;
+ try
+ {
+ std::vector<uno::Reference<xml::crypto::XXMLSecurityContext>> xSecContexts
+ {
+ maSignatureManager.getSecurityContext()
+ };
+ // Gpg signing is only possible with ODF >= 1.2 documents
+ if (DocumentSignatureHelper::CanSignWithGPG(maSignatureManager.getStore(), m_sODFVersion))
+ xSecContexts.push_back(maSignatureManager.getGpgSecurityContext());
+
+ CertificateChooser aChooser(m_xDialog.get(), std::move(xSecContexts), UserAction::Sign);
+ if (aChooser.run() == RET_OK)
+ {
+ sal_Int32 nSecurityId;
+ if (!maSignatureManager.add(aChooser.GetSelectedCertificates()[0], aChooser.GetSelectedSecurityContext(),
+ aChooser.GetDescription(), nSecurityId, m_bAdESCompliant))
+ return;
+ mbSignaturesChanged = true;
+
+ xml::crypto::SecurityOperationStatus nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+
+ if (maSignatureManager.getStore().is())
+ // In the PDF case the signature information is only available after parsing.
+ nStatus = maSignatureManager.getSignatureHelper().GetSignatureInformation( nSecurityId ).nStatus;
+
+ if ( nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED )
+ {
+ mbSignaturesChanged = true;
+
+ // Can't simply remember current information, need parsing for getting full information :(
+ // We need to verify the signatures again, otherwise the status in the signature information
+ // will not contain
+ // SecurityOperationStatus_OPERATION_SUCCEEDED
+ mbVerifySignatures = true;
+ ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
+ ImplFillSignaturesBox();
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "adding a signature!" );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Error, VclButtonsType::Ok,
+ XsResId(STR_XMLSECDLG_SIGNING_FAILED)));
+ xBox->run();
+ // Don't keep invalid entries...
+ ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
+ ImplFillSignaturesBox();
+ }
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, RemoveButtonHdl, weld::Button&, void)
+{
+ if (!canRemove())
+ return;
+ int nEntry = m_xSignaturesLB->get_selected_index();
+ if (nEntry == -1)
+ return;
+
+ try
+ {
+ sal_uInt16 nSelected = m_xSignaturesLB->get_id(nEntry).toUInt32();
+ maSignatureManager.remove(nSelected);
+
+ mbSignaturesChanged = true;
+
+ ImplFillSignaturesBox();
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "Exception while removing a signature!" );
+ // Don't keep invalid entries...
+ ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/true);
+ ImplFillSignaturesBox();
+ }
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, CertMgrButtonHdl, weld::Button&, void)
+{
+#ifdef _WIN32
+ // FIXME: call GpgME::dirInfo("bindir") somewhere in
+ // SecurityEnvironmentGpg or whatnot
+ // FIXME: perhaps poke GpgME for uiserver, and hope it returns something useful?
+ static const std::u16string_view aGUIServers[] = { u"Gpg4win\\kleopatra.exe",
+ u"Gpg4win\\bin\\kleopatra.exe",
+ u"GNU\\GnuPG\\kleopatra.exe",
+ u"GNU\\GnuPG\\launch-gpa.exe",
+ u"GNU\\GnuPG\\gpa.exe",
+ u"GnuPG\\bin\\gpa.exe",
+ u"GNU\\GnuPG\\bin\\kleopatra.exe",
+ u"GNU\\GnuPG\\bin\\launch-gpa.exe",
+ u"GNU\\GnuPG\\bin\\gpa.exe",
+ };
+ static const OUString aPath = [] {
+ OUString sRet;
+ PWSTR sPath = nullptr;
+ HRESULT hr
+ = SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, KF_FLAG_DEFAULT, nullptr, &sPath);
+ if (SUCCEEDED(hr))
+ {
+ sRet = o3tl::toU(sPath);
+ CoTaskMemFree(sPath);
+ }
+ return sRet;
+ }();
+ if (aPath.isEmpty())
+ return;
+#else
+ static const std::u16string_view aGUIServers[] = { u"kleopatra", u"seahorse", u"gpa", u"kgpg" };
+ const char* cPath = getenv("PATH");
+ if (!cPath)
+ return;
+ OUString aPath(cPath, strlen(cPath), osl_getThreadTextEncoding());
+#endif
+
+ OUString sFoundGUIServer, sExecutable;
+
+ for ( auto const &rServer : aGUIServers )
+ {
+ osl::FileBase::RC searchError = osl::File::searchFileURL(OUString(rServer), aPath, sFoundGUIServer );
+ if (searchError == osl::FileBase::E_None)
+ {
+ osl::File::getSystemPathFromFileURL( sFoundGUIServer, sExecutable );
+ break;
+ }
+
+ }
+
+ if ( !sExecutable.isEmpty() )
+ {
+ uno::Reference< uno::XComponentContext > xContext =
+ ::comphelper::getProcessComponentContext();
+ uno::Reference< css::system::XSystemShellExecute > xSystemShell(
+ css::system::SystemShellExecute::create(xContext) );
+
+ xSystemShell->execute( sExecutable, OUString(),
+ css::system::SystemShellExecuteFlags::DEFAULTS );
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ XsResId(STR_XMLSECDLG_NO_CERT_MANAGER)));
+ xInfoBox->run();
+ }
+}
+
+IMPL_LINK_NOARG(DigitalSignaturesDialog, StartVerifySignatureHdl, LinkParamNone*, bool)
+{
+ return mbVerifySignatures;
+}
+
+void DigitalSignaturesDialog::ImplFillSignaturesBox()
+{
+ m_xSignaturesLB->clear();
+
+ size_t nInfos = maSignatureManager.getCurrentSignatureInformations().size();
+ size_t nValidSigs = 0, nValidCerts = 0;
+ bool bAllNewSignatures = true;
+ bool bSomePartial = false;
+
+ if( nInfos )
+ {
+ for( size_t n = 0; n < nInfos; ++n )
+ {
+ DocumentSignatureAlgorithm mode = DocumentSignatureHelper::getDocumentAlgorithm(
+ m_sODFVersion, maSignatureManager.getCurrentSignatureInformations()[n]);
+ std::vector< OUString > aElementsToBeVerified;
+ if (maSignatureManager.getStore().is())
+ aElementsToBeVerified = DocumentSignatureHelper::CreateElementList(maSignatureManager.getStore(), maSignatureManager.getSignatureMode(), mode);
+
+ const SignatureInformation& rInfo = maSignatureManager.getCurrentSignatureInformations()[n];
+ uno::Reference< css::security::XCertificate > xCert = getCertificate(rInfo);
+
+ OUString aSubject;
+ OUString aIssuer;
+ OUString aDateTimeStr;
+ OUString aDescription;
+ OUString aType;
+
+ bool bCertValid = false;
+ if( xCert.is() )
+ {
+ //check the validity of the cert
+ try {
+ sal_Int32 certResult = getSecurityEnvironmentForCertificate(xCert)->verifyCertificate(xCert,
+ Sequence<uno::Reference<security::XCertificate> >());
+
+ bCertValid = certResult == css::security::CertificateValidity::VALID;
+ if ( bCertValid )
+ nValidCerts++;
+
+ } catch (css::uno::SecurityException& ) {
+ OSL_FAIL("Verification of certificate failed");
+ bCertValid = false;
+ }
+
+ aSubject = xmlsec::GetContentPart( xCert->getSubjectName(), xCert->getCertificateKind() );
+ aIssuer = xmlsec::GetContentPart( xCert->getIssuerName(), xCert->getCertificateKind() );
+ }
+ else if (!rInfo.ouGpgCertificate.isEmpty())
+ {
+ // In case we don't have the gpg key locally, get some data from the document
+ aIssuer = rInfo.ouGpgOwner;
+ }
+
+ aDateTimeStr = utl::GetDateTimeString( rInfo.stDateTime );
+ aDescription = rInfo.ouDescription;
+
+ // Decide type string.
+ if (maSignatureManager.getStore().is())
+ {
+ // OpenPGP
+ if (!rInfo.ouGpgCertificate.isEmpty())
+ aType = "OpenPGP";
+ // XML based: XAdES or not.
+ else if (rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->CertDigest.isEmpty())
+ aType = "XAdES";
+ else
+ aType = "XML-DSig";
+ }
+ else
+ {
+ // Assume PDF: PAdES or not.
+ if (rInfo.bHasSigningCertificate)
+ aType = "PAdES";
+ else
+ aType = "PDF";
+ }
+
+ bool bSigValid = rInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+
+ if ( bSigValid )
+ {
+ if (maSignatureManager.getStore().is())
+ {
+ // ZIP based.
+ bSigValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
+ aElementsToBeVerified, rInfo, mode);
+ }
+ else
+ {
+ // Assume PDF.
+ bSigValid = !rInfo.bPartialDocumentSignature;
+ }
+
+ if( bSigValid )
+ nValidSigs++;
+ else
+ {
+ bSomePartial = true;
+ }
+ }
+
+ OUString sImage;
+ if (!bSigValid)
+ {
+ sImage = BMP_SIG_INVALID;
+ }
+ else if (!bCertValid)
+ {
+ sImage = BMP_SIG_NOT_VALIDATED;
+ }
+ //Check if the signature is a "old" document signature, that is, which was created
+ //by a version of OOo previous to 3.2
+ // If there is no storage, then it's pointless to check storage
+ // stream references.
+ else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content
+ && (maSignatureManager.getStore().is() && !DocumentSignatureHelper::isOOo3_2_Signature(
+ maSignatureManager.getCurrentSignatureInformations()[n])))
+ {
+ sImage = BMP_SIG_NOT_VALIDATED;
+ bAllNewSignatures = false;
+ }
+ else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content
+ && DocumentSignatureHelper::isOOo3_2_Signature(
+ maSignatureManager.getCurrentSignatureInformations()[n]))
+ {
+ sImage = BMP_SIG_VALID;
+ }
+ else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Macros)
+ {
+ sImage = BMP_SIG_VALID;
+ }
+
+ m_xSignaturesLB->insert(nullptr, n, nullptr, nullptr,
+ &sImage, nullptr, false, nullptr);
+ m_xSignaturesLB->set_text(n, aSubject, 1);
+ m_xSignaturesLB->set_text(n, aIssuer, 2);
+ m_xSignaturesLB->set_text(n, aDateTimeStr, 3);
+ m_xSignaturesLB->set_text(n, aDescription, 4);
+ m_xSignaturesLB->set_text(n, aType, 5);
+ m_xSignaturesLB->set_id(n, OUString::number(n)); // misuse user data as index
+ }
+ }
+
+ bool bAllSigsValid = (nValidSigs == nInfos);
+ bool bAllCertsValid = (nValidCerts == nInfos);
+ bool bShowValidState = nInfos && (bAllSigsValid && bAllCertsValid && bAllNewSignatures);
+
+ m_xSigsValidImg->set_visible( bShowValidState);
+ m_xSigsValidFI->set_visible( bShowValidState );
+
+ bool bShowInvalidState = nInfos && !bAllSigsValid;
+
+ m_xSigsInvalidImg->set_visible( bShowInvalidState && !bSomePartial);
+ m_xSigsInvalidFI->set_visible( bShowInvalidState && !bSomePartial);
+
+ bool bShowNotValidatedState = nInfos && bAllSigsValid && !bAllCertsValid;
+
+ m_xSigsNotvalidatedImg->set_visible(bShowNotValidatedState);
+ m_xSigsNotvalidatedFI->set_visible(bShowNotValidatedState);
+
+ //bAllNewSignatures is always true if we are not in document mode
+ bool bShowOldSignature = nInfos && bAllSigsValid && bAllCertsValid && !bAllNewSignatures;
+ m_xSigsOldSignatureImg->set_visible(bShowOldSignature || bSomePartial);
+ m_xSigsOldSignatureFI->set_visible(bShowOldSignature || bSomePartial);
+
+ SignatureHighlightHdl(*m_xSignaturesLB);
+}
+
+uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(const SignatureInformation& rInfo)
+{
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = maSignatureManager.getSecurityEnvironment();
+ uno::Reference<xml::crypto::XSecurityEnvironment> xGpgSecEnv = maSignatureManager.getGpgSecurityEnvironment();
+ uno::Reference<security::XCertificate> xCert;
+
+ //First we try to get the certificate which is embedded in the XML Signature
+ if (xSecEnv.is() && rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->X509Certificate.isEmpty())
+ xCert = xSecEnv->createCertificateFromAscii(rInfo.GetSigningCertificate()->X509Certificate);
+ else {
+ //There must be an embedded certificate because we use it to get the
+ //issuer name. We cannot use /Signature/KeyInfo/X509Data/X509IssuerName
+ //because it could be modified by an attacker. The issuer is displayed
+ //in the digital signature dialog.
+ //Comparing the X509IssuerName with the one from the X509Certificate in order
+ //to find out if the X509IssuerName was modified does not work. See #i62684
+ SAL_WARN( "xmlsecurity.dialogs", "Could not find embedded certificate!");
+ }
+
+ //In case there is no embedded certificate we try to get it from a local store
+ if (!xCert.is() && xSecEnv.is() && rInfo.GetSigningCertificate())
+ {
+ xCert = xSecEnv->getCertificate(rInfo.GetSigningCertificate()->X509IssuerName,
+ xmlsecurity::numericStringToBigInteger(rInfo.GetSigningCertificate()->X509SerialNumber));
+ }
+ if (!xCert.is() && xGpgSecEnv.is() && !rInfo.ouGpgKeyID.isEmpty())
+ xCert = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger(u"") );
+
+ SAL_WARN_IF( !xCert.is(), "xmlsecurity.dialogs", "Certificate not found and can't be created!" );
+
+ return xCert;
+}
+
+uno::Reference<xml::crypto::XSecurityEnvironment> DigitalSignaturesDialog::getSecurityEnvironmentForCertificate(const uno::Reference<security::XCertificate>& xCert)
+{
+ if (xCert->getCertificateKind() == CertificateKind_OPENPGP)
+ return maSignatureManager.getGpgSecurityEnvironment();
+ else if (xCert->getCertificateKind() == CertificateKind_X509)
+ return maSignatureManager.getSecurityEnvironment();
+
+ throw RuntimeException("Unknown certificate kind");
+}
+
+//If bUseTempStream is true then the temporary signature stream is used.
+//Otherwise the real signature stream is used.
+void DigitalSignaturesDialog::ImplGetSignatureInformations(bool bUseTempStream, bool bCacheLastSignature)
+{
+ maSignatureManager.read(bUseTempStream, bCacheLastSignature);
+ mbVerifySignatures = false;
+}
+
+void DigitalSignaturesDialog::ImplShowSignaturesDetails()
+{
+ int nEntry = m_xSignaturesLB->get_selected_index();
+ if (nEntry == -1)
+ return;
+
+ sal_uInt16 nSelected = m_xSignaturesLB->get_id(nEntry).toUInt32();
+ const SignatureInformation& rInfo = maSignatureManager.getCurrentSignatureInformations()[ nSelected ];
+ uno::Reference<security::XCertificate> xCert = getCertificate(rInfo);
+
+ if ( xCert.is() )
+ {
+ if (m_xViewer)
+ m_xViewer->response(RET_OK);
+
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = getSecurityEnvironmentForCertificate(xCert);
+ m_xViewer = std::make_shared<CertificateViewer>(m_xDialog.get(), xSecEnv, xCert, false, nullptr);
+ weld::DialogController::runAsync(m_xViewer, [this] (sal_Int32) { m_xViewer = nullptr; });
+ }
+ else
+ {
+ if (m_xInfoBox)
+ m_xInfoBox->response(RET_OK);
+
+ m_xInfoBox = std::shared_ptr<weld::MessageDialog>(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ XsResId(STR_XMLSECDLG_NO_CERT_FOUND)));
+ m_xInfoBox->runAsync(m_xInfoBox, [this] (sal_Int32) { m_xInfoBox = nullptr; });
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/dialogs/macrosecurity.cxx b/xmlsecurity/source/dialogs/macrosecurity.cxx
new file mode 100644
index 000000000..bf82606f4
--- /dev/null
+++ b/xmlsecurity/source/dialogs/macrosecurity.cxx
@@ -0,0 +1,446 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <macrosecurity.hxx>
+#include <certificateviewer.hxx>
+#include <biginteger.hxx>
+#include <resourcemanager.hxx>
+#include <strings.hrc>
+
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/xmlsechelper.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/ui/dialogs/FolderPicker.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <sfx2/filedlghelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <unotools/datetime.hxx>
+
+#include <vcl/svapp.hxx>
+
+using namespace comphelper;
+using namespace ::com::sun::star;
+
+
+IMPL_LINK_NOARG(MacroSecurity, OkBtnHdl, weld::Button&, void)
+{
+ m_xLevelTP->ClosePage();
+ m_xTrustSrcTP->ClosePage();
+ m_xDialog->response(RET_OK);
+}
+
+MacroSecurity::MacroSecurity(weld::Window* pParent,
+ const css::uno::Reference<css::xml::crypto::XSecurityEnvironment>& rxSecurityEnvironment)
+ : GenericDialogController(pParent, "xmlsec/ui/macrosecuritydialog.ui", "MacroSecurityDialog")
+ , m_xSecurityEnvironment(rxSecurityEnvironment)
+ , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
+ , m_xOkBtn(m_xBuilder->weld_button("ok"))
+ , m_xResetBtn(m_xBuilder->weld_button("reset"))
+{
+ m_xTabCtrl->connect_enter_page(LINK(this, MacroSecurity, ActivatePageHdl));
+
+ m_xLevelTP.reset(new MacroSecurityLevelTP(m_xTabCtrl->get_page("SecurityLevelPage"), this));
+ m_xTrustSrcTP.reset(new MacroSecurityTrustedSourcesTP(m_xTabCtrl->get_page("SecurityTrustPage"), this));
+
+ m_xTabCtrl->set_current_page("SecurityLevelPage");
+ m_xOkBtn->connect_clicked(LINK(this, MacroSecurity, OkBtnHdl));
+}
+
+IMPL_LINK(MacroSecurity, ActivatePageHdl, const OString&, rPage, void)
+{
+ if (rPage == "SecurityLevelPage")
+ m_xLevelTP->ActivatePage();
+ else if (rPage == "SecurityTrustPage")
+ m_xTrustSrcTP->ActivatePage();
+}
+
+MacroSecurityTP::MacroSecurityTP(weld::Container* pParent, const OUString& rUIXMLDescription,
+ const OString& rID, MacroSecurity* pDlg)
+ : m_xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription))
+ , m_xContainer(m_xBuilder->weld_container(rID))
+ , m_pDlg(pDlg)
+{
+}
+
+void MacroSecurityTP::ActivatePage()
+{
+}
+
+MacroSecurityTP::~MacroSecurityTP()
+{
+}
+
+MacroSecurityLevelTP::MacroSecurityLevelTP(weld::Container* pParent, MacroSecurity* pDlg)
+ : MacroSecurityTP(pParent, "xmlsec/ui/securitylevelpage.ui", "SecurityLevelPage", pDlg)
+ , m_xVeryHighRB(m_xBuilder->weld_radio_button("vhigh"))
+ , m_xHighRB(m_xBuilder->weld_radio_button("high"))
+ , m_xMediumRB(m_xBuilder->weld_radio_button("med"))
+ , m_xLowRB(m_xBuilder->weld_radio_button("low"))
+ , m_xVHighImg(m_xBuilder->weld_widget("vhighimg"))
+ , m_xHighImg(m_xBuilder->weld_widget("highimg"))
+ , m_xMedImg(m_xBuilder->weld_widget("medimg"))
+ , m_xLowImg(m_xBuilder->weld_widget("lowimg"))
+{
+ m_xLowRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) );
+ m_xMediumRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) );
+ m_xHighRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) );
+ m_xVeryHighRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) );
+
+ int nPrefWidth(std::max({m_xVeryHighRB->get_preferred_size().Width(),
+ m_xHighRB->get_preferred_size().Width(),
+ m_xMediumRB->get_preferred_size().Width(),
+ m_xLowRB->get_preferred_size().Width()}));
+ int nMaxWidth = m_xLowRB->get_approximate_digit_width() * 60;
+ if (nPrefWidth > nMaxWidth)
+ {
+ m_xLowRB->set_label_wrap(true);
+ m_xLowRB->set_size_request(nMaxWidth, -1);
+ m_xMediumRB->set_label_wrap(true);
+ m_xMediumRB->set_size_request(nMaxWidth, -1);
+ m_xHighRB->set_label_wrap(true);
+ m_xHighRB->set_size_request(nMaxWidth, -1);
+ m_xVeryHighRB->set_label_wrap(true);
+ m_xVeryHighRB->set_size_request(nMaxWidth, -1);
+ }
+
+ mnCurLevel = static_cast<sal_uInt16>(SvtSecurityOptions::GetMacroSecurityLevel());
+ bool bReadonly = SvtSecurityOptions::IsReadOnly( SvtSecurityOptions::EOption::MacroSecLevel );
+
+ weld::RadioButton* pCheck = nullptr;
+ weld::Widget* pImage = nullptr;
+ switch (mnCurLevel)
+ {
+ case 3:
+ pCheck = m_xVeryHighRB.get();
+ pImage = m_xVHighImg.get();
+ break;
+ case 2:
+ pCheck = m_xHighRB.get();
+ pImage = m_xHighImg.get();
+ break;
+ case 1:
+ pCheck = m_xMediumRB.get();
+ pImage = m_xMedImg.get();
+ break;
+ case 0:
+ pCheck = m_xLowRB.get();
+ pImage = m_xLowImg.get();
+ break;
+ }
+ if (pCheck)
+ pCheck->set_active(true);
+ else
+ {
+ OSL_FAIL("illegal macro security level");
+ }
+ if (bReadonly && pImage)
+ {
+ pImage->show();
+ m_xVeryHighRB->set_sensitive(false);
+ m_xHighRB->set_sensitive(false);
+ m_xMediumRB->set_sensitive(false);
+ m_xLowRB->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(MacroSecurityLevelTP, RadioButtonHdl, weld::Toggleable&, void)
+{
+ sal_uInt16 nNewLevel = 0;
+ if( m_xVeryHighRB->get_active() )
+ nNewLevel = 3;
+ else if( m_xHighRB->get_active() )
+ nNewLevel = 2;
+ else if( m_xMediumRB->get_active() )
+ nNewLevel = 1;
+
+ if ( nNewLevel != mnCurLevel )
+ {
+ mnCurLevel = nNewLevel;
+ m_pDlg->EnableReset();
+ }
+}
+
+void MacroSecurityLevelTP::ClosePage()
+{
+ SvtSecurityOptions::SetMacroSecurityLevel( mnCurLevel );
+}
+
+void MacroSecurityTrustedSourcesTP::ImplCheckButtons()
+{
+ bool bCertSelected = m_xTrustCertLB->get_selected_index() != -1;
+ m_xViewCertPB->set_sensitive( bCertSelected );
+ m_xRemoveCertPB->set_sensitive( bCertSelected && !mbAuthorsReadonly);
+
+ bool bLocationSelected = m_xTrustFileLocLB->get_selected_index() != -1;
+ m_xRemoveLocPB->set_sensitive( bLocationSelected && !mbURLsReadonly);
+}
+
+void MacroSecurityTrustedSourcesTP::ShowBrokenCertificateError(std::u16string_view rData)
+{
+ OUString aMsg = XsResId(STR_BROKEN_MACRO_CERTIFICATE_DATA);
+ aMsg = aMsg.replaceFirst("%{data}", rData);
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDlg->getDialog(),
+ VclMessageType::Error, VclButtonsType::Ok, aMsg));
+ xErrorBox->run();
+}
+
+IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, ViewCertPBHdl, weld::Button&, void)
+{
+ int nEntry = m_xTrustCertLB->get_selected_index();
+ if (nEntry == -1)
+ return;
+
+ const sal_uInt16 nSelected = m_xTrustCertLB->get_id(nEntry).toUInt32();
+ uno::Reference< css::security::XCertificate > xCert;
+ try
+ {
+ xCert = m_pDlg->m_xSecurityEnvironment->getCertificate(m_aTrustedAuthors[nSelected].SubjectName,
+ xmlsecurity::numericStringToBigInteger(m_aTrustedAuthors[nSelected].SerialNumber));
+ }
+ catch (...)
+ {
+ TOOLS_WARN_EXCEPTION("xmlsecurity.dialogs", "matching certificate not found for: " << m_aTrustedAuthors[nSelected].SubjectName);
+ }
+
+ if (!xCert.is())
+ {
+ try
+ {
+ xCert = m_pDlg->m_xSecurityEnvironment->createCertificateFromAscii(m_aTrustedAuthors[nSelected].RawData);
+ }
+ catch (...)
+ {
+ TOOLS_WARN_EXCEPTION("xmlsecurity.dialogs", "certificate data couldn't be parsed: " << m_aTrustedAuthors[nSelected].RawData);
+ }
+ }
+
+ if ( xCert.is() )
+ {
+ CertificateViewer aViewer(m_pDlg->getDialog(), m_pDlg->m_xSecurityEnvironment, xCert, false, nullptr);
+ aViewer.run();
+ }
+ else
+ // should never happen, as we parsed the certificate data when we added it!
+ ShowBrokenCertificateError(m_aTrustedAuthors[nSelected].RawData);
+}
+
+IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, RemoveCertPBHdl, weld::Button&, void)
+{
+ int nEntry = m_xTrustCertLB->get_selected_index();
+ if (nEntry != -1)
+ {
+ sal_uInt16 nAuthor = m_xTrustCertLB->get_id(nEntry).toUInt32();
+ m_aTrustedAuthors.erase(m_aTrustedAuthors.begin() + nAuthor);
+
+ FillCertLB();
+ ImplCheckButtons();
+ }
+}
+
+IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, AddLocPBHdl, weld::Button&, void)
+{
+ try
+ {
+ uno::Reference < uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference < ui::dialogs::XFolderPicker2 > xFolderPicker = sfx2::createFolderPicker(xContext, m_pDlg->getDialog());
+
+ short nRet = xFolderPicker->execute();
+
+ if( ui::dialogs::ExecutableDialogResults::OK != nRet )
+ return;
+
+ OUString aPathStr = xFolderPicker->getDirectory();
+ INetURLObject aNewObj( aPathStr );
+ aNewObj.removeFinalSlash();
+
+ // then the new path also a URL else system path
+ OUString aSystemFileURL = ( aNewObj.GetProtocol() != INetProtocol::NotValid ) ?
+ aPathStr : aNewObj.getFSysPath( FSysStyle::Detect );
+
+ OUString aNewPathStr(aSystemFileURL);
+
+ if ( osl::FileBase::getSystemPathFromFileURL( aSystemFileURL, aSystemFileURL ) == osl::FileBase::E_None )
+ aNewPathStr = aSystemFileURL;
+
+ if (m_xTrustFileLocLB->find_text(aNewPathStr) == -1)
+ m_xTrustFileLocLB->append_text(aNewPathStr);
+
+ ImplCheckButtons();
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "MacroSecurityTrustedSourcesTP::AddLocPBHdl(): exception from folder picker" );
+ }
+}
+
+IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, RemoveLocPBHdl, weld::Button&, void)
+{
+ sal_Int32 nSel = m_xTrustFileLocLB->get_selected_index();
+ if (nSel == -1)
+ return;
+
+ m_xTrustFileLocLB->remove(nSel);
+ // Trusted Path could not be removed (#i33584#)
+ // after remove an entry, select another one if exists
+ int nNewCount = m_xTrustFileLocLB->n_children();
+ if (nNewCount > 0)
+ {
+ if (nSel >= nNewCount)
+ nSel = nNewCount - 1;
+ m_xTrustFileLocLB->select(nSel);
+ }
+ ImplCheckButtons();
+}
+
+IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, TrustCertLBSelectHdl, weld::TreeView&, void)
+{
+ ImplCheckButtons();
+}
+
+IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, TrustFileLocLBSelectHdl, weld::TreeView&, void)
+{
+ ImplCheckButtons();
+}
+
+void MacroSecurityTrustedSourcesTP::FillCertLB(const bool bShowWarnings)
+{
+ m_xTrustCertLB->clear();
+
+ sal_uInt32 nEntries = m_aTrustedAuthors.size();
+
+ if ( !(nEntries && m_pDlg->m_xSecurityEnvironment.is()) )
+ return;
+
+ for( sal_uInt32 nEntry = 0 ; nEntry < nEntries ; ++nEntry )
+ {
+ SvtSecurityOptions::Certificate& rEntry = m_aTrustedAuthors[ nEntry ];
+
+ try
+ {
+ // create from RawData
+ uno::Reference< css::security::XCertificate > xCert = m_pDlg->m_xSecurityEnvironment->createCertificateFromAscii(rEntry.RawData);
+ m_xTrustCertLB->append(OUString::number(nEntry), xmlsec::GetContentPart(xCert->getSubjectName(), xCert->getCertificateKind()));
+ m_xTrustCertLB->set_text(nEntry, xmlsec::GetContentPart(xCert->getIssuerName(), xCert->getCertificateKind()), 1);
+ m_xTrustCertLB->set_text(nEntry, utl::GetDateTimeString(xCert->getNotValidAfter()), 2);
+ }
+ catch (...)
+ {
+ if (bShowWarnings)
+ {
+ TOOLS_WARN_EXCEPTION("xmlsecurity.dialogs", "certificate data couldn't be parsed: " << rEntry.RawData);
+ OUString sData = rEntry.RawData;
+ css::uno::Any tools_warn_exception(DbgGetCaughtException());
+ OUString sException = OStringToOUString(exceptionToString(tools_warn_exception), RTL_TEXTENCODING_UTF8);
+ if (!sException.isEmpty())
+ sData += " / " + sException;
+ ShowBrokenCertificateError(sData);
+ }
+ }
+ }
+}
+
+MacroSecurityTrustedSourcesTP::MacroSecurityTrustedSourcesTP(weld::Container* pParent, MacroSecurity* pDlg)
+ : MacroSecurityTP(pParent, "xmlsec/ui/securitytrustpage.ui", "SecurityTrustPage", pDlg)
+ , m_xTrustCertROFI(m_xBuilder->weld_image("lockcertimg"))
+ , m_xTrustCertLB(m_xBuilder->weld_tree_view("certificates"))
+ , m_xViewCertPB(m_xBuilder->weld_button("viewcert"))
+ , m_xRemoveCertPB(m_xBuilder->weld_button("removecert"))
+ , m_xTrustFileROFI(m_xBuilder->weld_image("lockfileimg"))
+ , m_xTrustFileLocLB(m_xBuilder->weld_tree_view("locations"))
+ , m_xAddLocPB(m_xBuilder->weld_button("addfile"))
+ , m_xRemoveLocPB(m_xBuilder->weld_button("removefile"))
+{
+ auto nColWidth = m_xTrustCertLB->get_approximate_digit_width() * 12;
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(nColWidth * 2),
+ o3tl::narrowing<int>(nColWidth * 2)
+ };
+ m_xTrustCertLB->set_column_fixed_widths(aWidths);
+ m_xTrustCertLB->set_size_request(nColWidth * 5.5, m_xTrustCertLB->get_height_rows(5));
+
+ m_xTrustCertLB->connect_changed( LINK( this, MacroSecurityTrustedSourcesTP, TrustCertLBSelectHdl ) );
+ m_xViewCertPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, ViewCertPBHdl ) );
+ m_xViewCertPB->set_sensitive(false);
+ m_xRemoveCertPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, RemoveCertPBHdl ) );
+ m_xRemoveCertPB->set_sensitive(false);
+
+ m_xTrustFileLocLB->connect_changed( LINK( this, MacroSecurityTrustedSourcesTP, TrustFileLocLBSelectHdl ) );
+ m_xTrustFileLocLB->set_size_request(nColWidth * 5, m_xTrustFileLocLB->get_height_rows(5));
+ m_xAddLocPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, AddLocPBHdl ) );
+ m_xRemoveLocPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, RemoveLocPBHdl ) );
+ m_xRemoveLocPB->set_sensitive(false);
+
+ m_aTrustedAuthors = SvtSecurityOptions::GetTrustedAuthors();
+ mbAuthorsReadonly = SvtSecurityOptions::IsReadOnly( SvtSecurityOptions::EOption::MacroTrustedAuthors );
+ m_xTrustCertROFI->set_visible(mbAuthorsReadonly);
+
+ FillCertLB(true);
+
+ std::vector< OUString > aSecureURLs = SvtSecurityOptions::GetSecureURLs();
+ mbURLsReadonly = SvtSecurityOptions::IsReadOnly( SvtSecurityOptions::EOption::SecureUrls );
+ m_xTrustFileROFI->set_visible(mbURLsReadonly);
+ m_xAddLocPB->set_sensitive(!mbURLsReadonly);
+
+ for (const auto& rSecureURL : aSecureURLs)
+ {
+ OUString aSystemFileURL( rSecureURL );
+ osl::FileBase::getSystemPathFromFileURL( aSystemFileURL, aSystemFileURL );
+ m_xTrustFileLocLB->append_text(aSystemFileURL);
+ }
+}
+
+void MacroSecurityTrustedSourcesTP::ActivatePage()
+{
+ m_pDlg->EnableReset( false );
+ FillCertLB();
+}
+
+void MacroSecurityTrustedSourcesTP::ClosePage()
+{
+ sal_Int32 nEntryCnt = m_xTrustFileLocLB->n_children();
+ if( nEntryCnt )
+ {
+ std::vector< OUString > aSecureURLs;
+ for (sal_Int32 i = 0; i < nEntryCnt; ++i)
+ {
+ OUString aURL(m_xTrustFileLocLB->get_text(i));
+ osl::FileBase::getFileURLFromSystemPath( aURL, aURL );
+ aSecureURLs.push_back(aURL);
+ }
+
+ SvtSecurityOptions::SetSecureURLs( std::move(aSecureURLs) );
+ }
+ // Trusted Path could not be removed (#i33584#)
+ // don't forget to remove the old saved SecureURLs
+ else
+ SvtSecurityOptions::SetSecureURLs( std::vector< OUString >() );
+
+ SvtSecurityOptions::SetTrustedAuthors( m_aTrustedAuthors );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/buffernode.cxx b/xmlsecurity/source/framework/buffernode.cxx
new file mode 100644
index 000000000..cc6c37c18
--- /dev/null
+++ b/xmlsecurity/source/framework/buffernode.cxx
@@ -0,0 +1,886 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "elementmark.hxx"
+#include "elementcollector.hxx"
+#include "buffernode.hxx"
+#include <com/sun/star/xml/crypto/sax/ConstOfSecurityId.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+
+BufferNode::BufferNode( const css::uno::Reference< css::xml::wrapper::XXMLElementWrapper >& xXMLElement )
+ :m_pParent(nullptr),
+ m_pBlocker(nullptr),
+ m_bAllReceived(false),
+ m_xXMLElement(xXMLElement)
+{
+}
+
+bool BufferNode::isECOfBeforeModifyIncluded(sal_Int32 nIgnoredSecurityId) const
+/****** BufferNode/isECOfBeforeModifyIncluded ********************************
+ *
+ * NAME
+ * isECOfBeforeModifyIncluded -- checks whether there is some
+ * ElementCollector on this BufferNode, that has BEFORE-MODIFY priority.
+ *
+ * SYNOPSIS
+ * bExist = isECOfBeforeModifyIncluded(nIgnoredSecurityId);
+ *
+ * FUNCTION
+ * checks each ElementCollector on this BufferNode, if all following
+ * conditions are satisfied, then returns true:
+ * 1. the ElementCollector's priority is BEFOREMODIFY;
+ * 2. the ElementCollector's securityId can't be ignored.
+ * otherwise, returns false.
+ *
+ * INPUTS
+ * nIgnoredSecurityId - the security Id to be ignored. If it equals
+ * to UNDEFINEDSECURITYID, then no security Id
+ * will be ignored.
+ *
+ * RESULT
+ * bExist - true if a match found, false otherwise
+ ******************************************************************************/
+{
+ return std::any_of(m_vElementCollectors.cbegin(), m_vElementCollectors.cend(),
+ [nIgnoredSecurityId](const ElementCollector* pElementCollector) {
+ return (nIgnoredSecurityId == css::xml::crypto::sax::ConstOfSecurityId::UNDEFINEDSECURITYID ||
+ pElementCollector->getSecurityId() != nIgnoredSecurityId) &&
+ (pElementCollector->getPriority() == css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY);
+ });
+}
+
+void BufferNode::setReceivedAll()
+/****** BufferNode/setReceiveAll *********************************************
+ *
+ * NAME
+ * setReceivedAll -- indicates that the element in this BufferNode has
+ * been completely buffered.
+ *
+ * SYNOPSIS
+ * setReceivedAll();
+ *
+ * FUNCTION
+ * sets the all-received flag and launches ElementCollector's notify
+ * process.
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * empty
+ ******************************************************************************/
+{
+ m_bAllReceived = true;
+ elementCollectorNotify();
+}
+
+
+void BufferNode::addElementCollector(const ElementCollector* pElementCollector)
+/****** BufferNode/addElementCollector ***************************************
+ *
+ * NAME
+ * addElementCollector -- adds a new ElementCollector to this BufferNode.
+ *
+ * SYNOPSIS
+ * addElementCollector(pElementCollector);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pElementCollector - the ElementCollector to be added
+ *
+ * RESULT
+ * empty
+ ******************************************************************************/
+{
+ m_vElementCollectors.push_back( pElementCollector );
+ const_cast<ElementCollector*>(pElementCollector)->setBufferNode(this);
+}
+
+void BufferNode::removeElementCollector(const ElementCollector* pElementCollector)
+/****** BufferNode/removeElementCollector ************************************
+ *
+ * NAME
+ * removeElementCollector -- removes an ElementCollector from this
+ * BufferNode.
+ *
+ * SYNOPSIS
+ * removeElementCollector(pElementCollector);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pElementCollector - the ElementCollector to be removed
+ *
+ * RESULT
+ * empty
+ ******************************************************************************/
+{
+ auto ii = std::find(m_vElementCollectors.begin(), m_vElementCollectors.end(), pElementCollector);
+ if (ii != m_vElementCollectors.end())
+ {
+ m_vElementCollectors.erase( ii );
+ const_cast<ElementCollector*>(pElementCollector)->setBufferNode(nullptr);
+ }
+}
+
+
+void BufferNode::setBlocker(const ElementMark* pBlocker)
+/****** BufferNode/setBlocker ************************************************
+ *
+ * NAME
+ * setBlocker -- adds a blocker to this BufferNode.
+ *
+ * SYNOPSIS
+ * setBlocker(pBlocker);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pBlocker - the new blocker to be attached
+ *
+ * RESULT
+ * empty
+ *
+ * NOTES
+ * Because there is only one blocker permitted for a BufferNode, so the
+ * old blocker on this BufferNode, if there is one, will be overcasted.
+ ******************************************************************************/
+{
+ OSL_ASSERT(!(m_pBlocker != nullptr && pBlocker != nullptr));
+
+ m_pBlocker = const_cast<ElementMark*>(pBlocker);
+ if (m_pBlocker != nullptr)
+ {
+ m_pBlocker->setBufferNode(this);
+ }
+}
+
+OUString BufferNode::printChildren() const
+/****** BufferNode/printChildren *********************************************
+ *
+ * NAME
+ * printChildren -- prints children information into a string.
+ *
+ * SYNOPSIS
+ * result = printChildren();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * result - the information string
+ ******************************************************************************/
+{
+ OUStringBuffer rc;
+
+ for( const ElementCollector* ii : m_vElementCollectors )
+ {
+ rc.append("BufID=" + OUString::number(ii->getBufferId()));
+
+ if (ii->getModify())
+ {
+ rc.append("[M]");
+ }
+
+ rc.append(",Pri=");
+
+ switch (ii->getPriority())
+ {
+ case css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY:
+ rc.append("BEFOREMODIFY");
+ break;
+ case css::xml::crypto::sax::ElementMarkPriority_AFTERMODIFY:
+ rc.append("AFTERMODIFY");
+ break;
+ default:
+ rc.append("UNKNOWN");
+ break;
+ }
+
+ rc.append("(SecID=" + OUString::number(ii->getSecurityId()) + ") ");
+ }
+
+ return rc.makeStringAndClear();
+}
+
+bool BufferNode::hasAnything() const
+/****** BufferNode/hasAnything ***********************************************
+ *
+ * NAME
+ * hasAnything -- checks whether there is any ElementCollector or blocker
+ * on this BufferNode.
+ *
+ * SYNOPSIS
+ * bExist = hasAnything();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * bExist - true if there is, false otherwise.
+ ******************************************************************************/
+{
+ return (m_pBlocker || !m_vElementCollectors.empty());
+}
+
+bool BufferNode::hasChildren() const
+/****** BufferNode/hasChildren ***********************************************
+ *
+ * NAME
+ * hasChildren -- checks whether this BufferNode has any child
+ * BufferNode.
+ *
+ * SYNOPSIS
+ * bExist = hasChildren();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * bExist - true if there is, false otherwise.
+ ******************************************************************************/
+{
+ return (!m_vChildren.empty());
+}
+
+std::vector< std::unique_ptr<BufferNode> > const & BufferNode::getChildren() const
+{
+ return m_vChildren;
+}
+
+std::vector< std::unique_ptr<BufferNode> > BufferNode::releaseChildren()
+{
+ return std::move(m_vChildren);
+}
+
+const BufferNode* BufferNode::getFirstChild() const
+/****** BufferNode/getFirstChild *********************************************
+ *
+ * NAME
+ * getFirstChild -- retrieves the first child BufferNode.
+ *
+ * SYNOPSIS
+ * child = getFirstChild();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * child - the first child BufferNode, or NULL if there is no child
+ * BufferNode.
+ ******************************************************************************/
+{
+ BufferNode* rc = nullptr;
+
+ if (!m_vChildren.empty())
+ {
+ rc = m_vChildren.front().get();
+ }
+
+ return rc;
+}
+
+void BufferNode::addChild(std::unique_ptr<BufferNode> pChild, sal_Int32 nPosition)
+/****** BufferNode/addChild(pChild,nPosition) ********************************
+ *
+ * NAME
+ * addChild -- inserts a child BufferNode at specific position.
+ *
+ * SYNOPSIS
+ * addChild(pChild, nPosition);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pChild - the child BufferNode to be added.
+ * nPosition - the position where the new child locates.
+ *
+ * RESULT
+ * empty
+ *
+ * NOTES
+ * If the nPosition is -1, then the new child BufferNode is appended
+ * at the end.
+ ******************************************************************************/
+{
+ if (nPosition == -1)
+ {
+ m_vChildren.push_back( std::move(pChild) );
+ }
+ else
+ {
+ m_vChildren.insert(m_vChildren.begin() + nPosition, std::move(pChild));
+ }
+}
+
+void BufferNode::addChild(std::unique_ptr<BufferNode> pChild)
+/****** BufferNode/addChild() ************************************************
+ *
+ * NAME
+ * addChild -- add a new child BufferNode.
+ *
+ * SYNOPSIS
+ * addChild(pChild);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pChild - the child BufferNode to be added.
+ *
+ * RESULT
+ * empty
+ *
+ * NOTES
+ * The new child BufferNode is appended at the end.
+ ******************************************************************************/
+{
+ addChild(std::move(pChild), -1);
+}
+
+void BufferNode::removeChild(const BufferNode* pChild)
+/****** BufferNode/removeChild ***********************************************
+ *
+ * NAME
+ * removeChild -- removes and deletes a child BufferNode from the children list.
+ *
+ * SYNOPSIS
+ * removeChild(pChild);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pChild - the child BufferNode to be removed
+ *
+ * RESULT
+ * empty
+ ******************************************************************************/
+{
+ auto ii = std::find_if(m_vChildren.begin(), m_vChildren.end(),
+ [pChild] (const std::unique_ptr<BufferNode>& i)
+ { return i.get() == pChild; });
+ if (ii != m_vChildren.end())
+ m_vChildren.erase( ii );
+}
+
+sal_Int32 BufferNode::indexOfChild(const BufferNode* pChild) const
+/****** BufferNode/indexOfChild **********************************************
+ *
+ * NAME
+ * indexOfChild -- gets the index of a child BufferNode.
+ *
+ * SYNOPSIS
+ * index = indexOfChild(pChild);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pChild - the child BufferNode whose index to be gotten
+ *
+ * RESULT
+ * index - the index of that child BufferNode. If that child BufferNode
+ * is not found, -1 is returned.
+ ******************************************************************************/
+{
+ auto ii = std::find_if(m_vChildren.begin(), m_vChildren.end(),
+ [pChild] (const std::unique_ptr<BufferNode>& i)
+ { return i.get() == pChild; });
+ if (ii == m_vChildren.end())
+ return -1;
+
+ return std::distance(m_vChildren.begin(), ii);
+}
+
+
+void BufferNode::setParent(const BufferNode* pParent)
+{
+ m_pParent = const_cast<BufferNode*>(pParent);
+}
+
+const BufferNode* BufferNode::getNextSibling() const
+/****** BufferNode/getNextSibling ********************************************
+ *
+ * NAME
+ * getNextSibling -- retrieves the next sibling BufferNode.
+ *
+ * SYNOPSIS
+ * sibling = getNextSibling();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * sibling - the next sibling BufferNode, or NULL if there is none.
+ ******************************************************************************/
+{
+ BufferNode* rc = nullptr;
+
+ if (m_pParent != nullptr)
+ {
+ rc = const_cast<BufferNode*>(m_pParent->getNextChild(this));
+ }
+
+ return rc;
+}
+
+const BufferNode* BufferNode::isAncestor(const BufferNode* pDescendant) const
+/****** BufferNode/isAncestor ************************************************
+ *
+ * NAME
+ * isAncestor -- checks whether this BufferNode is an ancestor of another
+ * BufferNode.
+ *
+ * SYNOPSIS
+ * bIs = isAncestor(pDescendant);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pDescendant - the BufferNode to be checked as a descendant
+ *
+ * RESULT
+ * bIs - true if this BufferNode is an ancestor of the pDescendant,
+ * false otherwise.
+ ******************************************************************************/
+{
+ BufferNode* rc = nullptr;
+
+ if (pDescendant != nullptr)
+ {
+ auto ii = std::find_if(m_vChildren.cbegin(), m_vChildren.cend(),
+ [&pDescendant](const std::unique_ptr<BufferNode>& pChild) {
+ return (pChild.get() == pDescendant) || (pChild->isAncestor(pDescendant) != nullptr);
+ });
+
+ if (ii != m_vChildren.end())
+ rc = ii->get();
+ }
+
+ return rc;
+}
+
+bool BufferNode::isPrevious(const BufferNode* pFollowing) const
+/****** BufferNode/isPrevious ************************************************
+ *
+ * NAME
+ * isPrevious -- checks whether this BufferNode is ahead of another
+ * BufferNode in the tree order.
+ *
+ * SYNOPSIS
+ * bIs = isPrevious(pFollowing);
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pFollowing - the BufferNode to be checked as a following
+ *
+ * RESULT
+ * bIs - true if this BufferNode is ahead in the tree order, false
+ * otherwise.
+ ******************************************************************************/
+{
+ bool rc = false;
+
+ BufferNode* pNextBufferNode = const_cast<BufferNode*>(getNextNodeByTreeOrder());
+ while (pNextBufferNode != nullptr)
+ {
+ if (pNextBufferNode == pFollowing)
+ {
+ rc = true;
+ break;
+ }
+
+ pNextBufferNode = const_cast<BufferNode*>(pNextBufferNode->getNextNodeByTreeOrder());
+ }
+
+ return rc;
+}
+
+const BufferNode* BufferNode::getNextNodeByTreeOrder() const
+/****** BufferNode/getNextNodeByTreeOrder ************************************
+ *
+ * NAME
+ * getNextNodeByTreeOrder -- retrieves the next BufferNode in the tree
+ * order.
+ *
+ * SYNOPSIS
+ * next = getNextNodeByTreeOrder();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * next - the BufferNode following this BufferNode in the tree order,
+ * or NULL if there is none.
+ *
+ * NOTES
+ * The "next" node in tree order is defined as:
+ * 1. If a node has children, then the first child is;
+ * 2. otherwise, if it has a following sibling, then this sibling node is;
+ * 3. otherwise, if it has a parent node, the parent's next sibling
+ * node is;
+ * 4. otherwise, no "next" node exists.
+ ******************************************************************************/
+{
+ /*
+ * If this buffer node has m_vChildren, then return the first
+ * child.
+ */
+ if (hasChildren())
+ {
+ return getFirstChild();
+ }
+
+ /*
+ * Otherwise, it this buffer node has a following sibling,
+ * then return that sibling.
+ */
+ BufferNode* pNextSibling = const_cast<BufferNode*>(getNextSibling());
+ if (pNextSibling != nullptr)
+ {
+ return pNextSibling;
+ }
+
+ /*
+ * Otherwise, it this buffer node has parent, then return
+ * its parent's following sibling.
+ */
+ BufferNode* pNode = const_cast<BufferNode*>(this);
+ BufferNode* pParent;
+ BufferNode* pNextSiblingParent = nullptr;
+
+ do
+ {
+ if (pNode == nullptr)
+ {
+ break;
+ }
+
+ pParent = const_cast<BufferNode*>(pNode->getParent());
+ if (pParent != nullptr)
+ {
+ pNextSiblingParent = const_cast<BufferNode*>(pParent->getNextSibling());
+ }
+ pNode = pParent;
+
+ } while (pNextSiblingParent == nullptr);
+
+ return pNextSiblingParent;
+}
+
+
+void BufferNode::setXMLElement( const css::uno::Reference< css::xml::wrapper::XXMLElementWrapper >& xXMLElement )
+{
+ m_xXMLElement = xXMLElement;
+}
+
+void BufferNode::notifyBranch()
+/****** BufferNode/notifyBranch **********************************************
+ *
+ * NAME
+ * notifyBranch -- notifies each BufferNode in the branch of this
+ * BufferNode in the tree order.
+ *
+ * SYNOPSIS
+ * notifyBranch();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * empty
+ ******************************************************************************/
+{
+ for( std::unique_ptr<BufferNode>& pBufferNode : m_vChildren )
+ {
+ pBufferNode->elementCollectorNotify();
+ pBufferNode->notifyBranch();
+ }
+}
+
+void BufferNode::elementCollectorNotify()
+/****** BufferNode/elementCollectorNotify ************************************
+ *
+ * NAME
+ * elementCollectorNotify -- notifies this BufferNode.
+ *
+ * SYNOPSIS
+ * elementCollectorNotify();
+ *
+ * FUNCTION
+ * Notifies this BufferNode if the notification is not suppressed.
+ *
+ * INPUTS
+ * empty
+ *
+ * RESULT
+ * child - the first child BufferNode, or NULL if there is no child
+ * BufferNode.
+ ******************************************************************************/
+{
+ if (m_vElementCollectors.empty())
+ return;
+
+ css::xml::crypto::sax::ElementMarkPriority nMaxPriority = css::xml::crypto::sax::ElementMarkPriority_MINIMUM;
+ css::xml::crypto::sax::ElementMarkPriority nPriority;
+
+ /*
+ * get the max priority among ElementCollectors on this BufferNode
+ */
+ for( const ElementCollector* pElementCollector : m_vElementCollectors )
+ {
+ nPriority = pElementCollector->getPriority();
+ if (nPriority > nMaxPriority)
+ {
+ nMaxPriority = nPriority;
+ }
+ }
+
+ std::vector< const ElementCollector* > vElementCollectors( m_vElementCollectors );
+
+ for( const ElementCollector* ii : vElementCollectors )
+ {
+ ElementCollector* pElementCollector = const_cast<ElementCollector*>(ii);
+ nPriority = pElementCollector->getPriority();
+ bool bToModify = pElementCollector->getModify();
+
+ /*
+ * Only ElementCollector with the max priority can
+ * perform notify operation.
+ * Moreover, if any blocker exists in the subtree of
+ * this BufferNode, this ElementCollector can't do notify
+ * unless its priority is BEFOREMODIFY.
+ */
+ if (nPriority == nMaxPriority &&
+ (nPriority == css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY ||
+ !isBlockerInSubTreeIncluded(pElementCollector->getSecurityId())))
+ {
+ /*
+ * If this ElementCollector will modify the buffered element, then
+ * special attention must be paid.
+ *
+ * If there is any ElementCollector in the subtree or any ancestor
+ * ElementCollector with PRI_BEFPREMODIFY priority, this
+ * ElementCollector can't perform notify operation, otherwise, it
+ * will destroy the buffered element, in turn, ElementCollectors
+ * mentioned above can't perform their mission.
+ */
+ //if (!(nMaxPriority == css::xml::crypto::sax::ElementMarkPriority_PRI_MODIFY &&
+ if (!(bToModify &&
+ (isECInSubTreeIncluded(pElementCollector->getSecurityId()) ||
+ isECOfBeforeModifyInAncestorIncluded(pElementCollector->getSecurityId()))
+ ))
+ {
+ pElementCollector->notifyListener();
+ }
+ }
+ }
+}
+
+bool BufferNode::isECInSubTreeIncluded(sal_Int32 nIgnoredSecurityId) const
+/****** BufferNode/isECInSubTreeIncluded *************************************
+ *
+ * NAME
+ * isECInSubTreeIncluded -- checks whether there is any ElementCollector
+ * in the branch of this BufferNode.
+ *
+ * SYNOPSIS
+ * bExist = isECInSubTreeIncluded(nIgnoredSecurityId);
+ *
+ * FUNCTION
+ * checks each BufferNode in the branch of this BufferNode, if there is
+ * an ElementCollector whose signatureId is not ignored, then return
+ * true, otherwise, false returned.
+ *
+ * INPUTS
+ * nIgnoredSecurityId - the security Id to be ignored. If it equals
+ * to UNDEFINEDSECURITYID, then no security Id
+ * will be ignored.
+ *
+ * RESULT
+ * bExist - true if a match found, false otherwise.
+ ******************************************************************************/
+{
+ bool rc = std::any_of(m_vElementCollectors.begin(), m_vElementCollectors.end(),
+ [nIgnoredSecurityId](const ElementCollector* pElementCollector) {
+ return nIgnoredSecurityId == css::xml::crypto::sax::ConstOfSecurityId::UNDEFINEDSECURITYID ||
+ pElementCollector->getSecurityId() != nIgnoredSecurityId;
+ });
+
+ if ( !rc )
+ {
+ rc = std::any_of(m_vChildren.begin(), m_vChildren.end(),
+ [nIgnoredSecurityId](const std::unique_ptr<BufferNode>& pBufferNode) {
+ return pBufferNode->isECInSubTreeIncluded(nIgnoredSecurityId);
+ });
+ }
+
+ return rc;
+}
+
+bool BufferNode::isECOfBeforeModifyInAncestorIncluded(sal_Int32 nIgnoredSecurityId) const
+/****** BufferNode/isECOfBeforeModifyInAncestorIncluded **********************
+ *
+ * NAME
+ * isECOfBeforeModifyInAncestorIncluded -- checks whether there is some
+ * ancestor BufferNode which has ElementCollector with PRI_BEFPREMODIFY
+ * priority.
+ *
+ * SYNOPSIS
+ * bExist = isECOfBeforeModifyInAncestorIncluded(nIgnoredSecurityId);
+ *
+ * FUNCTION
+ * checks each ancestor BufferNode through the parent link, if there is
+ * an ElementCollector with PRI_BEFPREMODIFY priority and its
+ * signatureId is not ignored, then return true, otherwise, false
+ * returned.
+ *
+ * INPUTS
+ * nIgnoredSecurityId - the security Id to be ignored. If it equals
+ * to UNDEFINEDSECURITYID, then no security Id
+ * will be ignored.
+ *
+ * RESULT
+ * bExist - true if a match found, false otherwise.
+ ******************************************************************************/
+{
+ bool rc = false;
+
+ BufferNode* pParentNode = m_pParent;
+ while (pParentNode != nullptr)
+ {
+ if (pParentNode->isECOfBeforeModifyIncluded(nIgnoredSecurityId))
+ {
+ rc = true;
+ break;
+ }
+
+ pParentNode = const_cast<BufferNode*>(pParentNode->getParent());
+ }
+
+ return rc;
+}
+
+bool BufferNode::isBlockerInSubTreeIncluded(sal_Int32 nIgnoredSecurityId) const
+/****** BufferNode/isBlockerInSubTreeIncluded ********************************
+ *
+ * NAME
+ * isBlockerInSubTreeIncluded -- checks whether there is some BufferNode
+ * which has blocker on it
+ *
+ * SYNOPSIS
+ * bExist = isBlockerInSubTreeIncluded(nIgnoredSecurityId);
+ *
+ * FUNCTION
+ * checks each BufferNode in the branch of this BufferNode, if one has
+ * a blocker on it, and the blocker's securityId is not ignored, then
+ * returns true; otherwise, false returns.
+ *
+ * INPUTS
+ * nIgnoredSecurityId - the security Id to be ignored. If it equals
+ * to UNDEFINEDSECURITYID, then no security Id
+ * will be ignored.
+ *
+ * RESULT
+ * bExist - true if a match found, false otherwise.
+ ******************************************************************************/
+{
+ return std::any_of(m_vChildren.begin(), m_vChildren.end(),
+ [nIgnoredSecurityId](const std::unique_ptr<BufferNode>& pBufferNode) {
+ ElementMark* pBlocker = pBufferNode->getBlocker();
+ return (pBlocker != nullptr &&
+ (nIgnoredSecurityId == css::xml::crypto::sax::ConstOfSecurityId::UNDEFINEDSECURITYID ||
+ pBlocker->getSecurityId() != nIgnoredSecurityId )) ||
+ pBufferNode->isBlockerInSubTreeIncluded(nIgnoredSecurityId);
+ });
+}
+
+const BufferNode* BufferNode::getNextChild(const BufferNode* pChild) const
+/****** BufferNode/getNextChild **********************************************
+ *
+ * NAME
+ * getNextChild -- get the next child BufferNode.
+ *
+ * SYNOPSIS
+ * nextChild = getNextChild();
+ *
+ * FUNCTION
+ * see NAME
+ *
+ * INPUTS
+ * pChild - the child BufferNode whose next node is retrieved.
+ *
+ * RESULT
+ * nextChild - the next child BufferNode after the pChild, or NULL if
+ * there is none.
+ ******************************************************************************/
+{
+ BufferNode* rc = nullptr;
+ bool bChildFound = false;
+
+ for( std::unique_ptr<BufferNode> const & i : m_vChildren )
+ {
+ if (bChildFound)
+ {
+ rc = i.get();
+ break;
+ }
+
+ if( i.get() == pChild )
+ {
+ bChildFound = true;
+ }
+ }
+
+ return rc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/buffernode.hxx b/xmlsecurity/source/framework/buffernode.hxx
new file mode 100644
index 000000000..0202195de
--- /dev/null
+++ b/xmlsecurity/source/framework/buffernode.hxx
@@ -0,0 +1,119 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <memory>
+#include <vector>
+
+namespace com::sun::star::xml::wrapper
+{
+class XXMLElementWrapper;
+}
+
+class ElementMark;
+class ElementCollector;
+
+class BufferNode final
+/****** buffernode.hxx/CLASS BufferNode ***************************************
+ *
+ * NAME
+ * BufferNode -- Class to maintain the tree of buffered elements
+ *
+ * FUNCTION
+ * One BufferNode object represents a buffered element in the document
+ * wrapper component.
+ * All BufferNode objects construct a tree which has the same structure
+ * of all buffered elements. That is to say, if one buffered element is
+ * an ancestor of another buffered element, then the corresponding
+ * BufferNode objects are also in ancestor/descendant relationship.
+ * This class is used to manipulate the tree of buffered elements.
+ ******************************************************************************/
+{
+private:
+ /* the parent BufferNode */
+ BufferNode* m_pParent;
+
+ /* all child BufferNodes */
+ std::vector<std::unique_ptr<BufferNode>> m_vChildren;
+
+ /* all ElementCollector holding this BufferNode */
+ std::vector<const ElementCollector*> m_vElementCollectors;
+
+ /*
+ * the blocker holding this BufferNode, one BufferNode can have one
+ * blocker at most
+ */
+ ElementMark* m_pBlocker;
+
+ /*
+ * whether the element has completely buffered by the document wrapper
+ * component
+ */
+ bool m_bAllReceived;
+
+ /* the XMLElementWrapper of the buffered element */
+ css::uno::Reference<css::xml::wrapper::XXMLElementWrapper> m_xXMLElement;
+
+private:
+ bool isECInSubTreeIncluded(sal_Int32 nIgnoredSecurityId) const;
+ bool isECOfBeforeModifyInAncestorIncluded(sal_Int32 nIgnoredSecurityId) const;
+ bool isBlockerInSubTreeIncluded(sal_Int32 nIgnoredSecurityId) const;
+ const BufferNode* getNextChild(const BufferNode* pChild) const;
+
+public:
+ explicit BufferNode(
+ const css::uno::Reference<css::xml::wrapper::XXMLElementWrapper>& xXMLElement);
+
+ bool isECOfBeforeModifyIncluded(sal_Int32 nIgnoredSecurityId) const;
+ void setReceivedAll();
+ bool isAllReceived() const { return m_bAllReceived; }
+ void addElementCollector(const ElementCollector* pElementCollector);
+ void removeElementCollector(const ElementCollector* pElementCollector);
+ ElementMark* getBlocker() const { return m_pBlocker; }
+ void setBlocker(const ElementMark* pBlocker);
+ OUString printChildren() const;
+ bool hasAnything() const;
+ bool hasChildren() const;
+ std::vector<std::unique_ptr<BufferNode>> const& getChildren() const;
+ std::vector<std::unique_ptr<BufferNode>> releaseChildren();
+ const BufferNode* getFirstChild() const;
+ void addChild(std::unique_ptr<BufferNode> pChild, sal_Int32 nPosition);
+ void addChild(std::unique_ptr<BufferNode> pChild);
+ void removeChild(const BufferNode* pChild);
+ sal_Int32 indexOfChild(const BufferNode* pChild) const;
+ const BufferNode* getParent() const { return m_pParent; }
+ void setParent(const BufferNode* pParent);
+ const BufferNode* getNextSibling() const;
+ const BufferNode* isAncestor(const BufferNode* pDescendant) const;
+ bool isPrevious(const BufferNode* pFollowing) const;
+ const BufferNode* getNextNodeByTreeOrder() const;
+ const css::uno::Reference<css::xml::wrapper::XXMLElementWrapper>& getXMLElement() const
+ {
+ return m_xXMLElement;
+ }
+ void
+ setXMLElement(const css::uno::Reference<css::xml::wrapper::XXMLElementWrapper>& xXMLElement);
+ void notifyBranch();
+ void elementCollectorNotify();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/elementcollector.cxx b/xmlsecurity/source/framework/elementcollector.cxx
new file mode 100644
index 000000000..7801d6328
--- /dev/null
+++ b/xmlsecurity/source/framework/elementcollector.cxx
@@ -0,0 +1,137 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "elementmark.hxx"
+#include "elementcollector.hxx"
+#include <com/sun/star/xml/crypto/sax/ConstOfSecurityId.hpp>
+#include <com/sun/star/xml/crypto/sax/XReferenceResolvedListener.hpp>
+
+ElementCollector::ElementCollector(
+ sal_Int32 nBufferId,
+ css::xml::crypto::sax::ElementMarkPriority nPriority,
+ bool bToModify,
+ const css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener >& xReferenceResolvedListener)
+ :ElementMark(css::xml::crypto::sax::ConstOfSecurityId::UNDEFINEDSECURITYID, nBufferId),
+ m_nPriority(nPriority),
+ m_bToModify(bToModify),
+ m_bAbleToNotify(false),
+ m_bNotified(false),
+ m_xReferenceResolvedListener(xReferenceResolvedListener)
+/****** ElementCollector/ElementCollector *************************************
+ *
+ * NAME
+ * ElementCollector -- constructor method
+ *
+ * SYNOPSIS
+ * ElementCollector(nSecurityId, nBufferId, nPriority, bToModify
+ * xReferenceResolvedListener);
+ *
+ * FUNCTION
+ * construct an ElementCollector object.
+ *
+ * INPUTS
+ * nSecurityId - represents which security entity the buffer node is
+ * related with. Either a signature or an encryption is
+ * a security entity.
+ * nBufferId - the id of the element buffered in the document
+ * wrapper component. The document wrapper component
+ * uses this id to search the particular buffered
+ * element.
+ * nPriority - the priority value. ElementCollector with lower
+ * priority value can't notify until all ElementCollectors
+ * with higher priority value have notified.
+ * bToModify - A flag representing whether this ElementCollector
+ * notification will cause the modification of its working
+ * element.
+ * xReferenceResolvedListener
+ * - the listener that this ElementCollector notifies to.
+ ******************************************************************************/
+{
+ m_type = css::xml::crypto::sax::ElementMarkType_ELEMENTCOLLECTOR;
+}
+
+
+void ElementCollector::notifyListener()
+/****** ElementCollector/notifyListener ***************************************
+ *
+ * NAME
+ * notifyListener -- enable the ability to notify the listener
+ *
+ * SYNOPSIS
+ * notifyListener();
+ *
+ * FUNCTION
+ * enable the ability to notify the listener and try to notify then.
+ ******************************************************************************/
+{
+ m_bAbleToNotify = true;
+ doNotify();
+}
+
+void ElementCollector::setReferenceResolvedListener(
+ const css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener >& xReferenceResolvedListener)
+/****** ElementCollector/setReferenceResolvedListener *************************
+ *
+ * NAME
+ * setReferenceResolvedListener -- configures a listener for the buffer
+ * node in this object
+ *
+ * SYNOPSIS
+ * setReferenceResolvedListener(xReferenceResolvedListener);
+ *
+ * FUNCTION
+ * configures a new listener and try to notify then.
+ *
+ * INPUTS
+ * xReferenceResolvedListener - the new listener
+ ******************************************************************************/
+{
+ m_xReferenceResolvedListener = xReferenceResolvedListener;
+ doNotify();
+}
+
+void ElementCollector::doNotify()
+/****** ElementCollector/doNotify *********************************************
+ *
+ * NAME
+ * doNotify -- tries to notify the listener
+ *
+ * SYNOPSIS
+ * doNotify();
+ *
+ * FUNCTION
+ * notifies the listener when all below conditions are satisfied:
+ * the listener has not been notified;
+ * the notify right is granted;
+ * the listener has already been configured;
+ * the security id has already been configure
+ ******************************************************************************/
+{
+ if (!m_bNotified &&
+ m_bAbleToNotify &&
+ m_xReferenceResolvedListener.is() &&
+ m_nSecurityId != css::xml::crypto::sax::ConstOfSecurityId::UNDEFINEDSECURITYID)
+ {
+ m_bNotified = true;
+ m_xReferenceResolvedListener->referenceResolved(m_nBufferId);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/elementcollector.hxx b/xmlsecurity/source/framework/elementcollector.hxx
new file mode 100644
index 000000000..0f797572a
--- /dev/null
+++ b/xmlsecurity/source/framework/elementcollector.hxx
@@ -0,0 +1,80 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "elementmark.hxx"
+#include <com/sun/star/xml/crypto/sax/ElementMarkPriority.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star::xml::crypto::sax { class XReferenceResolvedListener; }
+
+class ElementCollector : public ElementMark
+/****** elementcollector.hxx/CLASS ElementCollector ***************************
+ *
+ * NAME
+ * ElementCollector -- Class to manipulate an element collector
+ *
+ * FUNCTION
+ * This class is derived from the ElementMark class. Beyond the function
+ * of the ElementMark class, this class also maintains the priority, and
+ * manages the notify process
+ ******************************************************************************/
+{
+private:
+ /*
+ * the notify priority, is one of following values:
+ * AFTERMODIFY - this ElementCollector will notify after all
+ * internal modifications have finished.
+ * BEFOREMODIFY - this ElementCollector must notify before any
+ * internal modification happens.
+ */
+ css::xml::crypto::sax::ElementMarkPriority const m_nPriority;
+
+ /*
+ * the modify flag, representing whether which elementcollector will
+ * modify its data.
+ */
+ bool const m_bToModify;
+
+ /* the notify enable flag, see notifyListener method */
+ bool m_bAbleToNotify;
+
+ /* whether the listener has been notified */
+ bool m_bNotified;
+
+ /* the listener to be notified */
+ css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > m_xReferenceResolvedListener;
+
+public:
+ ElementCollector(
+ sal_Int32 nBufferId,
+ css::xml::crypto::sax::ElementMarkPriority nPriority,
+ bool bToModify,
+ const css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener >& xReferenceResolvedListener);
+
+ css::xml::crypto::sax::ElementMarkPriority getPriority() const { return m_nPriority;}
+ bool getModify() const { return m_bToModify;}
+ void notifyListener();
+ void setReferenceResolvedListener(
+ const css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener >& referenceResolvedListener);
+ void doNotify();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/elementmark.cxx b/xmlsecurity/source/framework/elementmark.cxx
new file mode 100644
index 000000000..71444d7cd
--- /dev/null
+++ b/xmlsecurity/source/framework/elementmark.cxx
@@ -0,0 +1,64 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "elementmark.hxx"
+
+ElementMark::ElementMark(sal_Int32 nSecurityId, sal_Int32 nBufferId)
+ :m_pBufferNode(nullptr),
+ m_nSecurityId(nSecurityId),
+ m_nBufferId(nBufferId),
+ m_type(css::xml::crypto::sax::ElementMarkType_ELEMENTMARK)
+/****** ElementMark/ElementMark ***********************************************
+ *
+ * NAME
+ * ElementMark -- constructor method
+ *
+ * SYNOPSIS
+ * ElementMark(nSecurityId, nBufferId);
+ *
+ * FUNCTION
+ * construct an ElementMark object.
+ *
+ * INPUTS
+ * nSecurityId - represents which security entity the buffer node is
+ * related with. Either a signature or an encryption is
+ * a security entity.
+ * nBufferId - the id of the element buffered in the document
+ * wrapper component. The document wrapper component
+ * uses this id to search the particular buffered
+ * element.
+ ******************************************************************************/
+{
+}
+
+
+void ElementMark::setBufferNode(const BufferNode* pBufferNode)
+{
+ m_pBufferNode = const_cast<BufferNode*>(pBufferNode);
+}
+
+
+void ElementMark::setSecurityId(sal_Int32 nSecurityId)
+{
+ m_nSecurityId = nSecurityId;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/elementmark.hxx b/xmlsecurity/source/framework/elementmark.hxx
new file mode 100644
index 000000000..94ca71fdf
--- /dev/null
+++ b/xmlsecurity/source/framework/elementmark.hxx
@@ -0,0 +1,67 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/sax/ElementMarkType.hpp>
+
+class BufferNode;
+
+class ElementMark
+/****** elementmark.hxx/CLASS ElementMark *************************************
+ *
+ * NAME
+ * ElementMark -- Class to manipulate an element mark
+ *
+ * FUNCTION
+ * This class maintains the security id, buffer id and its type for a
+ * buffer node.
+ ******************************************************************************/
+{
+protected:
+ /* the BufferNode maintained by this object */
+ BufferNode* m_pBufferNode;
+
+ /* the security Id */
+ sal_Int32 m_nSecurityId;
+
+ /* the buffer Id */
+ sal_Int32 const m_nBufferId;
+
+ /*
+ * the type value, is one of following values:
+ * TYPEOFELEMENTMARK - the default value, represents a blocker if
+ * not changed
+ * TYPEOFELEMENTCOLLECTOR - represents an ElementCollector
+ */
+ css::xml::crypto::sax::ElementMarkType m_type;
+
+public:
+ ElementMark(sal_Int32 nSecurityId, sal_Int32 nBufferId);
+ virtual ~ElementMark(){};
+
+ BufferNode* getBufferNode() const { return m_pBufferNode; }
+ void setBufferNode(const BufferNode* pBufferNode);
+ sal_Int32 getSecurityId() const { return m_nSecurityId; }
+ void setSecurityId(sal_Int32 nSecurityId);
+ css::xml::crypto::sax::ElementMarkType getType() const { return m_type; }
+ sal_Int32 getBufferId() const { return m_nBufferId; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/saxeventkeeperimpl.cxx b/xmlsecurity/source/framework/saxeventkeeperimpl.cxx
new file mode 100644
index 000000000..b5a7f0272
--- /dev/null
+++ b/xmlsecurity/source/framework/saxeventkeeperimpl.cxx
@@ -0,0 +1,1152 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <framework/saxeventkeeperimpl.hxx>
+#include "buffernode.hxx"
+#include "elementmark.hxx"
+#include "elementcollector.hxx"
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/crypto/sax/ConstOfSecurityId.hpp>
+#include <com/sun/star/xml/wrapper/XXMLDocumentWrapper.hpp>
+#include <com/sun/star/xml/csax/XCompressedDocumentHandler.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+
+#include <algorithm>
+
+SAXEventKeeperImpl::SAXEventKeeperImpl( )
+ :m_pCurrentBufferNode(nullptr),
+ m_nNextElementMarkId(1),
+ m_pNewBlocker(nullptr),
+ m_pCurrentBlockingBufferNode(nullptr),
+ m_bIsReleasing(false),
+ m_bIsForwarding(false)
+{
+ m_vElementMarkBuffers.reserve(2);
+ m_vNewElementCollectors.reserve(2);
+ m_vReleasedElementMarkBuffers.reserve(2);
+}
+
+SAXEventKeeperImpl::~SAXEventKeeperImpl()
+{
+ /*
+ * delete the BufferNode tree
+ */
+ m_pRootBufferNode.reset();
+
+ m_pCurrentBufferNode = m_pCurrentBlockingBufferNode = nullptr;
+
+ /*
+ * delete all unfreed ElementMarks
+ */
+ m_vNewElementCollectors.clear();
+ m_pNewBlocker = nullptr;
+}
+
+void SAXEventKeeperImpl::setCurrentBufferNode(BufferNode* pBufferNode)
+/****** SAXEventKeeperImpl/setCurrentBufferNode ******************************
+ *
+ * NAME
+ * setCurrentBufferNode -- set a new active BufferNode.
+ *
+ * SYNOPSIS
+ * setCurrentBufferNode( pBufferNode );
+ *
+ * FUNCTION
+ * connects this BufferNode into the BufferNode tree as a child of the
+ * current active BufferNode. Then makes this BufferNode as the current
+ * active BufferNode.
+ * If the previous active BufferNode points to the root
+ * BufferNode, which means that no buffering operation was proceeding,
+ * then notifies the status change listener that buffering operation
+ * will begin at once.
+ *
+ * INPUTS
+ * pBufferNode - a BufferNode which will be the new active BufferNode
+ ******************************************************************************/
+{
+ if (pBufferNode == m_pCurrentBufferNode)
+ return;
+
+ if ( m_pCurrentBufferNode == m_pRootBufferNode.get() &&
+ m_xSAXEventKeeperStatusChangeListener.is())
+ {
+ m_xSAXEventKeeperStatusChangeListener->collectionStatusChanged(true);
+ }
+
+ if (pBufferNode->getParent() == nullptr)
+ {
+ m_pCurrentBufferNode->addChild(std::unique_ptr<BufferNode>(pBufferNode));
+ pBufferNode->setParent(m_pCurrentBufferNode);
+ }
+
+ m_pCurrentBufferNode = pBufferNode;
+}
+
+BufferNode* SAXEventKeeperImpl::addNewElementMarkBuffers()
+/****** SAXEventKeeperImpl/addNewElementMarkBuffers **************************
+ *
+ * NAME
+ * addNewElementMarkBuffers -- add new ElementCollectors and new Blocker.
+ *
+ * SYNOPSIS
+ * pBufferNode = addNewElementMarkBuffers( );
+ *
+ * FUNCTION
+ * if there are new ElementCollector or new Blocker to be added, then
+ * connect all of them with the current BufferNode. In case of the
+ * current BufferNode doesn't exist, creates one.
+ * Clears up the new ElementCollector list and the new Blocker pointer.
+ *
+ * RESULT
+ * pBufferNode - the BufferNode that has been connected with both new
+ * ElementCollectors and new Blocker.
+ ******************************************************************************/
+{
+ BufferNode* pBufferNode = nullptr;
+
+ if (m_pNewBlocker || !m_vNewElementCollectors.empty() )
+ {
+ /*
+ * When the current BufferNode is right pointing to the current
+ * working element in the XMLDocumentWrapper component, then
+ * no new BufferNode is needed to create.
+ * This situation can only happen in the "Forwarding" mode.
+ */
+ if ( (m_pCurrentBufferNode != nullptr) &&
+ (m_xXMLDocument->isCurrent(m_pCurrentBufferNode->getXMLElement())))
+ {
+ pBufferNode = m_pCurrentBufferNode;
+ }
+ else
+ {
+ pBufferNode = new BufferNode(m_xXMLDocument->getCurrentElement());
+ }
+
+ if (m_pNewBlocker != nullptr)
+ {
+ pBufferNode->setBlocker(m_pNewBlocker);
+
+ /*
+ * If no blocking before, then notify the status change listener that
+ * the SAXEventKeeper has entered "blocking" status, during which, no
+ * SAX events will be forwarded to the next document handler.
+ */
+ if (m_pCurrentBlockingBufferNode == nullptr)
+ {
+ m_pCurrentBlockingBufferNode = pBufferNode;
+
+ if (m_xSAXEventKeeperStatusChangeListener.is())
+ {
+ m_xSAXEventKeeperStatusChangeListener->blockingStatusChanged(true);
+ }
+ }
+
+ m_pNewBlocker = nullptr;
+ }
+
+ for( const auto& i : m_vNewElementCollectors )
+ {
+ pBufferNode->addElementCollector(i);
+ }
+ m_vNewElementCollectors.clear();
+ }
+
+ return pBufferNode;
+}
+
+ElementMark* SAXEventKeeperImpl::findElementMarkBuffer(sal_Int32 nId) const
+/****** SAXEventKeeperImpl/findElementMarkBuffer *****************************
+ *
+ * NAME
+ * findElementMarkBuffer -- finds an ElementMark.
+ *
+ * SYNOPSIS
+ * pElementMark = findElementMarkBuffer( nId );
+ *
+ * FUNCTION
+ * searches an ElementMark with the particular Id in the ElementMark
+ * list.
+ *
+ * INPUTS
+ * nId - the Id of the ElementMark to be searched.
+ *
+ * RESULT
+ * pElementMark - the ElementMark with the particular Id, or NULL when
+ * no such Id exists.
+ ******************************************************************************/
+{
+ ElementMark* pElementMark = nullptr;
+
+ for( auto&& ii : m_vElementMarkBuffers )
+ {
+ if ( nId == ii->getBufferId())
+ {
+ pElementMark = const_cast<ElementMark*>(ii.get());
+ break;
+ }
+ }
+
+ return pElementMark;
+}
+
+void SAXEventKeeperImpl::removeElementMarkBuffer(sal_Int32 nId)
+/****** SAXEventKeeperImpl/removeElementMarkBuffer ***************************
+ *
+ * NAME
+ * removeElementMarkBuffer -- removes an ElementMark
+ *
+ * SYNOPSIS
+ * removeElementMarkBuffer( nId );
+ *
+ * FUNCTION
+ * removes an ElementMark with the particular Id in the ElementMark list.
+ *
+ * INPUTS
+ * nId - the Id of the ElementMark to be removed.
+ ******************************************************************************/
+{
+ auto ii = std::find_if(m_vElementMarkBuffers.begin(), m_vElementMarkBuffers.end(),
+ [nId](std::unique_ptr<const ElementMark>& rElementMark) { return nId == rElementMark->getBufferId(); }
+ );
+ if (ii == m_vElementMarkBuffers.end())
+ return;
+
+ /*
+ * checks whether this ElementMark still in the new ElementCollect array
+ */
+ auto jj = std::find_if(m_vNewElementCollectors.begin(), m_vNewElementCollectors.end(),
+ [&ii](const ElementCollector* pElementCollector) { return ii->get() == pElementCollector; }
+ );
+ if (jj != m_vNewElementCollectors.end())
+ m_vNewElementCollectors.erase(jj);
+
+ /*
+ * checks whether this ElementMark is the new Blocker
+ */
+ if (ii->get() == m_pNewBlocker)
+ {
+ m_pNewBlocker = nullptr;
+ }
+
+ m_vElementMarkBuffers.erase( ii );
+}
+
+OUString SAXEventKeeperImpl::printBufferNode(
+ BufferNode const * pBufferNode, sal_Int32 nIndent) const
+/****** SAXEventKeeperImpl/printBufferNode ***********************************
+ *
+ * NAME
+ * printBufferNode -- retrieves the information of a BufferNode and its
+ * branch.
+ *
+ * SYNOPSIS
+ * info = printBufferNode( pBufferNode, nIndent );
+ *
+ * FUNCTION
+ * all retrieved information includes:
+ * 1. whether it is the current BufferNode;
+ * 2. whether it is the current blocking BufferNode;
+ * 3. the name of the parent element;
+ * 4. the name of this element;
+ * 5. all ElementCollectors working on this BufferNode;
+ * 6. the Blocker working on this BufferNode;
+ * 7. all child BufferNodes' information.
+ *
+ * INPUTS
+ * pBufferNode - the BufferNode from where information will be retrieved.
+ * nIndent - how many space characters prefixed before the output
+ * message.
+ *
+ * RESULT
+ * info - the information string
+ ******************************************************************************/
+{
+ OUStringBuffer rc;
+
+ for ( int i=0; i<nIndent; ++i )
+ {
+ rc.append(" ");
+ }
+
+ if (pBufferNode == m_pCurrentBufferNode)
+ {
+ rc.append("[%]");
+ }
+
+ if (pBufferNode == m_pCurrentBlockingBufferNode)
+ {
+ rc.append("[B]");
+ }
+
+ rc.append(" " + m_xXMLDocument->getNodeName(pBufferNode->getXMLElement()));
+
+ BufferNode* pParent = const_cast<BufferNode*>(pBufferNode->getParent());
+ if (pParent != nullptr)
+ {
+ rc.append("[" + m_xXMLDocument->getNodeName(pParent->getXMLElement()) + "]");
+ }
+
+ rc.append(":EC=" + pBufferNode->printChildren() + " BR=");
+
+ ElementMark * pBlocker = pBufferNode->getBlocker();
+ if (pBlocker != nullptr)
+ {
+ rc.append( OUString::number(pBlocker->getBufferId()) +
+ "(SecId=" + OUString::number( pBlocker->getSecurityId() ) + ") ");
+ }
+ rc.append("\n");
+
+ std::vector< std::unique_ptr<BufferNode> > const & vChildren = pBufferNode->getChildren();
+ for( const auto& jj : vChildren )
+ {
+ rc.append(printBufferNode(jj.get(), nIndent+4));
+ }
+
+ return rc.makeStringAndClear();
+}
+
+css::uno::Sequence< css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > >
+ SAXEventKeeperImpl::collectChildWorkingElement(BufferNode const * pBufferNode)
+/****** SAXEventKeeperImpl/collectChildWorkingElement ************************
+ *
+ * NAME
+ * collectChildWorkingElement -- collects a BufferNode's all child
+ * Elements.
+ *
+ * SYNOPSIS
+ * list = collectChildWorkingElement( pBufferNode );
+ *
+ * INPUTS
+ * pBufferNode - the BufferNode whose child Elements will be collected.
+ *
+ * RESULT
+ * list - the child Elements list.
+ ******************************************************************************/
+{
+ std::vector< std::unique_ptr<BufferNode> > const & vChildren = pBufferNode->getChildren();
+
+ css::uno::Sequence < css::uno::Reference<
+ css::xml::wrapper::XXMLElementWrapper > > aChildrenCollection ( vChildren.size());
+
+ std::transform(vChildren.begin(), vChildren.end(), aChildrenCollection.getArray(),
+ [](const auto& i) { return i->getXMLElement(); });
+
+ return aChildrenCollection;
+}
+
+void SAXEventKeeperImpl::smashBufferNode(
+ BufferNode* pBufferNode, bool bClearRoot) const
+/****** SAXEventKeeperImpl/smashBufferNode ***********************************
+ *
+ * NAME
+ * smashBufferNode -- removes a BufferNode along with its working
+ * element.
+ *
+ * SYNOPSIS
+ * smashBufferNode( pBufferNode, bClearRoot );
+ *
+ * FUNCTION
+ * removes the BufferNode's working element from the DOM document, while
+ * reserves all ancestor paths for its child BufferNodes.
+ * when any of the BufferNode's ancestor element is useless, removes it
+ * too.
+ * removes the BufferNode from the BufferNode tree.
+ *
+ * INPUTS
+ * pBufferNode - the BufferNode to be removed
+ * bClearRoot - whether the root element also needs to be cleared up.
+ *
+ * NOTES
+ * when removing a Blocker's BufferNode, the bClearRoot flag should be
+ * true. Because a Blocker can buffer many SAX events which are not used
+ * by any other ElementCollector or Blocker.
+ * When the bClearRoot is set to true, the root BufferNode will be first
+ * cleared, with a stop flag setting at the next Blocking BufferNode. This
+ * operation can delete all useless buffered SAX events which are only
+ * needed by the Blocker to be deleted.
+ ******************************************************************************/
+{
+ if (pBufferNode->hasAnything())
+ return;
+
+ BufferNode* pParent = const_cast<BufferNode*>(pBufferNode->getParent());
+
+ /*
+ * delete the XML data
+ */
+ if (pParent == m_pRootBufferNode.get())
+ {
+ bool bIsNotBlocking = (m_pCurrentBlockingBufferNode == nullptr);
+ bool bIsBlockInside = false;
+ bool bIsBlockingAfterward = false;
+
+ /*
+ * If this is a blocker, then remove any out-element data
+ * which caused by blocking. The removal process will stop
+ * at the next blocker to avoid removing any useful data.
+ */
+ if (bClearRoot)
+ {
+ css::uno::Sequence< css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > >
+ aChildElements = collectChildWorkingElement(m_pRootBufferNode.get());
+
+ /*
+ * the clearUselessData only clearup the content in the
+ * node, not the node itself.
+ */
+ m_xXMLDocument->clearUselessData(m_pRootBufferNode->getXMLElement(),
+ aChildElements,
+ bIsNotBlocking?nullptr:
+ (m_pCurrentBlockingBufferNode->getXMLElement()));
+
+ /*
+ * remove the node if it is empty, then if its parent is also
+ * empty, remove it, then if the next parent is also empty,
+ * remove it,..., until parent become null.
+ */
+ m_xXMLDocument->collapse( m_pRootBufferNode->getXMLElement() );
+ }
+
+ /*
+ * if blocking, check the relationship between this BufferNode and
+ * the current blocking BufferNode.
+ */
+ if ( !bIsNotBlocking )
+ {
+ /*
+ * the current blocking BufferNode is a descendant of this BufferNode.
+ */
+ bIsBlockInside = (nullptr != pBufferNode->isAncestor(m_pCurrentBlockingBufferNode));
+
+ /*
+ * the current blocking BufferNode locates behind this BufferNode in tree
+ * order.
+ */
+ bIsBlockingAfterward = pBufferNode->isPrevious(m_pCurrentBlockingBufferNode);
+ }
+
+ /*
+ * this BufferNode's working element needs to be deleted only when
+ * 1. there is no blocking, or
+ * 2. the current blocking BufferNode is a descendant of this BufferNode,
+ * (then in the BufferNode's working element, the useless data before the blocking
+ * element should be deleted.) or
+ * 3. the current blocking BufferNode is locates behind this BufferNode in tree,
+ * (then the useless data between the blocking element and the working element
+ * should be deleted.).
+ * Otherwise, this working element should not be deleted.
+ */
+ if ( bIsNotBlocking || bIsBlockInside || bIsBlockingAfterward )
+ {
+ css::uno::Sequence< css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > >
+ aChildElements = collectChildWorkingElement(pBufferNode);
+
+ /*
+ * the clearUselessData only clearup the content in the
+ * node, not the node itself.
+ */
+ m_xXMLDocument->clearUselessData(pBufferNode->getXMLElement(),
+ aChildElements,
+ bIsBlockInside?(m_pCurrentBlockingBufferNode->getXMLElement()):
+ nullptr);
+
+ /*
+ * remove the node if it is empty, then if its parent is also
+ * empty, remove it, then if the next parent is also empty,
+ * remove it,..., until parent become null.
+ */
+ m_xXMLDocument->collapse( pBufferNode->getXMLElement() );
+ }
+ }
+
+ sal_Int32 nIndex = pParent->indexOfChild(pBufferNode);
+
+ std::vector< std::unique_ptr<BufferNode> > vChildren = pBufferNode->releaseChildren();
+ pParent->removeChild(pBufferNode); // delete buffernode
+
+ for( auto& i : vChildren )
+ {
+ i->setParent(pParent);
+ pParent->addChild(std::move(i), nIndex);
+ nIndex++;
+ }
+}
+
+BufferNode* SAXEventKeeperImpl::findNextBlockingBufferNode(
+ BufferNode* pStartBufferNode)
+/****** SAXEventKeeperImpl/findNextBlockingBufferNode ************************
+ *
+ * NAME
+ * findNextBlockingBufferNode -- finds the next blocking BufferNode
+ * behind the particular BufferNode.
+ *
+ * SYNOPSIS
+ * pBufferNode = findNextBlockingBufferNode( pStartBufferNode );
+ *
+ * INPUTS
+ * pStartBufferNode - the BufferNode from where to search the next
+ * blocking BufferNode.
+ *
+ * RESULT
+ * pBufferNode - the next blocking BufferNode, or NULL if no such
+ * BufferNode exists.
+ ******************************************************************************/
+{
+ BufferNode* pNext = nullptr;
+
+ if (pStartBufferNode != nullptr)
+ {
+ pNext = pStartBufferNode;
+
+ while (nullptr != (pNext = const_cast<BufferNode*>(pNext->getNextNodeByTreeOrder())))
+ {
+ if (pNext->getBlocker() != nullptr)
+ {
+ break;
+ }
+ }
+ }
+
+ return pNext;
+}
+
+void SAXEventKeeperImpl::diffuse(BufferNode* pBufferNode)
+/****** SAXEventKeeperImpl/diffuse *******************************************
+ *
+ * NAME
+ * diffuse -- diffuse the notification.
+ *
+ * SYNOPSIS
+ * diffuse( pBufferNode );
+ *
+ * FUNCTION
+ * diffuse the collecting completion notification from the specific
+ * BufferNode along its parent link, until an ancestor which is not
+ * completely received is met.
+ *
+ * INPUTS
+ * pBufferNode - the BufferNode from which the notification will be
+ * diffused.
+ ******************************************************************************/
+{
+ BufferNode* pParent = pBufferNode;
+
+ while(pParent->isAllReceived())
+ {
+ pParent->elementCollectorNotify();
+ pParent = const_cast<BufferNode*>(pParent->getParent());
+ }
+}
+
+void SAXEventKeeperImpl::releaseElementMarkBuffer()
+/****** SAXEventKeeperImpl/releaseElementMarkBuffer **************************
+ *
+ * NAME
+ * releaseElementMarkBuffer -- releases useless ElementMarks
+ *
+ * SYNOPSIS
+ * releaseElementMarkBuffer( );
+ *
+ * FUNCTION
+ * releases each ElementMark in the releasing list
+ * m_vReleasedElementMarkBuffers.
+ * The operation differs between an ElementCollector and a Blocker.
+ ******************************************************************************/
+{
+ m_bIsReleasing = true;
+ while (!m_vReleasedElementMarkBuffers.empty())
+ {
+ auto pId = m_vReleasedElementMarkBuffers.begin();
+ sal_Int32 nId = *pId;
+ m_vReleasedElementMarkBuffers.erase( pId );
+
+ ElementMark* pElementMark = findElementMarkBuffer(nId);
+
+ if (pElementMark != nullptr)
+ {
+ if (css::xml::crypto::sax::ElementMarkType_ELEMENTCOLLECTOR
+ == pElementMark->getType())
+ /*
+ * it is a EC
+ */
+ {
+ ElementCollector* pElementCollector = static_cast<ElementCollector*>(pElementMark);
+
+ css::xml::crypto::sax::ElementMarkPriority nPriority = pElementCollector->getPriority();
+ /*
+ * Delete the EC from the buffer node.
+ */
+ BufferNode* pBufferNode = pElementCollector->getBufferNode();
+ pBufferNode->removeElementCollector(pElementCollector);
+
+ if ( nPriority == css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY)
+ {
+ pBufferNode->notifyBranch();
+ }
+
+ /*
+ * delete the ElementMark
+ */
+ pElementCollector = nullptr;
+ pElementMark = nullptr;
+ removeElementMarkBuffer(nId);
+
+ /*
+ * delete the BufferNode
+ */
+ diffuse(pBufferNode);
+ smashBufferNode(pBufferNode, false);
+ }
+ else
+ /*
+ * it is a Blocker
+ */
+ {
+ /*
+ * Delete the TH from the buffer node.
+ */
+ BufferNode *pBufferNode = pElementMark->getBufferNode();
+ pBufferNode->setBlocker(nullptr);
+
+ /*
+ * If there is a following handler and no blocking now, then
+ * forward this event
+ */
+ if (m_pCurrentBlockingBufferNode == pBufferNode)
+ {
+ /*
+ * Before forwarding, the next blocking point needs to be
+ * found.
+ */
+ m_pCurrentBlockingBufferNode = findNextBlockingBufferNode(pBufferNode);
+
+ /*
+ * Forward the blocked events between these two STHs.
+ */
+ if (m_xNextHandler.is())
+ {
+ BufferNode* pTempCurrentBufferNode = m_pCurrentBufferNode;
+ BufferNode* pTempCurrentBlockingBufferNode = m_pCurrentBlockingBufferNode;
+
+ m_pCurrentBufferNode = pBufferNode;
+ m_pCurrentBlockingBufferNode = nullptr;
+
+ m_bIsForwarding = true;
+
+ m_xXMLDocument->generateSAXEvents(
+ m_xNextHandler,
+ this,
+ pBufferNode->getXMLElement(),
+ (pTempCurrentBlockingBufferNode == nullptr)?nullptr:(pTempCurrentBlockingBufferNode->getXMLElement()));
+
+ m_bIsForwarding = false;
+
+ m_pCurrentBufferNode = pTempCurrentBufferNode;
+ if (m_pCurrentBlockingBufferNode == nullptr)
+ {
+ m_pCurrentBlockingBufferNode = pTempCurrentBlockingBufferNode;
+ }
+ }
+
+ if (m_pCurrentBlockingBufferNode == nullptr &&
+ m_xSAXEventKeeperStatusChangeListener.is())
+ {
+ m_xSAXEventKeeperStatusChangeListener->blockingStatusChanged(false);
+ }
+ }
+
+ /*
+ * delete the ElementMark
+ */
+ pElementMark = nullptr;
+ removeElementMarkBuffer(nId);
+
+ /*
+ * delete the BufferNode
+ */
+ diffuse(pBufferNode);
+ smashBufferNode(pBufferNode, true);
+ }
+ }
+ }
+
+ m_bIsReleasing = false;
+
+ if (!m_pRootBufferNode->hasAnything() &&
+ !m_pRootBufferNode->hasChildren() &&
+ m_xSAXEventKeeperStatusChangeListener.is())
+ {
+ m_xSAXEventKeeperStatusChangeListener->bufferStatusChanged(true);
+ }
+}
+
+void SAXEventKeeperImpl::markElementMarkBuffer(sal_Int32 nId)
+/****** SAXEventKeeperImpl/markElementMarkBuffer *****************************
+ *
+ * NAME
+ * markElementMarkBuffer -- marks an ElementMark to be released
+ *
+ * SYNOPSIS
+ * markElementMarkBuffer( nId );
+ *
+ * FUNCTION
+ * puts the ElementMark with the particular Id into the releasing list,
+ * checks whether the releasing process is running, if not then launch
+ * this process.
+ *
+ * INPUTS
+ * nId - the Id of the ElementMark which will be released
+ ******************************************************************************/
+{
+ m_vReleasedElementMarkBuffers.push_back( nId );
+ if ( !m_bIsReleasing )
+ {
+ releaseElementMarkBuffer();
+ }
+}
+
+sal_Int32 SAXEventKeeperImpl::createElementCollector(
+ css::xml::crypto::sax::ElementMarkPriority nPriority,
+ bool bModifyElement,
+ const css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener >& xReferenceResolvedListener)
+/****** SAXEventKeeperImpl/createElementCollector ****************************
+ *
+ * NAME
+ * createElementCollector -- creates a new ElementCollector on the
+ * incoming element.
+ *
+ * SYNOPSIS
+ * nId = createElementCollector( nSecurityId, nPriority,
+ * bModifyElement,
+ * xReferenceResolvedListener );
+ *
+ * FUNCTION
+ * allocs a new Id, then create an ElementCollector with this Id value.
+ * Add the new created ElementCollector to the new ElementCollecotor list.
+ *
+ * INPUTS
+ * nPriority - the priority of the new ElementCollector
+ * bModifyElement -whether this BufferNode will modify the content of
+ * the corresponding element it works on
+ * xReferenceResolvedListener - the listener for the new ElementCollector.
+ *
+ * RESULT
+ * nId - the Id of the new ElementCollector
+ ******************************************************************************/
+{
+ sal_Int32 nId = m_nNextElementMarkId;
+ m_nNextElementMarkId ++;
+
+ ElementCollector* pElementCollector
+ = new ElementCollector(
+ nId,
+ nPriority,
+ bModifyElement,
+ xReferenceResolvedListener);
+
+ m_vElementMarkBuffers.push_back(
+ std::unique_ptr<const ElementMark>(pElementCollector));
+
+ /*
+ * All the new EC to initial EC array.
+ */
+ m_vNewElementCollectors.push_back( pElementCollector );
+
+ return nId;
+}
+
+
+sal_Int32 SAXEventKeeperImpl::createBlocker()
+/****** SAXEventKeeperImpl/createBlocker *************************************
+ *
+ * NAME
+ * createBlocker -- creates a new Blocker on the incoming element.
+ *
+ * SYNOPSIS
+ * nId = createBlocker( nSecurityId );
+ *
+ * RESULT
+ * nId - the Id of the new Blocker
+ ******************************************************************************/
+{
+ sal_Int32 nId = m_nNextElementMarkId;
+ m_nNextElementMarkId ++;
+
+ OSL_ASSERT(m_pNewBlocker == nullptr);
+
+ m_pNewBlocker = new ElementMark(css::xml::crypto::sax::ConstOfSecurityId::UNDEFINEDSECURITYID, nId);
+ m_vElementMarkBuffers.push_back(
+ std::unique_ptr<const ElementMark>(m_pNewBlocker));
+
+ return nId;
+}
+
+/* XSAXEventKeeper */
+sal_Int32 SAL_CALL SAXEventKeeperImpl::addElementCollector( )
+{
+ return createElementCollector(
+ css::xml::crypto::sax::ElementMarkPriority_AFTERMODIFY,
+ false,
+ nullptr);
+}
+
+void SAL_CALL SAXEventKeeperImpl::removeElementCollector( sal_Int32 id )
+{
+ markElementMarkBuffer(id);
+}
+
+sal_Int32 SAL_CALL SAXEventKeeperImpl::addBlocker( )
+{
+ return createBlocker();
+}
+
+void SAL_CALL SAXEventKeeperImpl::removeBlocker( sal_Int32 id )
+{
+ markElementMarkBuffer(id);
+}
+
+sal_Bool SAL_CALL SAXEventKeeperImpl::isBlocking( )
+{
+ return (m_pCurrentBlockingBufferNode != nullptr);
+}
+
+css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > SAL_CALL
+ SAXEventKeeperImpl::getElement( sal_Int32 id )
+{
+ css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > rc;
+
+ ElementMark* pElementMark = findElementMarkBuffer(id);
+ if (pElementMark != nullptr)
+ {
+ rc = pElementMark->getBufferNode()->getXMLElement();
+ }
+
+ return rc;
+}
+
+void SAL_CALL SAXEventKeeperImpl::setElement(
+ sal_Int32 id,
+ const css::uno::Reference< css::xml::wrapper::XXMLElementWrapper >& aElement )
+{
+ if (aElement.is())
+ {
+ m_xXMLDocument->rebuildIDLink(aElement);
+
+ ElementMark* pElementMark = findElementMarkBuffer(id);
+
+ if (pElementMark != nullptr)
+ {
+ BufferNode* pBufferNode = pElementMark->getBufferNode();
+ if (pBufferNode != nullptr)
+ {
+ const bool bIsCurrent = m_xXMLDocument->isCurrent(pBufferNode->getXMLElement());
+ pBufferNode->setXMLElement(aElement);
+
+ if (bIsCurrent)
+ {
+ m_xXMLDocument->setCurrentElement(aElement);
+ }
+ }
+ }
+ }
+ else
+ {
+ removeElementCollector( id );
+ }
+}
+
+css::uno::Reference< css::xml::sax::XDocumentHandler > SAL_CALL SAXEventKeeperImpl::setNextHandler(
+ const css::uno::Reference< css::xml::sax::XDocumentHandler >& xNewHandler )
+{
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xOldHandler = m_xNextHandler;
+
+ m_xNextHandler = xNewHandler;
+ return xOldHandler;
+}
+
+OUString SAL_CALL SAXEventKeeperImpl::printBufferNodeTree()
+{
+ OUString rc = "ElementMarkBuffers: size = "
+ + OUString::number(m_vElementMarkBuffers.size())
+ + "\nCurrentBufferNode: "
+ + m_xXMLDocument->getNodeName(m_pCurrentBufferNode->getXMLElement())
+ + "\n" + printBufferNode(m_pRootBufferNode.get(), 0);
+
+ return rc;
+}
+
+css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > SAL_CALL SAXEventKeeperImpl::getCurrentBlockingNode()
+{
+ css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > rc;
+
+ if (m_pCurrentBlockingBufferNode != nullptr)
+ {
+ rc = m_pCurrentBlockingBufferNode->getXMLElement();
+ }
+
+ return rc;
+}
+
+/* XSecuritySAXEventKeeper */
+sal_Int32 SAL_CALL SAXEventKeeperImpl::addSecurityElementCollector(
+ css::xml::crypto::sax::ElementMarkPriority priority,
+ sal_Bool modifyElement )
+{
+ return createElementCollector(
+ priority,
+ modifyElement,
+ nullptr);
+}
+
+void SAL_CALL SAXEventKeeperImpl::setSecurityId( sal_Int32 id, sal_Int32 securityId )
+{
+ ElementMark* pElementMark = findElementMarkBuffer(id);
+ if (pElementMark != nullptr)
+ {
+ pElementMark->setSecurityId(securityId);
+ }
+}
+
+
+/* XReferenceResolvedBroadcaster */
+void SAL_CALL SAXEventKeeperImpl::addReferenceResolvedListener(
+ sal_Int32 referenceId,
+ const css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener >& listener )
+{
+ ElementCollector* pElementCollector = static_cast<ElementCollector*>(findElementMarkBuffer(referenceId));
+ if (pElementCollector != nullptr)
+ {
+ pElementCollector->setReferenceResolvedListener(listener);
+ }
+}
+
+void SAL_CALL SAXEventKeeperImpl::removeReferenceResolvedListener(
+ sal_Int32 /*referenceId*/,
+ const css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener >&)
+{
+}
+
+/* XSAXEventKeeperStatusChangeBroadcaster */
+void SAL_CALL SAXEventKeeperImpl::addSAXEventKeeperStatusChangeListener(
+ const css::uno::Reference< css::xml::crypto::sax::XSAXEventKeeperStatusChangeListener >& listener )
+{
+ m_xSAXEventKeeperStatusChangeListener = listener;
+}
+
+void SAL_CALL SAXEventKeeperImpl::removeSAXEventKeeperStatusChangeListener(
+ const css::uno::Reference< css::xml::crypto::sax::XSAXEventKeeperStatusChangeListener >&)
+{
+}
+
+/* XDocumentHandler */
+void SAL_CALL SAXEventKeeperImpl::startDocument( )
+{
+ if ( m_xNextHandler.is())
+ {
+ m_xNextHandler->startDocument();
+ }
+}
+
+void SAL_CALL SAXEventKeeperImpl::endDocument( )
+{
+ if ( m_xNextHandler.is())
+ {
+ m_xNextHandler->endDocument();
+ }
+}
+
+void SAL_CALL SAXEventKeeperImpl::startElement(
+ const OUString& aName,
+ const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ /*
+ * If there is a following handler and no blocking now, then
+ * forward this event
+ */
+ if ((m_pCurrentBlockingBufferNode == nullptr) &&
+ (m_xNextHandler.is()) &&
+ (!m_bIsForwarding) &&
+ (m_pNewBlocker == nullptr))
+ {
+ m_xNextHandler->startElement(aName, xAttribs);
+ }
+ /*
+ * If not forwarding, buffer this startElement.
+ */
+ if (!m_bIsForwarding)
+ {
+ sal_Int32 nLength = xAttribs->getLength();
+ css::uno::Sequence< css::xml::csax::XMLAttribute > aAttributes (nLength);
+ auto aAttributesRange = asNonConstRange(aAttributes);
+
+ for ( int i = 0; i<nLength; ++i )
+ {
+ aAttributesRange[i].sName = xAttribs->getNameByIndex(static_cast<short>(i));
+ aAttributesRange[i].sValue =xAttribs->getValueByIndex(static_cast<short>(i));
+ }
+
+ m_xCompressedDocumentHandler->compressedStartElement(aName, aAttributes);
+ }
+
+ BufferNode* pBufferNode = addNewElementMarkBuffers();
+ if (pBufferNode != nullptr)
+ {
+ setCurrentBufferNode(pBufferNode);
+ }
+}
+
+void SAL_CALL SAXEventKeeperImpl::endElement( const OUString& aName )
+{
+ const bool bIsCurrent = m_xXMLDocument->isCurrent(m_pCurrentBufferNode->getXMLElement());
+
+ /*
+ * If there is a following handler and no blocking now, then
+ * forward this event
+ */
+ if ((m_pCurrentBlockingBufferNode == nullptr) &&
+ (m_xNextHandler.is()) &&
+ (!m_bIsForwarding))
+ {
+ m_xNextHandler->endElement(aName);
+ }
+
+ if ((m_pCurrentBlockingBufferNode != nullptr) ||
+ (m_pCurrentBufferNode != m_pRootBufferNode.get()) ||
+ (!m_xXMLDocument->isCurrentElementEmpty()))
+ {
+ if (!m_bIsForwarding)
+ {
+ m_xCompressedDocumentHandler->compressedEndElement(aName);
+ }
+
+ /*
+ * If the current buffer node has not notified yet, and
+ * the current buffer node is waiting for the current element,
+ * then let it notify.
+ */
+ if (bIsCurrent && (m_pCurrentBufferNode != m_pRootBufferNode.get()))
+ {
+ BufferNode* pOldCurrentBufferNode = m_pCurrentBufferNode;
+ m_pCurrentBufferNode = const_cast<BufferNode*>(m_pCurrentBufferNode->getParent());
+
+ pOldCurrentBufferNode->setReceivedAll();
+
+ if ((m_pCurrentBufferNode == m_pRootBufferNode.get()) &&
+ m_xSAXEventKeeperStatusChangeListener.is())
+ {
+ m_xSAXEventKeeperStatusChangeListener->collectionStatusChanged(false);
+ }
+ }
+ }
+ else
+ {
+ if (!m_bIsForwarding)
+ {
+ m_xXMLDocument->removeCurrentElement();
+ }
+ }
+}
+
+void SAL_CALL SAXEventKeeperImpl::characters( const OUString& aChars )
+{
+ if (m_bIsForwarding)
+ return;
+
+ if ((m_pCurrentBlockingBufferNode == nullptr) && m_xNextHandler.is())
+ {
+ m_xNextHandler->characters(aChars);
+ }
+
+ if ((m_pCurrentBlockingBufferNode != nullptr) ||
+ (m_pCurrentBufferNode != m_pRootBufferNode.get()))
+ {
+ m_xCompressedDocumentHandler->compressedCharacters(aChars);
+ }
+}
+
+void SAL_CALL SAXEventKeeperImpl::ignorableWhitespace( const OUString& aWhitespaces )
+{
+ characters( aWhitespaces );
+}
+
+void SAL_CALL SAXEventKeeperImpl::processingInstruction(
+ const OUString& aTarget, const OUString& aData )
+{
+ if (m_bIsForwarding)
+ return;
+
+ if ((m_pCurrentBlockingBufferNode == nullptr) && m_xNextHandler.is())
+ {
+ m_xNextHandler->processingInstruction(aTarget, aData);
+ }
+
+ if ((m_pCurrentBlockingBufferNode != nullptr) ||
+ (m_pCurrentBufferNode != m_pRootBufferNode.get()))
+ {
+ m_xCompressedDocumentHandler->compressedProcessingInstruction(aTarget, aData);
+ }
+}
+
+void SAL_CALL SAXEventKeeperImpl::setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >&)
+{
+}
+
+/* XInitialization */
+void SAL_CALL SAXEventKeeperImpl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ OSL_ASSERT(aArguments.getLength() == 1);
+
+ aArguments[0] >>= m_xXMLDocument;
+ m_xDocumentHandler.set( m_xXMLDocument, css::uno::UNO_QUERY );
+ m_xCompressedDocumentHandler.set( m_xXMLDocument, css::uno::UNO_QUERY );
+
+ m_pRootBufferNode.reset( new BufferNode(m_xXMLDocument->getCurrentElement()) );
+ m_pCurrentBufferNode = m_pRootBufferNode.get();
+}
+
+OUString SAXEventKeeperImpl_getImplementationName ()
+{
+ return "com.sun.star.xml.security.framework.SAXEventKeeperImpl";
+}
+
+css::uno::Sequence< OUString > SAXEventKeeperImpl_getSupportedServiceNames( )
+{
+ return { "com.sun.star.xml.crypto.sax.SAXEventKeeper" };
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SAXEventKeeperImpl::getImplementationName( )
+{
+ return SAXEventKeeperImpl_getImplementationName();
+}
+
+sal_Bool SAL_CALL SAXEventKeeperImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SAXEventKeeperImpl::getSupportedServiceNames( )
+{
+ return SAXEventKeeperImpl_getSupportedServiceNames();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/securityengine.cxx b/xmlsecurity/source/framework/securityengine.cxx
new file mode 100644
index 000000000..e276163d4
--- /dev/null
+++ b/xmlsecurity/source/framework/securityengine.cxx
@@ -0,0 +1,67 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <framework/securityengine.hxx>
+
+
+SecurityEngine::SecurityEngine()
+ :m_nIdOfTemplateEC(-1),
+ m_nNumOfResolvedReferences(0),
+ m_nIdOfKeyEC(-1),
+ m_bMissionDone(false),
+ m_nSecurityId(-1),
+ m_nStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN)
+{
+}
+
+/* XReferenceResolvedListener */
+void SAL_CALL SecurityEngine::referenceResolved( sal_Int32 /*referenceId*/)
+{
+ m_nNumOfResolvedReferences++;
+ tryToPerform();
+}
+
+/* XKeyCollector */
+void SAL_CALL SecurityEngine::setKeyId( sal_Int32 id )
+{
+ m_nIdOfKeyEC = id;
+ tryToPerform();
+}
+
+/* XMissionTaker */
+sal_Bool SAL_CALL SecurityEngine::endMission( )
+{
+ bool rc = m_bMissionDone;
+
+ if (!rc)
+ {
+ clearUp( );
+
+ notifyResultListener();
+ m_bMissionDone = true;
+ }
+
+ m_xResultListener = nullptr;
+ m_xSAXEventKeeper = nullptr;
+
+ return rc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/signaturecreatorimpl.cxx b/xmlsecurity/source/framework/signaturecreatorimpl.cxx
new file mode 100644
index 000000000..4371b7ae6
--- /dev/null
+++ b/xmlsecurity/source/framework/signaturecreatorimpl.cxx
@@ -0,0 +1,175 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <framework/signaturecreatorimpl.hxx>
+#include <framework/xmlsignaturetemplateimpl.hxx>
+#include <com/sun/star/xml/crypto/XXMLSignatureTemplate.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star::xml::wrapper { class XXMLElementWrapper; }
+
+using namespace com::sun::star::uno;
+
+SignatureCreatorImpl::SignatureCreatorImpl()
+ : m_nIdOfBlocker(-1)
+{
+}
+
+SignatureCreatorImpl::~SignatureCreatorImpl( )
+{
+}
+
+void SignatureCreatorImpl::notifyResultListener() const
+/****** SignatureCreatorImpl/notifyResultListener *****************************
+ *
+ * NAME
+ * notifyResultListener -- notifies the listener about the signature
+ * creation result.
+ ******************************************************************************/
+{
+ css::uno::Reference< css::xml::crypto::sax::XSignatureCreationResultListener >
+ xSignatureCreationResultListener ( m_xResultListener , css::uno::UNO_QUERY ) ;
+
+ xSignatureCreationResultListener->signatureCreated( m_nSecurityId, m_nStatus );
+}
+
+void SignatureCreatorImpl::startEngine(const rtl::Reference<XMLSignatureTemplateImpl>& xSignatureTemplate)
+/****** SignatureCreatorImpl/startEngine *************************************
+ *
+ * NAME
+ * startEngine -- generates the signature.
+ *
+ * FUNCTION
+ * generates the signature element, then if succeeds, updates the link
+ * of old template element to the new signature element in
+ * SAXEventKeeper.
+ *
+ * INPUTS
+ * xSignatureTemplate - the signature template (along with all referenced
+ * elements) to be signed.
+ ******************************************************************************/
+{
+ css::uno::Reference< css::xml::crypto::XXMLSignatureTemplate > xResultTemplate;
+ try
+ {
+ xResultTemplate = m_xXMLSignature->generate(css::uno::Reference<css::xml::crypto::XXMLSignatureTemplate>(xSignatureTemplate), m_xSecurityEnvironment);
+ m_nStatus = xResultTemplate->getStatus();
+ }
+ catch( css::uno::Exception& )
+ {
+ m_nStatus = css::xml::crypto::SecurityOperationStatus_RUNTIMEERROR_FAILED;
+ }
+
+ if (m_nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED)
+ {
+ css::uno::Reference < css::xml::wrapper::XXMLElementWrapper > xResultSignature = xResultTemplate->getTemplate();
+ m_xSAXEventKeeper->setElement(m_nIdOfTemplateEC, xResultSignature);
+ }
+}
+
+void SignatureCreatorImpl::clearUp() const
+/****** SignatureCreatorImpl/clearUp *****************************************
+ *
+ * NAME
+ * clearUp -- clear up all resources used by the signature generation.
+ *
+ * SYNOPSIS
+ * clearUp( );
+ *
+ * FUNCTION
+ * cleaning resources up includes:
+ * 1. SignatureEngine's clearing up;
+ * 2. releases the Blocker for the signature template element.
+ ******************************************************************************/
+{
+ SignatureEngine::clearUp();
+
+ if (m_nIdOfBlocker != -1)
+ {
+ m_xSAXEventKeeper->removeBlocker(m_nIdOfBlocker);
+ }
+}
+
+/* XBlockerMonitor */
+void SAL_CALL SignatureCreatorImpl::setBlockerId( sal_Int32 id )
+{
+ m_nIdOfBlocker = id;
+ tryToPerform();
+}
+
+/* XSignatureCreationResultBroadcaster */
+void SAL_CALL SignatureCreatorImpl::addSignatureCreationResultListener(
+ const css::uno::Reference< css::xml::crypto::sax::XSignatureCreationResultListener >& listener )
+{
+ m_xResultListener = listener;
+ tryToPerform();
+}
+
+void SAL_CALL SignatureCreatorImpl::removeSignatureCreationResultListener(
+ const css::uno::Reference< css::xml::crypto::sax::XSignatureCreationResultListener >&)
+{
+}
+
+/* XInitialization */
+void SAL_CALL SignatureCreatorImpl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ OSL_ASSERT(aArguments.getLength() == 5);
+
+ OUString ouTempString;
+
+ aArguments[0] >>= ouTempString;
+ m_nSecurityId = ouTempString.toInt32();
+ aArguments[1] >>= m_xSAXEventKeeper;
+ aArguments[2] >>= ouTempString;
+ m_nIdOfTemplateEC = ouTempString.toInt32();
+ aArguments[3] >>= m_xSecurityEnvironment;
+ aArguments[4] >>= m_xXMLSignature;
+}
+
+
+OUString SignatureCreatorImpl_getImplementationName ()
+{
+ return "com.sun.star.xml.security.framework.SignatureCreatorImpl";
+}
+
+css::uno::Sequence< OUString > SignatureCreatorImpl_getSupportedServiceNames( )
+{
+ return { "com.sun.star.xml.crypto.sax.SignatureCreator" };
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SignatureCreatorImpl::getImplementationName( )
+{
+ return SignatureCreatorImpl_getImplementationName();
+}
+
+sal_Bool SAL_CALL SignatureCreatorImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SignatureCreatorImpl::getSupportedServiceNames( )
+{
+ return SignatureCreatorImpl_getSupportedServiceNames();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/signatureengine.cxx b/xmlsecurity/source/framework/signatureengine.cxx
new file mode 100644
index 000000000..0390ea7e6
--- /dev/null
+++ b/xmlsecurity/source/framework/signatureengine.cxx
@@ -0,0 +1,197 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <framework/signatureengine.hxx>
+#include <framework/xmlsignaturetemplateimpl.hxx>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star::xml::wrapper { class XXMLElementWrapper; }
+
+using namespace com::sun::star::uno;
+
+SignatureEngine::SignatureEngine()
+ : m_nTotalReferenceNumber(-1)
+{
+}
+
+bool SignatureEngine::checkReady() const
+/****** SignatureEngine/checkReady *******************************************
+ *
+ * NAME
+ * checkReady -- checks the conditions for the main operation.
+ *
+ * SYNOPSIS
+ * bReady = checkReady( );
+ *
+ * FUNCTION
+ * checks whether all following conditions are satisfied:
+ * 1. the main operation has't begun yet;
+ * 2. the key material is known;
+ * 3. the amount of reference is known;
+ * 4. all of referenced elements, the key element and the signature
+ * template are buffered.
+ *
+ * RESULT
+ * bReady - true if all conditions are satisfied, false otherwise
+ ******************************************************************************/
+{
+ bool rc = true;
+
+ sal_Int32 nKeyInc = 0;
+ if (m_nIdOfKeyEC != 0)
+ {
+ nKeyInc = 1;
+ }
+
+ if (m_bMissionDone ||
+ m_nIdOfKeyEC == -1 ||
+ m_nTotalReferenceNumber == -1 ||
+ m_nTotalReferenceNumber+1+nKeyInc > m_nNumOfResolvedReferences)
+ {
+ rc = false;
+ }
+
+ return rc;
+}
+
+void SignatureEngine::tryToPerform( )
+/****** SignatureEngine/tryToPerform *****************************************
+ *
+ * NAME
+ * tryToPerform -- tries to perform the signature operation.
+ *
+ * FUNCTION
+ * if the situation is ready, perform following operations.
+ * 1. prepares a signature template;
+ * 2. calls the signature bridge component;
+ * 3. clears up all used resources;
+ * 4. notifies the result listener;
+ * 5. sets the "accomplishment" flag.
+ ******************************************************************************/
+{
+ if (!checkReady())
+ return;
+
+ rtl::Reference<XMLSignatureTemplateImpl> xSignatureTemplate = new XMLSignatureTemplateImpl();
+
+ css::uno::Reference< css::xml::wrapper::XXMLElementWrapper >
+ xXMLElement = m_xSAXEventKeeper->getElement( m_nIdOfTemplateEC );
+
+ xSignatureTemplate->setTemplate(xXMLElement);
+
+ for( const auto i : m_vReferenceIds )
+ {
+ xXMLElement = m_xSAXEventKeeper->getElement( i );
+ xSignatureTemplate->setTarget(xXMLElement);
+ }
+
+ /*
+ * set the Uri binding
+ */
+ xSignatureTemplate->setBinding( this );
+
+ startEngine(xSignatureTemplate);
+
+ /*
+ * done
+ */
+ clearUp( );
+
+ notifyResultListener();
+
+ m_bMissionDone = true;
+}
+
+void SignatureEngine::clearUp( ) const
+/****** SignatureEngine/clearUp **********************************************
+ *
+ * NAME
+ * clearUp -- clear up all resources used by this operation.
+ *
+ * FUNCTION
+ * cleaning resources up includes:
+ * 1. releases the ElementCollector for the signature template element;
+ * 2. releases ElementCollectors for referenced elements;
+ * 3. releases the ElementCollector for the key element, if there is one.
+ ******************************************************************************/
+{
+ css::uno::Reference < css::xml::crypto::sax::XReferenceResolvedBroadcaster >
+ xReferenceResolvedBroadcaster( m_xSAXEventKeeper, css::uno::UNO_QUERY );
+ xReferenceResolvedBroadcaster->removeReferenceResolvedListener(
+ m_nIdOfTemplateEC,
+ static_cast<const css::uno::Reference < css::xml::crypto::sax::XReferenceResolvedListener > >(static_cast<SecurityEngine *>(const_cast<SignatureEngine *>(this))));
+
+ m_xSAXEventKeeper->removeElementCollector(m_nIdOfTemplateEC);
+
+ for( const auto& i : m_vReferenceIds )
+ {
+ xReferenceResolvedBroadcaster->removeReferenceResolvedListener(
+ i,
+ static_cast<const css::uno::Reference < css::xml::crypto::sax::XReferenceResolvedListener > >(static_cast<SecurityEngine *>(const_cast<SignatureEngine *>(this))));
+ m_xSAXEventKeeper->removeElementCollector(i);
+ }
+
+ if (m_nIdOfKeyEC != 0 && m_nIdOfKeyEC != -1)
+ {
+ m_xSAXEventKeeper->removeElementCollector(m_nIdOfKeyEC);
+ }
+}
+
+/* XReferenceCollector */
+void SAL_CALL SignatureEngine::setReferenceCount( sal_Int32 count )
+{
+ m_nTotalReferenceNumber = count;
+ tryToPerform();
+}
+
+void SAL_CALL SignatureEngine::setReferenceId( sal_Int32 id )
+{
+ m_vReferenceIds.push_back( id );
+}
+
+/* XUriBinding */
+void SAL_CALL SignatureEngine::setUriBinding(
+ const OUString& uri,
+ const css::uno::Reference< css::io::XInputStream >& aInputStream )
+{
+ m_vUris.push_back(uri);
+ m_vXInputStreams.push_back(aInputStream);
+}
+
+css::uno::Reference< css::io::XInputStream > SAL_CALL SignatureEngine::getUriBinding( const OUString& uri )
+{
+ css::uno::Reference< css::io::XInputStream > xInputStream;
+
+ int size = m_vUris.size();
+
+ for( int i=0; i<size; ++i)
+ {
+ if (m_vUris[i] == uri)
+ {
+ xInputStream = m_vXInputStreams[i];
+ break;
+ }
+ }
+
+ return xInputStream;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/signatureverifierimpl.cxx b/xmlsecurity/source/framework/signatureverifierimpl.cxx
new file mode 100644
index 000000000..44e9e4c0a
--- /dev/null
+++ b/xmlsecurity/source/framework/signatureverifierimpl.cxx
@@ -0,0 +1,130 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <framework/signatureverifierimpl.hxx>
+#include <framework/xmlsignaturetemplateimpl.hxx>
+#include <com/sun/star/xml/crypto/XXMLSignatureTemplate.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+
+
+SignatureVerifierImpl::SignatureVerifierImpl()
+{
+}
+
+SignatureVerifierImpl::~SignatureVerifierImpl()
+{
+}
+
+void SignatureVerifierImpl::notifyResultListener() const
+/****** SignatureVerifierImpl/notifyResultListener ***************************
+ *
+ * NAME
+ * notifyResultListener -- notifies the listener about the verify result.
+ ******************************************************************************/
+{
+ css::uno::Reference< css::xml::crypto::sax::XSignatureVerifyResultListener >
+ xSignatureVerifyResultListener ( m_xResultListener , css::uno::UNO_QUERY ) ;
+
+ xSignatureVerifyResultListener->signatureVerified( m_nSecurityId, m_nStatus );
+}
+
+void SignatureVerifierImpl::startEngine( const rtl::Reference<XMLSignatureTemplateImpl>& xSignatureTemplate)
+/****** SignatureVerifierImpl/startEngine ************************************
+ *
+ * NAME
+ * startEngine -- verifies the signature.
+ *
+ * INPUTS
+ * xSignatureTemplate - the signature template (along with all referenced
+ * elements) to be verified.
+ ******************************************************************************/
+{
+ css::uno::Reference< css::xml::crypto::XXMLSignatureTemplate > xResultTemplate;
+ try
+ {
+ xResultTemplate = m_xXMLSignature->validate(css::uno::Reference<css::xml::crypto::XXMLSignatureTemplate>(xSignatureTemplate), m_xXMLSecurityContext);
+ m_nStatus = xResultTemplate->getStatus();
+ }
+ catch( css::uno::Exception& )
+ {
+ m_nStatus = css::xml::crypto::SecurityOperationStatus_RUNTIMEERROR_FAILED;
+ }
+}
+
+/* XSignatureVerifyResultBroadcaster */
+void SAL_CALL SignatureVerifierImpl::addSignatureVerifyResultListener(
+ const css::uno::Reference< css::xml::crypto::sax::XSignatureVerifyResultListener >& listener )
+{
+ m_xResultListener = listener;
+ tryToPerform();
+}
+
+void SAL_CALL SignatureVerifierImpl::removeSignatureVerifyResultListener(
+ const css::uno::Reference< css::xml::crypto::sax::XSignatureVerifyResultListener >&)
+{
+}
+
+/* XInitialization */
+void SAL_CALL SignatureVerifierImpl::initialize(
+ const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ OSL_ASSERT(aArguments.getLength() == 5);
+
+ OUString ouTempString;
+
+ aArguments[0] >>= ouTempString;
+ m_nSecurityId = ouTempString.toInt32();
+ aArguments[1] >>= m_xSAXEventKeeper;
+ aArguments[2] >>= ouTempString;
+ m_nIdOfTemplateEC = ouTempString.toInt32();
+ aArguments[3] >>= m_xXMLSecurityContext;
+ aArguments[4] >>= m_xXMLSignature;
+}
+
+
+OUString SignatureVerifierImpl_getImplementationName ()
+{
+ return "com.sun.star.xml.security.framework.SignatureVerifierImpl";
+}
+
+css::uno::Sequence< OUString > SignatureVerifierImpl_getSupportedServiceNames( )
+{
+ return { "com.sun.star.xml.crypto.sax.SignatureVerifier" };
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SignatureVerifierImpl::getImplementationName( )
+{
+ return SignatureVerifierImpl_getImplementationName();
+}
+
+sal_Bool SAL_CALL SignatureVerifierImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SignatureVerifierImpl::getSupportedServiceNames( )
+{
+ return SignatureVerifierImpl_getSupportedServiceNames();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/framework/xmlsignaturetemplateimpl.cxx b/xmlsecurity/source/framework/xmlsignaturetemplateimpl.cxx
new file mode 100644
index 000000000..203f0fea2
--- /dev/null
+++ b/xmlsecurity/source/framework/xmlsignaturetemplateimpl.cxx
@@ -0,0 +1,113 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <framework/xmlsignaturetemplateimpl.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star::uno ;
+using ::com::sun::star::lang::XMultiServiceFactory ;
+
+using ::com::sun::star::xml::wrapper::XXMLElementWrapper ;
+using ::com::sun::star::xml::crypto::XXMLSignatureTemplate ;
+
+XMLSignatureTemplateImpl::XMLSignatureTemplateImpl()
+ :m_nStatus ( css::xml::crypto::SecurityOperationStatus_UNKNOWN )
+{
+}
+
+XMLSignatureTemplateImpl::~XMLSignatureTemplateImpl() {
+}
+
+/* XXMLSignatureTemplate */
+void SAL_CALL XMLSignatureTemplateImpl::setTemplate( const Reference< XXMLElementWrapper >& aTemplate )
+{
+ m_xTemplate = aTemplate ;
+}
+
+/* XXMLSignatureTemplate */
+Reference< XXMLElementWrapper > SAL_CALL XMLSignatureTemplateImpl::getTemplate()
+{
+ return m_xTemplate ;
+}
+
+void SAL_CALL XMLSignatureTemplateImpl::setTarget( const css::uno::Reference< css::xml::wrapper::XXMLElementWrapper >& aXmlElement )
+{
+ targets.push_back( aXmlElement );
+}
+
+css::uno::Sequence< css::uno::Reference< css::xml::wrapper::XXMLElementWrapper > > SAL_CALL XMLSignatureTemplateImpl::getTargets()
+{
+ return comphelper::containerToSequence(targets);
+}
+
+void SAL_CALL XMLSignatureTemplateImpl::setBinding(
+ const css::uno::Reference< css::xml::crypto::XUriBinding >& aUriBinding )
+{
+ m_xUriBinding = aUriBinding;
+}
+
+css::uno::Reference< css::xml::crypto::XUriBinding > SAL_CALL XMLSignatureTemplateImpl::getBinding()
+{
+ return m_xUriBinding;
+}
+
+void SAL_CALL XMLSignatureTemplateImpl::setStatus(
+ css::xml::crypto::SecurityOperationStatus status )
+{
+ m_nStatus = status;
+}
+
+css::xml::crypto::SecurityOperationStatus SAL_CALL XMLSignatureTemplateImpl::getStatus( )
+{
+ return m_nStatus;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSignatureTemplateImpl::getImplementationName() {
+ return impl_getImplementationName() ;
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSignatureTemplateImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL XMLSignatureTemplateImpl::getSupportedServiceNames() {
+ return impl_getSupportedServiceNames() ;
+}
+
+//Helper for XServiceInfo
+Sequence< OUString > XMLSignatureTemplateImpl::impl_getSupportedServiceNames() {
+ Sequence<OUString> seqServiceNames { "com.sun.star.xml.crypto.XMLSignatureTemplate" };
+ return seqServiceNames ;
+}
+
+OUString XMLSignatureTemplateImpl::impl_getImplementationName() {
+ return "com.sun.star.xml.security.framework.XMLSignatureTemplateImpl" ;
+}
+
+//Helper for registry
+Reference< XInterface > XMLSignatureTemplateImpl::impl_createInstance( const Reference< XMultiServiceFactory >& ) {
+ return Reference< XInterface >( *new XMLSignatureTemplateImpl ) ;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/CertificateImpl.cxx b/xmlsecurity/source/gpg/CertificateImpl.cxx
new file mode 100644
index 000000000..a0a08fc80
--- /dev/null
+++ b/xmlsecurity/source/gpg/CertificateImpl.cxx
@@ -0,0 +1,261 @@
+/* -*- 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 <config_gpgme.h>
+
+#include "CertificateImpl.hxx"
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/security/KeyUsage.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <svl/sigstruct.hxx>
+
+#include <context.h>
+#include <data.h>
+
+using namespace css;
+using namespace css::uno;
+using namespace css::security;
+using namespace css::util;
+
+CertificateImpl::CertificateImpl()
+{
+}
+
+CertificateImpl::~CertificateImpl()
+{
+}
+
+//Methods from XCertificateImpl
+sal_Int16 SAL_CALL CertificateImpl::getVersion()
+{
+ return 0;
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSerialNumber()
+{
+ // TODO: perhaps map to subkey's cardSerialNumber - if you have
+ // one to test
+ return Sequence< sal_Int8 >();
+}
+
+OUString SAL_CALL CertificateImpl::getIssuerName()
+{
+ const GpgME::UserID userId = m_pKey.userID(0);
+ if (userId.isNull())
+ return OUString();
+
+ return OStringToOUString(userId.id(), RTL_TEXTENCODING_UTF8);
+}
+
+OUString SAL_CALL CertificateImpl::getSubjectName()
+{
+ // Same as issuer name (user ID)
+ return getIssuerName();
+}
+
+namespace {
+ DateTime convertUnixTimeToDateTime(time_t time)
+ {
+ DateTime dateTime;
+ struct tm *timeStruct = gmtime(&time);
+ dateTime.Year = timeStruct->tm_year + 1900;
+ dateTime.Month = timeStruct->tm_mon + 1;
+ dateTime.Day = timeStruct->tm_mday;
+ dateTime.Hours = timeStruct->tm_hour;
+ dateTime.Minutes = timeStruct->tm_min;
+ dateTime.Seconds = timeStruct->tm_sec;
+ return dateTime;
+ }
+}
+
+DateTime SAL_CALL CertificateImpl::getNotValidBefore()
+{
+ const GpgME::Subkey subkey = m_pKey.subkey(0);
+ if (subkey.isNull())
+ return DateTime();
+
+ return convertUnixTimeToDateTime(m_pKey.subkey(0).creationTime());
+}
+
+DateTime SAL_CALL CertificateImpl::getNotValidAfter()
+{
+ const GpgME::Subkey subkey = m_pKey.subkey(0);
+ if (subkey.isNull() || subkey.neverExpires())
+ return DateTime();
+
+ return convertUnixTimeToDateTime(m_pKey.subkey(0).expirationTime());
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getIssuerUniqueID()
+{
+ // Empty for gpg
+ return Sequence< sal_Int8 > ();
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSubjectUniqueID()
+{
+ // Empty for gpg
+ return Sequence< sal_Int8 > ();
+}
+
+Sequence< Reference< XCertificateExtension > > SAL_CALL CertificateImpl::getExtensions()
+{
+ // Empty for gpg
+ return Sequence< Reference< XCertificateExtension > > ();
+}
+
+Reference< XCertificateExtension > SAL_CALL CertificateImpl::findCertificateExtension( const Sequence< sal_Int8 >& /*oid*/ )
+{
+ // Empty for gpg
+ return Reference< XCertificateExtension > ();
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getEncoded()
+{
+ // Export key to base64Empty for gpg
+ return m_aBits;
+}
+
+OUString SAL_CALL CertificateImpl::getSubjectPublicKeyAlgorithm()
+{
+ const GpgME::Subkey subkey = m_pKey.subkey(0);
+ if (subkey.isNull())
+ return OUString();
+
+ return OStringToOUString(subkey.publicKeyAlgorithmAsString(), RTL_TEXTENCODING_UTF8);
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSubjectPublicKeyValue()
+{
+ return Sequence< sal_Int8 > ();
+}
+
+OUString SAL_CALL CertificateImpl::getSignatureAlgorithm()
+{
+ const GpgME::UserID userId = m_pKey.userID(0);
+ if (userId.isNull())
+ return OUString();
+
+ const GpgME::UserID::Signature signature = userId.signature(0);
+ if (signature.isNull())
+ return OUString();
+
+ return OStringToOUString(signature.algorithmAsString(), RTL_TEXTENCODING_UTF8);
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSHA1Thumbprint()
+{
+ // This is mapped to the fingerprint for gpg
+ const char* keyId = m_pKey.primaryFingerprint();
+ return comphelper::arrayToSequence<sal_Int8>(
+ keyId, strlen(keyId)+1);
+}
+
+Sequence<sal_Int8> CertificateImpl::getSHA256Thumbprint()
+{
+ // This is mapped to the fingerprint for gpg (though that's only
+ // SHA1 actually)
+ const char* keyId = m_pKey.primaryFingerprint();
+ return comphelper::arrayToSequence<sal_Int8>(
+ keyId, strlen(keyId)+1);
+}
+
+svl::crypto::SignatureMethodAlgorithm CertificateImpl::getSignatureMethodAlgorithm()
+{
+ return svl::crypto::SignatureMethodAlgorithm::RSA;
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getMD5Thumbprint()
+{
+ // This is mapped to the shorter keyID for gpg
+ const char* keyId = m_pKey.keyID();
+ return comphelper::arrayToSequence<sal_Int8>(
+ keyId, strlen(keyId)+1);
+}
+
+CertificateKind SAL_CALL CertificateImpl::getCertificateKind()
+{
+ return CertificateKind_OPENPGP;
+}
+
+sal_Int32 SAL_CALL CertificateImpl::getCertificateUsage()
+{
+ return KeyUsage::DIGITAL_SIGNATURE | KeyUsage::NON_REPUDIATION | KeyUsage::KEY_ENCIPHERMENT | KeyUsage::DATA_ENCIPHERMENT;
+}
+
+/* XUnoTunnel */
+sal_Int64 SAL_CALL CertificateImpl::getSomething(const Sequence< sal_Int8 >& aIdentifier)
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XUnoTunnel extension */
+
+const Sequence< sal_Int8>& CertificateImpl::getUnoTunnelId() {
+ static const comphelper::UnoIdInit theCertificateImplUnoTunnelId;
+ return theCertificateImplUnoTunnelId.getSeq();
+}
+
+void CertificateImpl::setCertificate(GpgME::Context* ctx, const GpgME::Key& key)
+{
+ m_pKey = key;
+
+ // extract key data, store into m_aBits
+ GpgME::Data data_out;
+ ctx->setArmor(false); // caller will base64-encode anyway
+ GpgME::Error err = ctx->exportPublicKeys(
+ key.primaryFingerprint(),
+ data_out,
+ officecfg::Office::Common::Security::OpenPGP::MinimalKeyExport::get()
+ ? GpgME::Context::ExportMinimal : 0
+ );
+
+ if (err)
+ throw RuntimeException("The GpgME library failed to retrieve the public key");
+
+ off_t result = data_out.seek(0,SEEK_SET);
+ (void) result;
+ assert(result == 0);
+ int len=0, curr=0; char buf;
+ while( (curr=data_out.read(&buf, 1)) )
+ len += curr;
+
+ // write bits to sequence of bytes
+ m_aBits.realloc(len);
+ result = data_out.seek(0,SEEK_SET);
+ assert(result == 0);
+ if( data_out.read(m_aBits.getArray(), len) != len )
+ throw RuntimeException("The GpgME library failed to read the key");
+}
+
+const GpgME::Key* CertificateImpl::getCertificate() const
+{
+ return &m_pKey;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL CertificateImpl::getImplementationName()
+{
+ return "com.sun.star.xml.security.gpg.XCertificate_GpgImpl";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL CertificateImpl::supportsService(const OUString& serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence<OUString> SAL_CALL CertificateImpl::getSupportedServiceNames() { return { OUString() }; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/CertificateImpl.hxx b/xmlsecurity/source/gpg/CertificateImpl.hxx
new file mode 100644
index 000000000..b6c50cb2e
--- /dev/null
+++ b/xmlsecurity/source/gpg/CertificateImpl.hxx
@@ -0,0 +1,100 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <certificate.hxx>
+
+#include <sal/types.h>
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/security/CertificateKind.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundef"
+#endif
+#include <key.h>
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+class CertificateImpl : public cppu::WeakImplHelper< css::security::XCertificate,
+ css::lang::XUnoTunnel,
+ css::lang::XServiceInfo >,
+ public xmlsecurity::Certificate
+{
+private:
+ GpgME::Key m_pKey;
+ css::uno::Sequence< sal_Int8 > m_aBits;
+
+public:
+ CertificateImpl();
+ virtual ~CertificateImpl() override;
+
+ //Methods from XCertificate
+ virtual sal_Int16 SAL_CALL getVersion() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSerialNumber() override;
+
+ virtual OUString SAL_CALL getIssuerName() override;
+ virtual OUString SAL_CALL getSubjectName() override;
+
+ virtual css::util::DateTime SAL_CALL getNotValidBefore() override;
+ virtual css::util::DateTime SAL_CALL getNotValidAfter() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getIssuerUniqueID() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectUniqueID() override;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > SAL_CALL getExtensions() override;
+
+ virtual css::uno::Reference< css::security::XCertificateExtension > SAL_CALL findCertificateExtension(const css::uno::Sequence< sal_Int8 >& oid) override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getEncoded() override;
+
+ virtual OUString SAL_CALL getSubjectPublicKeyAlgorithm() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectPublicKeyValue() override;
+
+ virtual OUString SAL_CALL getSignatureAlgorithm() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSHA1Thumbprint() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getMD5Thumbprint() override;
+
+ virtual sal_Int32 SAL_CALL getCertificateUsage() override;
+
+ //Methods from XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence< sal_Int8 >& aIdentifier) override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ /// @see xmlsecurity::Certificate::getSHA256Thumbprint().
+ virtual css::uno::Sequence<sal_Int8> getSHA256Thumbprint() override;
+ /// @see xmlsecurity::Certificate::getSignatureMethodAlgorithm().
+ virtual svl::crypto::SignatureMethodAlgorithm getSignatureMethodAlgorithm() override;
+ virtual css::security::CertificateKind SAL_CALL getCertificateKind() override;
+
+ // Helper methods
+ void setCertificate(GpgME::Context* ctx, const GpgME::Key& key);
+ const GpgME::Key* getCertificate() const;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/CipherContext.cxx b/xmlsecurity/source/gpg/CipherContext.cxx
new file mode 100644
index 000000000..291db0ba6
--- /dev/null
+++ b/xmlsecurity/source/gpg/CipherContext.cxx
@@ -0,0 +1,27 @@
+/* -*- 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 "CipherContext.hxx"
+
+using namespace css;
+using namespace css::uno;
+using namespace css::lang;
+
+Sequence<sal_Int8>
+ SAL_CALL CipherContext::convertWithCipherContext(const Sequence<sal_Int8>& /*aData*/)
+{
+ return Sequence<sal_Int8>();
+}
+
+uno::Sequence<sal_Int8> SAL_CALL CipherContext::finalizeCipherContextAndDispose()
+{
+ return Sequence<sal_Int8>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/CipherContext.hxx b/xmlsecurity/source/gpg/CipherContext.hxx
new file mode 100644
index 000000000..2c0ffcdc9
--- /dev/null
+++ b/xmlsecurity/source/gpg/CipherContext.hxx
@@ -0,0 +1,26 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+class CipherContext : public cppu::WeakImplHelper<css::xml::crypto::XCipherContext>
+{
+private:
+public:
+ // XCipherContext
+ virtual css::uno::Sequence<::sal_Int8>
+ SAL_CALL convertWithCipherContext(const css::uno::Sequence<::sal_Int8>& aData) override;
+ virtual css::uno::Sequence<::sal_Int8> SAL_CALL finalizeCipherContextAndDispose() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/DigestContext.cxx b/xmlsecurity/source/gpg/DigestContext.cxx
new file mode 100644
index 000000000..4864191bb
--- /dev/null
+++ b/xmlsecurity/source/gpg/DigestContext.cxx
@@ -0,0 +1,23 @@
+/* -*- 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 "DigestContext.hxx"
+
+using namespace css;
+using namespace css::uno;
+using namespace css::lang;
+
+void SAL_CALL DigestContext::updateDigest(const Sequence<sal_Int8>& /*aData*/) {}
+
+uno::Sequence<sal_Int8> SAL_CALL DigestContext::finalizeDigestAndDispose()
+{
+ return Sequence<sal_Int8>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/DigestContext.hxx b/xmlsecurity/source/gpg/DigestContext.hxx
new file mode 100644
index 000000000..2d48daf34
--- /dev/null
+++ b/xmlsecurity/source/gpg/DigestContext.hxx
@@ -0,0 +1,24 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+class DigestContext : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext>
+{
+public:
+ // XDigestContext
+ virtual void SAL_CALL updateDigest(const css::uno::Sequence<::sal_Int8>& aData) override;
+ virtual css::uno::Sequence<::sal_Int8> SAL_CALL finalizeDigestAndDispose() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/SEInitializer.cxx b/xmlsecurity/source/gpg/SEInitializer.cxx
new file mode 100644
index 000000000..5cfe325aa
--- /dev/null
+++ b/xmlsecurity/source/gpg/SEInitializer.cxx
@@ -0,0 +1,80 @@
+/* -*- 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 <cppuhelper/supportsservice.hxx>
+#include <gpg/SEInitializer.hxx>
+#include "SecurityEnvironment.hxx"
+#include "XMLSecurityContext.hxx"
+
+#include <global.h>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+using namespace css;
+using namespace css::lang;
+using namespace css::uno;
+using namespace css::xml::crypto;
+
+SEInitializerGpg::SEInitializerGpg()
+{
+ // Also init GpgME while we're at it
+ GpgME::initializeLibrary();
+}
+
+SEInitializerGpg::~SEInitializerGpg() {}
+
+/* XSEInitializer */
+Reference<XXMLSecurityContext> SAL_CALL SEInitializerGpg::createSecurityContext(const OUString&)
+{
+ try
+ {
+ /* Build XML Security Context */
+ Reference<XXMLSecurityContext> xSecCtx(new XMLSecurityContextGpg());
+
+ Reference<XSecurityEnvironment> xSecEnv(new SecurityEnvironmentGpg());
+ sal_Int32 n = xSecCtx->addSecurityEnvironment(xSecEnv);
+ //originally the SecurityEnvironment with the internal slot was set as default
+ xSecCtx->setDefaultSecurityEnvironmentIndex(n);
+ return xSecCtx;
+ }
+ catch (const uno::Exception&)
+ {
+ return nullptr;
+ }
+}
+
+void SAL_CALL SEInitializerGpg::freeSecurityContext(const uno::Reference<XXMLSecurityContext>&) {}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SEInitializerGpg::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SEInitializerGpg::getSupportedServiceNames()
+{
+ return { "com.sun.star.xml.crypto.GPGSEInitializer" };
+}
+
+OUString SAL_CALL SEInitializerGpg::getImplementationName()
+{
+ return "com.sun.star.xml.security.SEInitializer_Gpg";
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_security_SEInitializer_Gpg_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SEInitializerGpg());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/SecurityEnvironment.cxx b/xmlsecurity/source/gpg/SecurityEnvironment.cxx
new file mode 100644
index 000000000..da2310765
--- /dev/null
+++ b/xmlsecurity/source/gpg/SecurityEnvironment.cxx
@@ -0,0 +1,232 @@
+/* -*- 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 "SecurityEnvironment.hxx"
+#include "CertificateImpl.hxx"
+
+#include <com/sun/star/security/CertificateCharacters.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+
+#include <comphelper/servicehelper.hxx>
+#include <vector>
+#include <rtl/ref.hxx>
+
+#ifdef _WIN32
+#include <config_folders.h>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/bootstrap.hxx>
+#include <tools/urlobj.hxx>
+#endif
+
+#include <key.h>
+#include <keylistresult.h>
+#include <xmlsec-wrapper.h>
+
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundef"
+#endif
+#include <gpgme.h>
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic pop
+#endif
+#include <context.h>
+
+using namespace css;
+using namespace css::security;
+using namespace css::uno;
+using namespace css::lang;
+
+SecurityEnvironmentGpg::SecurityEnvironmentGpg()
+{
+#ifdef _WIN32
+ // On Windows, gpgme expects gpgme-w32spawn.exe to be in the same directory as the current
+ // process executable. This assumption might be wrong, e.g., for bundled python, which is
+ // in instdir/program/python-core-x.y.z/bin, while gpgme-w32spawn.exe is in instdir/program.
+ // If we can't find gpgme-w32spawn.exe in the current executable location, then try to find
+ // the spawn executable, and inform gpgme about actual location using gpgme_set_global_flag.
+ [[maybe_unused]] static bool bSpawnPathInitialized = [] {
+ auto accessUrl = [](const INetURLObject& url) {
+ osl::File file(url.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ return file.open(osl_File_OpenFlag_Read) == osl::FileBase::E_None;
+ };
+ OUString sPath;
+ osl_getExecutableFile(&sPath.pData);
+ INetURLObject aPathUrl(sPath);
+ aPathUrl.setName(u"gpgme-w32spawn.exe");
+ if (!accessUrl(aPathUrl))
+ {
+ sPath = "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/gpgme-w32spawn.exe";
+ rtl::Bootstrap::expandMacros(sPath);
+ aPathUrl.SetURL(sPath);
+ if (accessUrl(aPathUrl))
+ {
+ aPathUrl.removeSegment();
+ GpgME::setGlobalFlag("w32-inst-dir",
+ aPathUrl.getFSysPath(FSysStyle::Dos).toUtf8().getStr());
+ }
+ }
+ return true;
+ }();
+#endif
+ GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
+ if (err)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ m_ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
+ if (m_ctx == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ m_ctx->setArmor(false);
+}
+
+SecurityEnvironmentGpg::~SecurityEnvironmentGpg()
+{
+}
+
+/* XUnoTunnel */
+sal_Int64 SAL_CALL SecurityEnvironmentGpg::getSomething( const Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XUnoTunnel extension */
+
+namespace
+{
+}
+
+const Sequence< sal_Int8>& SecurityEnvironmentGpg::getUnoTunnelId() {
+ static const comphelper::UnoIdInit theSecurityEnvironmentUnoTunnelId;
+ return theSecurityEnvironmentUnoTunnelId.getSeq();
+}
+
+OUString SecurityEnvironmentGpg::getSecurityEnvironmentInformation()
+{
+ return OUString();
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getCertificatesImpl( bool bPrivateOnly )
+{
+ std::vector< GpgME::Key > keyList;
+ std::vector< rtl::Reference<CertificateImpl> > certsList;
+
+ m_ctx->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL);
+ GpgME::Error err = m_ctx->startKeyListing("", bPrivateOnly );
+ while (!err) {
+ GpgME::Key k = m_ctx->nextKey(err);
+ if (err)
+ break;
+ if (!k.isRevoked() && !k.isExpired() && !k.isDisabled() && !k.isInvalid()) {
+ // We can't create CertificateImpl here as CertificateImpl::setCertificate uses GpgME API
+ // which interrupts our key listing here. So first get the keys from GpgME, then create the CertificateImpls
+ keyList.push_back(k);
+ }
+ }
+ m_ctx->endKeyListing();
+
+ for (auto const& key : keyList) {
+ rtl::Reference<CertificateImpl> xCert = new CertificateImpl();
+ xCert->setCertificate(m_ctx.get(),key);
+ certsList.push_back(xCert);
+ }
+
+ Sequence< Reference< XCertificate > > xCertificateSequence(certsList.size());
+ auto xCertificateSequenceRange = asNonConstRange(xCertificateSequence);
+ int i = 0;
+ for (const auto& cert : certsList) {
+ xCertificateSequenceRange[i++] = cert;
+ }
+
+ return xCertificateSequence;
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getPersonalCertificates()
+{
+ return getCertificatesImpl( true );
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getAllCertificates()
+{
+ return getCertificatesImpl( false );
+}
+
+Reference< XCertificate > SecurityEnvironmentGpg::getCertificate( const OUString& keyId, const Sequence< sal_Int8 >& /*serialNumber*/ )
+{
+ //xmlChar* pSignatureValue=xmlNodeGetContent(cur);
+ OString ostr = OUStringToOString( keyId , RTL_TEXTENCODING_UTF8 );
+ const xmlChar* strKeyId = reinterpret_cast<const xmlChar*>(ostr.getStr());
+ if(xmlSecBase64Decode(strKeyId, const_cast<xmlSecByte*>(strKeyId), xmlStrlen(strKeyId)) < 0)
+ throw RuntimeException("Base64 decode failed");
+
+ m_ctx->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL);
+ GpgME::Error err = m_ctx->startKeyListing("", false);
+ while (!err) {
+ GpgME::Key k = m_ctx->nextKey(err);
+ if (err)
+ break;
+ if (!k.isInvalid() && strcmp(k.primaryFingerprint(), reinterpret_cast<const char*>(strKeyId)) == 0) {
+ rtl::Reference<CertificateImpl> xCert = new CertificateImpl();
+ xCert->setCertificate(m_ctx.get(), k);
+ m_ctx->endKeyListing();
+ return xCert;
+ }
+ }
+ m_ctx->endKeyListing();
+
+ return nullptr;
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::buildCertificatePath( const Reference< XCertificate >& /*begin*/ )
+{
+ return Sequence< Reference < XCertificate > >();
+}
+
+Reference< XCertificate > SecurityEnvironmentGpg::createCertificateFromRaw( const Sequence< sal_Int8 >& /*rawCertificate*/ )
+{
+ return nullptr;
+}
+
+Reference< XCertificate > SecurityEnvironmentGpg::createCertificateFromAscii( const OUString& /*asciiCertificate*/ )
+{
+ return nullptr;
+}
+
+sal_Int32 SecurityEnvironmentGpg::verifyCertificate( const Reference< XCertificate >& aCert,
+ const Sequence< Reference< XCertificate > >& /*intermediateCerts*/ )
+{
+ const CertificateImpl* xCert = dynamic_cast<CertificateImpl*>(aCert.get());
+ if (xCert == nullptr) {
+ // Can't find the key locally -> unknown owner
+ return security::CertificateValidity::ISSUER_UNKNOWN;
+ }
+
+ const GpgME::Key* key = xCert->getCertificate();
+ if (key->ownerTrust() == GpgME::Key::OwnerTrust::Marginal ||
+ key->ownerTrust() == GpgME::Key::OwnerTrust::Full ||
+ key->ownerTrust() == GpgME::Key::OwnerTrust::Ultimate)
+ {
+ return security::CertificateValidity::VALID;
+ }
+
+ return security::CertificateValidity::ISSUER_UNTRUSTED;
+}
+
+sal_Int32 SecurityEnvironmentGpg::getCertificateCharacters(
+ const Reference< XCertificate >& aCert)
+{
+ if (comphelper::getFromUnoTunnel<CertificateImpl>(aCert) == nullptr)
+ throw RuntimeException();
+
+ // we only listed private keys anyway, up in
+ // SecurityEnvironmentGpg::getPersonalCertificates
+ return CertificateCharacters::HAS_PRIVATE_KEY;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/SecurityEnvironment.hxx b/xmlsecurity/source/gpg/SecurityEnvironment.hxx
new file mode 100644
index 000000000..262578682
--- /dev/null
+++ b/xmlsecurity/source/gpg/SecurityEnvironment.hxx
@@ -0,0 +1,72 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+
+namespace com::sun::star::security { class XCertificate; }
+namespace GpgME { class Context; }
+
+class SecurityEnvironmentGpg : public cppu::WeakImplHelper< css::xml::crypto::XSecurityEnvironment,
+ css::lang::XUnoTunnel >
+{
+ std::unique_ptr<GpgME::Context> m_ctx;
+
+public:
+ SecurityEnvironmentGpg();
+ virtual ~SecurityEnvironmentGpg() override;
+
+ //Methods from XSecurityEnvironment
+ virtual ::sal_Int32 SAL_CALL verifyCertificate(
+ const css::uno::Reference<
+ css::security::XCertificate >& xCert,
+ const css::uno::Sequence<
+ css::uno::Reference< css::security::XCertificate > > &
+ intermediateCerts) override;
+
+ virtual ::sal_Int32 SAL_CALL getCertificateCharacters( const css::uno::Reference< css::security::XCertificate >& xCert ) override;
+
+ virtual OUString SAL_CALL getSecurityEnvironmentInformation() override;
+
+ //Methods from XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId() ;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getPersonalCertificates() override;
+
+ /** We reinterpret the first parameter (originally issuerName) as keyId. We have no other way to identify a gpg key. */
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL getCertificate( const OUString& keyId, const css::uno::Sequence< sal_Int8 >& serialNumber ) override;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL buildCertificatePath(
+ const css::uno::Reference< css::security::XCertificate >& beginCert ) override;
+
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromRaw(
+ const css::uno::Sequence< sal_Int8 >& rawCertificate ) override;
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromAscii(
+ const OUString& asciiCertificate ) override;
+
+ GpgME::Context& getGpgContext() { return *m_ctx; }
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getAllCertificates() override;
+
+private:
+ css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > getCertificatesImpl( bool bPrivateOnly );
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLEncryption.cxx b/xmlsecurity/source/gpg/XMLEncryption.cxx
new file mode 100644
index 000000000..65083ff68
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLEncryption.cxx
@@ -0,0 +1,37 @@
+/* -*- 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 "XMLEncryption.hxx"
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::xml::wrapper;
+using namespace css::xml::crypto;
+
+XMLEncryptionGpg::XMLEncryptionGpg() {
+}
+
+XMLEncryptionGpg::~XMLEncryptionGpg() {
+}
+
+/* XXMLEncryption */
+Reference< XXMLEncryptionTemplate > SAL_CALL XMLEncryptionGpg::encrypt(const Reference< XXMLEncryptionTemplate >& /*aTemplate*/,
+ const Reference< XSecurityEnvironment >& /*aEnvironment*/)
+{
+ return nullptr;
+}
+
+/* XXMLEncryption */
+Reference< XXMLEncryptionTemplate > SAL_CALL XMLEncryptionGpg::decrypt(const Reference< XXMLEncryptionTemplate >& /*aTemplate*/,
+ const Reference< XXMLSecurityContext >& /*aSecurityCtx*/)
+{
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLEncryption.hxx b/xmlsecurity/source/gpg/XMLEncryption.hxx
new file mode 100644
index 000000000..4ff47ccc4
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLEncryption.hxx
@@ -0,0 +1,37 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/crypto/XXMLEncryption.hpp>
+
+namespace com::sun::star::xml::crypto { class XXMLEncryptionTemplate; }
+namespace com::sun::star::xml::crypto { class XXMLSecurityContext; }
+
+class XMLEncryptionGpg : public cppu::WeakImplHelper< css::xml::crypto::XXMLEncryption >
+{
+public:
+ explicit XMLEncryptionGpg();
+ virtual ~XMLEncryptionGpg() override;
+
+ // XXMLEncryption
+ virtual css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate > SAL_CALL encrypt(
+ const css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate >& aTemplate,
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& aEnvironment) override;
+
+ virtual css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate > SAL_CALL decrypt(
+ const css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate >& aTemplate,
+ const css::uno::Reference< css::xml::crypto::XXMLSecurityContext >& aContext) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLSecurityContext.cxx b/xmlsecurity/source/gpg/XMLSecurityContext.cxx
new file mode 100644
index 000000000..1fb407113
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLSecurityContext.cxx
@@ -0,0 +1,87 @@
+/* -*- 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 "XMLSecurityContext.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::xml::crypto;
+
+XMLSecurityContextGpg::XMLSecurityContextGpg()
+ : m_nDefaultEnvIndex(-1)
+{
+}
+
+XMLSecurityContextGpg::~XMLSecurityContextGpg() {}
+
+sal_Int32 SAL_CALL XMLSecurityContextGpg::addSecurityEnvironment(
+ const Reference<XSecurityEnvironment>& aSecurityEnvironment)
+{
+ if (!aSecurityEnvironment.is())
+ throw RuntimeException("Invalid SecurityEnvironment given!");
+
+ m_vSecurityEnvironments.push_back(aSecurityEnvironment);
+ return m_vSecurityEnvironments.size() - 1;
+}
+
+sal_Int32 SAL_CALL XMLSecurityContextGpg::getSecurityEnvironmentNumber()
+{
+ return m_vSecurityEnvironments.size();
+}
+
+Reference<XSecurityEnvironment>
+ SAL_CALL XMLSecurityContextGpg::getSecurityEnvironmentByIndex(sal_Int32 index)
+{
+ if (index < 0 || o3tl::make_unsigned(index) >= m_vSecurityEnvironments.size())
+ throw RuntimeException("Invalid index");
+
+ return m_vSecurityEnvironments[index];
+}
+
+Reference<XSecurityEnvironment> SAL_CALL XMLSecurityContextGpg::getSecurityEnvironment()
+{
+ if (m_nDefaultEnvIndex < 0
+ || o3tl::make_unsigned(m_nDefaultEnvIndex) >= m_vSecurityEnvironments.size())
+ throw RuntimeException("Invalid index");
+
+ return getSecurityEnvironmentByIndex(m_nDefaultEnvIndex);
+}
+
+sal_Int32 SAL_CALL XMLSecurityContextGpg::getDefaultSecurityEnvironmentIndex()
+{
+ return m_nDefaultEnvIndex;
+}
+
+void SAL_CALL XMLSecurityContextGpg::setDefaultSecurityEnvironmentIndex(sal_Int32 nDefaultEnvIndex)
+{
+ m_nDefaultEnvIndex = nDefaultEnvIndex;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSecurityContextGpg::getImplementationName()
+{
+ return "com.sun.star.xml.security.gpg.XMLSecurityContext_GpgImpl";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSecurityContextGpg::supportsService(const OUString& serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence<OUString> SAL_CALL XMLSecurityContextGpg::getSupportedServiceNames()
+{
+ return { OUString("com.sun.star.xml.crypto.XMLSecurityContext") };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLSecurityContext.hxx b/xmlsecurity/source/gpg/XMLSecurityContext.hxx
new file mode 100644
index 000000000..e3dea7310
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLSecurityContext.hxx
@@ -0,0 +1,58 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
+
+#include <vector>
+
+namespace com::sun::star::xml::crypto { class XSecurityEnvironment; }
+
+class XMLSecurityContextGpg : public cppu::WeakImplHelper< css::xml::crypto::XXMLSecurityContext,
+ css::lang::XServiceInfo>
+{
+private:
+ std::vector< css::uno::Reference< css::xml::crypto::XSecurityEnvironment > > m_vSecurityEnvironments;
+
+ sal_Int32 m_nDefaultEnvIndex;
+
+public:
+ XMLSecurityContextGpg();
+ virtual ~XMLSecurityContextGpg() override;
+
+ // XXMLSecurityContext
+ virtual sal_Int32 SAL_CALL addSecurityEnvironment(
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& aSecurityEnvironment) override;
+
+ virtual ::sal_Int32 SAL_CALL getSecurityEnvironmentNumber() override;
+
+ virtual css::uno::Reference<css::xml::crypto::XSecurityEnvironment > SAL_CALL getSecurityEnvironmentByIndex(sal_Int32 index) override;
+
+ virtual css::uno::Reference<css::xml::crypto::XSecurityEnvironment > SAL_CALL getSecurityEnvironment() override;
+
+ virtual sal_Int32 SAL_CALL getDefaultSecurityEnvironmentIndex() override;
+
+ virtual void SAL_CALL setDefaultSecurityEnvironmentIndex( sal_Int32 nDefaultEnvIndex ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override ;
+
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override ;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override ;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx b/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx
new file mode 100644
index 000000000..3b4e138ae
--- /dev/null
+++ b/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx
@@ -0,0 +1,527 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <xmlsec-wrapper.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <gpg/xmlsignature_gpgimpl.hxx>
+
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundef"
+#endif
+#include <gpgme.h>
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic pop
+#endif
+#include <context.h>
+#include <key.h>
+#include <data.h>
+#include <signingresult.h>
+#include <importresult.h>
+
+#include <xmlelementwrapper_xmlsecimpl.hxx>
+#include <xmlsec/xmlstreamio.hxx>
+#include <xmlsec/errorcallback.hxx>
+
+#include "SecurityEnvironment.hxx"
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::xml::wrapper;
+using namespace css::xml::crypto;
+
+XMLSignature_GpgImpl::XMLSignature_GpgImpl() {
+}
+
+XMLSignature_GpgImpl::~XMLSignature_GpgImpl() {
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_GpgImpl::generate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XSecurityEnvironment >& aEnvironment
+)
+{
+ xmlSecDSigCtxPtr pDsigCtx = nullptr ;
+ xmlNodePtr pNode = nullptr ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aEnvironment.is() )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() ) {
+ throw RuntimeException() ;
+ }
+
+ XMLElementWrapper_XmlSecImpl* pElement =
+ dynamic_cast<XMLElementWrapper_XmlSecImpl*>(xElement.get());
+ if( pElement == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ //Get Keys Manager
+ SecurityEnvironmentGpg* pSecEnv =
+ dynamic_cast<SecurityEnvironmentGpg*>(aEnvironment.get());
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ setErrorRecorder();
+
+ //Create Signature context
+ pDsigCtx = xmlSecDSigCtxCreate( nullptr ) ;
+ if( pDsigCtx == nullptr )
+ {
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ // set intended operation to sign - several asserts inside libxmlsec
+ // wanting that for digest / transforms
+ pDsigCtx->operation = xmlSecTransformOperationSign;
+
+ // we default to SHA512 for all digests - nss crypto does not have it...
+ //pDsigCtx->defDigestMethodId = xmlSecTransformSha512Id;
+
+ // Calculate digest for all references
+ xmlNodePtr cur = xmlSecGetNextElementNode(pNode->children);
+ if( cur != nullptr )
+ cur = xmlSecGetNextElementNode(cur->children);
+ while( cur != nullptr )
+ {
+ // some of those children I suppose should be reference elements
+ if( xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs) )
+ {
+ xmlSecDSigReferenceCtxPtr pDsigRefCtx =
+ xmlSecDSigReferenceCtxCreate(pDsigCtx,
+ xmlSecDSigReferenceOriginSignedInfo);
+ if(pDsigRefCtx == nullptr)
+ throw RuntimeException();
+
+ // add this one to the list
+ if( xmlSecPtrListAdd(&(pDsigCtx->signedInfoReferences),
+ pDsigRefCtx) < 0 )
+ {
+ // TODO resource handling
+ xmlSecDSigReferenceCtxDestroy(pDsigRefCtx);
+ throw RuntimeException();
+ }
+
+ if( xmlSecDSigReferenceCtxProcessNode(pDsigRefCtx, cur) < 0 )
+ throw RuntimeException();
+
+ // final check - all good?
+ if(pDsigRefCtx->status != xmlSecDSigStatusSucceeded)
+ {
+ pDsigCtx->status = xmlSecDSigStatusInvalid;
+ return aTemplate; // TODO - harder error?
+ }
+ }
+
+ cur = xmlSecGetNextElementNode(cur->next);
+ }
+
+ // get me a digestible buffer from the signature template!
+ // -------------------------------------------------------
+
+ // run the transformations over SignedInfo element (first child of
+ // pNode)
+ xmlSecNodeSetPtr nodeset = nullptr;
+ cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO assert that...
+ nodeset = xmlSecNodeSetGetChildren(pNode->doc, cur, 1, 0);
+ if(nodeset == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ if( xmlSecTransformCtxXmlExecute(&(pDsigCtx->transformCtx), nodeset) < 0 )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // now extract the keyid from PGPData
+ // walk xml tree to PGPData node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue, 3rd is KeyInfo
+ // 1st child is PGPData, 1st grandchild is PGPKeyID
+ cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO error handling
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->children);
+ // check that this is now PGPData
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPData, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ // check that this is now PGPKeyID
+ cur = xmlSecGetNextElementNode(cur->children);
+ static const xmlChar xmlSecNodePGPKeyID[] = "PGPKeyID";
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyID, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ GpgME::Context& rCtx=pSecEnv->getGpgContext();
+ rCtx.setKeyListMode(GPGME_KEYLIST_MODE_LOCAL);
+ GpgME::Error err;
+ xmlChar* pKey=xmlNodeGetContent(cur);
+ if(xmlSecBase64Decode(pKey, reinterpret_cast<xmlSecByte*>(pKey), xmlStrlen(pKey)) < 0)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ if( rCtx.addSigningKey(
+ rCtx.key(
+ reinterpret_cast<char*>(pKey), err, true)) )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ xmlFree(pKey);
+
+ // good, ctx is setup now, let's sign the lot
+ GpgME::Data data_in(
+ reinterpret_cast<char*>(xmlSecBufferGetData(pDsigCtx->transformCtx.result)),
+ xmlSecBufferGetSize(pDsigCtx->transformCtx.result), false);
+ GpgME::Data data_out;
+
+ SAL_INFO("xmlsecurity.xmlsec.gpg", "Generating signature for: " << xmlSecBufferGetData(pDsigCtx->transformCtx.result));
+
+ // we base64-encode anyway
+ rCtx.setArmor(false);
+ GpgME::SigningResult sign_res=rCtx.sign(data_in, data_out,
+ GpgME::Detached);
+ off_t result = data_out.seek(0,SEEK_SET);
+ (void) result;
+ assert(result == 0);
+ int len=0, curr=0; char buf;
+ while( (curr=data_out.read(&buf, 1)) )
+ len += curr;
+
+ if(sign_res.error() || !len)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // write signed data to xml
+ xmlChar* signature = static_cast<xmlChar*>(xmlMalloc(len + 1));
+ if(signature == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ result = data_out.seek(0,SEEK_SET);
+ assert(result == 0);
+ if( data_out.read(signature, len) != len )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // conversion to base64
+ xmlChar* signatureEncoded=nullptr;
+ if( !(signatureEncoded=xmlSecBase64Encode(reinterpret_cast<xmlSecByte*>(signature), len, 79)) )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ xmlFree(signature);
+
+ // walk xml tree to sign value node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue
+ cur = xmlSecGetNextElementNode(pNode->children);
+ cur = xmlSecGetNextElementNode(cur->next);
+
+ // TODO some assert would be good...
+ xmlNodeSetContentLen(cur, signatureEncoded, xmlStrlen(signatureEncoded));
+ xmlFree(signatureEncoded);
+
+ aTemplate->setStatus(SecurityOperationStatus_OPERATION_SUCCEEDED);
+
+ // done
+ xmlSecDSigCtxDestroy( pDsigCtx ) ;
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+ clearErrorRecorder();
+ return aTemplate ;
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_GpgImpl::validate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XXMLSecurityContext >& aSecurityCtx
+) {
+ xmlSecDSigCtxPtr pDsigCtx = nullptr ;
+ xmlNodePtr pNode = nullptr ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aSecurityCtx.is() )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() )
+ throw RuntimeException() ;
+
+ XMLElementWrapper_XmlSecImpl* pElement =
+ dynamic_cast<XMLElementWrapper_XmlSecImpl*>(xElement.get());
+ if( pElement == nullptr )
+ throw RuntimeException() ;
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ setErrorRecorder();
+
+ sal_Int32 nSecurityEnvironment = aSecurityCtx->getSecurityEnvironmentNumber();
+ sal_Int32 i;
+
+ for (i=0; i<nSecurityEnvironment; ++i)
+ {
+ Reference< XSecurityEnvironment > aEnvironment = aSecurityCtx->getSecurityEnvironmentByIndex(i);
+
+ SecurityEnvironmentGpg* pSecEnv =
+ dynamic_cast<SecurityEnvironmentGpg*>(aEnvironment.get());
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ // TODO figure out key from pSecEnv!
+ // unclear how/where that is transported in nss impl...
+
+ //Create Signature context
+ pDsigCtx = xmlSecDSigCtxCreate( nullptr ) ;
+ if( pDsigCtx == nullptr )
+ {
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ // set intended operation to verify - several asserts inside libxmlsec
+ // wanting that for digest / transforms
+ pDsigCtx->operation = xmlSecTransformOperationVerify;
+
+ // reset status - to be set later
+ pDsigCtx->status = xmlSecDSigStatusUnknown;
+
+ // get me a digestible buffer from the SignatureInfo node!
+ // -------------------------------------------------------
+
+ // run the transformations - first child node is required to
+ // be SignatureInfo
+ xmlSecNodeSetPtr nodeset = nullptr;
+ xmlNodePtr cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO assert that...
+ nodeset = xmlSecNodeSetGetChildren(pNode->doc, cur, 1, 0);
+ if(nodeset == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // TODO assert we really have the SignatureInfo here?
+ if( xmlSecTransformCtxXmlExecute(&(pDsigCtx->transformCtx), nodeset) < 0 )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // Validate the template via gpgme
+ GpgME::Context& rCtx=pSecEnv->getGpgContext();
+
+ GpgME::Data data_text(
+ reinterpret_cast<char*>(xmlSecBufferGetData(pDsigCtx->transformCtx.result)),
+ xmlSecBufferGetSize(pDsigCtx->transformCtx.result), false);
+
+ SAL_INFO("xmlsecurity.xmlsec.gpg", "Validating SignatureInfo: " << xmlSecBufferGetData(pDsigCtx->transformCtx.result));
+
+ // walk xml tree to sign value node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue
+ cur = xmlSecGetNextElementNode(pNode->children);
+ cur = xmlSecGetNextElementNode(cur->next);
+
+ if(!xmlSecCheckNodeName(cur, xmlSecNodeSignatureValue, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ xmlChar* pSignatureValue=xmlNodeGetContent(cur);
+ int nSigSize = xmlSecBase64Decode(pSignatureValue, reinterpret_cast<xmlSecByte*>(pSignatureValue), xmlStrlen(pSignatureValue));
+ if( nSigSize < 0)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ GpgME::Data data_signature(
+ reinterpret_cast<char*>(pSignatureValue),
+ nSigSize, false);
+
+ GpgME::VerificationResult verify_res=rCtx.verifyDetachedSignature(
+ data_signature, data_text);
+
+ // TODO: needs some more error handling, needs checking _all_ signatures
+ if( verify_res.isNull() || verify_res.numSignatures() == 0
+ // there is at least 1 signature and it is anything else than fully valid
+ || ( (verify_res.numSignatures() > 0)
+ && verify_res.signature(0).status().encodedError() > 0 ) )
+ {
+ // let's try again, but this time import the public key
+ // payload (avoiding that in a first cut for being a bit
+ // speedier. also prevents all too easy poisoning/sha1
+ // fingerprint collision attacks)
+
+ // walk xml tree to PGPData node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue, 3rd is KeyInfo
+ // 1st child is PGPData, 1st or 2nd grandchild is PGPKeyPacket
+ cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO error handling
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->children);
+ // check that this is now PGPData
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPData, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ // check that this is now PGPKeyPacket
+ cur = xmlSecGetNextElementNode(cur->children);
+ static const xmlChar xmlSecNodePGPKeyPacket[] = "PGPKeyPacket";
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyPacket, xmlSecDSigNs))
+ {
+ // not this one, maybe the next?
+ cur = xmlSecGetNextElementNode(cur->next);
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyPacket, xmlSecDSigNs))
+ {
+ // ok, giving up
+ clearErrorRecorder();
+ xmlFree(pSignatureValue);
+
+ return aTemplate;
+ }
+ }
+
+ // got a key packet, import & re-validate
+ xmlChar* pKeyPacket=xmlNodeGetContent(cur);
+ int nKeyLen = xmlSecBase64Decode(pKeyPacket, reinterpret_cast<xmlSecByte*>(pKeyPacket), xmlStrlen(pKeyPacket));
+ if( nKeyLen < 0)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ GpgME::Data data_key(
+ reinterpret_cast<char*>(pKeyPacket),
+ nKeyLen, false);
+
+ rCtx.importKeys(data_key);
+ xmlFree(pKeyPacket);
+
+ // and re-run (rewind text and signature streams to position 0)
+ (void)data_text.seek(0,SEEK_SET);
+ (void)data_signature.seek(0,SEEK_SET);
+ verify_res=rCtx.verifyDetachedSignature(data_signature, data_text);
+
+ // TODO: needs some more error handling, needs checking _all_ signatures
+ if( verify_res.isNull() || verify_res.numSignatures() == 0
+ // there is at least 1 signature and it is anything else than valid
+ || ( (verify_res.numSignatures() > 0)
+ && verify_res.signature(0).status().encodedError() > 0 ) )
+ {
+ clearErrorRecorder();
+ xmlFree(pSignatureValue);
+
+ return aTemplate;
+ }
+ }
+
+ xmlFree(pSignatureValue);
+
+ // now verify digest for all references
+ cur = xmlSecGetNextElementNode(pNode->children);
+ if( cur != nullptr )
+ cur = xmlSecGetNextElementNode(cur->children);
+ while( cur != nullptr )
+ {
+ // some of those children I suppose should be reference elements
+ if( xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs) )
+ {
+ xmlSecDSigReferenceCtxPtr pDsigRefCtx =
+ xmlSecDSigReferenceCtxCreate(pDsigCtx,
+ xmlSecDSigReferenceOriginSignedInfo);
+ if(pDsigRefCtx == nullptr)
+ throw RuntimeException();
+
+ // add this one to the list
+ if( xmlSecPtrListAdd(&(pDsigCtx->signedInfoReferences),
+ pDsigRefCtx) < 0 )
+ {
+ // TODO resource handling
+ xmlSecDSigReferenceCtxDestroy(pDsigRefCtx);
+ throw RuntimeException();
+ }
+
+ if( xmlSecDSigReferenceCtxProcessNode(pDsigRefCtx, cur) < 0 )
+ throw RuntimeException();
+
+ // final check - all good?
+ if(pDsigRefCtx->status != xmlSecDSigStatusSucceeded)
+ {
+ pDsigCtx->status = xmlSecDSigStatusInvalid;
+ return aTemplate; // TODO - harder error?
+ }
+ }
+
+ cur = xmlSecGetNextElementNode(cur->next);
+ }
+
+ // TODO - also verify manifest (only relevant for ooxml)?
+ aTemplate->setStatus(SecurityOperationStatus_OPERATION_SUCCEEDED);
+
+ // done
+ xmlSecDSigCtxDestroy( pDsigCtx ) ;
+ }
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+ clearErrorRecorder();
+ return aTemplate ;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSignature_GpgImpl::getImplementationName() {
+ return impl_getImplementationName() ;
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSignature_GpgImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL XMLSignature_GpgImpl::getSupportedServiceNames() {
+ return impl_getSupportedServiceNames() ;
+}
+
+//Helper for XServiceInfo
+Sequence< OUString > XMLSignature_GpgImpl::impl_getSupportedServiceNames() {
+ Sequence<OUString> seqServiceNames { "com.sun.star.xml.crypto.XMLSignature" };
+ return seqServiceNames ;
+}
+
+OUString XMLSignature_GpgImpl::impl_getImplementationName() {
+ return "com.sun.star.xml.security.bridge.xmlsec.XMLSignature_GpgImpl" ;
+}
+
+//Helper for registry
+Reference< XInterface > XMLSignature_GpgImpl::impl_createInstance( const Reference< XMultiServiceFactory >& ) {
+ return Reference< XInterface >( *new XMLSignature_GpgImpl ) ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/UriBindingHelper.cxx b/xmlsecurity/source/helper/UriBindingHelper.cxx
new file mode 100644
index 000000000..f6c36d2c8
--- /dev/null
+++ b/xmlsecurity/source/helper/UriBindingHelper.cxx
@@ -0,0 +1,113 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UriBindingHelper.hxx>
+
+#include <tools/solar.h>
+#include <unotools/streamhelper.hxx>
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <osl/diagnose.h>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+// XUriBinding
+
+UriBindingHelper::UriBindingHelper()
+{
+}
+
+UriBindingHelper::UriBindingHelper( const css::uno::Reference < css::embed::XStorage >& rxStorage )
+{
+ mxStorage = rxStorage;
+}
+
+void SAL_CALL UriBindingHelper::setUriBinding( const OUString& /*uri*/, const uno::Reference< io::XInputStream >&)
+{
+}
+
+uno::Reference< io::XInputStream > SAL_CALL UriBindingHelper::getUriBinding( const OUString& uri )
+{
+ uno::Reference< io::XInputStream > xInputStream;
+ if ( mxStorage.is() )
+ {
+ xInputStream = OpenInputStream( mxStorage, uri );
+ }
+ else
+ {
+ SvFileStream* pStream = new SvFileStream( uri, StreamMode::READ );
+ sal_uInt64 nBytes = pStream->TellEnd();
+ SvLockBytesRef xLockBytes = new SvLockBytes( pStream, true );
+ xInputStream = new utl::OInputStreamHelper( xLockBytes, nBytes );
+ }
+ return xInputStream;
+}
+
+uno::Reference < io::XInputStream > UriBindingHelper::OpenInputStream( const uno::Reference < embed::XStorage >& rxStore, const OUString& rURI )
+{
+ OSL_ASSERT(!rURI.isEmpty());
+ uno::Reference < io::XInputStream > xInStream;
+
+ OUString aURI(rURI);
+ // Ignore leading slash, don't attempt to open a storage with name "".
+ if (aURI.startsWith("/"))
+ aURI = aURI.copy(1);
+ // Ignore query part of the URI.
+ sal_Int32 nQueryPos = aURI.indexOf('?');
+ if (nQueryPos != -1)
+ aURI = aURI.copy(0, nQueryPos);
+
+
+ sal_Int32 nSepPos = aURI.indexOf( '/' );
+ if ( nSepPos == -1 )
+ {
+ // Cloning because of I can't keep all storage references open
+ // MBA with think about a better API...
+ const OUString sName = ::rtl::Uri::decode(
+ aURI, rtl_UriDecodeStrict, rtl_UriCharClassRelSegment);
+ if (sName.isEmpty() && !aURI.isEmpty())
+ throw uno::Exception("Could not decode URI for stream element.", nullptr);
+
+ uno::Reference< io::XStream > xStream;
+ if (!rxStore->hasByName(sName))
+ SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << sName);
+ else
+ xStream = rxStore->cloneStreamElement( sName );
+ if ( !xStream.is() )
+ throw uno::RuntimeException();
+ xInStream = xStream->getInputStream();
+ }
+ else
+ {
+ const OUString aStoreName = ::rtl::Uri::decode(
+ aURI.copy( 0, nSepPos ), rtl_UriDecodeStrict, rtl_UriCharClassRelSegment);
+ if (aStoreName.isEmpty() && !aURI.isEmpty())
+ throw uno::Exception("Could not decode URI for stream element.", nullptr);
+
+ OUString aElement = aURI.copy( nSepPos+1 );
+ uno::Reference < embed::XStorage > xSubStore = rxStore->openStorageElement( aStoreName, embed::ElementModes::READ );
+ xInStream = OpenInputStream( xSubStore, aElement );
+ }
+ return xInStream;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/documentsignaturehelper.cxx b/xmlsecurity/source/helper/documentsignaturehelper.cxx
new file mode 100644
index 000000000..af3d51154
--- /dev/null
+++ b/xmlsecurity/source/helper/documentsignaturehelper.cxx
@@ -0,0 +1,650 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <documentsignaturehelper.hxx>
+
+#include <algorithm>
+#include <functional>
+#include <string_view>
+
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <svx/xoutbmp.hxx>
+#include <tools/diagnose_ex.h>
+#include <xmloff/attrlist.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <xsecctl.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace css::xml::sax;
+
+namespace
+{
+OUString getElement(OUString const & version, ::sal_Int32 * index)
+{
+ while (*index < version.getLength() && version[*index] == '0') {
+ ++*index;
+ }
+ return version.getToken(0, '.', *index);
+}
+
+
+// Return 1 if version1 is greater than version 2, 0 if they are equal
+//and -1 if version1 is less version 2
+int compareVersions(
+ OUString const & version1, OUString const & version2)
+{
+ for (::sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0;) {
+ OUString e1(getElement(version1, &i1));
+ OUString e2(getElement(version2, &i2));
+ if (e1.getLength() < e2.getLength()) {
+ return -1;
+ } else if (e1.getLength() > e2.getLength()) {
+ return 1;
+ } else if (e1 < e2) {
+ return -1;
+ } else if (e1 > e2) {
+ return 1;
+ }
+ }
+ return 0;
+}
+}
+
+static void ImplFillElementList(
+ std::vector< OUString >& rList, const Reference < css::embed::XStorage >& rxStore,
+ std::u16string_view rRootStorageName, const bool bRecursive,
+ const DocumentSignatureAlgorithm mode)
+{
+ const Sequence< OUString > aElements = rxStore->getElementNames();
+
+ for ( const auto& rName : aElements )
+ {
+ if (rName == "[Content_Types].xml")
+ // OOXML
+ continue;
+
+ // If the user enabled validating according to OOo 3.0
+ // then mimetype and all content of META-INF must be excluded.
+ if (mode != DocumentSignatureAlgorithm::OOo3_2
+ && (rName == "META-INF" || rName == "mimetype"))
+ {
+ continue;
+ }
+ else
+ {
+ OUString sEncName = ::rtl::Uri::encode(
+ rName, rtl_UriCharClassRelSegment,
+ rtl_UriEncodeStrict, RTL_TEXTENCODING_UTF8);
+ if (sEncName.isEmpty() && !rName.isEmpty())
+ throw css::uno::RuntimeException("Failed to encode element name of XStorage", nullptr);
+
+ if ( rxStore->isStreamElement( rName ) )
+ {
+ //Exclude documentsignatures.xml!
+ if (rName ==
+ DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName())
+ continue;
+ OUString aFullName( rRootStorageName + sEncName );
+ rList.push_back(aFullName);
+ }
+ else if ( bRecursive && rxStore->isStorageElement( rName ) )
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( rName, css::embed::ElementModes::READ );
+ OUString aFullRootName( rRootStorageName + sEncName + "/" );
+ ImplFillElementList(rList, xSubStore, aFullRootName, bRecursive, mode);
+ }
+ }
+ }
+}
+
+
+bool DocumentSignatureHelper::isODFPre_1_2(const OUString & sVersion)
+{
+ //The property version exists only if the document is at least version 1.2
+ //That is, if the document has version 1.1 and sVersion is empty.
+ //The constant is defined in comphelper/documentconstants.hxx
+ return compareVersions(sVersion, ODFVER_012_TEXT) == -1;
+}
+
+bool DocumentSignatureHelper::isOOo3_2_Signature(const SignatureInformation & sigInfo)
+{
+ return std::any_of(sigInfo.vSignatureReferenceInfors.cbegin(),
+ sigInfo.vSignatureReferenceInfors.cend(),
+ [](const SignatureReferenceInformation& info) { return info.ouURI == "META-INF/manifest.xml"; });
+}
+
+DocumentSignatureAlgorithm
+DocumentSignatureHelper::getDocumentAlgorithm(
+ const OUString & sODFVersion, const SignatureInformation & sigInfo)
+{
+ OSL_ASSERT(!sODFVersion.isEmpty());
+ DocumentSignatureAlgorithm mode = DocumentSignatureAlgorithm::OOo3_2;
+ if (!isOOo3_2_Signature(sigInfo))
+ {
+ if (isODFPre_1_2(sODFVersion))
+ mode = DocumentSignatureAlgorithm::OOo2;
+ else
+ mode = DocumentSignatureAlgorithm::OOo3_0;
+ }
+ return mode;
+}
+
+//The function creates a list of files which are to be signed or for which
+//the signature is to be validated. The strings are UTF8 encoded URIs which
+//contain '/' as path separators.
+//
+//The algorithm how document signatures are created and validated has
+//changed over time. The change affects only which files within the document
+//are changed. Document signatures created by OOo 2.x only used particular files. Since
+//OOo 3.0 everything except "mimetype" and "META-INF" are signed. As of OOo 3.2 everything
+//except META-INF/documentsignatures.xml is signed.
+//Signatures are validated according to the algorithm which was then used for validation.
+//That is, when validating a signature which was created by OOo 3.0, then mimetype and
+//META-INF are not used.
+//
+//When a signature is created then we always use the latest algorithm. That is, we use
+//that of OOo 3.2
+std::vector< OUString >
+DocumentSignatureHelper::CreateElementList(
+ const Reference < css::embed::XStorage >& rxStore,
+ DocumentSignatureMode eMode,
+ const DocumentSignatureAlgorithm mode)
+{
+ std::vector< OUString > aElements;
+ OUString aSep( "/" );
+
+ switch ( eMode )
+ {
+ case DocumentSignatureMode::Content:
+ {
+ if (mode == DocumentSignatureAlgorithm::OOo2) //that is, ODF 1.0, 1.1
+ {
+ // 1) Main content
+ ImplFillElementList(aElements, rxStore, std::u16string_view(), false, mode);
+
+ // 2) Pictures...
+ OUString aSubStorageName( "Pictures" );
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, OUStringConcatenation(aSubStorageName+aSep), true, mode);
+ }
+ catch(css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ // 3) OLE...
+ aSubStorageName = "ObjectReplacements";
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, OUStringConcatenation(aSubStorageName+aSep), true, mode);
+ xSubStore.clear();
+
+ // Object folders...
+ const Sequence< OUString > aElementNames = rxStore->getElementNames();
+ for ( const auto& rName : aElementNames )
+ {
+ if ( ( rName.match( "Object " ) ) && rxStore->isStorageElement( rName ) )
+ {
+ Reference < css::embed::XStorage > xTmpSubStore = rxStore->openStorageElement( rName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xTmpSubStore, OUStringConcatenation(rName+aSep), true, mode);
+ }
+ }
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ }
+ else
+ {
+ // Everything except META-INF
+ ImplFillElementList(aElements, rxStore, std::u16string_view(), true, mode);
+ }
+ }
+ break;
+ case DocumentSignatureMode::Macros:
+ {
+ // 1) Macros
+ OUString aSubStorageName( "Basic" );
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, OUStringConcatenation(aSubStorageName+aSep), true, mode);
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+
+ // 2) Dialogs
+ aSubStorageName = "Dialogs";
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, OUStringConcatenation(aSubStorageName+aSep), true, mode);
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ // 3) Scripts
+ aSubStorageName = "Scripts";
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, OUStringConcatenation(aSubStorageName+aSep), true, mode);
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ }
+ break;
+ case DocumentSignatureMode::Package:
+ {
+ // Everything except META-INF
+ ImplFillElementList(aElements, rxStore, std::u16string_view(), true, mode);
+ }
+ break;
+ }
+
+ return aElements;
+}
+
+void DocumentSignatureHelper::AppendContentTypes(const uno::Reference<embed::XStorage>& xStorage, std::vector<OUString>& rElements)
+{
+ if (!xStorage.is() || !xStorage->hasByName("[Content_Types].xml"))
+ // ODF
+ return;
+
+ uno::Reference<io::XInputStream> xRelStream(xStorage->openStreamElement("[Content_Types].xml", embed::ElementModes::READ), uno::UNO_QUERY);
+ uno::Sequence< uno::Sequence<beans::StringPair> > aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xRelStream, comphelper::getProcessComponentContext());
+ if (aContentTypeInfo.getLength() < 2)
+ {
+ SAL_WARN("xmlsecurity.helper", "no defaults or overrides in aContentTypeInfo");
+ return;
+ }
+ const uno::Sequence<beans::StringPair>& rDefaults = aContentTypeInfo[0];
+ const uno::Sequence<beans::StringPair>& rOverrides = aContentTypeInfo[1];
+
+ for (OUString& rElement : rElements)
+ {
+ auto it = std::find_if(rOverrides.begin(), rOverrides.end(), [&](const beans::StringPair& rPair)
+ {
+ return rPair.First == OUStringConcatenation("/" + rElement);
+ });
+
+ if (it != rOverrides.end())
+ {
+ rElement = "/" + rElement + "?ContentType=" + it->Second;
+ continue;
+ }
+
+ it = std::find_if(rDefaults.begin(), rDefaults.end(), [&](const beans::StringPair& rPair)
+ {
+ return rElement.endsWith(OUStringConcatenation("." + rPair.First));
+ });
+
+ if (it != rDefaults.end())
+ {
+ rElement = "/" + rElement + "?ContentType=" + it->Second;
+ continue;
+ }
+ SAL_WARN("xmlsecurity.helper", "found no content type for " << rElement);
+ }
+
+ std::sort(rElements.begin(), rElements.end());
+}
+
+SignatureStreamHelper DocumentSignatureHelper::OpenSignatureStream(
+ const Reference < css::embed::XStorage >& rxStore, sal_Int32 nOpenMode, DocumentSignatureMode eDocSigMode )
+{
+ sal_Int32 nSubStorageOpenMode = css::embed::ElementModes::READ;
+ if ( nOpenMode & css::embed::ElementModes::WRITE )
+ nSubStorageOpenMode = css::embed::ElementModes::WRITE;
+
+ SignatureStreamHelper aHelper;
+
+ if (!rxStore.is())
+ return aHelper;
+
+ if (rxStore->hasByName("META-INF"))
+ {
+ try
+ {
+ aHelper.xSignatureStorage = rxStore->openStorageElement( "META-INF", nSubStorageOpenMode );
+ if ( aHelper.xSignatureStorage.is() )
+ {
+ OUString aSIGStreamName;
+ if ( eDocSigMode == DocumentSignatureMode::Content )
+ aSIGStreamName = DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName();
+ else if ( eDocSigMode == DocumentSignatureMode::Macros )
+ aSIGStreamName = DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName();
+ else
+ aSIGStreamName = DocumentSignatureHelper::GetPackageSignatureDefaultStreamName();
+
+#ifdef SAL_LOG_INFO
+ aHelper.xSignatureStream
+ = aHelper.xSignatureStorage->openStreamElement(aSIGStreamName, nOpenMode);
+ SAL_INFO("xmlsecurity.helper",
+ "DocumentSignatureHelper::OpenSignatureStream: stream name is '"
+ << aSIGStreamName << "'");
+ if (aHelper.xSignatureStream.is())
+ {
+ uno::Reference<io::XInputStream> xInputStream(aHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ sal_Int64 nSize = 0;
+ uno::Reference<beans::XPropertySet> xPropertySet(xInputStream, uno::UNO_QUERY);
+ xPropertySet->getPropertyValue("Size") >>= nSize;
+ if (nSize >= 0 || nSize < SAL_MAX_INT32)
+ {
+ uno::Sequence<sal_Int8> aData;
+ xInputStream->readBytes(aData, nSize);
+ SAL_INFO("xmlsecurity.helper",
+ "DocumentSignatureHelper::OpenSignatureStream: stream content is '"
+ << OString(reinterpret_cast<const char*>(aData.getArray()),
+ aData.getLength())
+ << "'");
+ }
+ }
+ aHelper.xSignatureStream.clear();
+#endif
+
+ aHelper.xSignatureStream = aHelper.xSignatureStorage->openStreamElement( aSIGStreamName, nOpenMode );
+ }
+ }
+ catch(css::io::IOException& )
+ {
+ // Doesn't have to exist...
+ SAL_WARN_IF( nOpenMode != css::embed::ElementModes::READ, "xmlsecurity.helper", "Error creating signature stream..." );
+ }
+ }
+ else if(rxStore->hasByName("[Content_Types].xml"))
+ {
+ try
+ {
+ if (rxStore->hasByName("_xmlsignatures") && (nOpenMode & embed::ElementModes::TRUNCATE))
+ // Truncate, then all signatures will be written -> remove previous ones.
+ rxStore->removeElement("_xmlsignatures");
+
+ aHelper.xSignatureStorage = rxStore->openStorageElement("_xmlsignatures", nSubStorageOpenMode);
+ aHelper.nStorageFormat = embed::StorageFormats::OFOPXML;
+ }
+ catch (const io::IOException&)
+ {
+ TOOLS_WARN_EXCEPTION_IF(nOpenMode != css::embed::ElementModes::READ, "xmlsecurity.helper", "DocumentSignatureHelper::OpenSignatureStream:");
+ }
+ }
+
+ return aHelper;
+}
+
+/** Check whether the current file can be signed with GPG (only ODF >= 1.2 can currently) */
+bool DocumentSignatureHelper::CanSignWithGPG(
+ const Reference < css::embed::XStorage >& rxStore,
+ const OUString& sOdfVersion)
+{
+ if (!rxStore.is())
+ return false;
+
+ if (rxStore->hasByName("META-INF")) // ODF
+ {
+ return !isODFPre_1_2(sOdfVersion);
+ }
+
+ return false;
+}
+
+
+
+//sElementList contains all files which are expected to be signed. Only those files must me signed,
+//no more, no less.
+//The DocumentSignatureAlgorithm indicates if the document was created with OOo 2.x. Then
+//the uri s in the Reference elements in the signature, were not properly encoded.
+// For example: <Reference URI="ObjectReplacements/Object 1">
+bool DocumentSignatureHelper::checkIfAllFilesAreSigned(
+ const ::std::vector< OUString > & sElementList,
+ const SignatureInformation & sigInfo,
+ const DocumentSignatureAlgorithm alg)
+{
+ // Can only be valid if ALL streams are signed, which means real stream count == signed stream count
+ unsigned int nRealCount = 0;
+ std::function<OUString(const OUString&)> fEncode = [](const OUString& rStr) { return rStr; };
+ if (alg == DocumentSignatureAlgorithm::OOo2)
+ //Comparing URIs is a difficult. Therefore we kind of normalize
+ //it before comparing. We assume that our URI do not have a leading "./"
+ //and fragments at the end (...#...)
+ fEncode = [](const OUString& rStr) {
+ return rtl::Uri::encode(rStr, rtl_UriCharClassPchar, rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
+ };
+
+ for ( int i = sigInfo.vSignatureReferenceInfors.size(); i; )
+ {
+ const SignatureReferenceInformation& rInf = sigInfo.vSignatureReferenceInfors[--i];
+ // There is also an extra entry of type SignatureReferenceType::SAMEDOCUMENT because of signature date.
+ if ( ( rInf.nType == SignatureReferenceType::BINARYSTREAM ) || ( rInf.nType == SignatureReferenceType::XMLSTREAM ) )
+ {
+ //find the file in the element list
+ if (std::any_of(sElementList.cbegin(), sElementList.cend(),
+ [&fEncode, &rInf](const OUString& rElement) { return fEncode(rElement) == fEncode(rInf.ouURI); }))
+ nRealCount++;
+ }
+ }
+ return sElementList.size() == nRealCount;
+}
+
+/*Compares the Uri which are obtained from CreateElementList with
+ the path obtained from the manifest.xml.
+ Returns true if both strings are equal.
+*/
+bool DocumentSignatureHelper::equalsReferenceUriManifestPath(
+ std::u16string_view rUri, std::u16string_view rPath)
+{
+ //split up the uri and path into segments. Both are separated by '/'
+ std::vector<OUString> vUriSegments;
+ for (sal_Int32 nIndex = 0; nIndex >= 0; )
+ vUriSegments.push_back(OUString(o3tl::getToken(rUri, 0, '/', nIndex )));
+
+ std::vector<OUString> vPathSegments;
+ for (sal_Int32 nIndex = 0; nIndex >= 0; )
+ vPathSegments.push_back(OUString(o3tl::getToken(rPath, 0, '/', nIndex )));
+
+ if (vUriSegments.size() != vPathSegments.size())
+ return false;
+
+ //Now compare each segment of the uri with its counterpart from the path
+ return std::equal(
+ vUriSegments.cbegin(), vUriSegments.cend(), vPathSegments.cbegin(),
+ [](const OUString& rUriSegment, const OUString& rPathSegment) {
+ //Decode the uri segment, so that %20 becomes ' ', etc.
+ OUString sDecUri = rtl::Uri::decode(rUriSegment, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
+ return sDecUri == rPathSegment;
+ });
+}
+
+OUString DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()
+{
+ return "documentsignatures.xml";
+}
+
+OUString DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName()
+{
+ return "macrosignatures.xml";
+}
+
+OUString DocumentSignatureHelper::GetPackageSignatureDefaultStreamName()
+{
+ return "packagesignatures.xml";
+}
+
+void DocumentSignatureHelper::writeDigestMethod(
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler)
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_XMLDSIGSHA256);
+ xDocumentHandler->startElement("DigestMethod", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ xDocumentHandler->endElement("DigestMethod");
+}
+
+static void WriteXadesCert(
+ uno::Reference<xml::sax::XDocumentHandler> const& xDocumentHandler,
+ SignatureInformation::X509CertInfo const& rCertInfo)
+{
+ xDocumentHandler->startElement("xd:Cert", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->startElement("xd:CertDigest", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ DocumentSignatureHelper::writeDigestMethod(xDocumentHandler);
+ xDocumentHandler->startElement("DigestValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ assert(!rCertInfo.CertDigest.isEmpty());
+ xDocumentHandler->characters(rCertInfo.CertDigest);
+ xDocumentHandler->endElement("DigestValue");
+ xDocumentHandler->endElement("xd:CertDigest");
+ xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->startElement("X509IssuerName", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->characters(rCertInfo.X509IssuerName);
+ xDocumentHandler->endElement("X509IssuerName");
+ xDocumentHandler->startElement("X509SerialNumber", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->characters(rCertInfo.X509SerialNumber);
+ xDocumentHandler->endElement("X509SerialNumber");
+ xDocumentHandler->endElement("xd:IssuerSerial");
+ xDocumentHandler->endElement("xd:Cert");
+}
+
+void DocumentSignatureHelper::writeSignedProperties(
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& signatureInfo,
+ const OUString& sDate, const bool bWriteSignatureLineData)
+{
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idSignedProperties_" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement("xd:SignedProperties", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ xDocumentHandler->startElement("xd:SignedSignatureProperties", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->startElement("xd:SigningTime", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->characters(sDate);
+ xDocumentHandler->endElement("xd:SigningTime");
+ xDocumentHandler->startElement("xd:SigningCertificate", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ assert(signatureInfo.GetSigningCertificate() || !signatureInfo.ouGpgKeyID.isEmpty());
+ if (signatureInfo.GetSigningCertificate())
+ {
+ // how should this deal with multiple X509Data elements?
+ // for now, let's write all of the certificates ...
+ for (auto const& rData : signatureInfo.X509Datas)
+ {
+ for (auto const& it : rData)
+ {
+ WriteXadesCert(xDocumentHandler, it);
+ }
+ }
+ }
+ else
+ {
+ // for PGP, write empty mandatory X509IssuerName, X509SerialNumber
+ SignatureInformation::X509CertInfo temp;
+ temp.CertDigest = signatureInfo.ouGpgKeyID;
+ WriteXadesCert(xDocumentHandler, temp);
+ }
+ xDocumentHandler->endElement("xd:SigningCertificate");
+ xDocumentHandler->startElement("xd:SignaturePolicyIdentifier", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->startElement("xd:SignaturePolicyImplied", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->endElement("xd:SignaturePolicyImplied");
+ xDocumentHandler->endElement("xd:SignaturePolicyIdentifier");
+
+ if (bWriteSignatureLineData && !signatureInfo.ouSignatureLineId.isEmpty()
+ && signatureInfo.aValidSignatureImage.is() && signatureInfo.aInvalidSignatureImage.is())
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute(
+ "xmlns:loext", "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0");
+ xDocumentHandler->startElement(
+ "loext:SignatureLine",
+ Reference<XAttributeList>(pAttributeList));
+
+ {
+ // Write SignatureLineId element
+ xDocumentHandler->startElement(
+ "loext:SignatureLineId",
+ Reference<XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->characters(signatureInfo.ouSignatureLineId);
+ xDocumentHandler->endElement("loext:SignatureLineId");
+ }
+
+ {
+ // Write SignatureLineValidImage element
+ xDocumentHandler->startElement(
+ "loext:SignatureLineValidImage",
+ Reference<XAttributeList>(new SvXMLAttributeList()));
+
+ OUString aGraphicInBase64;
+ Graphic aGraphic(signatureInfo.aValidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+
+ xDocumentHandler->characters(aGraphicInBase64);
+ xDocumentHandler->endElement("loext:SignatureLineValidImage");
+ }
+
+ {
+ // Write SignatureLineInvalidImage element
+ xDocumentHandler->startElement(
+ "loext:SignatureLineInvalidImage",
+ Reference<XAttributeList>(new SvXMLAttributeList()));
+ OUString aGraphicInBase64;
+ Graphic aGraphic(signatureInfo.aInvalidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+ xDocumentHandler->characters(aGraphicInBase64);
+ xDocumentHandler->endElement("loext:SignatureLineInvalidImage");
+ }
+
+ xDocumentHandler->endElement("loext:SignatureLine");
+ }
+
+ xDocumentHandler->endElement("xd:SignedSignatureProperties");
+
+ xDocumentHandler->endElement("xd:SignedProperties");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx
new file mode 100644
index 000000000..83606dc96
--- /dev/null
+++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx
@@ -0,0 +1,705 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <documentsignaturemanager.hxx>
+#include <config_gpgme.h>
+
+#include <gpg/SEInitializer.hxx>
+
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/packages/manifest/ManifestReader.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/XWriter.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/datetime.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <certificate.hxx>
+#include <biginteger.hxx>
+
+#include <xmlsec/xmlsec_init.hxx>
+
+#include <pdfsignaturehelper.hxx>
+
+#include <memory>
+
+using namespace css;
+using namespace css::graphic;
+using namespace css::uno;
+
+DocumentSignatureManager::DocumentSignatureManager(
+ const uno::Reference<uno::XComponentContext>& xContext, DocumentSignatureMode eMode)
+ : mxContext(xContext)
+ , maSignatureHelper(xContext)
+ , meSignatureMode(eMode)
+{
+}
+
+DocumentSignatureManager::~DocumentSignatureManager() { deInitXmlSec(); }
+
+bool DocumentSignatureManager::init()
+{
+ SAL_WARN_IF(mxSEInitializer.is(), "xmlsecurity.helper",
+ "DocumentSignatureManager::Init - mxSEInitializer already set!");
+ SAL_WARN_IF(mxSecurityContext.is(), "xmlsecurity.helper",
+ "DocumentSignatureManager::Init - mxSecurityContext already set!");
+ SAL_WARN_IF(mxGpgSEInitializer.is(), "xmlsecurity.helper",
+ "DocumentSignatureManager::Init - mxGpgSEInitializer already set!");
+
+ // xmlsec is needed by both services, so init before those
+ initXmlSec();
+
+ mxSEInitializer = xml::crypto::SEInitializer::create(mxContext);
+#if HAVE_FEATURE_GPGME
+ mxGpgSEInitializer.set(new SEInitializerGpg());
+#endif
+
+ if (mxSEInitializer.is())
+ mxSecurityContext = mxSEInitializer->createSecurityContext(OUString());
+
+#if HAVE_FEATURE_GPGME
+ if (mxGpgSEInitializer.is())
+ mxGpgSecurityContext = mxGpgSEInitializer->createSecurityContext(OUString());
+
+ return mxSecurityContext.is() || mxGpgSecurityContext.is();
+#else
+ return mxSecurityContext.is();
+#endif
+}
+
+PDFSignatureHelper& DocumentSignatureManager::getPDFSignatureHelper()
+{
+ bool bInit = true;
+ if (!mxSecurityContext.is())
+ bInit = init();
+
+ SAL_WARN_IF(!bInit, "xmlsecurity.comp", "Error initializing security context!");
+
+ if (!mpPDFSignatureHelper)
+ mpPDFSignatureHelper = std::make_unique<PDFSignatureHelper>();
+
+ return *mpPDFSignatureHelper;
+}
+
+#if 0 // For some reason does not work
+bool DocumentSignatureManager::IsXAdESRelevant()
+{
+ if (mxStore.is())
+ {
+ // ZIP-based: ODF or OOXML.
+ maSignatureHelper.StartMission();
+
+ SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(embed::ElementModes::READ, /*bUseTempStream=*/true);
+ if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ {
+ maSignatureHelper.EndMission();
+ return false;
+ }
+ // FIXME: How to figure out if it is ODF 1.2?
+ maSignatureHelper.EndMission();
+ return true;
+ }
+ return false;
+}
+#endif
+
+bool DocumentSignatureManager::readManifest()
+{
+ // Check if manifest was already read
+ if (m_manifest.hasElements())
+ return true;
+
+ if (!mxContext.is())
+ return false;
+
+ if (!mxStore.is())
+ return false;
+
+ uno::Reference<packages::manifest::XManifestReader> xReader
+ = packages::manifest::ManifestReader::create(mxContext);
+
+ if (mxStore->hasByName("META-INF"))
+ {
+ //Get the manifest.xml
+ uno::Reference<embed::XStorage> xSubStore(
+ mxStore->openStorageElement("META-INF", embed::ElementModes::READ), UNO_SET_THROW);
+
+ uno::Reference<io::XInputStream> xStream(
+ xSubStore->openStreamElement("manifest.xml", css::embed::ElementModes::READ),
+ UNO_QUERY_THROW);
+
+ m_manifest = xReader->readManifestSequence(xStream);
+ }
+ return true;
+}
+
+/* Using the zip storage, we cannot get the properties "MediaType" and "IsEncrypted"
+ We use the manifest to find out if a file is xml and if it is encrypted.
+ The parameter is an encoded uri. However, the manifest contains paths. Therefore
+ the path is encoded as uri, so they can be compared.
+*/
+bool DocumentSignatureManager::isXML(std::u16string_view rURI)
+{
+ SAL_WARN_IF(!mxStore.is(), "xmlsecurity.helper", "empty storage reference");
+
+ bool bIsXML = false;
+ bool bPropsAvailable = false;
+ static const OUStringLiteral sPropFullPath(u"FullPath");
+ static const OUStringLiteral sPropMediaType(u"MediaType");
+ static const OUStringLiteral sPropDigest(u"Digest");
+
+ if (readManifest())
+ {
+ for (const uno::Sequence<beans::PropertyValue>& entry : std::as_const(m_manifest))
+ {
+ OUString sPath;
+ OUString sMediaType;
+ bool bEncrypted = false;
+ for (const beans::PropertyValue& prop : entry)
+ {
+ if (prop.Name == sPropFullPath)
+ prop.Value >>= sPath;
+ else if (prop.Name == sPropMediaType)
+ prop.Value >>= sMediaType;
+ else if (prop.Name == sPropDigest)
+ bEncrypted = true;
+ }
+ if (DocumentSignatureHelper::equalsReferenceUriManifestPath(rURI, sPath))
+ {
+ bIsXML = sMediaType == "text/xml" && !bEncrypted;
+ bPropsAvailable = true;
+ break;
+ }
+ }
+ }
+ if (!bPropsAvailable)
+ {
+ //This would be the case for at least mimetype, META-INF/manifest.xml
+ //META-INF/macrosignatures.xml.
+ //Files can only be encrypted if they are in the manifest.xml.
+ //That is, the current file cannot be encrypted, otherwise bPropsAvailable
+ //would be true.
+ size_t nSep = rURI.rfind('.');
+ if (nSep != std::u16string_view::npos)
+ {
+ std::u16string_view aExt = rURI.substr(nSep + 1);
+ if (o3tl::equalsIgnoreAsciiCase(aExt, u"XML"))
+ bIsXML = true;
+ }
+ }
+ return bIsXML;
+}
+
+//If bTempStream is true, then a temporary stream is return. If it is false then, the actual
+//signature stream is used.
+//Every time the user presses Add a new temporary stream is created.
+//We keep the temporary stream as member because ImplGetSignatureInformations
+//will later access the stream to create DocumentSignatureInformation objects
+//which are stored in maCurrentSignatureInformations.
+SignatureStreamHelper DocumentSignatureManager::ImplOpenSignatureStream(sal_Int32 nStreamOpenMode,
+ bool bTempStream)
+{
+ SignatureStreamHelper aHelper;
+ if (mxStore.is() && mxStore->hasByName("[Content_Types].xml"))
+ aHelper.nStorageFormat = embed::StorageFormats::OFOPXML;
+
+ if (bTempStream)
+ {
+ if (nStreamOpenMode & embed::ElementModes::TRUNCATE)
+ {
+ //We write always into a new temporary stream.
+ mxTempSignatureStream.set(io::TempFile::create(mxContext), uno::UNO_QUERY_THROW);
+ if (aHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ aHelper.xSignatureStream = mxTempSignatureStream;
+ else
+ {
+ mxTempSignatureStorage = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, mxTempSignatureStream);
+ aHelper.xSignatureStorage = mxTempSignatureStorage;
+ }
+ }
+ else
+ {
+ //When we read from the temp stream, then we must have previously
+ //created one.
+ SAL_WARN_IF(!mxTempSignatureStream.is(), "xmlsecurity.helper",
+ "empty temp. signature stream reference");
+ }
+ aHelper.xSignatureStream = mxTempSignatureStream;
+ if (aHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ aHelper.xSignatureStorage = mxTempSignatureStorage;
+ }
+ else
+ {
+ //No temporary stream
+ if (!mxSignatureStream.is())
+ {
+ //We may not have a dedicated stream for writing the signature
+ //So we take one directly from the storage
+ //Or DocumentDigitalSignatures::showDocumentContentSignatures was called,
+ //in which case Add/Remove is not allowed. This is done, for example, if the
+ //document is readonly
+ aHelper = DocumentSignatureHelper::OpenSignatureStream(mxStore, nStreamOpenMode,
+ meSignatureMode);
+ }
+ else
+ {
+ aHelper.xSignatureStream = mxSignatureStream;
+ }
+ }
+
+ if (nStreamOpenMode & embed::ElementModes::TRUNCATE)
+ {
+ if (aHelper.xSignatureStream.is()
+ && aHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ uno::Reference<io::XTruncate> xTruncate(aHelper.xSignatureStream, uno::UNO_QUERY_THROW);
+ xTruncate->truncate();
+ }
+ }
+ else if (bTempStream || mxSignatureStream.is())
+ {
+ //In case we read the signature stream from the storage directly,
+ //which is the case when DocumentDigitalSignatures::showDocumentContentSignatures
+ //then XSeakable is not supported
+ uno::Reference<io::XSeekable> xSeek(aHelper.xSignatureStream, uno::UNO_QUERY_THROW);
+ xSeek->seek(0);
+ }
+
+ return aHelper;
+}
+
+bool DocumentSignatureManager::add(
+ const uno::Reference<security::XCertificate>& xCert,
+ const uno::Reference<xml::crypto::XXMLSecurityContext>& xSecurityContext,
+ const OUString& rDescription, sal_Int32& nSecurityId, bool bAdESCompliant,
+ const OUString& rSignatureLineId, const Reference<XGraphic>& xValidGraphic,
+ const Reference<XGraphic>& xInvalidGraphic)
+{
+ if (!xCert.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "no certificate selected");
+ return false;
+ }
+
+ // GPG or X509 key?
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xSecurityContext, uno::UNO_QUERY);
+ if (xServiceInfo->getImplementationName()
+ == "com.sun.star.xml.security.gpg.XMLSecurityContext_GpgImpl")
+ {
+ // GPG keys only really have PGPKeyId and PGPKeyPacket
+ if (!mxStore.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "cannot sign pdfs with GPG keys");
+ return false;
+ }
+
+ maSignatureHelper.StartMission(xSecurityContext);
+
+ nSecurityId = maSignatureHelper.GetNewSecurityId();
+
+ OUStringBuffer aStrBuffer;
+ comphelper::Base64::encode(aStrBuffer, xCert->getEncoded());
+
+ OUString aKeyId;
+ if (auto pCertificate = dynamic_cast<xmlsecurity::Certificate*>(xCert.get()))
+ {
+ OUStringBuffer aBuffer;
+ comphelper::Base64::encode(aBuffer, pCertificate->getSHA256Thumbprint());
+ aKeyId = aBuffer.makeStringAndClear();
+ }
+ else
+ SAL_WARN("xmlsecurity.helper",
+ "XCertificate implementation without an xmlsecurity::Certificate one");
+
+ maSignatureHelper.SetGpgCertificate(nSecurityId, aKeyId, aStrBuffer.makeStringAndClear(),
+ xCert->getIssuerName());
+ }
+ else
+ {
+ OUString aCertSerial = xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber());
+ if (aCertSerial.isEmpty())
+ {
+ SAL_WARN("xmlsecurity.helper", "Error in Certificate, problem with serial number!");
+ return false;
+ }
+
+ if (!mxStore.is())
+ {
+ // Something not ZIP based, try PDF.
+ nSecurityId = getPDFSignatureHelper().GetNewSecurityId();
+ getPDFSignatureHelper().SetX509Certificate(xCert);
+ getPDFSignatureHelper().SetDescription(rDescription);
+ uno::Reference<io::XInputStream> xInputStream(mxSignatureStream, uno::UNO_QUERY);
+ if (!getPDFSignatureHelper().Sign(mxModel, xInputStream, bAdESCompliant))
+ {
+ SAL_WARN("xmlsecurity.helper", "PDFSignatureHelper::Sign() failed");
+ return false;
+ }
+ return true;
+ }
+
+ maSignatureHelper.StartMission(xSecurityContext);
+
+ nSecurityId = maSignatureHelper.GetNewSecurityId();
+
+ OUStringBuffer aStrBuffer;
+ comphelper::Base64::encode(aStrBuffer, xCert->getEncoded());
+
+ OUString aCertDigest;
+ svl::crypto::SignatureMethodAlgorithm eAlgorithmID
+ = svl::crypto::SignatureMethodAlgorithm::RSA;
+ if (auto pCertificate = dynamic_cast<xmlsecurity::Certificate*>(xCert.get()))
+ {
+ OUStringBuffer aBuffer;
+ comphelper::Base64::encode(aBuffer, pCertificate->getSHA256Thumbprint());
+ aCertDigest = aBuffer.makeStringAndClear();
+
+ eAlgorithmID = pCertificate->getSignatureMethodAlgorithm();
+ }
+ else
+ SAL_WARN("xmlsecurity.helper",
+ "XCertificate implementation without an xmlsecurity::Certificate one");
+
+ maSignatureHelper.SetX509Certificate(nSecurityId, xCert->getIssuerName(), aCertSerial,
+ aStrBuffer.makeStringAndClear(), aCertDigest,
+ eAlgorithmID);
+ }
+
+ const uno::Sequence<uno::Reference<security::XCertificate>> aCertPath
+ = xSecurityContext->getSecurityEnvironment()->buildCertificatePath(xCert);
+
+ OUStringBuffer aStrBuffer;
+ for (uno::Reference<security::XCertificate> const& rxCertificate : aCertPath)
+ {
+ comphelper::Base64::encode(aStrBuffer, rxCertificate->getEncoded());
+ OUString aString = aStrBuffer.makeStringAndClear();
+ maSignatureHelper.AddEncapsulatedX509Certificate(aString);
+ }
+
+ std::vector<OUString> aElements = DocumentSignatureHelper::CreateElementList(
+ mxStore, meSignatureMode, DocumentSignatureAlgorithm::OOo3_2);
+ DocumentSignatureHelper::AppendContentTypes(mxStore, aElements);
+
+ for (OUString const& rUri : aElements)
+ {
+ bool bBinaryMode = !isXML(rUri);
+ maSignatureHelper.AddForSigning(nSecurityId, rUri, bBinaryMode, bAdESCompliant);
+ }
+
+ maSignatureHelper.SetDateTime(nSecurityId, DateTime(DateTime::SYSTEM));
+ maSignatureHelper.SetDescription(nSecurityId, rDescription);
+
+ if (!rSignatureLineId.isEmpty())
+ maSignatureHelper.SetSignatureLineId(nSecurityId, rSignatureLineId);
+
+ if (xValidGraphic.is())
+ maSignatureHelper.SetSignatureLineValidGraphic(nSecurityId, xValidGraphic);
+
+ if (xInvalidGraphic.is())
+ maSignatureHelper.SetSignatureLineInvalidGraphic(nSecurityId, xInvalidGraphic);
+
+ // We open a signature stream in which the existing and the new
+ //signature is written. ImplGetSignatureInformation (later in this function) will
+ //then read the stream and fill maCurrentSignatureInformations. The final signature
+ //is written when the user presses OK. Then only maCurrentSignatureInformation and
+ //a sax writer are used to write the information.
+ SignatureStreamHelper aStreamHelper
+ = ImplOpenSignatureStream(embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, true);
+
+ if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<xml::sax::XWriter> xSaxWriter
+ = maSignatureHelper.CreateDocumentHandlerWithHeader(xOutputStream);
+
+ // Export old signatures...
+ uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xSaxWriter,
+ uno::UNO_QUERY_THROW);
+ std::size_t nInfos = maCurrentSignatureInformations.size();
+ for (std::size_t n = 0; n < nInfos; n++)
+ XMLSignatureHelper::ExportSignature(xDocumentHandler, maCurrentSignatureInformations[n],
+ bAdESCompliant);
+
+ // Create a new one...
+ maSignatureHelper.CreateAndWriteSignature(xDocumentHandler, bAdESCompliant);
+
+ // That's it...
+ XMLSignatureHelper::CloseDocumentHandler(xDocumentHandler);
+ }
+ else
+ {
+ // OOXML
+
+ // Handle relations.
+ maSignatureHelper.EnsureSignaturesRelation(mxStore, /*bAdd=*/true);
+ // Old signatures + the new one.
+ int nSignatureCount = maCurrentSignatureInformations.size() + 1;
+ maSignatureHelper.ExportSignatureRelations(aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+
+ // Export old signatures.
+ for (std::size_t i = 0; i < maCurrentSignatureInformations.size(); ++i)
+ maSignatureHelper.ExportOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ maCurrentSignatureInformations[i], i + 1);
+
+ // Create a new signature.
+ maSignatureHelper.CreateAndWriteOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+
+ // Flush objects.
+ uno::Reference<embed::XTransactedObject> xTransact(aStreamHelper.xSignatureStorage,
+ uno::UNO_QUERY);
+ xTransact->commit();
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ xOutputStream->closeOutput();
+
+ uno::Reference<io::XTempFile> xTempFile(aStreamHelper.xSignatureStream, uno::UNO_QUERY);
+ SAL_INFO("xmlsecurity.helper",
+ "DocumentSignatureManager::add temporary storage at " << xTempFile->getUri());
+ }
+
+ maSignatureHelper.EndMission();
+ return true;
+}
+
+void DocumentSignatureManager::remove(sal_uInt16 nPosition)
+{
+ if (!mxStore.is())
+ {
+ // Something not ZIP based, try PDF.
+ uno::Reference<io::XInputStream> xInputStream(mxSignatureStream, uno::UNO_QUERY);
+ if (!PDFSignatureHelper::RemoveSignature(xInputStream, nPosition))
+ {
+ SAL_WARN("xmlsecurity.helper", "PDFSignatureHelper::RemoveSignature() failed");
+ return;
+ }
+
+ // Only erase when the removal was successful, it may fail for PDF.
+ // Also, erase the requested and all following signatures, as PDF signatures are always chained.
+ maCurrentSignatureInformations.erase(maCurrentSignatureInformations.begin() + nPosition,
+ maCurrentSignatureInformations.end());
+ return;
+ }
+
+ maCurrentSignatureInformations.erase(maCurrentSignatureInformations.begin() + nPosition);
+
+ // Export all other signatures...
+ SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, /*bTempStream=*/true);
+
+ if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<xml::sax::XWriter> xSaxWriter
+ = maSignatureHelper.CreateDocumentHandlerWithHeader(xOutputStream);
+
+ uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xSaxWriter,
+ uno::UNO_QUERY_THROW);
+ std::size_t nInfos = maCurrentSignatureInformations.size();
+ for (std::size_t n = 0; n < nInfos; ++n)
+ XMLSignatureHelper::ExportSignature(xDocumentHandler, maCurrentSignatureInformations[n],
+ false /* ??? */);
+
+ XMLSignatureHelper::CloseDocumentHandler(xDocumentHandler);
+ }
+ else
+ {
+ // OOXML
+
+ // Handle relations.
+ int nSignatureCount = maCurrentSignatureInformations.size();
+ maSignatureHelper.ExportSignatureRelations(aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+
+ // Export old signatures.
+ for (std::size_t i = 0; i < maCurrentSignatureInformations.size(); ++i)
+ maSignatureHelper.ExportOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ maCurrentSignatureInformations[i], i + 1);
+
+ // Flush objects.
+ uno::Reference<embed::XTransactedObject> xTransact(aStreamHelper.xSignatureStorage,
+ uno::UNO_QUERY);
+ xTransact->commit();
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ xOutputStream->closeOutput();
+
+ uno::Reference<io::XTempFile> xTempFile(aStreamHelper.xSignatureStream, uno::UNO_QUERY);
+ SAL_INFO("xmlsecurity.helper", "DocumentSignatureManager::remove: temporary storage is at "
+ << xTempFile->getUri());
+ }
+}
+
+void DocumentSignatureManager::read(bool bUseTempStream, bool bCacheLastSignature)
+{
+ maCurrentSignatureInformations.clear();
+
+ if (mxStore.is())
+ {
+ // ZIP-based: ODF or OOXML.
+ maSignatureHelper.StartMission(mxSecurityContext);
+
+ SignatureStreamHelper aStreamHelper
+ = ImplOpenSignatureStream(embed::ElementModes::READ, bUseTempStream);
+ if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML
+ && aStreamHelper.xSignatureStream.is())
+ {
+ uno::Reference<io::XInputStream> xInputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ maSignatureHelper.ReadAndVerifySignature(xInputStream);
+ }
+ else if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML
+ && aStreamHelper.xSignatureStorage.is())
+ maSignatureHelper.ReadAndVerifySignatureStorage(aStreamHelper.xSignatureStorage,
+ bCacheLastSignature);
+ maSignatureHelper.EndMission();
+
+ // this parses the XML independently from ImplVerifySignatures() - check
+ // certificates here too ...
+ for (auto const& it : maSignatureHelper.GetSignatureInformations())
+ {
+ if (!it.X509Datas.empty())
+ {
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(
+ getSecurityEnvironment());
+ getSignatureHelper().CheckAndUpdateSignatureInformation(xSecEnv, it);
+ }
+ }
+
+ maCurrentSignatureInformations = maSignatureHelper.GetSignatureInformations();
+ }
+ else
+ {
+ // Something not ZIP based, try PDF.
+ uno::Reference<io::XInputStream> xInputStream(mxSignatureStream, uno::UNO_QUERY);
+ if (getPDFSignatureHelper().ReadAndVerifySignature(xInputStream))
+ maCurrentSignatureInformations = getPDFSignatureHelper().GetSignatureInformations();
+ }
+}
+
+void DocumentSignatureManager::write(bool bXAdESCompliantIfODF)
+{
+ if (!mxStore.is())
+ {
+ // Something not ZIP based, assume PDF, which is written directly in add() already.
+ return;
+ }
+
+ // Export all other signatures...
+ SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, false);
+
+ if (aStreamHelper.xSignatureStream.is()
+ && aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ // ODF
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ uno::Reference<xml::sax::XWriter> xSaxWriter
+ = maSignatureHelper.CreateDocumentHandlerWithHeader(xOutputStream);
+
+ uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xSaxWriter,
+ uno::UNO_QUERY_THROW);
+ std::size_t nInfos = maCurrentSignatureInformations.size();
+ for (std::size_t n = 0; n < nInfos; ++n)
+ XMLSignatureHelper::ExportSignature(xDocumentHandler, maCurrentSignatureInformations[n],
+ bXAdESCompliantIfODF);
+
+ XMLSignatureHelper::CloseDocumentHandler(xDocumentHandler);
+ }
+ else if (aStreamHelper.xSignatureStorage.is()
+ && aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ {
+ // OOXML
+ std::size_t nSignatureCount = maCurrentSignatureInformations.size();
+ maSignatureHelper.ExportSignatureContentTypes(mxStore, nSignatureCount);
+ if (nSignatureCount > 0)
+ maSignatureHelper.ExportSignatureRelations(aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+ else
+ {
+ // Removing all signatures: then need to remove the signature relation as well.
+ maSignatureHelper.EnsureSignaturesRelation(mxStore, /*bAdd=*/false);
+ // Also remove the whole signature sub-storage: release our read-write reference + remove the element.
+ aStreamHelper = SignatureStreamHelper();
+ mxStore->removeElement("_xmlsignatures");
+ }
+
+ for (std::size_t i = 0; i < nSignatureCount; ++i)
+ maSignatureHelper.ExportOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ maCurrentSignatureInformations[i], i + 1);
+ }
+
+ // If stream was not provided, we are responsible for committing it...
+ if (!mxSignatureStream.is() && aStreamHelper.xSignatureStorage.is())
+ {
+ uno::Reference<embed::XTransactedObject> xTrans(aStreamHelper.xSignatureStorage,
+ uno::UNO_QUERY);
+ xTrans->commit();
+ }
+}
+
+uno::Reference<xml::crypto::XSecurityEnvironment> DocumentSignatureManager::getSecurityEnvironment()
+{
+ return mxSecurityContext.is() ? mxSecurityContext->getSecurityEnvironment()
+ : uno::Reference<xml::crypto::XSecurityEnvironment>();
+}
+
+uno::Reference<xml::crypto::XSecurityEnvironment>
+DocumentSignatureManager::getGpgSecurityEnvironment()
+{
+ return mxGpgSecurityContext.is() ? mxGpgSecurityContext->getSecurityEnvironment()
+ : uno::Reference<xml::crypto::XSecurityEnvironment>();
+}
+
+uno::Reference<xml::crypto::XXMLSecurityContext> const&
+DocumentSignatureManager::getSecurityContext() const
+{
+ return mxSecurityContext;
+}
+
+uno::Reference<xml::crypto::XXMLSecurityContext> const&
+DocumentSignatureManager::getGpgSecurityContext() const
+{
+ return mxGpgSecurityContext;
+}
+
+void DocumentSignatureManager::setModel(const uno::Reference<frame::XModel>& xModel)
+{
+ mxModel = xModel;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.cxx b/xmlsecurity/source/helper/ooxmlsecexporter.cxx
new file mode 100644
index 000000000..2294af680
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecexporter.cxx
@@ -0,0 +1,558 @@
+/* -*- 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 "ooxmlsecexporter.hxx"
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <comphelper/ofopxmlhelper.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <svx/xoutbmp.hxx>
+#include <unotools/datetime.hxx>
+#include <vcl/salctype.hxx>
+#include <xmloff/attrlist.hxx>
+
+#include <documentsignaturehelper.hxx>
+#include <xsecctl.hxx>
+
+using namespace com::sun::star;
+using namespace css::xml::sax;
+
+struct OOXMLSecExporter::Impl
+{
+private:
+ const uno::Reference<uno::XComponentContext>& m_xComponentContext;
+ const uno::Reference<embed::XStorage>& m_xRootStorage;
+ const uno::Reference<xml::sax::XDocumentHandler>& m_xDocumentHandler;
+ const SignatureInformation& m_rInformation;
+ OUString m_aSignatureTimeValue;
+
+public:
+ Impl(const uno::Reference<uno::XComponentContext>& xComponentContext,
+ const uno::Reference<embed::XStorage>& xRootStorage,
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& rInformation)
+ : m_xComponentContext(xComponentContext)
+ , m_xRootStorage(xRootStorage)
+ , m_xDocumentHandler(xDocumentHandler)
+ , m_rInformation(rInformation)
+ {
+ }
+
+ /// Should we intentionally not sign this stream?
+ static bool isOOXMLDenylist(std::u16string_view rStreamName);
+ /// Should we intentionally not sign this relation type?
+ static bool isOOXMLRelationDenylist(const OUString& rRelationName);
+
+ const uno::Reference<xml::sax::XDocumentHandler>& getDocumentHandler() const
+ {
+ return m_xDocumentHandler;
+ }
+
+ void writeSignature();
+ void writeSignedInfo();
+ void writeCanonicalizationMethod();
+ void writeCanonicalizationTransform();
+ void writeSignatureMethod();
+ void writeSignedInfoReferences();
+ void writeSignatureValue();
+ void writeKeyInfo();
+ void writePackageObject();
+ void writeManifest();
+ void writeRelationshipTransform(const OUString& rURI);
+ /// Writes <SignatureProperties> inside idPackageObject.
+ void writePackageObjectSignatureProperties();
+ /// Writes a single <Reference> inside <Manifest>.
+ void writeManifestReference(const SignatureReferenceInformation& rReference);
+ void writeOfficeObject();
+ /// Writes <SignatureInfoV1>.
+ void writeSignatureInfo();
+ void writePackageSignature();
+ void writeSignatureLineImages();
+};
+
+bool OOXMLSecExporter::Impl::isOOXMLDenylist(std::u16string_view rStreamName)
+{
+ static const std::initializer_list<std::u16string_view> vDenylist
+ = { u"/%5BContent_Types%5D.xml", u"/docProps/app.xml", u"/docProps/core.xml",
+ // Don't attempt to sign other signatures for now.
+ u"/_xmlsignatures" };
+ // Just check the prefix, as we don't care about the content type part of the stream name.
+ return std::any_of(vDenylist.begin(), vDenylist.end(),
+ [&](const std::u16string_view& rLiteral) {
+ return o3tl::starts_with(rStreamName, rLiteral);
+ });
+}
+
+bool OOXMLSecExporter::Impl::isOOXMLRelationDenylist(const OUString& rRelationName)
+{
+ static const std::initializer_list<std::u16string_view> vDenylist = {
+ u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
+ u"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
+ u"http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin"
+ };
+ return std::find(vDenylist.begin(), vDenylist.end(), rRelationName) != vDenylist.end();
+}
+
+void OOXMLSecExporter::Impl::writeSignedInfo()
+{
+ m_xDocumentHandler->startElement(
+ "SignedInfo", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+
+ writeCanonicalizationMethod();
+ writeSignatureMethod();
+ writeSignedInfoReferences();
+
+ m_xDocumentHandler->endElement("SignedInfo");
+}
+
+void OOXMLSecExporter::Impl::writeCanonicalizationMethod()
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_C14N);
+ m_xDocumentHandler->startElement("CanonicalizationMethod",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("CanonicalizationMethod");
+}
+
+void OOXMLSecExporter::Impl::writeCanonicalizationTransform()
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_C14N);
+ m_xDocumentHandler->startElement("Transform",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("Transform");
+}
+
+void OOXMLSecExporter::Impl::writeSignatureMethod()
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+
+ if (m_rInformation.eAlgorithmID == svl::crypto::SignatureMethodAlgorithm::ECDSA)
+ pAttributeList->AddAttribute("Algorithm", ALGO_ECDSASHA256);
+ else
+ pAttributeList->AddAttribute("Algorithm", ALGO_RSASHA256);
+
+ m_xDocumentHandler->startElement("SignatureMethod",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("SignatureMethod");
+}
+
+void OOXMLSecExporter::Impl::writeSignedInfoReferences()
+{
+ const SignatureReferenceInformations& rReferences = m_rInformation.vSignatureReferenceInfors;
+ for (const SignatureReferenceInformation& rReference : rReferences)
+ {
+ if (rReference.nType == SignatureReferenceType::SAMEDOCUMENT)
+ {
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ if (!rReference.ouURI.startsWith("idSignedProperties"))
+ pAttributeList->AddAttribute("Type",
+ "http://www.w3.org/2000/09/xmldsig#Object");
+ else
+ pAttributeList->AddAttribute("Type",
+ "http://uri.etsi.org/01903#SignedProperties");
+ pAttributeList->AddAttribute("URI", "#" + rReference.ouURI);
+ m_xDocumentHandler->startElement(
+ "Reference", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ if (rReference.ouURI.startsWith("idSignedProperties"))
+ {
+ m_xDocumentHandler->startElement(
+ "Transforms",
+ uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ writeCanonicalizationTransform();
+ m_xDocumentHandler->endElement("Transforms");
+ }
+
+ DocumentSignatureHelper::writeDigestMethod(m_xDocumentHandler);
+ m_xDocumentHandler->startElement(
+ "DigestValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(rReference.ouDigestValue);
+ m_xDocumentHandler->endElement("DigestValue");
+ m_xDocumentHandler->endElement("Reference");
+ }
+ }
+}
+
+void OOXMLSecExporter::Impl::writeSignatureValue()
+{
+ m_xDocumentHandler->startElement(
+ "SignatureValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(m_rInformation.ouSignatureValue);
+ m_xDocumentHandler->endElement("SignatureValue");
+}
+
+void OOXMLSecExporter::Impl::writeKeyInfo()
+{
+ m_xDocumentHandler->startElement(
+ "KeyInfo", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ assert(m_rInformation.GetSigningCertificate());
+ for (auto const& rData : m_rInformation.X509Datas)
+ {
+ m_xDocumentHandler->startElement(
+ "X509Data", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ for (auto const& it : rData)
+ {
+ m_xDocumentHandler->startElement(
+ "X509Certificate",
+ uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(it.X509Certificate);
+ m_xDocumentHandler->endElement("X509Certificate");
+ }
+ m_xDocumentHandler->endElement("X509Data");
+ }
+ m_xDocumentHandler->endElement("KeyInfo");
+}
+
+void OOXMLSecExporter::Impl::writePackageObject()
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idPackageObject_" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ writeManifest();
+ writePackageObjectSignatureProperties();
+
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeManifest()
+{
+ m_xDocumentHandler->startElement(
+ "Manifest", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ const SignatureReferenceInformations& rReferences = m_rInformation.vSignatureReferenceInfors;
+ for (const SignatureReferenceInformation& rReference : rReferences)
+ {
+ if (rReference.nType != SignatureReferenceType::SAMEDOCUMENT)
+ {
+ if (OOXMLSecExporter::Impl::isOOXMLDenylist(rReference.ouURI))
+ continue;
+
+ writeManifestReference(rReference);
+ }
+ }
+ m_xDocumentHandler->endElement("Manifest");
+}
+
+void OOXMLSecExporter::Impl::writeRelationshipTransform(const OUString& rURI)
+{
+ uno::Reference<embed::XHierarchicalStorageAccess> xHierarchicalStorageAccess(m_xRootStorage,
+ uno::UNO_QUERY);
+ uno::Reference<io::XInputStream> xRelStream(
+ xHierarchicalStorageAccess->openStreamElementByHierarchicalName(rURI,
+ embed::ElementModes::READ),
+ uno::UNO_QUERY);
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_RELATIONSHIP);
+ m_xDocumentHandler->startElement("Transform",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ const uno::Sequence<uno::Sequence<beans::StringPair>> aRelationsInfo
+ = comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream, rURI,
+ m_xComponentContext);
+ for (const uno::Sequence<beans::StringPair>& rPairs : aRelationsInfo)
+ {
+ OUString aId;
+ OUString aType;
+ for (const beans::StringPair& rPair : rPairs)
+ {
+ if (rPair.First == "Id")
+ aId = rPair.Second;
+ else if (rPair.First == "Type")
+ aType = rPair.Second;
+ }
+
+ if (OOXMLSecExporter::Impl::isOOXMLRelationDenylist(aType))
+ continue;
+
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("xmlns:mdssi", NS_MDSSI);
+ pAttributeList->AddAttribute("SourceId", aId);
+ m_xDocumentHandler->startElement("mdssi:RelationshipReference",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("mdssi:RelationshipReference");
+ }
+
+ m_xDocumentHandler->endElement("Transform");
+}
+
+void OOXMLSecExporter::Impl::writePackageObjectSignatureProperties()
+{
+ m_xDocumentHandler->startElement(
+ "SignatureProperties", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idSignatureTime_" + m_rInformation.ouSignatureId);
+ pAttributeList->AddAttribute("Target", "#" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("SignatureProperty",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("xmlns:mdssi", NS_MDSSI);
+ m_xDocumentHandler->startElement("mdssi:SignatureTime",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ m_xDocumentHandler->startElement(
+ "mdssi:Format", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("YYYY-MM-DDThh:mm:ssTZD");
+ m_xDocumentHandler->endElement("mdssi:Format");
+
+ m_xDocumentHandler->startElement(
+ "mdssi:Value", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ if (!m_rInformation.ouDateTime.isEmpty())
+ m_aSignatureTimeValue = m_rInformation.ouDateTime;
+ else
+ {
+ m_aSignatureTimeValue = utl::toISO8601(m_rInformation.stDateTime);
+ // Ignore sub-seconds.
+ sal_Int32 nCommaPos = m_aSignatureTimeValue.indexOf(',');
+ if (nCommaPos != -1)
+ {
+ m_aSignatureTimeValue
+ = OUString::Concat(m_aSignatureTimeValue.subView(0, nCommaPos)) + "Z";
+ }
+ }
+ m_xDocumentHandler->characters(m_aSignatureTimeValue);
+ m_xDocumentHandler->endElement("mdssi:Value");
+
+ m_xDocumentHandler->endElement("mdssi:SignatureTime");
+ m_xDocumentHandler->endElement("SignatureProperty");
+ m_xDocumentHandler->endElement("SignatureProperties");
+}
+
+void OOXMLSecExporter::Impl::writeManifestReference(const SignatureReferenceInformation& rReference)
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("URI", rReference.ouURI);
+ m_xDocumentHandler->startElement("Reference",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ // Transforms
+ if (rReference.ouURI.endsWith(
+ "?ContentType=application/vnd.openxmlformats-package.relationships+xml"))
+ {
+ OUString aURI = rReference.ouURI;
+ // Ignore leading slash.
+ if (aURI.startsWith("/"))
+ aURI = aURI.copy(1);
+ // Ignore query part of the URI.
+ sal_Int32 nQueryPos = aURI.indexOf('?');
+ if (nQueryPos != -1)
+ aURI = aURI.copy(0, nQueryPos);
+
+ m_xDocumentHandler->startElement(
+ "Transforms", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+
+ writeRelationshipTransform(aURI);
+ writeCanonicalizationTransform();
+
+ m_xDocumentHandler->endElement("Transforms");
+ }
+
+ DocumentSignatureHelper::writeDigestMethod(m_xDocumentHandler);
+ m_xDocumentHandler->startElement(
+ "DigestValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(rReference.ouDigestValue);
+ m_xDocumentHandler->endElement("DigestValue");
+ m_xDocumentHandler->endElement("Reference");
+}
+
+void OOXMLSecExporter::Impl::writeOfficeObject()
+{
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idOfficeObject_" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ m_xDocumentHandler->startElement(
+ "SignatureProperties", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idOfficeV1Details_" + m_rInformation.ouSignatureId);
+ pAttributeList->AddAttribute("Target", "#" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("SignatureProperty",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ writeSignatureInfo();
+ m_xDocumentHandler->endElement("SignatureProperty");
+ m_xDocumentHandler->endElement("SignatureProperties");
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeSignatureInfo()
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("xmlns", "http://schemas.microsoft.com/office/2006/digsig");
+ m_xDocumentHandler->startElement("SignatureInfoV1",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ m_xDocumentHandler->startElement(
+ "SetupID", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(m_rInformation.ouSignatureLineId);
+ m_xDocumentHandler->endElement("SetupID");
+ m_xDocumentHandler->startElement(
+ "SignatureText", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->endElement("SignatureText");
+ m_xDocumentHandler->startElement(
+ "SignatureImage", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->endElement("SignatureImage");
+ m_xDocumentHandler->startElement(
+ "SignatureComments", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(m_rInformation.ouDescription);
+ m_xDocumentHandler->endElement("SignatureComments");
+ // Just hardcode something valid according to [MS-OFFCRYPTO].
+ m_xDocumentHandler->startElement(
+ "WindowsVersion", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("6.1");
+ m_xDocumentHandler->endElement("WindowsVersion");
+ m_xDocumentHandler->startElement(
+ "OfficeVersion", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("16.0");
+ m_xDocumentHandler->endElement("OfficeVersion");
+ m_xDocumentHandler->startElement(
+ "ApplicationVersion", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("16.0");
+ m_xDocumentHandler->endElement("ApplicationVersion");
+ m_xDocumentHandler->startElement(
+ "Monitors", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("1");
+ m_xDocumentHandler->endElement("Monitors");
+ m_xDocumentHandler->startElement(
+ "HorizontalResolution", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("1280");
+ m_xDocumentHandler->endElement("HorizontalResolution");
+ m_xDocumentHandler->startElement(
+ "VerticalResolution", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("800");
+ m_xDocumentHandler->endElement("VerticalResolution");
+ m_xDocumentHandler->startElement(
+ "ColorDepth", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("32");
+ m_xDocumentHandler->endElement("ColorDepth");
+ m_xDocumentHandler->startElement(
+ "SignatureProviderId", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("{00000000-0000-0000-0000-000000000000}");
+ m_xDocumentHandler->endElement("SignatureProviderId");
+ m_xDocumentHandler->startElement(
+ "SignatureProviderUrl", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->endElement("SignatureProviderUrl");
+ m_xDocumentHandler->startElement(
+ "SignatureProviderDetails",
+ uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters(
+ "9"); // This is what MSO 2016 writes, though [MS-OFFCRYPTO] doesn't document what the value means.
+ m_xDocumentHandler->endElement("SignatureProviderDetails");
+ m_xDocumentHandler->startElement(
+ "SignatureType", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ m_xDocumentHandler->characters("2");
+ m_xDocumentHandler->endElement("SignatureType");
+
+ m_xDocumentHandler->endElement("SignatureInfoV1");
+}
+
+void OOXMLSecExporter::Impl::writePackageSignature()
+{
+ m_xDocumentHandler->startElement(
+ "Object", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("xmlns:xd", NS_XD);
+ pAttributeList->AddAttribute("Target", "#" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("xd:QualifyingProperties",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ DocumentSignatureHelper::writeSignedProperties(m_xDocumentHandler, m_rInformation,
+ m_aSignatureTimeValue, false);
+
+ m_xDocumentHandler->endElement("xd:QualifyingProperties");
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeSignatureLineImages()
+{
+ if (m_rInformation.aValidSignatureImage.is())
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idValidSigLnImg");
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ OUString aGraphicInBase64;
+ Graphic aGraphic(m_rInformation.aValidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false, ConvertDataFormat::EMF))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+ m_xDocumentHandler->characters(aGraphicInBase64);
+ m_xDocumentHandler->endElement("Object");
+ }
+ if (!m_rInformation.aInvalidSignatureImage.is())
+ return;
+
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idInvalidSigLnImg");
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ OUString aGraphicInBase64;
+ Graphic aGraphic(m_rInformation.aInvalidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false, ConvertDataFormat::EMF))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+ m_xDocumentHandler->characters(aGraphicInBase64);
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeSignature()
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("xmlns", NS_XMLDSIG);
+ pAttributeList->AddAttribute("Id", m_rInformation.ouSignatureId);
+ getDocumentHandler()->startElement("Signature",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ writeSignedInfo();
+ writeSignatureValue();
+ writeKeyInfo();
+ writePackageObject();
+ writeOfficeObject();
+ writePackageSignature();
+ writeSignatureLineImages();
+
+ getDocumentHandler()->endElement("Signature");
+}
+
+OOXMLSecExporter::OOXMLSecExporter(
+ const uno::Reference<uno::XComponentContext>& xComponentContext,
+ const uno::Reference<embed::XStorage>& xRootStorage,
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& rInformation)
+ : m_pImpl(
+ std::make_unique<Impl>(xComponentContext, xRootStorage, xDocumentHandler, rInformation))
+{
+}
+
+OOXMLSecExporter::~OOXMLSecExporter() = default;
+
+void OOXMLSecExporter::writeSignature() { m_pImpl->writeSignature(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.hxx b/xmlsecurity/source/helper/ooxmlsecexporter.hxx
new file mode 100644
index 000000000..77ea7c484
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecexporter.hxx
@@ -0,0 +1,47 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <svl/sigstruct.hxx>
+
+namespace com::sun::star
+{
+namespace embed
+{
+class XStorage;
+}
+namespace uno
+{
+class XComponentContext;
+}
+namespace xml::sax
+{
+class XDocumentHandler;
+}
+}
+
+/// Writes a single OOXML digital signature.
+class OOXMLSecExporter
+{
+ struct Impl;
+ std::unique_ptr<Impl> m_pImpl;
+
+public:
+ OOXMLSecExporter(const css::uno::Reference<css::uno::XComponentContext>& xComponentContext,
+ const css::uno::Reference<css::embed::XStorage>& xRootStorage,
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& rInformation);
+ ~OOXMLSecExporter();
+ void writeSignature();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecparser.cxx b/xmlsecurity/source/helper/ooxmlsecparser.cxx
new file mode 100644
index 000000000..2692190cc
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecparser.cxx
@@ -0,0 +1,1352 @@
+/* -*- 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 "ooxmlsecparser.hxx"
+#include <xmlsignaturehelper.hxx>
+#include <xsecctl.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltkmap.hxx>
+#include <xmloff/xmlimp.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+class OOXMLSecParser::Context
+{
+ protected:
+ friend class OOXMLSecParser;
+ OOXMLSecParser & m_rParser;
+ private:
+ std::optional<SvXMLNamespaceMap> m_pOldNamespaceMap;
+
+ public:
+ Context(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : m_rParser(rParser)
+ , m_pOldNamespaceMap(std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual ~Context() = default;
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/)
+ {
+ }
+
+ virtual void EndElement()
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/);
+
+ virtual void Characters(OUString const& /*rChars*/)
+ {
+ }
+};
+
+// it's possible that an unsupported element has an Id attribute and a
+// ds:Reference digesting it - probably this means XSecController needs to know
+// about it. (For known elements, the Id attribute is only processed according
+// to the schema.)
+class OOXMLSecParser::UnknownContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ UnknownContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+};
+
+auto OOXMLSecParser::Context::CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/)
+-> std::unique_ptr<Context>
+{
+ // default: create new base context
+ return std::make_unique<UnknownContext>(m_rParser, std::move(pOldNamespaceMap));
+}
+
+/**
+note: anything in ds:Object should be trusted *only* if there is a ds:Reference
+ to it so it is signed (exception: the xades:EncapsulatedX509Certificate).
+ ds:SignedInfo precedes all ds:Object.
+
+ There may be multiple ds:Signature for purpose of counter-signatures
+ but the way XAdES describes these, only the ds:SignatureValue element
+ would be referenced, so requiring a ds:Reference for anything in
+ ds:Object shouldn't cause issues.
+ */
+class OOXMLSecParser::ReferencedContextImpl
+ : public OOXMLSecParser::Context
+{
+ protected:
+ bool m_isReferenced;
+
+ public:
+ ReferencedContextImpl(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_isReferenced(isReferenced)
+ {
+ }
+
+ OUString CheckIdAttrReferenced(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+ {
+ OUString const id(m_rParser.HandleIdAttr(xAttrs));
+ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id))
+ {
+ m_isReferenced = true;
+ }
+ return id;
+ }
+};
+
+class OOXMLSecParser::DsX509CertificateContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509CertificateContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509SerialNumberContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509SerialNumberContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509IssuerNameContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509IssuerNameContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509IssuerSerialContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rX509IssuerName;
+ OUString & m_rX509SerialNumber;
+
+ public:
+ DsX509IssuerSerialContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rIssuerName, OUString& rSerialNumber)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rX509IssuerName(rIssuerName)
+ , m_rX509SerialNumber(rSerialNumber)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName")
+ {
+ return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber")
+ {
+ return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber);
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+/// can't be sure what is supposed to happen here because the spec is clear as mud
+class OOXMLSecParser::DsX509DataContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ // sigh... "No ordering is implied by the above constraints."
+ // so store the ball of mud in vectors and try to figure it out later.
+ std::vector<std::pair<OUString, OUString>> m_X509IssuerSerials;
+ std::vector<OUString> m_X509Certificates;
+
+ public:
+ DsX509DataContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial")
+ {
+ m_X509IssuerSerials.emplace_back();
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate")
+ {
+ m_X509Certificates.emplace_back();
+ return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back());
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsKeyInfoContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsKeyInfoContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data")
+ {
+ return std::make_unique<DsX509DataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:PGPData
+ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData
+ // (old code would read ds:Transform inside ds:RetrievalMethod but
+ // presumably that was a bug)
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+};
+
+class OOXMLSecParser::DsSignatureValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsSignatureValueContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setSignatureValue(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class OOXMLSecParser::DsDigestValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsDigestValueContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rValue.clear();
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsDigestMethodContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ DsDigestMethodContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_Int32& rReferenceDigestID)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" );
+ if (ouAlgorithm.isEmpty())
+ return;
+
+ SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1
+ && ouAlgorithm != ALGO_XMLDSIGSHA256
+ && ouAlgorithm != ALGO_XMLDSIGSHA512,
+ "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512");
+ if (ouAlgorithm == ALGO_XMLDSIGSHA1)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA256)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA512)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512;
+ else
+ m_rReferenceDigestID = 0;
+ }
+};
+
+class OOXMLSecParser::DsTransformContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString aAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ if (aAlgorithm == ALGO_RELATIONSHIP)
+ {
+ m_rIsC14N = true;
+ }
+ }
+};
+
+class OOXMLSecParser::DsTransformsContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformsContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform")
+ {
+ return std::make_unique<DsTransformContext>(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsReferenceContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString m_URI;
+ OUString m_Type;
+ OUString m_DigestValue;
+ bool m_IsC14N = false;
+ // Relevant for ODF. The digest algorithm selected by the DigestMethod
+ // element's Algorithm attribute. @see css::xml::crypto::DigestID.
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+
+ public:
+ DsReferenceContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+
+ m_URI = xAttrs->getValueByName("URI");
+ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty");
+ // Remember the type of this reference.
+ m_Type = xAttrs->getValueByName("Type");
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_URI.startsWith("#"))
+ {
+ /*
+ * remove the first character '#' from the attribute value
+ */
+ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type);
+ }
+ else
+ {
+ if (m_IsC14N) // this is determined by nested ds:Transform
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID);
+ }
+ else
+ /*
+ * it must be an octet stream
+ */
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID);
+ }
+ }
+
+ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms")
+ {
+ return std::make_unique<DsTransformsContext>(m_rParser, std::move(pOldNamespaceMap), m_IsC14N);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_DigestValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignatureMethodContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignatureMethodContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
+ || ouAlgorithm == ALGO_ECDSASHA512)
+ {
+ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
+ }
+ }
+};
+
+class OOXMLSecParser::DsSignedInfoContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignedInfoContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod")
+ {
+ return std::make_unique<DsSignatureMethodContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesCertDigestContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rDigestValue;
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ XadesCertDigestContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rDigestValue, sal_Int32& rReferenceDigestID)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rDigestValue(rDigestValue)
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesCertContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ OUString m_CertDigest;
+ OUString m_X509IssuerName;
+ OUString m_X509SerialNumber;
+
+ public:
+ XadesCertContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest")
+ {
+ return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial")
+ {
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSigningCertificateContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSigningCertificateContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert")
+ {
+ return std::make_unique<XadesCertContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSigningTimeContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesSigningTimeContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setDate("", m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class OOXMLSecParser::XadesSignedSignaturePropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedSignaturePropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime")
+ {
+ return std::make_unique<XadesSigningTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate")
+ {
+ return std::make_unique<XadesSigningCertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSignedPropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedPropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties")
+ {
+ return std::make_unique<XadesSignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignedDataObjectProperties
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesQualifyingPropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesQualifyingPropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties")
+ {
+ return std::make_unique<XadesSignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:UnsignedSignatureProperties
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::MsodigsigSetupIDContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MsodigsigSetupIDContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MsodigsigSignatureCommentsContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MsodigsigSignatureCommentsContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MsodigsigSignatureInfoV1Context
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_SetupID;
+ OUString m_SignatureComments;
+
+ public:
+ MsodigsigSignatureInfoV1Context(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SetupID")
+ {
+ return std::make_unique<MsodigsigSetupIDContext>(m_rParser, std::move(pOldNamespaceMap), m_SetupID);
+ }
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureComments")
+ {
+ return std::make_unique<MsodigsigSignatureCommentsContext>(m_rParser, std::move(pOldNamespaceMap), m_SignatureComments);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ if (!m_SetupID.isEmpty())
+ {
+ m_rParser.m_pXSecController->setSignatureLineId(m_SetupID);
+ }
+ if (!m_SignatureComments.isEmpty())
+ {
+ m_rParser.m_pXSecController->setDescription("", m_SignatureComments);
+
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureInfoV1");
+ }
+ }
+};
+
+class OOXMLSecParser::MdssiValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MdssiValueContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MdssiSignatureTimeContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MdssiSignatureTimeContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "Value")
+ {
+ return std::make_unique<MdssiValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+class OOXMLSecParser::DsSignaturePropertyContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ enum class SignatureProperty { Unknown, Date, Info };
+ SignatureProperty m_Property = SignatureProperty::Unknown;
+ OUString m_Id;
+ OUString m_Value;
+
+ public:
+ DsSignaturePropertyContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_Id = CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ switch (m_Property)
+ {
+ case SignatureProperty::Unknown:
+ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored");
+ break;
+ case SignatureProperty::Info:
+ break; // handled by child context
+ case SignatureProperty::Date:
+ m_rParser.m_pXSecController->setDate(m_Id, m_Value);
+ break;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "SignatureTime")
+ {
+ m_Property = SignatureProperty::Date;
+ return std::make_unique<MdssiSignatureTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureInfoV1")
+ {
+ return std::make_unique<MsodigsigSignatureInfoV1Context>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignaturePropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ DsSignaturePropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty")
+ {
+ return std::make_unique<DsSignaturePropertyContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsManifestContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ DsManifestContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+#if 0
+ ???
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+#endif
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsObjectContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ enum class Mode { Default, ValidSignatureLineImage, InvalidSignatureLineImage };
+ Mode m_Mode = Mode::Default;
+ OUString m_Value;
+
+ public:
+ DsObjectContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ // init with "false" here - the Signature element can't be referenced by its child
+ : OOXMLSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const id(CheckIdAttrReferenced(xAttrs));
+ if (id == "idValidSigLnImg")
+ {
+ m_Mode = Mode::ValidSignatureLineImage;
+ }
+ else if (id == "idInvalidSigLnImg")
+ {
+ m_Mode = Mode::InvalidSignatureLineImage;
+ }
+ }
+
+ virtual void EndElement() override
+ {
+ switch (m_Mode)
+ {
+ case Mode::ValidSignatureLineImage:
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setValidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage");
+ }
+ break;
+ case Mode::InvalidSignatureLineImage:
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage");
+ }
+ break;
+ case Mode::Default:
+ break;
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties")
+ {
+ return std::make_unique<DsSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties")
+ {
+ return std::make_unique<XadesQualifyingPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Manifest")
+ {
+ return std::make_unique<DsManifestContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignatureContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignatureContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs));
+ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement();
+ m_rParser.m_pXSecController->addSignature();
+ if (!ouIdAttr.isEmpty())
+ {
+ m_rParser.m_pXSecController->setId( ouIdAttr );
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo")
+ {
+ return std::make_unique<DsSignedInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue")
+ {
+ return std::make_unique<DsSignatureValueContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo")
+ {
+ return std::make_unique<DsKeyInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Object")
+ {
+ return std::make_unique<DsObjectContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+OOXMLSecParser::OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController)
+ : m_pNamespaceMap(SvXMLNamespaceMap())
+ , m_pXSecController(pXSecController)
+ ,m_rXMLSignatureHelper(rXMLSignatureHelper)
+{
+ using namespace xmloff::token;
+ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML );
+ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS );
+ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132);
+ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141);
+ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
+ m_pNamespaceMap->Add( "_mdssi", NS_MDSSI, XML_NAMESPACE_MDSSI );
+ m_pNamespaceMap->Add( "_msodigsig", "http://schemas.microsoft.com/office/2006/digsig", XML_NAMESPACE_MSODIGSIG );
+ m_pNamespaceMap->Add( "_office_libo",
+ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT);
+}
+
+OOXMLSecParser::~OOXMLSecParser()
+{
+}
+
+OUString OOXMLSecParser::HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+{
+ OUString const aId = xAttrs->getValueByName("Id");
+ if (!aId.isEmpty())
+ {
+ m_pXSecController->collectToVerify(aId);
+ }
+ return aId;
+}
+
+void SAL_CALL OOXMLSecParser::startDocument()
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->startDocument();
+}
+
+void SAL_CALL OOXMLSecParser::endDocument()
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->endDocument();
+}
+
+void SAL_CALL OOXMLSecParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
+{
+ assert(m_pNamespaceMap);
+ std::optional<SvXMLNamespaceMap> pRewindMap(
+ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs));
+
+ OUString localName;
+ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName));
+
+ std::unique_ptr<Context> pContext;
+
+ if (m_ContextStack.empty())
+ {
+ if (nPrefix != XML_NAMESPACE_DS || localName != "Signature")
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected root element", nullptr,
+ css::uno::Any());
+ }
+
+ pContext.reset(new DsSignatureContext(*this, std::move(pRewindMap)));
+
+ }
+ else
+ {
+ pContext = m_ContextStack.top()->CreateChildContext(
+ std::move(pRewindMap), nPrefix, localName);
+ }
+
+ m_ContextStack.push(std::move(pContext));
+
+ m_ContextStack.top()->StartElement(xAttribs);
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->startElement(rName, xAttribs);
+ }
+
+}
+
+void SAL_CALL OOXMLSecParser::endElement(const OUString& rName)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+
+ m_ContextStack.top()->EndElement();
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->endElement(rName);
+ }
+
+ if (m_ContextStack.top()->m_pOldNamespaceMap)
+ {
+ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap);
+ }
+ m_ContextStack.pop();
+}
+
+void SAL_CALL OOXMLSecParser::characters(const OUString& rChars)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+ m_ContextStack.top()->Characters(rChars);
+
+ if (m_xNextHandler.is())
+ m_xNextHandler->characters(rChars);
+}
+
+void SAL_CALL OOXMLSecParser::ignorableWhitespace(const OUString& rWhitespace)
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->ignorableWhitespace(rWhitespace);
+}
+
+void SAL_CALL OOXMLSecParser::processingInstruction(const OUString& rTarget, const OUString& rData)
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->processingInstruction(rTarget, rData);
+}
+
+void SAL_CALL OOXMLSecParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& xLocator)
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->setDocumentLocator(xLocator);
+}
+
+void SAL_CALL OOXMLSecParser::initialize(const uno::Sequence<uno::Any>& rArguments)
+{
+ rArguments[0] >>= m_xNextHandler;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecparser.hxx b/xmlsecurity/source/helper/ooxmlsecparser.hxx
new file mode 100644
index 000000000..1ac72a46f
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecparser.hxx
@@ -0,0 +1,110 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <xmloff/namespacemap.hxx>
+
+#include <memory>
+#include <optional>
+#include <stack>
+
+class XSecController;
+class XMLSignatureHelper;
+
+/// Parses an OOXML digital signature.
+class OOXMLSecParser: public cppu::WeakImplHelper
+ <
+ css::xml::sax::XDocumentHandler,
+ css::lang::XInitialization
+ >
+{
+public:
+ class Context;
+private:
+ class UnknownContext;
+ class ReferencedContextImpl;
+ class DsX509CertificateContext;
+ class DsX509SerialNumberContext;
+ class DsX509IssuerNameContext;
+ class DsX509IssuerSerialContext;
+ class DsX509DataContext;
+ class DsKeyInfoContext;
+ class DsSignatureValueContext;
+ class DsDigestValueContext;
+ class DsDigestMethodContext;
+ class DsTransformContext;
+ class DsTransformsContext;
+ class DsReferenceContext;
+ class DsSignatureMethodContext;
+ class DsSignedInfoContext;
+ class XadesEncapsulatedX509CertificateContext;
+ class XadesCertificateValuesContext;
+ class XadesUnsignedSignaturePropertiesContext;
+ class XadesUnsignedPropertiesContext;
+ class XadesCertDigestContext;
+ class XadesCertContext;
+ class XadesSigningCertificateContext;
+ class XadesSigningTimeContext;
+ class XadesSignedSignaturePropertiesContext;
+ class XadesSignedPropertiesContext;
+ class XadesQualifyingPropertiesContext;
+ class MdssiValueContext;
+ class MdssiSignatureTimeContext;
+ class MsodigsigSetupIDContext;
+ class MsodigsigSignatureCommentsContext;
+ class MsodigsigSignatureInfoV1Context;
+ class DsSignaturePropertyContext;
+ class DsSignaturePropertiesContext;
+ class DsManifestContext;
+ class DsObjectContext;
+ class DsSignatureContext;
+ class DsigSignaturesContext;
+
+ std::stack<std::unique_ptr<Context>> m_ContextStack;
+ std::optional<SvXMLNamespaceMap> m_pNamespaceMap;
+
+ XSecController* m_pXSecController;
+ css::uno::Reference<css::xml::sax::XDocumentHandler> m_xNextHandler;
+
+ XMLSignatureHelper& m_rXMLSignatureHelper;
+
+ OUString HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs);
+
+public:
+ explicit OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController);
+ virtual ~OOXMLSecParser() override;
+
+ // XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+
+ virtual void SAL_CALL endDocument() override;
+
+ virtual void SAL_CALL startElement(const OUString& aName, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override;
+
+ virtual void SAL_CALL endElement(const OUString& aName) override;
+
+ virtual void SAL_CALL characters(const OUString& aChars) override;
+
+ virtual void SAL_CALL ignorableWhitespace(const OUString& aWhitespaces) override;
+
+ virtual void SAL_CALL processingInstruction(const OUString& aTarget, const OUString& aData) override;
+
+ virtual void SAL_CALL setDocumentLocator(const css::uno::Reference<css::xml::sax::XLocator>& xLocator) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
new file mode 100644
index 000000000..48d5c7f2d
--- /dev/null
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -0,0 +1,626 @@
+/* -*- 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 <pdfsignaturehelper.hxx>
+
+#include <memory>
+
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/uno/SecurityException.hpp>
+#include <com/sun/star/security/DocumentSignatureInformation.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+
+#include <comphelper/propertysequence.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/filter/pdfdocument.hxx>
+#include <vcl/checksum.hxx>
+#include <svl/cryptosign.hxx>
+#include <vcl/filter/PDFiumLibrary.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Gets the current page of the current view from xModel and puts it to the 1-based rPage.
+bool GetSignatureLinePage(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage)
+{
+ uno::Reference<drawing::XDrawView> xController(xModel->getCurrentController(), uno::UNO_QUERY);
+ if (!xController.is())
+ {
+ return false;
+ }
+
+ uno::Reference<beans::XPropertySet> xPage(xController->getCurrentPage(), uno::UNO_QUERY);
+ if (!xPage.is())
+ {
+ return false;
+ }
+
+ return xPage->getPropertyValue("Number") >>= rPage;
+}
+
+/// If the currently selected shape is a Draw signature line, export that to PDF.
+void GetSignatureLineShape(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage,
+ std::vector<sal_Int8>& rSignatureLineShape)
+{
+ if (!xModel.is())
+ {
+ return;
+ }
+
+ if (!GetSignatureLinePage(xModel, rPage))
+ {
+ return;
+ }
+
+ uno::Reference<drawing::XShapes> xShapes(xModel->getCurrentSelection(), uno::UNO_QUERY);
+ if (!xShapes.is() || xShapes->getCount() < 1)
+ {
+ return;
+ }
+
+ uno::Reference<beans::XPropertySet> xShapeProps(xShapes->getByIndex(0), uno::UNO_QUERY);
+ if (!xShapeProps.is())
+ {
+ return;
+ }
+
+ comphelper::SequenceAsHashMap aMap(xShapeProps->getPropertyValue("InteropGrabBag"));
+ auto it = aMap.find("SignatureCertificate");
+ if (it == aMap.end())
+ {
+ return;
+ }
+
+ // We know that we add a signature line shape to an existing PDF at this point.
+
+ uno::Reference<frame::XStorable> xStorable(xModel, uno::UNO_QUERY);
+ if (!xStorable.is())
+ {
+ return;
+ }
+
+ // Export just the signature line.
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xStream(new utl::OStreamWrapper(aStream));
+ aMediaDescriptor["OutputStream"] <<= xStream;
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "Selection", uno::Any(xShapes) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ xStream->flush();
+
+ aStream.Seek(0);
+ rSignatureLineShape = std::vector<sal_Int8>(aStream.GetSize());
+ aStream.ReadBytes(rSignatureLineShape.data(), rSignatureLineShape.size());
+}
+
+/// Represents a parsed signature.
+struct Signature
+{
+ std::unique_ptr<vcl::pdf::PDFiumSignature> m_pSignature;
+ /// Offset+length pairs.
+ std::vector<std::pair<size_t, size_t>> m_aByteRanges;
+};
+
+/// Turns an array of floats into offset + length pairs.
+void GetByteRangesFromPDF(const std::unique_ptr<vcl::pdf::PDFiumSignature>& pSignature,
+ std::vector<std::pair<size_t, size_t>>& rByteRanges)
+{
+ std::vector<int> aByteRange = pSignature->getByteRange();
+ if (aByteRange.empty())
+ {
+ SAL_WARN("xmlsecurity.helper", "GetByteRangesFromPDF: no byte ranges");
+ return;
+ }
+
+ size_t nByteRangeOffset = 0;
+ for (size_t i = 0; i < aByteRange.size(); ++i)
+ {
+ if (i % 2 == 0)
+ {
+ nByteRangeOffset = aByteRange[i];
+ continue;
+ }
+
+ size_t nLength = aByteRange[i];
+ rByteRanges.emplace_back(nByteRangeOffset, nLength);
+ }
+}
+
+/// Determines the last position that is covered by a signature.
+bool GetEOFOfSignature(const Signature& rSignature, size_t& rEOF)
+{
+ if (rSignature.m_aByteRanges.size() < 2)
+ {
+ return false;
+ }
+
+ rEOF = rSignature.m_aByteRanges[1].first + rSignature.m_aByteRanges[1].second;
+ return true;
+}
+
+/**
+ * Get the value of the "modification detection and prevention" permission:
+ * Valid values are 1, 2 and 3: only 3 allows annotations after signing.
+ */
+int GetMDPPerm(const std::vector<Signature>& rSignatures)
+{
+ int nRet = 3;
+
+ if (rSignatures.empty())
+ {
+ return nRet;
+ }
+
+ for (const auto& rSignature : rSignatures)
+ {
+ int nPerm = rSignature.m_pSignature->getDocMDPPermission();
+ if (nPerm != 0)
+ {
+ return nPerm;
+ }
+ }
+
+ return nRet;
+}
+
+/// Checks if there are unsigned incremental updates between the signatures or after the last one.
+bool IsCompleteSignature(SvStream& rStream, const Signature& rSignature,
+ const std::set<unsigned int>& rSignedEOFs,
+ const std::vector<unsigned int>& rAllEOFs)
+{
+ size_t nSignatureEOF = 0;
+ if (!GetEOFOfSignature(rSignature, nSignatureEOF))
+ {
+ return false;
+ }
+
+ bool bFoundOwn = false;
+ for (const auto& rEOF : rAllEOFs)
+ {
+ if (rEOF == nSignatureEOF)
+ {
+ bFoundOwn = true;
+ continue;
+ }
+
+ if (!bFoundOwn)
+ {
+ continue;
+ }
+
+ if (rSignedEOFs.find(rEOF) == rSignedEOFs.end())
+ {
+ // Unsigned incremental update found.
+ return false;
+ }
+ }
+
+ // Make sure we find the incremental update of the signature itself.
+ if (!bFoundOwn)
+ {
+ return false;
+ }
+
+ // No additional content after the last incremental update.
+ rStream.Seek(STREAM_SEEK_TO_END);
+ size_t nFileEnd = rStream.Tell();
+ return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
+}
+
+/**
+ * Contains checksums of a PDF page, which is rendered without annotations. It also contains
+ * the geometry of a few dangerous annotation types.
+ */
+struct PageChecksum
+{
+ BitmapChecksum m_nPageContent;
+ std::vector<basegfx::B2DRectangle> m_aAnnotations;
+ bool operator==(const PageChecksum& rChecksum) const;
+};
+
+bool PageChecksum::operator==(const PageChecksum& rChecksum) const
+{
+ if (m_nPageContent != rChecksum.m_nPageContent)
+ {
+ return false;
+ }
+
+ return m_aAnnotations == rChecksum.m_aAnnotations;
+}
+
+/// Collects the checksum of each page of one version of the PDF.
+void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<PageChecksum>& rPageChecksums,
+ int nMDPPerm)
+{
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPdfium->openDocument(rStream.GetData(), rStream.GetSize(), OString());
+ if (!pPdfDocument)
+ {
+ return;
+ }
+
+ int nPageCount = pPdfDocument->getPageCount();
+ for (int nPage = 0; nPage < nPageCount; ++nPage)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPage);
+ if (!pPdfPage)
+ {
+ return;
+ }
+
+ PageChecksum aPageChecksum;
+ aPageChecksum.m_nPageContent = pPdfPage->getChecksum(nMDPPerm);
+ for (int i = 0; i < pPdfPage->getAnnotationCount(); ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumAnnotation> pPdfAnnotation = pPdfPage->getAnnotation(i);
+ if (!pPdfAnnotation)
+ {
+ SAL_WARN("xmlsecurity.helper", "Cannot get PDFiumAnnotation");
+ continue;
+ }
+ vcl::pdf::PDFAnnotationSubType eType = pPdfAnnotation->getSubType();
+ switch (eType)
+ {
+ case vcl::pdf::PDFAnnotationSubType::Unknown:
+ case vcl::pdf::PDFAnnotationSubType::FreeText:
+ case vcl::pdf::PDFAnnotationSubType::Stamp:
+ case vcl::pdf::PDFAnnotationSubType::Redact:
+ aPageChecksum.m_aAnnotations.push_back(pPdfAnnotation->getRectangle());
+ break;
+ default:
+ break;
+ }
+ }
+ rPageChecksums.push_back(aPageChecksum);
+ }
+}
+
+/**
+ * Checks if incremental updates after singing performed valid modifications only.
+ * nMDPPerm decides if annotations/commenting is OK, other changes are always not.
+ */
+bool IsValidSignature(SvStream& rStream, const Signature& rSignature, int nMDPPerm)
+{
+ size_t nSignatureEOF = 0;
+ if (!GetEOFOfSignature(rSignature, nSignatureEOF))
+ {
+ return false;
+ }
+
+ SvMemoryStream aSignatureStream;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.Seek(0);
+ aSignatureStream.WriteStream(rStream, nSignatureEOF);
+ rStream.Seek(nPos);
+ aSignatureStream.Seek(0);
+ std::vector<PageChecksum> aSignedPages;
+ AnalyizeSignatureStream(aSignatureStream, aSignedPages, nMDPPerm);
+
+ SvMemoryStream aFullStream;
+ nPos = rStream.Tell();
+ rStream.Seek(0);
+ aFullStream.WriteStream(rStream);
+ rStream.Seek(nPos);
+ aFullStream.Seek(0);
+ std::vector<PageChecksum> aAllPages;
+ AnalyizeSignatureStream(aFullStream, aAllPages, nMDPPerm);
+
+ // Fail if any page looks different after signing and at the end. Annotations/commenting doesn't
+ // count, though.
+ return aSignedPages == aAllPages;
+}
+
+/**
+ * @param rInformation The actual result.
+ * @param rDocument the parsed document to see if the signature is partial.
+ * @return If we can determinate a result.
+ */
+bool ValidateSignature(SvStream& rStream, const Signature& rSignature,
+ SignatureInformation& rInformation, int nMDPPerm,
+ const std::set<unsigned int>& rSignatureEOFs,
+ const std::vector<unsigned int>& rTrailerEnds)
+{
+ std::vector<unsigned char> aContents = rSignature.m_pSignature->getContents();
+ if (aContents.empty())
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: no contents");
+ return false;
+ }
+
+ OString aSubFilter = rSignature.m_pSignature->getSubFilter();
+
+ const bool bNonDetached = aSubFilter == "adbe.pkcs7.sha1";
+ if (aSubFilter.isEmpty()
+ || (aSubFilter != "adbe.pkcs7.detached" && !bNonDetached
+ && aSubFilter != "ETSI.CAdES.detached"))
+ {
+ if (aSubFilter.isEmpty())
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: missing sub-filter");
+ else
+ SAL_WARN("xmlsecurity.helper",
+ "ValidateSignature: unsupported sub-filter: '" << aSubFilter << "'");
+ return false;
+ }
+
+ // Reason / comment / description is optional.
+ rInformation.ouDescription = rSignature.m_pSignature->getReason();
+
+ // Date: used only when the time of signing is not available in the
+ // signature.
+ rInformation.stDateTime = rSignature.m_pSignature->getTime();
+
+ // Detect if the byte ranges don't cover everything, but the signature itself.
+ if (rSignature.m_aByteRanges.size() < 2)
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: expected 2 byte ranges");
+ return false;
+ }
+ if (rSignature.m_aByteRanges[0].first != 0)
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: first range start is not 0");
+ return false;
+ }
+ // Binary vs hex dump and 2 is the leading "<" and the trailing ">" around the hex string.
+ size_t nSignatureLength = aContents.size() * 2 + 2;
+ if (rSignature.m_aByteRanges[1].first
+ != (rSignature.m_aByteRanges[0].second + nSignatureLength))
+ {
+ SAL_WARN("xmlsecurity.helper",
+ "ValidateSignature: second range start is not the end of the signature");
+ return false;
+ }
+ rInformation.bPartialDocumentSignature
+ = !IsCompleteSignature(rStream, rSignature, rSignatureEOFs, rTrailerEnds);
+ if (!IsValidSignature(rStream, rSignature, nMDPPerm))
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: invalid incremental update detected");
+ return false;
+ }
+
+ // At this point there is no obviously missing info to validate the
+ // signature.
+ return svl::crypto::Signing::Verify(rStream, rSignature.m_aByteRanges, bNonDetached, aContents,
+ rInformation);
+}
+}
+
+PDFSignatureHelper::PDFSignatureHelper() = default;
+
+bool PDFSignatureHelper::ReadAndVerifySignature(
+ const uno::Reference<io::XInputStream>& xInputStream)
+{
+ if (!xInputStream.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "input stream missing");
+ return false;
+ }
+
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ return ReadAndVerifySignatureSvStream(*pStream);
+}
+
+bool PDFSignatureHelper::ReadAndVerifySignatureSvStream(SvStream& rStream)
+{
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ {
+ return true;
+ }
+
+ SvMemoryStream aStream;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.Seek(0);
+ aStream.WriteStream(rStream);
+ rStream.Seek(nPos);
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPdfium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
+ if (!pPdfDocument)
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to read the document");
+ return false;
+ }
+
+ int nSignatureCount = pPdfDocument->getSignatureCount();
+ if (nSignatureCount <= 0)
+ {
+ return true;
+ }
+ std::vector<Signature> aSignatures(nSignatureCount);
+ for (int i = 0; i < nSignatureCount; ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumSignature> pSignature = pPdfDocument->getSignature(i);
+ std::vector<std::pair<size_t, size_t>> aByteRanges;
+ GetByteRangesFromPDF(pSignature, aByteRanges);
+ aSignatures[i] = Signature{ std::move(pSignature), aByteRanges };
+ }
+
+ std::set<unsigned int> aSignatureEOFs;
+ for (const auto& rSignature : aSignatures)
+ {
+ size_t nEOF = 0;
+ if (GetEOFOfSignature(rSignature, nEOF))
+ {
+ aSignatureEOFs.insert(nEOF);
+ }
+ }
+
+ std::vector<unsigned int> aTrailerEnds = pPdfDocument->getTrailerEnds();
+
+ m_aSignatureInfos.clear();
+
+ int nMDPPerm = GetMDPPerm(aSignatures);
+
+ for (size_t i = 0; i < aSignatures.size(); ++i)
+ {
+ SignatureInformation aInfo(i);
+
+ if (!ValidateSignature(rStream, aSignatures[i], aInfo, nMDPPerm, aSignatureEOFs,
+ aTrailerEnds))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
+ }
+
+ m_aSignatureInfos.push_back(aInfo);
+ }
+
+ return true;
+}
+
+SignatureInformations const& PDFSignatureHelper::GetSignatureInformations() const
+{
+ return m_aSignatureInfos;
+}
+
+uno::Sequence<security::DocumentSignatureInformation>
+PDFSignatureHelper::GetDocumentSignatureInformations(
+ const uno::Reference<xml::crypto::XSecurityEnvironment>& xSecEnv) const
+{
+ uno::Sequence<security::DocumentSignatureInformation> aRet(m_aSignatureInfos.size());
+ auto aRetRange = asNonConstRange(aRet);
+
+ for (size_t i = 0; i < m_aSignatureInfos.size(); ++i)
+ {
+ const SignatureInformation& rInternal = m_aSignatureInfos[i];
+ security::DocumentSignatureInformation& rExternal = aRetRange[i];
+ rExternal.SignatureIsValid
+ = rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+ if (rInternal.GetSigningCertificate()
+ && !rInternal.GetSigningCertificate()->X509Certificate.isEmpty())
+ {
+ rExternal.Signer = xSecEnv->createCertificateFromAscii(
+ rInternal.GetSigningCertificate()->X509Certificate);
+ }
+ rExternal.PartialDocumentSignature = rInternal.bPartialDocumentSignature;
+
+ // Verify certificate.
+ if (rExternal.Signer.is())
+ {
+ try
+ {
+ rExternal.CertificateStatus = xSecEnv->verifyCertificate(rExternal.Signer, {});
+ }
+ catch (const uno::SecurityException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper", "failed to verify certificate");
+ rExternal.CertificateStatus = security::CertificateValidity::INVALID;
+ }
+ }
+ else
+ rExternal.CertificateStatus = security::CertificateValidity::INVALID;
+ }
+
+ return aRet;
+}
+
+sal_Int32 PDFSignatureHelper::GetNewSecurityId() const { return m_aSignatureInfos.size(); }
+
+void PDFSignatureHelper::SetX509Certificate(
+ const uno::Reference<security::XCertificate>& xCertificate)
+{
+ m_xCertificate = xCertificate;
+}
+
+void PDFSignatureHelper::SetDescription(const OUString& rDescription)
+{
+ m_aDescription = rDescription;
+}
+
+bool PDFSignatureHelper::Sign(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<io::XInputStream>& xInputStream, bool bAdES)
+{
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ vcl::filter::PDFDocument aDocument;
+ if (!aDocument.Read(*pStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to read the document");
+ return false;
+ }
+
+ sal_Int32 nPage = 0;
+ std::vector<sal_Int8> aSignatureLineShape;
+ GetSignatureLineShape(xModel, nPage, aSignatureLineShape);
+ if (nPage > 0)
+ {
+ // UNO page number is 1-based.
+ aDocument.SetSignaturePage(nPage - 1);
+ }
+ if (!aSignatureLineShape.empty())
+ {
+ aDocument.SetSignatureLine(std::move(aSignatureLineShape));
+ }
+
+ if (!aDocument.Sign(m_xCertificate, m_aDescription, bAdES))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to sign");
+ return false;
+ }
+
+ uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
+ if (!aDocument.Write(*pOutStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to write signed data");
+ return false;
+ }
+
+ return true;
+}
+
+bool PDFSignatureHelper::RemoveSignature(const uno::Reference<io::XInputStream>& xInputStream,
+ sal_uInt16 nPosition)
+{
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ vcl::filter::PDFDocument aDocument;
+ if (!aDocument.Read(*pStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to read the document");
+ return false;
+ }
+
+ if (!aDocument.RemoveSignature(nPosition))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to remove signature");
+ return false;
+ }
+
+ uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
+ uno::Reference<io::XTruncate> xTruncate(xStream, uno::UNO_QUERY);
+ if (!xTruncate.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to truncate");
+ return false;
+ }
+ xTruncate->truncate();
+ std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
+ if (!aDocument.Write(*pOutStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to write without signature");
+ return false;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx
new file mode 100644
index 000000000..0d9755d0e
--- /dev/null
+++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx
@@ -0,0 +1,714 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <xmlsignaturehelper.hxx>
+#include <documentsignaturehelper.hxx>
+#include <xsecctl.hxx>
+#include <biginteger.hxx>
+
+#include <UriBindingHelper.hxx>
+
+#include <tools/datetime.hxx>
+
+#include <xmloff/attrlist.hxx>
+
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include <optional>
+
+constexpr OUStringLiteral NS_DOCUMENTSIGNATURES = u"http://openoffice.org/2004/documentsignatures";
+constexpr OUStringLiteral NS_DOCUMENTSIGNATURES_ODF_1_2 = u"urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0";
+constexpr OUStringLiteral OOXML_SIGNATURE_ORIGIN = u"http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
+constexpr OUStringLiteral OOXML_SIGNATURE_SIGNATURE = u"http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::graphic;
+using namespace ::com::sun::star::uno;
+
+XMLSignatureHelper::XMLSignatureHelper( const uno::Reference< uno::XComponentContext >& rxCtx)
+ : mxCtx(rxCtx), mbODFPre1_2(false)
+{
+ mpXSecController = new XSecController(rxCtx);
+ mbError = false;
+}
+
+XMLSignatureHelper::~XMLSignatureHelper()
+{
+}
+
+void XMLSignatureHelper::SetStorage(
+ const Reference < css::embed::XStorage >& rxStorage,
+ const OUString& sODFVersion)
+{
+ SAL_WARN_IF( mxUriBinding.is(), "xmlsecurity.helper", "SetStorage - UriBinding already set!" );
+ mxUriBinding = new UriBindingHelper( rxStorage );
+ SAL_WARN_IF(!rxStorage.is(), "xmlsecurity.helper", "SetStorage - empty storage!");
+ mbODFPre1_2 = DocumentSignatureHelper::isODFPre_1_2(sODFVersion);
+}
+
+
+void XMLSignatureHelper::SetStartVerifySignatureHdl( const Link<LinkParamNone*,bool>& rLink )
+{
+ maStartVerifySignatureHdl = rLink;
+}
+
+
+void XMLSignatureHelper::StartMission(const uno::Reference<xml::crypto::XXMLSecurityContext>& xSecurityContext)
+{
+ if ( !mxUriBinding.is() )
+ mxUriBinding = new UriBindingHelper();
+
+ mpXSecController->startMission(mxUriBinding, xSecurityContext);
+}
+
+void XMLSignatureHelper::EndMission()
+{
+ mpXSecController->endMission();
+}
+
+sal_Int32 XMLSignatureHelper::GetNewSecurityId()
+{
+ return mpXSecController->getNewSecurityId();
+}
+
+void XMLSignatureHelper::SetX509Certificate(
+ sal_Int32 nSecurityId,
+ const OUString& ouX509IssuerName,
+ const OUString& ouX509SerialNumber,
+ const OUString& ouX509Cert,
+ const OUString& ouX509CertDigest,
+ svl::crypto::SignatureMethodAlgorithm eAlgorithmID)
+{
+ mpXSecController->setX509Certificate(
+ nSecurityId,
+ ouX509IssuerName,
+ ouX509SerialNumber,
+ ouX509Cert,
+ ouX509CertDigest,
+ eAlgorithmID);
+}
+
+void XMLSignatureHelper::AddEncapsulatedX509Certificate(const OUString& ouEncapsulatedX509Certificate)
+{
+ mpXSecController->addEncapsulatedX509Certificate(ouEncapsulatedX509Certificate);
+}
+
+void XMLSignatureHelper::SetGpgCertificate(sal_Int32 nSecurityId,
+ const OUString& ouGpgCertDigest,
+ const OUString& ouGpgCert,
+ const OUString& ouGpgOwner)
+{
+ mpXSecController->setGpgCertificate(
+ nSecurityId,
+ ouGpgCertDigest,
+ ouGpgCert,
+ ouGpgOwner);
+}
+
+void XMLSignatureHelper::SetDateTime( sal_Int32 nSecurityId, const ::DateTime& rDateTime )
+{
+ css::util::DateTime stDateTime = rDateTime.GetUNODateTime();
+ mpXSecController->setDate( nSecurityId, stDateTime );
+}
+
+void XMLSignatureHelper::SetDescription(sal_Int32 nSecurityId, const OUString& rDescription)
+{
+ mpXSecController->setDescription(nSecurityId, rDescription);
+}
+
+void XMLSignatureHelper::SetSignatureLineId(sal_Int32 nSecurityId, const OUString& rSignatureLineId)
+{
+ mpXSecController->setSignatureLineId(nSecurityId, rSignatureLineId);
+}
+
+void XMLSignatureHelper::SetSignatureLineValidGraphic(
+ sal_Int32 nSecurityId, const css::uno::Reference<XGraphic>& xValidGraphic)
+{
+ mpXSecController->setSignatureLineValidGraphic(nSecurityId, xValidGraphic);
+}
+
+void XMLSignatureHelper::SetSignatureLineInvalidGraphic(
+ sal_Int32 nSecurityId, const css::uno::Reference<XGraphic>& xInvalidGraphic)
+{
+ mpXSecController->setSignatureLineInvalidGraphic(nSecurityId, xInvalidGraphic);
+}
+
+void XMLSignatureHelper::AddForSigning( sal_Int32 nSecurityId, const OUString& uri, bool bBinary, bool bXAdESCompliantIfODF )
+{
+ mpXSecController->signAStream( nSecurityId, uri, bBinary, bXAdESCompliantIfODF );
+}
+
+
+uno::Reference<xml::sax::XWriter> XMLSignatureHelper::CreateDocumentHandlerWithHeader(
+ const css::uno::Reference< css::io::XOutputStream >& xOutputStream )
+{
+ /*
+ * get SAX writer component
+ */
+ uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(mxCtx);
+
+ /*
+ * connect XML writer to output stream
+ */
+ xSaxWriter->setOutputStream( xOutputStream );
+
+ /*
+ * write the xml context for signatures
+ */
+ rtl::Reference<SvXMLAttributeList> pAttributeList = new SvXMLAttributeList();
+ OUString sNamespace;
+ if (mbODFPre1_2)
+ sNamespace = NS_DOCUMENTSIGNATURES;
+ else
+ sNamespace = NS_DOCUMENTSIGNATURES_ODF_1_2;
+
+ pAttributeList->AddAttribute(
+ "xmlns",
+ sNamespace);
+
+ xSaxWriter->startDocument();
+ xSaxWriter->startElement(
+ "document-signatures",
+ pAttributeList);
+
+ return xSaxWriter;
+}
+
+void XMLSignatureHelper::CloseDocumentHandler( const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler )
+{
+ xDocumentHandler->endElement( "document-signatures" );
+ xDocumentHandler->endDocument();
+}
+
+void XMLSignatureHelper::ExportSignature(
+ const uno::Reference< xml::sax::XDocumentHandler >& xDocumentHandler,
+ const SignatureInformation& signatureInfo,
+ bool bXAdESCompliantIfODF )
+{
+ XSecController::exportSignature(xDocumentHandler, signatureInfo, bXAdESCompliantIfODF);
+}
+
+void XMLSignatureHelper::ExportOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<embed::XStorage>& xSignatureStorage, const SignatureInformation& rInformation, int nSignatureIndex)
+{
+ uno::Reference<io::XOutputStream> xOutputStream(xSignatureStorage->openStreamElement("sig" + OUString::number(nSignatureIndex) + ".xml", embed::ElementModes::READWRITE), uno::UNO_QUERY);
+
+ if (rInformation.aSignatureBytes.hasElements())
+ // This is a signature roundtrip, just write back the signature as-is.
+ xOutputStream->writeBytes(rInformation.aSignatureBytes);
+ else
+ {
+ uno::Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(mxCtx);
+ xSaxWriter->setOutputStream(xOutputStream);
+ xSaxWriter->startDocument();
+
+ mpXSecController->exportOOXMLSignature(xRootStorage, xSaxWriter, rInformation);
+
+ xSaxWriter->endDocument();
+ }
+}
+
+void XMLSignatureHelper::CreateAndWriteSignature( const uno::Reference< xml::sax::XDocumentHandler >& xDocumentHandler, bool bXAdESCompliantIfODF )
+{
+ mbError = false;
+
+ if ( !mpXSecController->WriteSignature( xDocumentHandler, bXAdESCompliantIfODF ) )
+ {
+ mbError = true;
+ }
+}
+
+bool XMLSignatureHelper::ReadAndVerifySignature( const css::uno::Reference< css::io::XInputStream >& xInputStream )
+{
+ mbError = false;
+
+ SAL_WARN_IF(!xInputStream.is(), "xmlsecurity.helper", "input stream missing");
+
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // get SAX parser component
+ uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(mxCtx);
+
+ // create a signature reader
+ uno::Reference< xml::sax::XDocumentHandler > xHandler
+ = mpXSecController->createSignatureReader(*this);
+
+ // setup the connection:
+ // Parser -> SignatureReader
+ xParser->setDocumentHandler( xHandler );
+
+ // Parse the stream.
+ try
+ {
+ xParser->parseStream( aParserInput );
+ }
+ catch( uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper");
+ mbError = true;
+ }
+
+ // release the signature reader
+ mpXSecController->releaseSignatureReader( );
+
+ return !mbError;
+}
+
+SignatureInformation XMLSignatureHelper::GetSignatureInformation( sal_Int32 nSecurityId ) const
+{
+ return mpXSecController->getSignatureInformation( nSecurityId );
+}
+
+SignatureInformations XMLSignatureHelper::GetSignatureInformations() const
+{
+ return mpXSecController->getSignatureInformations();
+}
+
+void XMLSignatureHelper::StartVerifySignatureElement()
+{
+ if ( !maStartVerifySignatureHdl.IsSet() || maStartVerifySignatureHdl.Call(nullptr) )
+ {
+ sal_Int32 nSignatureId = mpXSecController->getNewSecurityId();
+ mpXSecController->addSignature( nSignatureId );
+ }
+}
+
+namespace
+{
+bool lcl_isSignatureType(const beans::StringPair& rPair)
+{
+ return rPair.First == "Type" && rPair.Second == OOXML_SIGNATURE_SIGNATURE;
+}
+bool lcl_isSignatureOriginType(const beans::StringPair& rPair)
+{
+ return rPair.First == "Type" && rPair.Second == OOXML_SIGNATURE_ORIGIN;
+}
+}
+
+bool XMLSignatureHelper::ReadAndVerifySignatureStorage(const uno::Reference<embed::XStorage>& xStorage, bool bCacheLastSignature)
+{
+ sal_Int32 nOpenMode = embed::ElementModes::READ;
+ if (xStorage.is() && !xStorage->hasByName("_rels"))
+ {
+ SAL_WARN("xmlsecurity.helper", "expected stream, in signature storage but not found: _rels");
+ return false;
+ }
+
+ uno::Reference<embed::XStorage> xSubStorage = xStorage->openStorageElement("_rels", nOpenMode);
+ uno::Reference<io::XInputStream> xRelStream(xSubStorage->openStreamElement("origin.sigs.rels", nOpenMode), uno::UNO_QUERY);
+ uno::Sequence< uno::Sequence<beans::StringPair> > aRelationsInfo = comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream, u"origin.sigs.rels", mxCtx);
+
+ for (sal_Int32 i = 0; i < aRelationsInfo.getLength(); ++i)
+ {
+ const uno::Sequence<beans::StringPair>& rRelation = aRelationsInfo[i];
+ if (std::any_of(rRelation.begin(), rRelation.end(), lcl_isSignatureType))
+ {
+ auto it = std::find_if(rRelation.begin(), rRelation.end(), [](const beans::StringPair& rPair) { return rPair.First == "Target"; });
+ if (it != rRelation.end())
+ {
+ if (xStorage.is() && !xStorage->hasByName(it->Second))
+ {
+ SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << it->Second);
+ continue;
+ }
+
+ uno::Reference<io::XInputStream> xInputStream(xStorage->openStreamElement(it->Second, nOpenMode), uno::UNO_QUERY);
+ if (!ReadAndVerifySignatureStorageStream(xInputStream))
+ return false;
+
+ // By default, we cache. If it's requested, then we don't cache the last signature.
+ bool bCache = true;
+ if (!bCacheLastSignature && i == aRelationsInfo.getLength() - 1)
+ bCache = false;
+
+ if (!bCache)
+ continue;
+ // Store the contents of the stream as is, in case we need to write it back later.
+ xInputStream.clear();
+ xInputStream.set(xStorage->openStreamElement(it->Second, nOpenMode), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropertySet(xInputStream, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ continue;
+
+ sal_Int64 nSize = 0;
+ xPropertySet->getPropertyValue("Size") >>= nSize;
+ if (nSize < 0 || nSize > SAL_MAX_INT32)
+ {
+ SAL_WARN("xmlsecurity.helper", "bogus signature size: " << nSize);
+ continue;
+ }
+ uno::Sequence<sal_Int8> aData;
+ xInputStream->readBytes(aData, nSize);
+ mpXSecController->setSignatureBytes(aData);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool XMLSignatureHelper::ReadAndVerifySignatureStorageStream(const css::uno::Reference<css::io::XInputStream>& xInputStream)
+{
+ mbError = false;
+
+ // Create the input source.
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // Create the sax parser.
+ uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(mxCtx);
+
+ // Create the signature reader.
+ uno::Reference<xml::sax::XDocumentHandler> xHandler = mpXSecController->createSignatureReader(*this, embed::StorageFormats::OFOPXML);
+
+ // Parser -> signature reader.
+ xParser->setDocumentHandler(xHandler);
+
+ // Parse the stream.
+ try
+ {
+ xParser->parseStream(aParserInput);
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper");
+ mbError = true;
+ }
+
+ // release the signature reader
+ mpXSecController->releaseSignatureReader();
+
+ return !mbError;
+}
+
+void XMLSignatureHelper::EnsureSignaturesRelation(const css::uno::Reference<css::embed::XStorage>& xStorage, bool bAdd)
+{
+ sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
+ uno::Reference<embed::XStorage> xSubStorage = xStorage->openStorageElement("_rels", nOpenMode);
+ uno::Reference<io::XInputStream> xRelStream(xSubStorage->openStreamElement(".rels", nOpenMode), uno::UNO_QUERY);
+ std::vector< uno::Sequence<beans::StringPair> > aRelationsInfo = comphelper::sequenceToContainer< std::vector< uno::Sequence<beans::StringPair> > >(comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream, u".rels", mxCtx));
+
+ // Do we have a relation already?
+ bool bHaveRelation = false;
+ int nCount = 0;
+ for (const uno::Sequence<beans::StringPair>& rRelation : aRelationsInfo)
+ {
+ auto aRelation = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(rRelation);
+ if (std::any_of(aRelation.begin(), aRelation.end(), lcl_isSignatureOriginType))
+ {
+ bHaveRelation = true;
+ break;
+ }
+ ++nCount;
+ }
+
+ if (!bHaveRelation && bAdd)
+ {
+ // No, and have to add one.
+ std::vector<beans::StringPair> aRelation;
+ aRelation.emplace_back("Id", "rId" + OUString::number(++nCount));
+ aRelation.emplace_back("Type", OOXML_SIGNATURE_ORIGIN);
+ aRelation.emplace_back("Target", "_xmlsignatures/origin.sigs");
+ aRelationsInfo.push_back(comphelper::containerToSequence(aRelation));
+ }
+ else if (bHaveRelation && !bAdd)
+ {
+ // Yes, and need to remove it.
+ for (std::vector< uno::Sequence<beans::StringPair> >::iterator it = aRelationsInfo.begin(); it != aRelationsInfo.end();)
+ {
+ auto aRelation = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(*it);
+ if (std::any_of(aRelation.begin(), aRelation.end(), lcl_isSignatureOriginType))
+ it = aRelationsInfo.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ // Write it back.
+ uno::Reference<io::XTruncate> xTruncate(xRelStream, uno::UNO_QUERY);
+ xTruncate->truncate();
+ uno::Reference<io::XOutputStream> xOutputStream(xRelStream, uno::UNO_QUERY);
+ comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xOutputStream, comphelper::containerToSequence(aRelationsInfo), mxCtx);
+
+ // Commit it.
+ uno::Reference<embed::XTransactedObject> xTransact(xSubStorage, uno::UNO_QUERY);
+ xTransact->commit();
+ xTransact.set(xStorage, uno::UNO_QUERY);
+ xTransact->commit();
+}
+
+void XMLSignatureHelper::ExportSignatureRelations(const css::uno::Reference<css::embed::XStorage>& xStorage, int nSignatureCount)
+{
+ // Write the empty file, its relations will be the signatures.
+ sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
+ uno::Reference<io::XOutputStream> xOriginStream(xStorage->openStreamElement("origin.sigs", nOpenMode), uno::UNO_QUERY);
+ uno::Reference<io::XTruncate> xTruncate(xOriginStream, uno::UNO_QUERY);
+ xTruncate->truncate();
+ xOriginStream->closeOutput();
+
+ // Write the relations.
+ uno::Reference<embed::XStorage> xSubStorage = xStorage->openStorageElement("_rels", nOpenMode);
+ uno::Reference<io::XOutputStream> xRelStream(xSubStorage->openStreamElement("origin.sigs.rels", nOpenMode), uno::UNO_QUERY);
+ std::vector< uno::Sequence<beans::StringPair> > aRelations;
+ for (int i = 0; i < nSignatureCount; ++i)
+ {
+ std::vector<beans::StringPair> aRelation;
+ aRelation.emplace_back("Id", "rId" + OUString::number(i + 1));
+ aRelation.emplace_back("Type", OOXML_SIGNATURE_SIGNATURE);
+ aRelation.emplace_back("Target", "sig" + OUString::number(i + 1) + ".xml");
+ aRelations.push_back(comphelper::containerToSequence(aRelation));
+ }
+ comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xRelStream, comphelper::containerToSequence(aRelations), mxCtx);
+ uno::Reference<embed::XTransactedObject> xTransact(xSubStorage, uno::UNO_QUERY);
+ xTransact->commit();
+}
+
+void XMLSignatureHelper::ExportSignatureContentTypes(const css::uno::Reference<css::embed::XStorage>& xStorage, int nSignatureCount)
+{
+ uno::Reference<io::XStream> xStream = xStorage->openStreamElement("[Content_Types].xml", embed::ElementModes::READWRITE);
+ uno::Reference<io::XInputStream> xInputStream = xStream->getInputStream();
+ uno::Sequence< uno::Sequence<beans::StringPair> > aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, mxCtx);
+ if (aContentTypeInfo.getLength() < 2)
+ {
+ SAL_WARN("xmlsecurity.helper", "no defaults or overrides in aContentTypeInfo");
+ return;
+ }
+ auto pContentTypeInfo = aContentTypeInfo.getArray();
+
+ // Append rels and sigs to defaults, if it's not there already.
+ uno::Sequence<beans::StringPair>& rDefaults = pContentTypeInfo[0];
+ auto aDefaults = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(rDefaults);
+ if (std::none_of(std::cbegin(rDefaults), std::cend(rDefaults), [](const beans::StringPair& rPair) { return rPair.First == "rels"; }))
+ aDefaults.emplace_back("rels", "application/vnd.openxmlformats-package.relationships+xml");
+
+ if (std::none_of(std::cbegin(rDefaults), std::cend(rDefaults), [](const beans::StringPair& rPair) { return rPair.First == "sigs"; }))
+ aDefaults.emplace_back("sigs", "application/vnd.openxmlformats-package.digital-signature-origin");
+ rDefaults = comphelper::containerToSequence(aDefaults);
+
+ // Remove existing signature overrides.
+ uno::Sequence<beans::StringPair>& rOverrides = pContentTypeInfo[1];
+ auto aOverrides = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(rOverrides);
+ aOverrides.erase(std::remove_if(aOverrides.begin(), aOverrides.end(), [](const beans::StringPair& rPair)
+ {
+ return rPair.First.startsWith("/_xmlsignatures/sig");
+ }), aOverrides.end());
+
+ // Add our signature overrides.
+ for (int i = 1; i <= nSignatureCount; ++i)
+ aOverrides.emplace_back("/_xmlsignatures/sig" + OUString::number(i) + ".xml", "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml");
+
+ rOverrides = comphelper::containerToSequence(aOverrides);
+ uno::Reference<io::XOutputStream> xOutputStream = xStream->getOutputStream();
+ uno::Reference <io::XTruncate> xTruncate(xOutputStream, uno::UNO_QUERY);
+ xTruncate->truncate();
+ comphelper::OFOPXMLHelper::WriteContentSequence(xOutputStream, rDefaults, rOverrides, mxCtx);
+ uno::Reference<embed::XTransactedObject> xTransact(xStorage, uno::UNO_QUERY);
+ xTransact->commit();
+}
+void XMLSignatureHelper::CreateAndWriteOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<embed::XStorage>& xSignatureStorage, int nSignatureIndex)
+{
+ uno::Reference<io::XOutputStream> xOutputStream(xSignatureStorage->openStreamElement("sig" + OUString::number(nSignatureIndex) + ".xml", embed::ElementModes::READWRITE), uno::UNO_QUERY);
+ uno::Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(mxCtx);
+ xSaxWriter->setOutputStream(xOutputStream);
+ xSaxWriter->startDocument();
+
+ mbError = false;
+ if (!mpXSecController->WriteOOXMLSignature(xRootStorage, xSaxWriter))
+ mbError = true;
+
+ xSaxWriter->endDocument();
+}
+
+/** check this constraint from xmldsig-core 4.5.4:
+
+ All certificates appearing in an X509Data element MUST relate to the
+ validation key by either containing it or being part of a certification
+ chain that terminates in a certificate containing the validation key.
+ */
+static auto CheckX509Data(
+ uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
+ std::vector<SignatureInformation::X509CertInfo> const& rX509CertInfos,
+ std::vector<uno::Reference<security::XCertificate>> & rCerts,
+ std::vector<SignatureInformation::X509CertInfo> & rSorted) -> bool
+{
+ assert(rCerts.empty());
+ assert(rSorted.empty());
+ if (rX509CertInfos.empty())
+ {
+ SAL_WARN("xmlsecurity.comp", "no X509Data");
+ return false;
+ }
+ std::vector<uno::Reference<security::XCertificate>> certs;
+ for (SignatureInformation::X509CertInfo const& it : rX509CertInfos)
+ {
+ if (!it.X509Certificate.isEmpty())
+ {
+ certs.emplace_back(xSecEnv->createCertificateFromAscii(it.X509Certificate));
+ }
+ else
+ {
+ certs.emplace_back(xSecEnv->getCertificate(
+ it.X509IssuerName,
+ xmlsecurity::numericStringToBigInteger(it.X509SerialNumber)));
+ }
+ if (!certs.back().is())
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data cannot be parsed");
+ return false;
+ }
+ }
+
+ // first, search one whose issuer isn't in the list, or a self-signed one
+ std::optional<size_t> start;
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ for (size_t j = 0; ; ++j)
+ {
+ if (j == certs.size())
+ {
+ if (start)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate has no issuer but already have start of chain: " << certs[i]->getSubjectName());
+ return false;
+ }
+ start = i; // issuer isn't in the list
+ break;
+ }
+ if (xmlsecurity::EqualDistinguishedNames(certs[i]->getIssuerName(), certs[j]->getSubjectName(), xmlsecurity::NOCOMPAT))
+ {
+ if (i == j) // self signed
+ {
+ if (start)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate is self-signed but already have start of chain: " << certs[i]->getSubjectName());
+ return false;
+ }
+ start = i;
+ }
+ break;
+ }
+ }
+ }
+ std::vector<size_t> chain;
+ if (!start)
+ {
+ // this can only be a cycle?
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: cycle detected");
+ return false;
+ }
+ chain.emplace_back(*start);
+
+ // second, check that there is a chain, no tree or cycle...
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ assert(chain.size() == i + 1);
+ for (size_t j = 0; j < certs.size(); ++j)
+ {
+ if (chain[i] != j)
+ {
+ if (xmlsecurity::EqualDistinguishedNames(
+ certs[chain[i]]->getSubjectName(), certs[j]->getIssuerName(), xmlsecurity::NOCOMPAT))
+ {
+ if (chain.size() != i + 1) // already found issue?
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 2 others: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ chain.emplace_back(j);
+ }
+ }
+ }
+ if (i == certs.size() - 1)
+ { // last one: must be a leaf
+ if (chain.size() != i + 1)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate in cycle: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ }
+ else if (chain.size() != i + 2)
+ { // not issuer of another?
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 0 others: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ }
+
+ // success
+ assert(chain.size() == rX509CertInfos.size());
+ for (auto const& it : chain)
+ {
+ rSorted.emplace_back(rX509CertInfos[it]);
+ rCerts.emplace_back(certs[it]);
+ }
+ return true;
+}
+
+std::vector<uno::Reference<security::XCertificate>>
+XMLSignatureHelper::CheckAndUpdateSignatureInformation(
+ uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
+ SignatureInformation const& rInfo)
+{
+ // if the check fails, it's not possible to determine which X509Data
+ // contained the signing certificate - the UI cannot display something
+ // useful in this case, so prevent anything misleading by clearing the
+ // X509Datas.
+
+ std::vector<uno::Reference<security::XCertificate>> certs;
+ std::vector<SignatureInformation::X509Data> datas;
+ // TODO: for now, just merge all X509Datas together for checking...
+ // (this will probably break round-trip of signature with multiple X509Data,
+ // no idea if that is a problem)
+ SignatureInformation::X509Data temp;
+ SignatureInformation::X509Data tempResult;
+ for (auto const& rData : rInfo.X509Datas)
+ {
+ for (auto const& it : rData)
+ {
+ temp.emplace_back(it);
+ }
+ }
+ if (CheckX509Data(xSecEnv, temp, certs, tempResult))
+ {
+ datas.emplace_back(tempResult);
+ }
+
+ // rInfo is a copy, update the original
+ mpXSecController->UpdateSignatureInformation(rInfo.nSecurityId, std::move(datas));
+ return certs;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecctl.cxx b/xmlsecurity/source/helper/xsecctl.cxx
new file mode 100644
index 000000000..5a9bf57ec
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecctl.cxx
@@ -0,0 +1,1004 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_gpgme.h>
+
+#include <xsecctl.hxx>
+#include <documentsignaturehelper.hxx>
+#include <framework/saxeventkeeperimpl.hxx>
+#include <xmlsec/xmldocumentwrapper_xmlsecimpl.hxx>
+#if HAVE_FEATURE_GPGME
+# include <gpg/xmlsignature_gpgimpl.hxx>
+#endif
+
+#include <com/sun/star/xml/crypto/sax/XMissionTaker.hpp>
+#include <com/sun/star/xml/crypto/SecurityOperationStatus.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/sax/XParser.hpp>
+#include <com/sun/star/xml/crypto/XXMLSignature.hpp>
+
+#include <xmloff/attrlist.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <unotools/datetime.hxx>
+#include "ooxmlsecexporter.hxx"
+#include <UriBindingHelper.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+OUString getDigestURI(sal_Int32 nID)
+{
+ switch( nID )
+ {
+ case css::xml::crypto::DigestID::SHA1:
+ return ALGO_XMLDSIGSHA1;
+ case css::xml::crypto::DigestID::SHA256:
+ return ALGO_XMLDSIGSHA256;
+ case css::xml::crypto::DigestID::SHA512:
+ return ALGO_XMLDSIGSHA512;
+ default:
+ return ALGO_XMLDSIGSHA1;
+ }
+}
+OUString getSignatureURI(svl::crypto::SignatureMethodAlgorithm eAlgorithm, sal_Int32 nDigestID)
+{
+ OUString aRet;
+
+ if (eAlgorithm == svl::crypto::SignatureMethodAlgorithm::ECDSA)
+ {
+ switch (nDigestID)
+ {
+ case css::xml::crypto::DigestID::SHA1:
+ aRet = ALGO_ECDSASHA1;
+ break;
+ case css::xml::crypto::DigestID::SHA256:
+ aRet = ALGO_ECDSASHA256;
+ break;
+ case css::xml::crypto::DigestID::SHA512:
+ aRet = ALGO_ECDSASHA512;
+ break;
+ default:
+ aRet = ALGO_ECDSASHA1;
+ break;
+ }
+ }
+ if (!aRet.isEmpty())
+ return aRet;
+
+ switch (nDigestID)
+ {
+ case css::xml::crypto::DigestID::SHA1:
+ return ALGO_RSASHA1;
+ case css::xml::crypto::DigestID::SHA256:
+ return ALGO_RSASHA256;
+ case css::xml::crypto::DigestID::SHA512:
+ return ALGO_RSASHA512;
+ default:
+ return ALGO_RSASHA1;
+ }
+}
+}
+
+XSecController::XSecController( const css::uno::Reference<css::uno::XComponentContext>& rxCtx )
+ : mxCtx(rxCtx)
+ , m_nNextSecurityId(1)
+ , m_bIsPreviousNodeInitializable(false)
+ , m_bIsSAXEventKeeperConnected(false)
+ , m_bIsCollectingElement(false)
+ , m_bIsBlocking(false)
+ , m_eStatusOfSecurityComponents(InitializationState::UNINITIALIZED)
+ , m_bIsSAXEventKeeperSticky(false)
+ , m_nReservedSignatureId(0)
+ , m_bVerifyCurrentSignature(false)
+{
+}
+
+XSecController::~XSecController()
+{
+}
+
+
+/*
+ * private methods
+ */
+int XSecController::findSignatureInfor( sal_Int32 nSecurityId) const
+/****** XSecController/findSignatureInfor *************************************
+ *
+ * NAME
+ * findSignatureInfor -- find SignatureInformation struct for a particular
+ * signature
+ *
+ * SYNOPSIS
+ * index = findSignatureInfor( nSecurityId );
+ *
+ * INPUTS
+ * nSecurityId - the signature's id
+ *
+ * RESULT
+ * index - the index of the signature, or -1 when no such signature
+ * existing
+ ******************************************************************************/
+{
+ int i;
+ int size = m_vInternalSignatureInformations.size();
+
+ for (i=0; i<size; ++i)
+ {
+ if (m_vInternalSignatureInformations[i].signatureInfor.nSecurityId == nSecurityId)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void XSecController::createXSecComponent( )
+/****** XSecController/createXSecComponent ************************************
+ *
+ * NAME
+ * bResult = createXSecComponent -- creates xml security components
+ *
+ * FUNCTION
+ * Creates xml security components, including:
+ * 1. an xml signature bridge component
+ * 2. an XMLDocumentWrapper component
+ * 3. a SAXEventKeeper component
+ ******************************************************************************/
+{
+ /*
+ * marks all security components are not available.
+ */
+ m_eStatusOfSecurityComponents = InitializationState::FAILTOINITIALIZED;
+ m_xXMLSignature = nullptr;
+ m_xXMLDocumentWrapper = nullptr;
+ m_xSAXEventKeeper = nullptr;
+
+ css::uno::Reference< css::lang::XMultiComponentFactory > xMCF( mxCtx->getServiceManager() );
+
+#if HAVE_FEATURE_GPGME
+ uno::Reference< lang::XServiceInfo > xServiceInfo( m_xSecurityContext, css::uno::UNO_QUERY );
+ if (xServiceInfo->getImplementationName() == "com.sun.star.xml.security.gpg.XMLSecurityContext_GpgImpl")
+ m_xXMLSignature.set(new XMLSignature_GpgImpl());
+ else // xmlsec or mscrypt
+#endif
+ m_xXMLSignature.set(xMCF->createInstanceWithContext("com.sun.star.xml.crypto.XMLSignature", mxCtx), css::uno::UNO_QUERY);
+
+ bool bSuccess = m_xXMLSignature.is();
+ if ( bSuccess )
+ /*
+ * XMLSignature created successfully.
+ */
+ m_xXMLDocumentWrapper = new XMLDocumentWrapper_XmlSecImpl();
+
+ bSuccess &= m_xXMLDocumentWrapper.is();
+ if ( bSuccess )
+ m_xSAXEventKeeper = new SAXEventKeeperImpl();
+
+ bSuccess &= m_xSAXEventKeeper.is();
+
+ if (!bSuccess)
+ /*
+ * SAXEventKeeper created successfully.
+ */
+ return;
+
+ css::uno::Sequence <css::uno::Any> arg{ css::uno::Any(
+ uno::Reference<xml::wrapper::XXMLDocumentWrapper>(m_xXMLDocumentWrapper)) };
+ m_xSAXEventKeeper->initialize(arg);
+
+ css::uno::Reference< css::xml::crypto::sax::XSAXEventKeeperStatusChangeListener >
+ xStatusChangeListener = this;
+
+ m_xSAXEventKeeper->addSAXEventKeeperStatusChangeListener( xStatusChangeListener );
+
+ m_eStatusOfSecurityComponents = InitializationState::INITIALIZED;
+}
+
+bool XSecController::chainOn()
+/****** XSecController/chainOn ************************************************
+ *
+ * NAME
+ * chainOn -- tries to connect the SAXEventKeeper with the SAX chain.
+ *
+ * SYNOPSIS
+ * bJustChainingOn = chainOn();
+ *
+ * FUNCTION
+ * First, checks whether the SAXEventKeeper is on the SAX chain. If not,
+ * creates xml security components, and chains the SAXEventKeeper into
+ * the SAX chain.
+ * Before being chained in, the SAXEventKeeper needs to receive all
+ * missed key SAX events, which can promise the DOM tree buffered by the
+ * SAXEventKeeper has the same structure with the original document.
+ *
+ * RESULT
+ * bJustChainingOn - whether the SAXEventKeeper is just chained into the
+ * SAX chain.
+ *
+ * NOTES
+ * Sometimes, the last key SAX event can't be transferred to the
+ * SAXEventKeeper together.
+ * For instance, at the time a referenced element is detected, the
+ * startElement event has already been reserved by the ElementStackKeeper.
+ * Meanwhile, an ElementCollector needs to be created before the
+ * SAXEventKeeper receives that startElement event.
+ * So for the SAXEventKeeper, it needs to receive all missed key SAX
+ * events except that startElement event, then adds a new
+ * ElementCollector, then receives that startElement event.
+ ******************************************************************************/
+{
+ bool rc = false;
+
+ if (!m_bIsSAXEventKeeperSticky && !m_bIsSAXEventKeeperConnected)
+ {
+ if ( m_eStatusOfSecurityComponents == InitializationState::UNINITIALIZED )
+ {
+ createXSecComponent();
+ }
+
+ if ( m_eStatusOfSecurityComponents == InitializationState::INITIALIZED )
+ /*
+ * if all security components are ready, chains on the SAXEventKeeper
+ */
+ {
+ /*
+ * disconnect the SAXEventKeeper with its current output handler,
+ * to make sure no SAX event is forwarded during the connecting
+ * phase.
+ */
+ m_xSAXEventKeeper->setNextHandler( nullptr );
+
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xSEKHandler(m_xSAXEventKeeper);
+
+ /*
+ * connects the previous document handler on the SAX chain
+ */
+ if ( m_xPreviousNodeOnSAXChain.is() )
+ {
+ if ( m_bIsPreviousNodeInitializable )
+ {
+ css::uno::Reference< css::lang::XInitialization > xInitialization
+ (m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+
+ xInitialization->initialize({ css::uno::Any(xSEKHandler) });
+ }
+ else
+ {
+ css::uno::Reference< css::xml::sax::XParser > xParser
+ (m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+ xParser->setDocumentHandler( xSEKHandler );
+ }
+ }
+
+ /*
+ * connects the next document handler on the SAX chain
+ */
+ m_xSAXEventKeeper->setNextHandler(uno::Reference<xml::sax::XDocumentHandler>());
+
+ m_bIsSAXEventKeeperConnected = true;
+
+ rc = true;
+ }
+ }
+
+ return rc;
+}
+
+void XSecController::chainOff()
+/****** XSecController/chainOff ***********************************************
+ *
+ * NAME
+ * chainOff -- disconnects the SAXEventKeeper from the SAX chain.
+ ******************************************************************************/
+{
+ if (m_bIsSAXEventKeeperSticky )
+ return;
+
+ if (!m_bIsSAXEventKeeperConnected)
+ return;
+
+ m_xSAXEventKeeper->setNextHandler( nullptr );
+
+ if ( m_xPreviousNodeOnSAXChain.is() )
+ {
+ if ( m_bIsPreviousNodeInitializable )
+ {
+ css::uno::Reference< css::lang::XInitialization > xInitialization
+ (m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+
+ css::uno::Sequence<css::uno::Any> aArgs{ css::uno::Any(
+ uno::Reference<xml::sax::XDocumentHandler>()) };
+ xInitialization->initialize(aArgs);
+ }
+ else
+ {
+ css::uno::Reference< css::xml::sax::XParser > xParser(m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+ xParser->setDocumentHandler(uno::Reference<xml::sax::XDocumentHandler>());
+ }
+ }
+
+ m_bIsSAXEventKeeperConnected = false;
+}
+
+void XSecController::checkChainingStatus()
+/****** XSecController/checkChainingStatus ************************************
+ *
+ * NAME
+ * checkChainingStatus -- connects or disconnects the SAXEventKeeper
+ * according to the current situation.
+ *
+ * SYNOPSIS
+ * checkChainingStatus( );
+ *
+ * FUNCTION
+ * The SAXEventKeeper is chained into the SAX chain, when:
+ * 1. some element is being collected, or
+ * 2. the SAX event stream is blocking.
+ * Otherwise, chain off the SAXEventKeeper.
+ ******************************************************************************/
+{
+ if ( m_bIsCollectingElement || m_bIsBlocking )
+ {
+ chainOn();
+ }
+ else
+ {
+ chainOff();
+ }
+}
+
+void XSecController::initializeSAXChain()
+/****** XSecController/initializeSAXChain *************************************
+ *
+ * NAME
+ * initializeSAXChain -- initializes the SAX chain according to the
+ * current setting.
+ *
+ * FUNCTION
+ * Initializes the SAX chain, if the SAXEventKeeper is asked to be always
+ * on the SAX chain, chains it on. Otherwise, starts the
+ * ElementStackKeeper to reserve key SAX events.
+ ******************************************************************************/
+{
+ m_bIsSAXEventKeeperConnected = false;
+ m_bIsCollectingElement = false;
+ m_bIsBlocking = false;
+
+ chainOff();
+}
+
+css::uno::Reference< css::io::XInputStream >
+ XSecController::getObjectInputStream( const OUString& objectURL )
+/****** XSecController/getObjectInputStream ************************************
+ *
+ * NAME
+ * getObjectInputStream -- get a XInputStream interface from a SotStorage
+ *
+ * SYNOPSIS
+ * xInputStream = getObjectInputStream( objectURL );
+ *
+ * INPUTS
+ * objectURL - the object uri
+ *
+ * RESULT
+ * xInputStream - the XInputStream interface
+ ******************************************************************************/
+{
+ css::uno::Reference< css::io::XInputStream > xObjectInputStream;
+
+ SAL_WARN_IF( !m_xUriBinding.is(), "xmlsecurity.helper", "Need XUriBinding!" );
+
+ xObjectInputStream = m_xUriBinding->getUriBinding(objectURL);
+
+ return xObjectInputStream;
+}
+
+/*
+ * public methods
+ */
+
+sal_Int32 XSecController::getNewSecurityId( )
+{
+ sal_Int32 nId = m_nNextSecurityId;
+ m_nNextSecurityId++;
+ return nId;
+}
+
+void XSecController::startMission(const rtl::Reference<UriBindingHelper>& xUriBinding, const css::uno::Reference< css::xml::crypto::XXMLSecurityContext >& xSecurityContext )
+/****** XSecController/startMission *******************************************
+ *
+ * NAME
+ * startMission -- starts a new security mission.
+ *
+ * FUNCTION
+ * get ready for a new mission.
+ *
+ * INPUTS
+ * xUriBinding - the Uri binding that provide maps between uris and
+ * XInputStreams
+ * xSecurityContext - the security context component which can provide
+ * cryptoken
+ ******************************************************************************/
+{
+ m_xUriBinding = xUriBinding;
+
+ m_eStatusOfSecurityComponents = InitializationState::UNINITIALIZED;
+ m_xSecurityContext = xSecurityContext;
+
+ m_vInternalSignatureInformations.clear();
+
+ m_bVerifyCurrentSignature = false;
+}
+
+void XSecController::setSAXChainConnector(const css::uno::Reference< css::lang::XInitialization >& xInitialization)
+/****** XSecController/setSAXChainConnector ***********************************
+ *
+ * NAME
+ * setSAXChainConnector -- configures the components which will
+ * collaborate with the SAXEventKeeper on the SAX chain.
+ *
+ * SYNOPSIS
+ * setSAXChainConnector(xInitialization);
+ *
+ * INPUTS
+ * xInitialization - the previous node on the SAX chain
+ ******************************************************************************/
+{
+ m_bIsPreviousNodeInitializable = true;
+ m_xPreviousNodeOnSAXChain = xInitialization;
+
+ initializeSAXChain( );
+}
+
+void XSecController::clearSAXChainConnector()
+/****** XSecController/clearSAXChainConnector *********************************
+ *
+ * NAME
+ * clearSAXChainConnector -- resets the collaborating components.
+ ******************************************************************************/
+{
+ chainOff();
+
+ m_xPreviousNodeOnSAXChain = nullptr;
+}
+
+void XSecController::endMission()
+/****** XSecController/endMission *********************************************
+ *
+ * NAME
+ * endMission -- forces to end all missions
+ *
+ * FUNCTION
+ * Deletes all signature information and forces all missions to an end.
+ ******************************************************************************/
+{
+ sal_Int32 size = m_vInternalSignatureInformations.size();
+
+ for (int i=0; i<size; ++i)
+ {
+ if ( m_eStatusOfSecurityComponents == InitializationState::INITIALIZED )
+ /*
+ * ResolvedListener only exist when the security components are created.
+ */
+ {
+ css::uno::Reference< css::xml::crypto::sax::XMissionTaker > xMissionTaker
+ ( m_vInternalSignatureInformations[i].xReferenceResolvedListener, css::uno::UNO_QUERY );
+
+ /*
+ * asks the SignatureCreator/SignatureVerifier to release
+ * all resources it uses.
+ */
+ xMissionTaker->endMission();
+ }
+ }
+
+ m_xUriBinding = nullptr;
+ m_xSecurityContext = nullptr;
+
+ /*
+ * free the status change listener reference to this object
+ */
+ if (m_xSAXEventKeeper.is())
+ m_xSAXEventKeeper->addSAXEventKeeperStatusChangeListener( nullptr );
+}
+
+namespace
+{
+void writeUnsignedProperties(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& signatureInfo)
+{
+ {
+ rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList());
+ pAttributeList->AddAttribute("Id", "idUnsignedProperties_" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement("xd:UnsignedProperties", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ {
+ xDocumentHandler->startElement("xd:UnsignedSignatureProperties", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+
+ {
+ xDocumentHandler->startElement("xd:CertificateValues", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+
+ {
+ for (const auto& i: signatureInfo.maEncapsulatedX509Certificates)
+ {
+ xDocumentHandler->startElement("xd:EncapsulatedX509Certificate", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList()));
+ xDocumentHandler->characters(i);
+ xDocumentHandler->endElement("xd:EncapsulatedX509Certificate");
+ }
+ }
+
+ xDocumentHandler->endElement("xd:CertificateValues");
+ }
+
+ xDocumentHandler->endElement("xd:UnsignedSignatureProperties");
+ }
+
+ xDocumentHandler->endElement("xd:UnsignedProperties");
+}
+
+}
+
+void XSecController::exportSignature(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& signatureInfo,
+ bool bXAdESCompliantIfODF )
+/****** XSecController/exportSignature ****************************************
+ *
+ * NAME
+ * exportSignature -- export a signature structure to an XDocumentHandler
+ *
+ * SYNOPSIS
+ * exportSignature( xDocumentHandler, signatureInfo);
+ *
+ * INPUTS
+ * xDocumentHandler - the document handler to receive the signature
+ * signatureInfo - signature to be exported
+ ******************************************************************************/
+{
+ const SignatureReferenceInformations& vReferenceInfors = signatureInfo.vSignatureReferenceInfors;
+ rtl::Reference<SvXMLAttributeList> pAttributeList;
+
+ /*
+ * Write Signature element
+ */
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute(
+ "xmlns",
+ NS_XMLDSIG);
+
+ if (!signatureInfo.ouSignatureId.isEmpty())
+ {
+ pAttributeList->AddAttribute(
+ "Id",
+ signatureInfo.ouSignatureId);
+ }
+
+ xDocumentHandler->startElement( "Signature", pAttributeList);
+ {
+ /* Write SignedInfo element */
+ xDocumentHandler->startElement(
+ "SignedInfo",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ /* Write CanonicalizationMethod element */
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ ALGO_C14N);
+ xDocumentHandler->startElement( "CanonicalizationMethod", pAttributeList );
+ xDocumentHandler->endElement( "CanonicalizationMethod" );
+
+ /* Write SignatureMethod element */
+ pAttributeList = new SvXMLAttributeList();
+
+ // TODO: actually roundtrip this value from parsing documentsignatures.xml - entirely
+ // broken to assume this would in any way relate to the 1st reference's digest algo
+
+ // Assume that all Reference elements use the same DigestMethod:Algorithm, and that the
+ // SignatureMethod:Algorithm should be the corresponding one.
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ getSignatureURI(signatureInfo.eAlgorithmID, vReferenceInfors[0].nDigestID));
+ xDocumentHandler->startElement( "SignatureMethod", pAttributeList );
+ xDocumentHandler->endElement( "SignatureMethod" );
+
+ /* Write Reference element */
+ int j;
+ int refNum = vReferenceInfors.size();
+
+ for(j=0; j<refNum; ++j)
+ {
+ const SignatureReferenceInformation& refInfor = vReferenceInfors[j];
+
+ pAttributeList = new SvXMLAttributeList();
+ if ( refInfor.nType != SignatureReferenceType::SAMEDOCUMENT )
+ /*
+ * stream reference
+ */
+ {
+ pAttributeList->AddAttribute(
+ "URI",
+ refInfor.ouURI);
+ }
+ else
+ /*
+ * same-document reference
+ */
+ {
+ if (refInfor.ouURI.startsWith("idSignedProperties"))
+ {
+ pAttributeList->AddAttribute("URI", "#idSignedProperties_" + signatureInfo.ouSignatureId);
+ if (bXAdESCompliantIfODF && !refInfor.ouType.isEmpty())
+ {
+ // The reference which points to the SignedProperties
+ // shall have this specific type.
+ pAttributeList->AddAttribute("Type", refInfor.ouType);
+ }
+ }
+ else
+ {
+ pAttributeList->AddAttribute(
+ "URI",
+ "#" + refInfor.ouURI);
+ }
+ }
+
+ xDocumentHandler->startElement( "Reference", pAttributeList );
+ {
+ /* Write Transforms element */
+ if (refInfor.nType == SignatureReferenceType::XMLSTREAM)
+ /*
+ * xml stream, so c14n transform is needed
+ */
+ {
+ xDocumentHandler->startElement(
+ "Transforms",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ ALGO_C14N);
+ xDocumentHandler->startElement(
+ "Transform",
+ pAttributeList );
+ xDocumentHandler->endElement( "Transform" );
+ }
+ xDocumentHandler->endElement( "Transforms" );
+ }
+
+ /* Write DigestMethod element */
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ getDigestURI(refInfor.nDigestID));
+ xDocumentHandler->startElement(
+ "DigestMethod",
+ pAttributeList );
+ xDocumentHandler->endElement( "DigestMethod" );
+
+ /* Write DigestValue element */
+ xDocumentHandler->startElement(
+ "DigestValue",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters( refInfor.ouDigestValue );
+ xDocumentHandler->endElement( "DigestValue" );
+ }
+ xDocumentHandler->endElement( "Reference" );
+ }
+ }
+ xDocumentHandler->endElement( "SignedInfo" );
+
+ /* Write SignatureValue element */
+ xDocumentHandler->startElement(
+ "SignatureValue",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters( signatureInfo.ouSignatureValue );
+ xDocumentHandler->endElement( "SignatureValue" );
+
+ /* Write KeyInfo element */
+ xDocumentHandler->startElement(
+ "KeyInfo",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ // GPG or X509 key?
+ if (!signatureInfo.ouGpgCertificate.isEmpty())
+ {
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute("xmlns:loext", NS_LOEXT);
+ /* Write PGPData element */
+ xDocumentHandler->startElement(
+ "PGPData",
+ pAttributeList);
+ {
+ /* Write keyid element */
+ xDocumentHandler->startElement(
+ "PGPKeyID",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters(signatureInfo.ouGpgKeyID);
+ xDocumentHandler->endElement( "PGPKeyID" );
+
+ /* Write PGPKeyPacket element */
+ if (!signatureInfo.ouGpgCertificate.isEmpty())
+ {
+ xDocumentHandler->startElement(
+ "PGPKeyPacket",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters( signatureInfo.ouGpgCertificate );
+ xDocumentHandler->endElement( "PGPKeyPacket" );
+ }
+
+ /* Write PGPOwner element */
+ xDocumentHandler->startElement(
+ "loext:PGPOwner",
+ css::uno::Reference< css::xml::sax::XAttributeList >(new SvXMLAttributeList()));
+ xDocumentHandler->characters( signatureInfo.ouGpgOwner );
+ xDocumentHandler->endElement( "loext:PGPOwner" );
+ }
+ xDocumentHandler->endElement( "PGPData" );
+ }
+ else
+ {
+ assert(signatureInfo.GetSigningCertificate());
+ for (auto const& rData : signatureInfo.X509Datas)
+ {
+ /* Write X509Data element */
+ xDocumentHandler->startElement(
+ "X509Data",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ for (auto const& it : rData)
+ {
+ /* Write X509IssuerSerial element */
+ xDocumentHandler->startElement(
+ "X509IssuerSerial",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ /* Write X509IssuerName element */
+ xDocumentHandler->startElement(
+ "X509IssuerName",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters(it.X509IssuerName);
+ xDocumentHandler->endElement( "X509IssuerName" );
+
+ /* Write X509SerialNumber element */
+ xDocumentHandler->startElement(
+ "X509SerialNumber",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters(it.X509SerialNumber);
+ xDocumentHandler->endElement( "X509SerialNumber" );
+ }
+ xDocumentHandler->endElement( "X509IssuerSerial" );
+
+ /* Write X509Certificate element */
+ if (!it.X509Certificate.isEmpty())
+ {
+ xDocumentHandler->startElement(
+ "X509Certificate",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ xDocumentHandler->characters(it.X509Certificate);
+ xDocumentHandler->endElement( "X509Certificate" );
+ }
+ }
+ }
+ xDocumentHandler->endElement( "X509Data" );
+ }
+ }
+ }
+ xDocumentHandler->endElement( "KeyInfo" );
+
+ OUString sDate;
+
+ /* Write Object element */
+ xDocumentHandler->startElement(
+ "Object",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ /* Write SignatureProperties element */
+ xDocumentHandler->startElement(
+ "SignatureProperties",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList()));
+ {
+ /* Write SignatureProperty element */
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute(
+ "Id",
+ signatureInfo.ouDateTimePropertyId);
+ pAttributeList->AddAttribute(
+ "Target",
+ "#" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement(
+ "SignatureProperty",
+ pAttributeList);
+ {
+ /* Write timestamp element */
+
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute(
+ "xmlns:dc",
+ NS_DC);
+
+ xDocumentHandler->startElement(
+ "dc:date",
+ pAttributeList);
+
+ OUStringBuffer buffer;
+ //If the xml signature was already contained in the document,
+ //then we use the original date and time string, rather than the
+ //converted one. This avoids writing a different string due to
+ //e.g. rounding issues and thus breaking the signature.
+ if (!signatureInfo.ouDateTime.isEmpty())
+ buffer = signatureInfo.ouDateTime;
+ else
+ {
+ buffer = utl::toISO8601(signatureInfo.stDateTime);
+ // xsd:dateTime must use period as separator for fractional seconds, while
+ // utl::toISO8601 uses comma (as allowed, and even recommended, by ISO8601).
+ buffer.replace(',', '.');
+ }
+ sDate = buffer.makeStringAndClear();
+ xDocumentHandler->characters( sDate );
+
+ xDocumentHandler->endElement(
+ "dc:date");
+ }
+ xDocumentHandler->endElement( "SignatureProperty" );
+ }
+
+ // Write signature description.
+ if (!signatureInfo.ouDescription.isEmpty())
+ {
+ // SignatureProperty element.
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute("Id", signatureInfo.ouDescriptionPropertyId);
+ pAttributeList->AddAttribute("Target", "#" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement("SignatureProperty", pAttributeList);
+
+ {
+ // Description element.
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute("xmlns:dc", NS_DC);
+
+ xDocumentHandler->startElement("dc:description", pAttributeList);
+ xDocumentHandler->characters(signatureInfo.ouDescription);
+ xDocumentHandler->endElement("dc:description");
+ }
+
+ xDocumentHandler->endElement("SignatureProperty");
+ }
+
+ xDocumentHandler->endElement( "SignatureProperties" );
+ }
+ xDocumentHandler->endElement( "Object" );
+
+ // In XAdES, write another Object element for the QualifyingProperties
+ if (bXAdESCompliantIfODF)
+ {
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute("xmlns:xd", NS_XD);
+ xDocumentHandler->startElement(
+ "Object",
+ pAttributeList);
+ {
+ pAttributeList = new SvXMLAttributeList();
+ pAttributeList->AddAttribute("Target", "#" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement(
+ "xd:QualifyingProperties",
+ pAttributeList);
+ DocumentSignatureHelper::writeSignedProperties(xDocumentHandler, signatureInfo, sDate, true);
+ writeUnsignedProperties(xDocumentHandler, signatureInfo);
+ xDocumentHandler->endElement( "xd:QualifyingProperties" );
+ }
+ xDocumentHandler->endElement( "Object" );
+ }
+ }
+ xDocumentHandler->endElement( "Signature" );
+}
+
+void XSecController::exportOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler, const SignatureInformation& rInformation)
+{
+ OOXMLSecExporter aExporter(mxCtx, xRootStorage, xDocumentHandler, rInformation);
+ aExporter.writeSignature();
+}
+
+void XSecController::UpdateSignatureInformation(sal_Int32 const nSecurityId,
+ std::vector<SignatureInformation::X509Data> && rDatas)
+{
+ int const nIndex = findSignatureInfor(nSecurityId);
+ assert(nIndex != -1); // nothing should touch this between parsing and verify
+ m_vInternalSignatureInformations[nIndex].signatureInfor.X509Datas = std::move(rDatas);
+}
+
+SignatureInformation XSecController::getSignatureInformation( sal_Int32 nSecurityId ) const
+{
+ SignatureInformation aInf( 0 );
+ int nIndex = findSignatureInfor(nSecurityId);
+ SAL_WARN_IF( nIndex == -1, "xmlsecurity.helper", "getSignatureInformation - SecurityId is invalid!" );
+ if ( nIndex != -1)
+ {
+ aInf = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ }
+ return aInf;
+}
+
+SignatureInformations XSecController::getSignatureInformations() const
+{
+ SignatureInformations vInfors;
+ int sigNum = m_vInternalSignatureInformations.size();
+
+ for (int i=0; i<sigNum; ++i)
+ {
+ SignatureInformation si = m_vInternalSignatureInformations[i].signatureInfor;
+ vInfors.push_back(si);
+ }
+
+ return vInfors;
+}
+
+/*
+ * XSAXEventKeeperStatusChangeListener
+ */
+
+void SAL_CALL XSecController::blockingStatusChanged( sal_Bool isBlocking )
+{
+ m_bIsBlocking = isBlocking;
+ checkChainingStatus();
+}
+
+void SAL_CALL XSecController::collectionStatusChanged(
+ sal_Bool isInsideCollectedElement )
+{
+ m_bIsCollectingElement = isInsideCollectedElement;
+ checkChainingStatus();
+}
+
+void SAL_CALL XSecController::bufferStatusChanged( sal_Bool /*isBufferEmpty*/)
+{
+
+}
+
+/*
+ * XSignatureCreationResultListener
+ */
+void SAL_CALL XSecController::signatureCreated( sal_Int32 securityId, css::xml::crypto::SecurityOperationStatus nResult )
+{
+ int index = findSignatureInfor(securityId);
+ assert(index != -1 && "Signature Not Found!");
+ SignatureInformation& signatureInfor = m_vInternalSignatureInformations.at(index).signatureInfor;
+ signatureInfor.nStatus = nResult;
+}
+
+/*
+ * XSignatureVerifyResultListener
+ */
+void SAL_CALL XSecController::signatureVerified( sal_Int32 securityId, css::xml::crypto::SecurityOperationStatus nResult )
+{
+ int index = findSignatureInfor(securityId);
+ assert(index != -1 && "Signature Not Found!");
+ SignatureInformation& signatureInfor = m_vInternalSignatureInformations.at(index).signatureInfor;
+ signatureInfor.nStatus = nResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecparser.cxx b/xmlsecurity/source/helper/xsecparser.cxx
new file mode 100644
index 000000000..af3562f46
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecparser.cxx
@@ -0,0 +1,1632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "xsecparser.hxx"
+#include <xsecctl.hxx>
+#include <xmlsignaturehelper.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltkmap.hxx>
+#include <xmloff/xmlimp.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <sal/log.hxx>
+
+class XSecParser::Context
+{
+ protected:
+ friend class XSecParser;
+ XSecParser & m_rParser;
+ private:
+ std::optional<SvXMLNamespaceMap> m_pOldNamespaceMap;
+
+ public:
+ Context(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : m_rParser(rParser)
+ , m_pOldNamespaceMap(std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual ~Context() = default;
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/)
+ {
+ }
+
+ virtual void EndElement()
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/);
+
+ virtual void Characters(OUString const& /*rChars*/)
+ {
+ }
+};
+
+// it's possible that an unsupported element has an Id attribute and a
+// ds:Reference digesting it - probably this means XSecController needs to know
+// about it. (For known elements, the Id attribute is only processed according
+// to the schema.)
+class XSecParser::UnknownContext
+ : public XSecParser::Context
+{
+ public:
+ UnknownContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+};
+
+auto XSecParser::Context::CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/)
+-> std::unique_ptr<Context>
+{
+ // default: create new base context
+ return std::make_unique<UnknownContext>(m_rParser, std::move(pOldNamespaceMap));
+}
+
+/**
+note: anything in ds:Object should be trusted *only* if there is a ds:Reference
+ to it so it is signed (exception: the xades:EncapsulatedX509Certificate).
+ ds:SignedInfo precedes all ds:Object.
+
+ There may be multiple ds:Signature for purpose of counter-signatures
+ but the way XAdES describes these, only the ds:SignatureValue element
+ would be referenced, so requiring a ds:Reference for anything in
+ ds:Object shouldn't cause issues.
+ */
+class XSecParser::ReferencedContextImpl
+ : public XSecParser::Context
+{
+ protected:
+ bool m_isReferenced;
+
+ public:
+ ReferencedContextImpl(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_isReferenced(isReferenced)
+ {
+ }
+
+ OUString CheckIdAttrReferenced(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+ {
+ OUString const id(m_rParser.HandleIdAttr(xAttrs));
+ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id))
+ {
+ m_isReferenced = true;
+ }
+ return id;
+ }
+};
+
+class XSecParser::LoPGPOwnerContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoPGPOwnerContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgOwner(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPKeyPacketContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsPGPKeyPacketContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgCertificate(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPKeyIDContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsPGPKeyIDContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgKeyID(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPDataContext
+ : public XSecParser::Context
+{
+ public:
+ DsPGPDataContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rParser.m_pXSecController->switchGpgSignature();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyID")
+ {
+ return std::make_unique<DsPGPKeyIDContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyPacket")
+ {
+ return std::make_unique<DsPGPKeyPacketContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "PGPOwner")
+ {
+ return std::make_unique<LoPGPOwnerContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsX509CertificateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509CertificateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509SerialNumberContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509SerialNumberContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509IssuerNameContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509IssuerNameContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509IssuerSerialContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rX509IssuerName;
+ OUString & m_rX509SerialNumber;
+
+ public:
+ DsX509IssuerSerialContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rIssuerName, OUString& rSerialNumber)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rX509IssuerName(rIssuerName)
+ , m_rX509SerialNumber(rSerialNumber)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName")
+ {
+ return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber")
+ {
+ return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber);
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+/// can't be sure what is supposed to happen here because the spec is clear as mud
+class XSecParser::DsX509DataContext
+ : public XSecParser::Context
+{
+ private:
+ // sigh... "No ordering is implied by the above constraints."
+ // so store the ball of mud in vectors and try to figure it out later.
+ std::vector<std::pair<OUString, OUString>> m_X509IssuerSerials;
+ std::vector<OUString> m_X509Certificates;
+
+ public:
+ DsX509DataContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial")
+ {
+ m_X509IssuerSerials.emplace_back();
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate")
+ {
+ m_X509Certificates.emplace_back();
+ return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back());
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsKeyInfoContext
+ : public XSecParser::Context
+{
+ public:
+ DsKeyInfoContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data")
+ {
+ return std::make_unique<DsX509DataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPData")
+ {
+ return std::make_unique<DsPGPDataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData
+ // (old code would read ds:Transform inside ds:RetrievalMethod but
+ // presumably that was a bug)
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+};
+
+class XSecParser::DsSignatureValueContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsSignatureValueContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setSignatureValue(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsDigestValueContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsDigestValueContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rValue.clear();
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsDigestMethodContext
+ : public XSecParser::Context
+{
+ private:
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ DsDigestMethodContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_Int32& rReferenceDigestID)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" );
+ if (ouAlgorithm.isEmpty())
+ return;
+
+ SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1
+ && ouAlgorithm != ALGO_XMLDSIGSHA256
+ && ouAlgorithm != ALGO_XMLDSIGSHA512,
+ "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512");
+ if (ouAlgorithm == ALGO_XMLDSIGSHA1)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA256)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA512)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512;
+ else
+ m_rReferenceDigestID = 0;
+ }
+};
+
+class XSecParser::DsTransformContext
+ : public XSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ if (ouAlgorithm == ALGO_C14N)
+ /*
+ * a xml stream
+ */
+ {
+ m_rIsC14N = true;
+ }
+ }
+};
+
+class XSecParser::DsTransformsContext
+ : public XSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformsContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform")
+ {
+ return std::make_unique<DsTransformContext>(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsReferenceContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_URI;
+ OUString m_Type;
+ OUString m_DigestValue;
+ bool m_IsC14N = false;
+ // Relevant for ODF. The digest algorithm selected by the DigestMethod
+ // element's Algorithm attribute. @see css::xml::crypto::DigestID.
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+
+ public:
+ DsReferenceContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+
+ m_URI = xAttrs->getValueByName("URI");
+ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty");
+ // Remember the type of this reference.
+ m_Type = xAttrs->getValueByName("Type");
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_URI.startsWith("#"))
+ {
+ /*
+ * remove the first character '#' from the attribute value
+ */
+ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type);
+ }
+ else
+ {
+ if (m_IsC14N) // this is determined by nested ds:Transform
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID);
+ }
+ else
+ /*
+ * it must be an octet stream
+ */
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID);
+ }
+ }
+
+ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms")
+ {
+ return std::make_unique<DsTransformsContext>(m_rParser, std::move(pOldNamespaceMap), m_IsC14N);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_DigestValue);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignatureMethodContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignatureMethodContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
+ || ouAlgorithm == ALGO_ECDSASHA512)
+ {
+ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
+ }
+ }
+};
+
+class XSecParser::DsSignedInfoContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignedInfoContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod")
+ {
+ return std::make_unique<DsSignatureMethodContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesEncapsulatedX509CertificateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesEncapsulatedX509CertificateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->addEncapsulatedX509Certificate(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::XadesCertificateValuesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesCertificateValuesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "EncapsulatedX509Certificate")
+ {
+ return std::make_unique<XadesEncapsulatedX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: xades:OtherCertificate
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesUnsignedSignaturePropertiesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesUnsignedSignaturePropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertificateValues")
+ {
+ return std::make_unique<XadesCertificateValuesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing:
+ // xades:CounterSignature
+ // ^ old code would read a ds:Signature inside it?
+ // xades:SignatureTimeStamp
+ // xades:CompleteCertificateRefs
+ // xades:CompleteRevocationRefs
+ // xades:AttributeCertificateRefs
+ // xades:AttributeRevocationRefs
+ // xades:SigAndRefsTimeStamp
+ // xades:RefsOnlyTimeStamp
+ // xades:RevocationValues
+ // xades:AttrAuthoritiesCertValues
+ // ^ old code: was equivalent to CertificateValues ???
+ // xades:AttributeRevocationValues
+ // xades:ArchiveTimeStamp
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesUnsignedPropertiesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesUnsignedPropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedSignatureProperties")
+ {
+ return std::make_unique<XadesUnsignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: xades:UnsignedDataObjectProperties
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::LoSignatureLineIdContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineIdContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setSignatureLineId(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineId");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::LoSignatureLineValidImageContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineValidImageContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setValidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::LoSignatureLineInvalidImageContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineInvalidImageContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::LoSignatureLineContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ LoSignatureLineContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineId")
+ {
+ return std::make_unique<LoSignatureLineIdContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineValidImage")
+ {
+ return std::make_unique<LoSignatureLineValidImageContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineInvalidImage")
+ {
+ return std::make_unique<LoSignatureLineInvalidImageContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesCertDigestContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rDigestValue;
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ XadesCertDigestContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rDigestValue, sal_Int32& rReferenceDigestID)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rDigestValue(rDigestValue)
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesCertContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ OUString m_CertDigest;
+ OUString m_X509IssuerName;
+ OUString m_X509SerialNumber;
+
+ public:
+ XadesCertContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest")
+ {
+ return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial")
+ {
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesSigningCertificateContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSigningCertificateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert")
+ {
+ return std::make_unique<XadesCertContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesSigningTimeContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesSigningTimeContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setDate("", m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::XadesSignedSignaturePropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedSignaturePropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime")
+ {
+ return std::make_unique<XadesSigningTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate")
+ {
+ return std::make_unique<XadesSigningCertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLine")
+ {
+ return std::make_unique<LoSignatureLineContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesSignedPropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedPropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties")
+ {
+ return std::make_unique<XadesSignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignedDataObjectProperties
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesQualifyingPropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesQualifyingPropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties")
+ {
+ return std::make_unique<XadesSignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedProperties")
+ {
+ return std::make_unique<XadesUnsignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DcDateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DcDateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DcDescriptionContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DcDescriptionContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsSignaturePropertyContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ enum class SignatureProperty { Unknown, Date, Description };
+ SignatureProperty m_Property = SignatureProperty::Unknown;
+ OUString m_Id;
+ OUString m_Value;
+
+ public:
+ DsSignaturePropertyContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_Id = CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ switch (m_Property)
+ {
+ case SignatureProperty::Unknown:
+ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored");
+ break;
+ case SignatureProperty::Date:
+ m_rParser.m_pXSecController->setDate(m_Id, m_Value);
+ break;
+ case SignatureProperty::Description:
+ m_rParser.m_pXSecController->setDescription(m_Id, m_Value);
+ break;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DC && rName == "date")
+ {
+ m_Property = SignatureProperty::Date;
+ return std::make_unique<DcDateContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ if (nNamespace == XML_NAMESPACE_DC && rName == "description")
+ {
+ m_Property = SignatureProperty::Description;
+ return std::make_unique<DcDescriptionContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignaturePropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ DsSignaturePropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty")
+ {
+ return std::make_unique<DsSignaturePropertyContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsObjectContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ DsObjectContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ // init with "false" here - the Signature element can't be referenced by its child
+ : XSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties")
+ {
+ return std::make_unique<DsSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties")
+ {
+ return std::make_unique<XadesQualifyingPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: ds:Manifest
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignatureContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignatureContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs));
+ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement();
+ m_rParser.m_pXSecController->addSignature();
+ if (!ouIdAttr.isEmpty())
+ {
+ m_rParser.m_pXSecController->setId( ouIdAttr );
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo")
+ {
+ return std::make_unique<DsSignedInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue")
+ {
+ return std::make_unique<DsSignatureValueContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo")
+ {
+ return std::make_unique<DsKeyInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Object")
+ {
+ return std::make_unique<DsObjectContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsigSignaturesContext
+ : public XSecParser::Context
+{
+ public:
+ DsigSignaturesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Signature")
+ {
+ return std::make_unique<DsSignatureContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+XSecParser::XSecParser(XMLSignatureHelper& rXMLSignatureHelper,
+ XSecController* pXSecController)
+ : m_pNamespaceMap(SvXMLNamespaceMap())
+ , m_pXSecController(pXSecController)
+ , m_rXMLSignatureHelper(rXMLSignatureHelper)
+{
+ using namespace xmloff::token;
+ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML );
+ m_pNamespaceMap->Add( "_dsig_ooo", GetXMLToken(XML_N_DSIG_OOO), XML_NAMESPACE_DSIG_OOO );
+ m_pNamespaceMap->Add( "_dsig", GetXMLToken(XML_N_DSIG), XML_NAMESPACE_DSIG );
+ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS );
+ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132);
+ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141);
+ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
+ m_pNamespaceMap->Add( "_office_libo",
+ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT);
+}
+
+OUString XSecParser::HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+{
+ OUString ouIdAttr = getIdAttr(xAttrs);
+ if (!ouIdAttr.isEmpty())
+ {
+ m_pXSecController->collectToVerify( ouIdAttr );
+ }
+ return ouIdAttr;
+}
+
+OUString XSecParser::getIdAttr(const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ OUString ouIdAttr = xAttribs->getValueByName("id");
+
+ if (ouIdAttr.isEmpty())
+ {
+ ouIdAttr = xAttribs->getValueByName("Id");
+ }
+
+ return ouIdAttr;
+}
+
+/*
+ * XDocumentHandler
+ */
+void SAL_CALL XSecParser::startDocument( )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->startDocument();
+ }
+}
+
+void SAL_CALL XSecParser::endDocument( )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->endDocument();
+ }
+}
+
+void SAL_CALL XSecParser::startElement(
+ const OUString& rName,
+ const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ assert(m_pNamespaceMap);
+ std::optional<SvXMLNamespaceMap> pRewindMap(
+ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs));
+
+ OUString localName;
+ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName));
+
+ std::unique_ptr<Context> pContext;
+
+ if (m_ContextStack.empty())
+ {
+ if ((nPrefix != XML_NAMESPACE_DSIG && nPrefix != XML_NAMESPACE_DSIG_OOO)
+ || localName != "document-signatures")
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected root element", nullptr,
+ css::uno::Any());
+ }
+
+ pContext.reset(new DsigSignaturesContext(*this, std::move(pRewindMap)));
+
+ }
+ else
+ {
+ pContext = m_ContextStack.top()->CreateChildContext(
+ std::move(pRewindMap), nPrefix, localName);
+ }
+
+ m_ContextStack.push(std::move(pContext));
+
+ try
+ {
+ m_ContextStack.top()->StartElement(xAttribs);
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->startElement(rName, xAttribs);
+ }
+ }
+ catch (css::uno::Exception& )
+ {//getCaughtException MUST be the first line in the catch block
+ css::uno::Any exc = cppu::getCaughtException();
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: Exception in XSecParser::startElement",
+ nullptr, exc);
+ }
+ catch (...)
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected exception in XSecParser::startElement", nullptr,
+ css::uno::Any());
+ }
+}
+
+void SAL_CALL XSecParser::endElement(const OUString& rName)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+
+ try
+ {
+ m_ContextStack.top()->EndElement();
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->endElement(rName);
+ }
+ }
+ catch (css::uno::Exception& )
+ {//getCaughtException MUST be the first line in the catch block
+ css::uno::Any exc = cppu::getCaughtException();
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: Exception in XSecParser::endElement",
+ nullptr, exc);
+ }
+ catch (...)
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected exception in XSecParser::endElement", nullptr,
+ css::uno::Any());
+ }
+
+ if (m_ContextStack.top()->m_pOldNamespaceMap)
+ {
+ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap);
+ }
+ m_ContextStack.pop();
+}
+
+void SAL_CALL XSecParser::characters(const OUString& rChars)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+ m_ContextStack.top()->Characters(rChars);
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->characters(rChars);
+ }
+}
+
+void SAL_CALL XSecParser::ignorableWhitespace( const OUString& aWhitespaces )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->ignorableWhitespace( aWhitespaces );
+ }
+}
+
+void SAL_CALL XSecParser::processingInstruction( const OUString& aTarget, const OUString& aData )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->processingInstruction(aTarget, aData);
+ }
+}
+
+void SAL_CALL XSecParser::setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->setDocumentLocator( xLocator );
+ }
+}
+
+/*
+ * XInitialization
+ */
+void SAL_CALL XSecParser::initialize(
+ const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ aArguments[0] >>= m_xNextHandler;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecparser.hxx b/xmlsecurity/source/helper/xsecparser.hxx
new file mode 100644
index 000000000..6279d6543
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecparser.hxx
@@ -0,0 +1,159 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <xmloff/namespacemap.hxx>
+
+#include <memory>
+#include <optional>
+#include <stack>
+
+class XMLSignatureHelper;
+class XSecController;
+
+class XSecParser: public cppu::WeakImplHelper
+<
+ css::xml::sax::XDocumentHandler,
+ css::lang::XInitialization
+>
+/****** XSecController.hxx/CLASS XSecParser ***********************************
+ *
+ * NAME
+ * XSecParser -- a SAX parser that can detect security elements
+ *
+ * FUNCTION
+ * The XSecParser object is connected on the SAX chain and detects
+ * security elements in the SAX event stream, then notifies
+ * the XSecController.
+ *
+ * NOTES
+ * This class is used when importing a document.
+ ******************************************************************************/
+{
+ friend class XSecController;
+public:
+ class Context;
+private:
+ class UnknownContext;
+ class ReferencedContextImpl;
+ class LoPGPOwnerContext;
+ class DsPGPKeyPacketContext;
+ class DsPGPKeyIDContext;
+ class DsPGPDataContext;
+ class DsX509CertificateContext;
+ class DsX509SerialNumberContext;
+ class DsX509IssuerNameContext;
+ class DsX509IssuerSerialContext;
+ class DsX509DataContext;
+ class DsKeyInfoContext;
+ class DsSignatureValueContext;
+ class DsDigestValueContext;
+ class DsDigestMethodContext;
+ class DsTransformContext;
+ class DsTransformsContext;
+ class DsReferenceContext;
+ class DsSignatureMethodContext;
+ class DsSignedInfoContext;
+ class XadesEncapsulatedX509CertificateContext;
+ class XadesCertificateValuesContext;
+ class XadesUnsignedSignaturePropertiesContext;
+ class XadesUnsignedPropertiesContext;
+ class LoSignatureLineIdContext;
+ class LoSignatureLineValidImageContext;
+ class LoSignatureLineInvalidImageContext;
+ class LoSignatureLineContext;
+ class XadesCertDigestContext;
+ class XadesCertContext;
+ class XadesSigningCertificateContext;
+ class XadesSigningTimeContext;
+ class XadesSignedSignaturePropertiesContext;
+ class XadesSignedPropertiesContext;
+ class XadesQualifyingPropertiesContext;
+ class DcDateContext;
+ class DcDescriptionContext;
+ class DsSignaturePropertyContext;
+ class DsSignaturePropertiesContext;
+ class DsObjectContext;
+ class DsSignatureContext;
+ class DsigSignaturesContext;
+
+ std::stack<std::unique_ptr<Context>> m_ContextStack;
+ std::optional<SvXMLNamespaceMap> m_pNamespaceMap;
+
+ /*
+ * the XSecController collaborating with XSecParser
+ */
+ XSecController* m_pXSecController;
+
+ /*
+ * the next XDocumentHandler on the SAX chain
+ */
+ css::uno::Reference<
+ css::xml::sax::XDocumentHandler > m_xNextHandler;
+
+ XMLSignatureHelper& m_rXMLSignatureHelper;
+
+ OUString HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs);
+ static OUString getIdAttr(const css::uno::Reference<
+ css::xml::sax::XAttributeList >& xAttribs );
+
+public:
+ XSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController);
+
+ /*
+ * XDocumentHandler
+ */
+ virtual void SAL_CALL startDocument( ) override;
+
+ virtual void SAL_CALL endDocument( ) override;
+
+ virtual void SAL_CALL startElement(
+ const OUString& aName,
+ const css::uno::Reference<
+ css::xml::sax::XAttributeList >& xAttribs ) override;
+
+ virtual void SAL_CALL endElement( const OUString& aName ) override;
+
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+
+ virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
+
+ virtual void SAL_CALL processingInstruction(
+ const OUString& aTarget,
+ const OUString& aData ) override;
+
+ virtual void SAL_CALL setDocumentLocator(
+ const css::uno::Reference<
+ css::xml::sax::XLocator >& xLocator ) override;
+
+ /*
+ * XInitialization
+ */
+ virtual void SAL_CALL initialize(
+ const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecsign.cxx b/xmlsecurity/source/helper/xsecsign.cxx
new file mode 100644
index 000000000..5634ed502
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecsign.cxx
@@ -0,0 +1,460 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <xsecctl.hxx>
+
+#include <com/sun/star/xml/crypto/sax/ElementMarkPriority.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+
+#include <framework/signaturecreatorimpl.hxx>
+#include <framework/saxeventkeeperimpl.hxx>
+
+namespace com::sun::star::graphic { class XGraphic; }
+
+using namespace css;
+using namespace css::uno;
+using namespace css::graphic;
+
+/* protected: for signature generation */
+OUString XSecController::createId()
+{
+ sal_uInt8 aSeq[16];
+ rtl_createUuid( aSeq, nullptr, true );
+
+ char str[68]="ID_";
+ int length = 3;
+ for (sal_uInt8 i : aSeq)
+ {
+ length += sprintf(str+length, "%04x", i);
+ }
+
+ return OUString::createFromAscii(str);
+}
+
+css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > XSecController::prepareSignatureToWrite(
+ InternalSignatureInformation& internalSignatureInfor,
+ sal_Int32 nStorageFormat,
+ bool bXAdESCompliantIfODF)
+{
+ sal_Int32 nSecurityId = internalSignatureInfor.signatureInfor.nSecurityId;
+ SignatureReferenceInformations& vReferenceInfors = internalSignatureInfor.signatureInfor.vSignatureReferenceInfors;
+
+ sal_Int32 nIdOfSignatureElementCollector;
+
+ nIdOfSignatureElementCollector =
+ m_xSAXEventKeeper->addSecurityElementCollector( css::xml::crypto::sax::ElementMarkPriority_AFTERMODIFY, true );
+
+ m_xSAXEventKeeper->setSecurityId(nIdOfSignatureElementCollector, nSecurityId);
+
+ rtl::Reference<SignatureCreatorImpl> xSignatureCreator(new SignatureCreatorImpl);
+
+ css::uno::Sequence<css::uno::Any> args
+ {
+ Any(OUString::number(nSecurityId)),
+ Any(uno::Reference<xml::crypto::sax::XSecuritySAXEventKeeper>(m_xSAXEventKeeper)),
+ Any(OUString::number(nIdOfSignatureElementCollector)),
+
+ //for nss, the internal module is used for signing, which needs to be improved later
+ Any(m_xSecurityContext->getSecurityEnvironment()),
+ Any(m_xXMLSignature)
+ };
+ xSignatureCreator->initialize(args);
+
+ sal_Int32 nBlockerId = m_xSAXEventKeeper->addBlocker();
+ m_xSAXEventKeeper->setSecurityId(nBlockerId, nSecurityId);
+
+ xSignatureCreator->setBlockerId(nBlockerId);
+
+ xSignatureCreator->addSignatureCreationResultListener(this);
+
+ m_xSAXEventKeeper->addReferenceResolvedListener(nIdOfSignatureElementCollector, xSignatureCreator);
+
+ int size = vReferenceInfors.size();
+ sal_Int32 nReferenceCount = 0;
+
+ for(int i=0; i<size; ++i)
+ {
+ sal_Int32 keeperId = internalSignatureInfor.vKeeperIds[i];
+
+ if ( keeperId != -1)
+ {
+ m_xSAXEventKeeper->setSecurityId(keeperId, nSecurityId);
+ m_xSAXEventKeeper->addReferenceResolvedListener( keeperId, xSignatureCreator);
+ xSignatureCreator->setReferenceId( keeperId );
+ nReferenceCount++;
+ }
+ }
+
+ xSignatureCreator->setReferenceCount( nReferenceCount );
+
+ /*
+ * adds all URI binding
+ */
+ for(int i=0; i<size; ++i)
+ {
+ const SignatureReferenceInformation& refInfor = vReferenceInfors[i];
+
+ css::uno::Reference< css::io::XInputStream > xInputStream
+ = getObjectInputStream( refInfor.ouURI );
+
+ if (xInputStream.is())
+ xSignatureCreator->setUriBinding(refInfor.ouURI,xInputStream);
+ }
+
+ xSignatureCreator->setKeyId(0);
+
+ // use sha512 for gpg signing unconditionally
+ const sal_Int32 digestID = !internalSignatureInfor.signatureInfor.ouGpgCertificate.isEmpty()?
+ css::xml::crypto::DigestID::SHA512 : (bXAdESCompliantIfODF ? css::xml::crypto::DigestID::SHA256 : css::xml::crypto::DigestID::SHA1);
+
+ if (nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ internalSignatureInfor.signatureInfor.ouSignatureId = createId();
+ internalSignatureInfor.signatureInfor.ouDateTimePropertyId = createId();
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouDateTimePropertyId, -1, OUString() );
+ size++;
+
+ if (bXAdESCompliantIfODF)
+ {
+ OUString aId = "idSignedProperties_" + internalSignatureInfor.signatureInfor.ouSignatureId;
+ // We write a new reference, so it's possible to use the correct type URI.
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, aId, -1, "http://uri.etsi.org/01903#SignedProperties");
+ size++;
+ }
+
+ if (!internalSignatureInfor.signatureInfor.ouDescription.isEmpty())
+ {
+ // Only mention the hash of the description in the signature if it's non-empty.
+ internalSignatureInfor.signatureInfor.ouDescriptionPropertyId = createId();
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouDescriptionPropertyId, -1, OUString());
+ size++;
+ }
+ }
+ else // OOXML
+ {
+ OUString aID = createId();
+ internalSignatureInfor.signatureInfor.ouSignatureId = aID;
+
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, "idPackageObject_" + aID, -1, OUString());
+ size++;
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, "idOfficeObject_" + aID, -1, OUString());
+ size++;
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, "idSignedProperties_" + aID, -1, OUString());
+ size++;
+ }
+
+ /*
+ * replace both digestValues and signatureValue to " "
+ */
+ for(int i=0; i<size; ++i)
+ {
+ SignatureReferenceInformation& refInfor = vReferenceInfors[i];
+ refInfor.ouDigestValue = " ";
+ }
+
+ internalSignatureInfor.signatureInfor.ouSignatureValue = " ";
+
+ return xSignatureCreator;
+}
+
+void XSecController::signAStream( sal_Int32 securityId, const OUString& uri, bool isBinary, bool bXAdESCompliantIfODF)
+{
+ const SignatureReferenceType type = isBinary ? SignatureReferenceType::BINARYSTREAM : SignatureReferenceType::XMLSTREAM;
+ sal_Int32 digestID = bXAdESCompliantIfODF ? css::xml::crypto::DigestID::SHA256 : css::xml::crypto::DigestID::SHA1;
+
+ int index = findSignatureInfor( securityId );
+
+ if (index == -1)
+ {
+ InternalSignatureInformation isi(securityId, nullptr);
+ isi.addReference(type, digestID, uri, -1, OUString());
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ // use sha512 for gpg signing unconditionally
+ if (!m_vInternalSignatureInformations[index].signatureInfor.ouGpgCertificate.isEmpty())
+ digestID = css::xml::crypto::DigestID::SHA512;
+ m_vInternalSignatureInformations[index].addReference(type, digestID, uri, -1, OUString());
+ }
+}
+
+// note: this is called when creating a new signature from scratch
+void XSecController::setX509Certificate(
+ sal_Int32 nSecurityId,
+ const OUString& ouX509IssuerName,
+ const OUString& ouX509SerialNumber,
+ const OUString& ouX509Cert,
+ const OUString& ouX509CertDigest,
+ svl::crypto::SignatureMethodAlgorithm eAlgorithmID)
+{
+ int index = findSignatureInfor( nSecurityId );
+
+ if ( index == -1 )
+ {
+ InternalSignatureInformation isi(nSecurityId, nullptr);
+ isi.signatureInfor.X509Datas.clear();
+ isi.signatureInfor.X509Datas.emplace_back();
+ isi.signatureInfor.X509Datas.back().emplace_back();
+ isi.signatureInfor.X509Datas.back().back().X509IssuerName = ouX509IssuerName;
+ isi.signatureInfor.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber;
+ isi.signatureInfor.X509Datas.back().back().X509Certificate = ouX509Cert;
+ isi.signatureInfor.X509Datas.back().back().CertDigest = ouX509CertDigest;
+ isi.signatureInfor.eAlgorithmID = eAlgorithmID;
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ SignatureInformation &si
+ = m_vInternalSignatureInformations[index].signatureInfor;
+ si.X509Datas.clear();
+ si.X509Datas.emplace_back();
+ si.X509Datas.back().emplace_back();
+ si.X509Datas.back().back().X509IssuerName = ouX509IssuerName;
+ si.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber;
+ si.X509Datas.back().back().X509Certificate = ouX509Cert;
+ si.X509Datas.back().back().CertDigest = ouX509CertDigest;
+ }
+}
+
+void XSecController::setGpgCertificate(
+ sal_Int32 nSecurityId,
+ const OUString& ouKeyDigest,
+ const OUString& ouCert,
+ const OUString& ouOwner)
+{
+ int index = findSignatureInfor( nSecurityId );
+
+ if ( index == -1 )
+ {
+ InternalSignatureInformation isi(nSecurityId, nullptr);
+ isi.signatureInfor.ouGpgCertificate = ouCert;
+ isi.signatureInfor.ouGpgOwner = ouOwner;
+ isi.signatureInfor.ouGpgKeyID = ouKeyDigest;
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ SignatureInformation &si
+ = m_vInternalSignatureInformations[index].signatureInfor;
+ si.X509Datas.clear(); // it is a PGP signature now
+ si.ouGpgCertificate = ouCert;
+ si.ouGpgOwner = ouOwner;
+ si.ouGpgKeyID = ouKeyDigest;
+ }
+}
+
+void XSecController::setDate(
+ sal_Int32 nSecurityId,
+ const css::util::DateTime& rDateTime )
+{
+ int index = findSignatureInfor( nSecurityId );
+
+ if ( index == -1 )
+ {
+ InternalSignatureInformation isi(nSecurityId, nullptr);
+ isi.signatureInfor.stDateTime = rDateTime;
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ SignatureInformation &si
+ = m_vInternalSignatureInformations[index].signatureInfor;
+ si.stDateTime = rDateTime;
+ }
+}
+
+void XSecController::setDescription(sal_Int32 nSecurityId, const OUString& rDescription)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.ouDescription = rDescription;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.ouDescription = rDescription;
+ }
+}
+
+void XSecController::setSignatureLineId(sal_Int32 nSecurityId, const OUString& rSignatureLineId)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.ouSignatureLineId = rSignatureLineId;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.ouSignatureLineId = rSignatureLineId;
+ }
+}
+
+void XSecController::setSignatureLineValidGraphic(sal_Int32 nSecurityId,
+ const Reference<XGraphic>& xValidGraphic)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.aValidSignatureImage = xValidGraphic;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation
+ = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.aValidSignatureImage = xValidGraphic;
+ }
+}
+
+void XSecController::setSignatureLineInvalidGraphic(
+ sal_Int32 nSecurityId, const Reference<XGraphic>& xInvalidGraphic)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.aInvalidSignatureImage = xInvalidGraphic;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation
+ = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.aInvalidSignatureImage = xInvalidGraphic;
+ }
+}
+
+bool XSecController::WriteSignature(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ bool bXAdESCompliantIfODF )
+{
+ bool rc = false;
+
+ SAL_WARN_IF( !xDocumentHandler.is(), "xmlsecurity.helper", "I really need a document handler!" );
+
+ /*
+ * chain the SAXEventKeeper to the SAX chain
+ */
+ chainOn();
+
+ if ( m_eStatusOfSecurityComponents == InitializationState::INITIALIZED )
+ /*
+ * if all security components are ready, add the signature
+ * stream.
+ */
+ {
+ m_bIsSAXEventKeeperSticky = true;
+ m_xSAXEventKeeper->setNextHandler(xDocumentHandler);
+
+ try
+ {
+ /*
+ * export the signature template
+ */
+ css::uno::Reference<css::xml::sax::XDocumentHandler> xSEKHandler(m_xSAXEventKeeper);
+
+ int i;
+ int sigNum = m_vInternalSignatureInformations.size();
+
+ for (i=0; i<sigNum; ++i)
+ {
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations[i];
+
+ // Prepare the signature creator.
+ // 0 is not a documented value of embed::StorageFormats, ugh
+ isi.xReferenceResolvedListener = prepareSignatureToWrite( isi, 0, bXAdESCompliantIfODF );
+
+ exportSignature( xSEKHandler, isi.signatureInfor, bXAdESCompliantIfODF );
+ }
+
+ m_bIsSAXEventKeeperSticky = false;
+ chainOff();
+
+ rc = true;
+ }
+ catch( css::uno::Exception& )
+ {
+ }
+
+ m_xSAXEventKeeper->setNextHandler( nullptr );
+ m_bIsSAXEventKeeperSticky = false;
+ }
+
+ return rc;
+}
+
+bool XSecController::WriteOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler)
+{
+ bool bRet = false;
+
+ SAL_WARN_IF(!xDocumentHandler.is(), "xmlsecurity.helper", "empty xDocumentHandler reference");
+
+ // Chain the SAXEventKeeper to the SAX chain.
+ chainOn();
+
+ if (m_eStatusOfSecurityComponents == InitializationState::INITIALIZED)
+ {
+ m_bIsSAXEventKeeperSticky = true;
+ m_xSAXEventKeeper->setNextHandler(xDocumentHandler);
+
+ try
+ {
+ // Export the signature template.
+ css::uno::Reference<xml::sax::XDocumentHandler> xSEKHandler(m_xSAXEventKeeper);
+
+ for (InternalSignatureInformation & rInformation : m_vInternalSignatureInformations)
+ {
+ // Prepare the signature creator.
+ rInformation.xReferenceResolvedListener = prepareSignatureToWrite(rInformation, embed::StorageFormats::OFOPXML, false);
+
+ exportOOXMLSignature(xRootStorage, xSEKHandler, rInformation.signatureInfor);
+ }
+
+ m_bIsSAXEventKeeperSticky = false;
+ chainOff();
+
+ bRet = true;
+ }
+ catch(const uno::Exception&)
+ {
+ }
+
+ m_xSAXEventKeeper->setNextHandler(nullptr);
+ m_bIsSAXEventKeeperSticky = false;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecverify.cxx b/xmlsecurity/source/helper/xsecverify.cxx
new file mode 100644
index 000000000..c45bece5f
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecverify.cxx
@@ -0,0 +1,630 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_gpgme.h>
+
+#include <xsecctl.hxx>
+#include "xsecparser.hxx"
+#include "ooxmlsecparser.hxx"
+#include <biginteger.hxx>
+#include <framework/signatureverifierimpl.hxx>
+#include <framework/saxeventkeeperimpl.hxx>
+#include <gpg/xmlsignature_gpgimpl.hxx>
+#include <gpg/SEInitializer.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/crypto/sax/XKeyCollector.hpp>
+#include <com/sun/star/xml/crypto/sax/ElementMarkPriority.hpp>
+#include <com/sun/star/xml/crypto/sax/XReferenceCollector.hpp>
+#include <com/sun/star/xml/crypto/sax/XSignatureVerifyResultBroadcaster.hpp>
+#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <sal/log.hxx>
+#include <unotools/datetime.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/seqstream.hxx>
+
+namespace com::sun::star::graphic { class XGraphic; }
+
+using namespace css;
+using namespace css::uno;
+using namespace css::beans;
+
+/* protected: for signature verify */
+css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > XSecController::prepareSignatureToRead(
+ sal_Int32 nSecurityId)
+{
+ if ( m_eStatusOfSecurityComponents != InitializationState::INITIALIZED )
+ {
+ return nullptr;
+ }
+
+ sal_Int32 nIdOfSignatureElementCollector;
+ css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > xReferenceResolvedListener;
+
+ nIdOfSignatureElementCollector =
+ m_xSAXEventKeeper->addSecurityElementCollector( css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY, false);
+
+ m_xSAXEventKeeper->setSecurityId(nIdOfSignatureElementCollector, nSecurityId);
+
+ /*
+ * create a SignatureVerifier
+ */
+ xReferenceResolvedListener = new SignatureVerifierImpl;
+
+ css::uno::Reference<css::lang::XInitialization> xInitialization(xReferenceResolvedListener, css::uno::UNO_QUERY);
+
+ css::uno::Sequence<css::uno::Any> args
+ {
+ Any(OUString::number(nSecurityId)),
+ Any(uno::Reference<xml::crypto::sax::XSecuritySAXEventKeeper>(m_xSAXEventKeeper)),
+ Any(OUString::number(nIdOfSignatureElementCollector)),
+ Any(m_xSecurityContext),
+ Any(m_xXMLSignature)
+ };
+ xInitialization->initialize(args);
+
+ css::uno::Reference< css::xml::crypto::sax::XSignatureVerifyResultBroadcaster >
+ signatureVerifyResultBroadcaster(xReferenceResolvedListener, css::uno::UNO_QUERY);
+
+ signatureVerifyResultBroadcaster->addSignatureVerifyResultListener( this );
+
+ m_xSAXEventKeeper->addReferenceResolvedListener(
+ nIdOfSignatureElementCollector,
+ xReferenceResolvedListener);
+
+ css::uno::Reference<css::xml::crypto::sax::XKeyCollector> keyCollector (xReferenceResolvedListener, css::uno::UNO_QUERY);
+ keyCollector->setKeyId(0);
+
+ return xReferenceResolvedListener;
+}
+
+void XSecController::addSignature()
+{
+ css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > xReferenceResolvedListener;
+ sal_Int32 nSignatureId = 0;
+
+
+ if (m_bVerifyCurrentSignature)
+ {
+ chainOn();
+ xReferenceResolvedListener = prepareSignatureToRead( m_nReservedSignatureId );
+ m_bVerifyCurrentSignature = false;
+ nSignatureId = m_nReservedSignatureId;
+ }
+
+ InternalSignatureInformation isi( nSignatureId, xReferenceResolvedListener );
+ m_vInternalSignatureInformations.push_back( isi );
+}
+
+void XSecController::setSignatureMethod(svl::crypto::SignatureMethodAlgorithm eAlgorithmID)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ m_vInternalSignatureInformations.back().signatureInfor.eAlgorithmID = eAlgorithmID;
+}
+
+void XSecController::switchGpgSignature()
+{
+#if HAVE_FEATURE_GPGME
+ // swap signature verifier for the Gpg one
+ m_xXMLSignature.set(new XMLSignature_GpgImpl());
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ SignatureVerifierImpl* pImpl=
+ dynamic_cast<SignatureVerifierImpl*>(
+ m_vInternalSignatureInformations.back().xReferenceResolvedListener.get());
+ if (pImpl)
+ {
+ css::uno::Reference<css::xml::crypto::XSEInitializer> xGpgSEInitializer(
+ new SEInitializerGpg());
+ pImpl->updateSignature(new XMLSignature_GpgImpl(),
+ xGpgSEInitializer->createSecurityContext(OUString()));
+ }
+#else
+ (void) this;
+#endif
+}
+
+bool XSecController::haveReferenceForId(std::u16string_view rId) const
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::haveReferenceForId: no signature");
+ return false;
+ }
+ InternalSignatureInformation const& rIsi(m_vInternalSignatureInformations.back());
+ for (SignatureReferenceInformation const& rSri : rIsi.signatureInfor.vSignatureReferenceInfors)
+ {
+ if (rSri.nType == SignatureReferenceType::SAMEDOCUMENT
+ && rSri.ouURI == rId) // ouUri has # stripped
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void XSecController::addReference( const OUString& ouUri, sal_Int32 nDigestID, const OUString& ouType )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::addReference: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.addReference(SignatureReferenceType::SAMEDOCUMENT, nDigestID, ouUri, -1, ouType );
+}
+
+void XSecController::addStreamReference(
+ const OUString& ouUri,
+ bool isBinary,
+ sal_Int32 nDigestID )
+{
+ SignatureReferenceType type = (isBinary?SignatureReferenceType::BINARYSTREAM:SignatureReferenceType::XMLSTREAM);
+
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::addStreamReference: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+
+ if ( isi.xReferenceResolvedListener.is() )
+ {
+ /*
+ * get the input stream
+ */
+ css::uno::Reference< css::io::XInputStream > xObjectInputStream
+ = getObjectInputStream( ouUri );
+
+ if ( xObjectInputStream.is() )
+ {
+ css::uno::Reference<css::xml::crypto::XUriBinding> xUriBinding
+ (isi.xReferenceResolvedListener, css::uno::UNO_QUERY);
+ xUriBinding->setUriBinding(ouUri, xObjectInputStream);
+ }
+ }
+
+ isi.addReference(type, nDigestID, ouUri, -1, OUString());
+}
+
+void XSecController::setReferenceCount() const
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setReferenceCount: no signature");
+ return;
+ }
+ const InternalSignatureInformation &isi =
+ m_vInternalSignatureInformations.back();
+
+ if ( !isi.xReferenceResolvedListener.is() )
+ return;
+
+ const SignatureReferenceInformations &refInfors = isi.signatureInfor.vSignatureReferenceInfors;
+
+ int refNum = refInfors.size();
+ sal_Int32 referenceCount = 0;
+
+ for(int i=0 ; i<refNum; ++i)
+ {
+ if (refInfors[i].nType == SignatureReferenceType::SAMEDOCUMENT )
+ /*
+ * same-document reference
+ */
+ {
+ referenceCount++;
+ }
+ }
+
+ css::uno::Reference<css::xml::crypto::sax::XReferenceCollector> xReferenceCollector
+ (isi.xReferenceResolvedListener, css::uno::UNO_QUERY);
+ xReferenceCollector->setReferenceCount( referenceCount );
+}
+
+void XSecController::setX509Data(
+ std::vector<std::pair<OUString, OUString>> & rX509IssuerSerials,
+ std::vector<OUString> const& rX509Certificates)
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setX509IssuerName: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ SignatureInformation::X509Data data;
+ // due to the excessive flexibility of the spec it's possible that there
+ // is both a reference to a cert and the cert itself in one X509Data
+ for (OUString const& it : rX509Certificates)
+ {
+ try
+ {
+ data.emplace_back();
+ data.back().X509Certificate = it;
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment());
+ uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it));
+ if (!xCert.is())
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ continue; // will be handled in CheckX509Data
+ }
+ OUString const issuerName(xCert->getIssuerName());
+ OUString const serialNumber(xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()));
+ auto const iter = std::find_if(rX509IssuerSerials.begin(), rX509IssuerSerials.end(),
+ [&](auto const& rX509IssuerSerial) {
+ return xmlsecurity::EqualDistinguishedNames(issuerName, rX509IssuerSerial.first, xmlsecurity::COMPAT_2ND)
+ && serialNumber == rX509IssuerSerial.second;
+ });
+ if (iter != rX509IssuerSerials.end())
+ {
+ data.back().X509IssuerName = iter->first;
+ data.back().X509SerialNumber = iter->second;
+ rX509IssuerSerials.erase(iter);
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ }
+ // now handle any that are left...
+ for (auto const& it : rX509IssuerSerials)
+ {
+ data.emplace_back();
+ data.back().X509IssuerName = it.first;
+ data.back().X509SerialNumber = it.second;
+ }
+ if (!data.empty())
+ {
+ isi.signatureInfor.X509Datas.push_back(data);
+ }
+}
+
+void XSecController::setSignatureValue( OUString const & ouSignatureValue )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setSignatureValue: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouSignatureValue = ouSignatureValue;
+}
+
+void XSecController::setDigestValue( sal_Int32 nDigestID, OUString const & ouDigestValue )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setDigestValue: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ if (isi.signatureInfor.vSignatureReferenceInfors.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setDigestValue: no signature reference");
+ return;
+ }
+ SignatureReferenceInformation &reference =
+ isi.signatureInfor.vSignatureReferenceInfors.back();
+ reference.nDigestID = nDigestID;
+ reference.ouDigestValue = ouDigestValue;
+}
+
+void XSecController::setGpgKeyID( OUString const & ouKeyID )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setGpgKeyID: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouGpgKeyID = ouKeyID;
+}
+
+void XSecController::setGpgCertificate( OUString const & ouGpgCert )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setGpgCertificate: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouGpgCertificate = ouGpgCert;
+}
+
+void XSecController::setGpgOwner( OUString const & ouGpgOwner )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setGpgOwner: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouGpgOwner = ouGpgOwner;
+}
+
+void XSecController::setDate(OUString const& rId, OUString const& ouDate)
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setDate: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ // there may be multiple timestamps in a signature - check them for consistency
+ if (!isi.signatureInfor.ouDateTime.isEmpty()
+ && isi.signatureInfor.ouDateTime != ouDate)
+ {
+ isi.signatureInfor.hasInconsistentSigningTime = true;
+ }
+ (void)utl::ISO8601parseDateTime( ouDate, isi.signatureInfor.stDateTime);
+ isi.signatureInfor.ouDateTime = ouDate;
+ if (!rId.isEmpty())
+ {
+ isi.signatureInfor.ouDateTimePropertyId = rId;
+ }
+}
+
+void XSecController::setDescription(OUString const& rId, OUString const& rDescription)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.ouDescription = rDescription;
+ if (!rId.isEmpty())
+ {
+ rInformation.signatureInfor.ouDescriptionPropertyId = rId;
+ }
+}
+
+void XSecController::setSignatureBytes(const uno::Sequence<sal_Int8>& rBytes)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.aSignatureBytes = rBytes;
+}
+
+void XSecController::setX509CertDigest(
+ OUString const& rCertDigest, sal_Int32 const /*TODO nReferenceDigestID*/,
+ std::u16string_view const& rX509IssuerName, std::u16string_view const& rX509SerialNumber)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ for (auto & rData : rInformation.signatureInfor.X509Datas)
+ {
+ for (auto & it : rData)
+ {
+ if (xmlsecurity::EqualDistinguishedNames(it.X509IssuerName, rX509IssuerName, xmlsecurity::COMPAT_BOTH)
+ && it.X509SerialNumber == rX509SerialNumber)
+ {
+ it.CertDigest = rCertDigest;
+ return;
+ }
+ }
+ }
+ // fall-back: read the actual certificates
+ for (auto & rData : rInformation.signatureInfor.X509Datas)
+ {
+ for (auto & it : rData)
+ {
+ if (!it.X509Certificate.isEmpty())
+ {
+ try
+ {
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment());
+ uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it.X509Certificate));
+ if (!xCert.is())
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ else if (xmlsecurity::EqualDistinguishedNames(xCert->getIssuerName(), rX509IssuerName, xmlsecurity::COMPAT_2ND)
+ && xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()) == rX509SerialNumber)
+ {
+ it.CertDigest = rCertDigest;
+ // note: testInsertCertificate_PEM_DOCX requires these!
+ it.X509SerialNumber = rX509SerialNumber;
+ it.X509IssuerName = rX509IssuerName;
+ return;
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ }
+ }
+ }
+ if (!rInformation.signatureInfor.ouGpgCertificate.isEmpty())
+ {
+ SAL_INFO_IF(rCertDigest != rInformation.signatureInfor.ouGpgKeyID,
+ "xmlsecurity.helper", "PGPKeyID vs CertDigest mismatch");
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot find X509Data for CertDigest");
+ }
+}
+
+namespace {
+Reference<css::graphic::XGraphic> lcl_getGraphicFromString(std::u16string_view rImage)
+{
+ Sequence<sal_Int8> seq;
+ comphelper::Base64::decode(seq, rImage);
+
+ Reference< graphic::XGraphic > xGraphic;
+ if( !seq.hasElements() )
+ return Reference<css::graphic::XGraphic>();
+
+ Reference< graphic::XGraphicProvider > xGraphicProvider(
+ graphic::GraphicProvider::create(comphelper::getProcessComponentContext()) );
+ Reference< io::XInputStream > xInputStream( new ::comphelper::SequenceInputStream( seq ) );
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("InputStream", xInputStream) };
+ xGraphic = xGraphicProvider->queryGraphic(aArgs);
+
+ return xGraphic;
+}
+}
+
+void XSecController::setValidSignatureImage(std::u16string_view rValidSigImg)
+{
+ if (m_vInternalSignatureInformations.empty() || rValidSigImg.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.aValidSignatureImage = lcl_getGraphicFromString(rValidSigImg);
+}
+
+void XSecController::setInvalidSignatureImage(std::u16string_view rInvalidSigImg)
+{
+ if (m_vInternalSignatureInformations.empty() || rInvalidSigImg.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.aInvalidSignatureImage = lcl_getGraphicFromString(rInvalidSigImg);
+}
+
+void XSecController::setSignatureLineId(const OUString& rSignatureLineId)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.ouSignatureLineId = rSignatureLineId;
+}
+
+void XSecController::addEncapsulatedX509Certificate(const OUString& rEncapsulatedX509Certificate)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ if (rEncapsulatedX509Certificate.isEmpty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.maEncapsulatedX509Certificates.insert(rEncapsulatedX509Certificate);
+}
+
+void XSecController::setId( OUString const & ouId )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setId: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouSignatureId = ouId;
+}
+
+/* public: for signature verify */
+void XSecController::collectToVerify( std::u16string_view referenceId )
+{
+ /* SAL_WARN_IF( !m_xSAXEventKeeper.is(), "xmlsecurity", "the SAXEventKeeper is NULL" ); */
+
+ if ( m_eStatusOfSecurityComponents != InitializationState::INITIALIZED )
+ /*
+ * if all security components are ready, verify the signature.
+ */
+ return;
+
+ bool bJustChainingOn = false;
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler;
+
+ int i,j;
+ int sigNum = m_vInternalSignatureInformations.size();
+
+ for (i=0; i<sigNum; ++i)
+ {
+ InternalSignatureInformation& isi = m_vInternalSignatureInformations[i];
+ SignatureReferenceInformations& vReferenceInfors = isi.signatureInfor.vSignatureReferenceInfors;
+ int refNum = vReferenceInfors.size();
+
+ for (j=0; j<refNum; ++j)
+ {
+ SignatureReferenceInformation &refInfor = vReferenceInfors[j];
+
+ if (refInfor.ouURI == referenceId)
+ {
+ if (chainOn())
+ {
+ bJustChainingOn = true;
+ xHandler = m_xSAXEventKeeper->setNextHandler(nullptr);
+ }
+
+ sal_Int32 nKeeperId = m_xSAXEventKeeper->addSecurityElementCollector(
+ css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY, false );
+
+ css::uno::Reference<css::xml::crypto::sax::XReferenceCollector> xReferenceCollector
+ ( isi.xReferenceResolvedListener, css::uno::UNO_QUERY );
+
+ m_xSAXEventKeeper->setSecurityId(nKeeperId, isi.signatureInfor.nSecurityId);
+ m_xSAXEventKeeper->addReferenceResolvedListener( nKeeperId, isi.xReferenceResolvedListener);
+ xReferenceCollector->setReferenceId( nKeeperId );
+
+ isi.vKeeperIds[j] = nKeeperId;
+ break;
+ }
+ }
+ }
+
+ if ( bJustChainingOn )
+ {
+ m_xSAXEventKeeper->setNextHandler(xHandler);
+ }
+}
+
+void XSecController::addSignature( sal_Int32 nSignatureId )
+{
+ SAL_WARN_IF( !m_xSecParser.is(), "xmlsecurity.helper", "No XSecParser initialized" );
+
+ m_nReservedSignatureId = nSignatureId;
+ m_bVerifyCurrentSignature = true;
+}
+
+css::uno::Reference< css::xml::sax::XDocumentHandler > const & XSecController::createSignatureReader(XMLSignatureHelper& rXMLSignatureHelper, sal_Int32 nType)
+{
+ if (nType == embed::StorageFormats::OFOPXML)
+ m_xSecParser = new OOXMLSecParser(rXMLSignatureHelper, this);
+ else
+ m_xSecParser = new XSecParser(rXMLSignatureHelper, this);
+ css::uno::Reference< css::lang::XInitialization > xInitialization(m_xSecParser, uno::UNO_QUERY);
+
+ setSAXChainConnector(xInitialization);
+
+ return m_xSecParser;
+}
+
+void XSecController::releaseSignatureReader()
+{
+ clearSAXChainConnector( );
+ m_xSecParser.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/biginteger.cxx b/xmlsecurity/source/xmlsec/biginteger.cxx
new file mode 100644
index 000000000..1a4ab6fd9
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/biginteger.cxx
@@ -0,0 +1,106 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <biginteger.hxx>
+
+#include <xmlsec-wrapper.h>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star::uno ;
+
+namespace xmlsecurity
+{
+Sequence< sal_Int8 > numericStringToBigInteger ( std::u16string_view numeral )
+{
+ xmlChar* chNumeral ;
+ const xmlSecByte* bnInteger ;
+ xmlSecSize length ;
+ xmlSecBn bn ;
+
+ OString onumeral = OUStringToOString( numeral , RTL_TEXTENCODING_ASCII_US ) ;
+
+ chNumeral = xmlStrndup( reinterpret_cast<const xmlChar*>(onumeral.getStr()), static_cast<int>(onumeral.getLength()) ) ;
+
+ if( xmlSecBnInitialize( &bn, 0 ) < 0 ) {
+ xmlFree( chNumeral ) ;
+ return Sequence< sal_Int8 >();
+ }
+
+ if( xmlSecBnFromDecString( &bn, chNumeral ) < 0 ) {
+ xmlFree( chNumeral ) ;
+ xmlSecBnFinalize( &bn ) ;
+ return Sequence< sal_Int8 >();
+ }
+
+ xmlFree( chNumeral ) ;
+
+ length = xmlSecBnGetSize( &bn ) ;
+ if( length <= 0 ) {
+ xmlSecBnFinalize( &bn ) ;
+ return Sequence< sal_Int8 >();
+ }
+
+ bnInteger = xmlSecBnGetData( &bn ) ;
+ if( bnInteger == nullptr ) {
+ xmlSecBnFinalize( &bn ) ;
+ return Sequence< sal_Int8 >();
+ }
+
+ Sequence< sal_Int8 > integer = comphelper::arrayToSequence<sal_Int8>(bnInteger, length);
+
+ xmlSecBnFinalize( &bn ) ;
+ return integer ;
+}
+
+OUString bigIntegerToNumericString ( const Sequence< sal_Int8 >& integer )
+{
+ OUString aRet ;
+
+ if( integer.hasElements() ) {
+ xmlSecBn bn ;
+ xmlChar* chNumeral ;
+
+ if( xmlSecBnInitialize( &bn, 0 ) < 0 )
+ return aRet ;
+
+ if( xmlSecBnSetData( &bn, reinterpret_cast<const unsigned char*>(integer.getConstArray()), integer.getLength() ) < 0 ) {
+ xmlSecBnFinalize( &bn ) ;
+ return aRet ;
+ }
+
+ chNumeral = xmlSecBnToDecString( &bn ) ;
+ if( chNumeral == nullptr ) {
+ xmlSecBnFinalize( &bn ) ;
+ return aRet ;
+ }
+
+ aRet = OUString::createFromAscii( reinterpret_cast<char*>(chNumeral) ) ;
+
+ xmlSecBnFinalize( &bn ) ;
+ xmlFree( chNumeral ) ;
+ }
+
+ return aRet ;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/certificateextension_certextn.cxx b/xmlsecurity/source/xmlsec/certificateextension_certextn.cxx
new file mode 100644
index 000000000..d7325759d
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/certificateextension_certextn.cxx
@@ -0,0 +1,47 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "certificateextension_certextn.hxx"
+
+#include <comphelper/sequence.hxx>
+
+CertificateExtension_CertExtn::CertificateExtension_CertExtn()
+ : m_critical(false)
+{
+}
+
+void CertificateExtension_CertExtn::setCertExtn(const unsigned char* value, unsigned int vlen,
+ const unsigned char* id, unsigned int idlen, bool critical)
+{
+ if( value != nullptr && vlen != 0 ) {
+ m_xExtnValue = comphelper::arrayToSequence<sal_Int8>(value, vlen);
+ } else {
+ m_xExtnValue = css::uno::Sequence<sal_Int8>();
+ }
+
+ if( id != nullptr && idlen != 0 ) {
+ m_xExtnId = comphelper::arrayToSequence<sal_Int8>(id, idlen);
+ } else {
+ m_xExtnId = css::uno::Sequence<sal_Int8>();
+ }
+
+ m_critical = critical ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/certificateextension_certextn.hxx b/xmlsecurity/source/xmlsec/certificateextension_certextn.hxx
new file mode 100644
index 000000000..ac84596c8
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/certificateextension_certextn.hxx
@@ -0,0 +1,36 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <com/sun/star/uno/Sequence.hxx>
+
+struct CertificateExtension_CertExtn
+{
+ bool m_critical;
+ css::uno::Sequence<sal_Int8> m_xExtnId;
+ css::uno::Sequence<sal_Int8> m_xExtnValue;
+
+ CertificateExtension_CertExtn();
+ void setCertExtn(unsigned char const* value, unsigned int vlen, unsigned char const* id,
+ unsigned int idlen, bool critical);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/certificateextension_xmlsecimpl.hxx b/xmlsecurity/source/xmlsec/certificateextension_xmlsecimpl.hxx
new file mode 100644
index 000000000..9db5867fc
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/certificateextension_xmlsecimpl.hxx
@@ -0,0 +1,56 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/security/XCertificateExtension.hpp>
+#include "certificateextension_certextn.hxx"
+
+class CertificateExtension_XmlSecImpl : public ::cppu::WeakImplHelper<
+ css::security::XCertificateExtension >
+{
+ private:
+ CertificateExtension_CertExtn m_Extn;
+
+ public:
+ //Methods from XCertificateExtension
+ virtual sal_Bool SAL_CALL isCritical() override
+ {
+ return m_Extn.m_critical;
+ }
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getExtensionId() override
+ {
+ return m_Extn.m_xExtnId;
+ }
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getExtensionValue() override
+ {
+ return m_Extn.m_xExtnValue;
+ }
+
+ void setCertExtn(unsigned char const * value, unsigned int vlen, unsigned char const * id, unsigned int idlen, bool critical)
+ {
+ m_Extn.setCertExtn(value, vlen, id, idlen, critical);
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/errorcallback.cxx b/xmlsecurity/source/xmlsec/errorcallback.cxx
new file mode 100644
index 000000000..a535ffa77
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/errorcallback.cxx
@@ -0,0 +1,68 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <xmlsec-wrapper.h>
+
+#include <xmlsec/errorcallback.hxx>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#ifdef _WIN32
+#include <prewin.h>
+#include <postwin.h>
+#include <comphelper/windowserrorstring.hxx>
+#endif
+
+extern "C" {
+
+static void errorCallback(const char* file,
+ int line,
+ const char* func,
+ const char* errorObject,
+ const char* errorSubject,
+ int reason,
+ const char* msg)
+{
+ const char* pErrorObject = errorObject ? errorObject : "";
+ const char* pErrorSubject = errorSubject ? errorSubject : "";
+ const char* pMsg = msg ? msg : "";
+ OUString systemErrorString;
+
+#ifdef _WIN32
+ systemErrorString = " " + WindowsErrorString(GetLastError());
+#endif
+
+ SAL_WARN("xmlsecurity.xmlsec", file << ":" << line << ": " << func << "() '" << pErrorObject << "' '" << pErrorSubject << "' " << reason << " '" << pMsg << "'" << systemErrorString);
+}
+
+}
+
+void setErrorRecorder()
+{
+ xmlSecErrorsSetCallback(errorCallback);
+}
+
+void clearErrorRecorder()
+{
+ xmlSecErrorsSetCallback(nullptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/akmngr.cxx b/xmlsecurity/source/xmlsec/mscrypt/akmngr.cxx
new file mode 100644
index 000000000..ec3cecd4a
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/akmngr.cxx
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <xmlsec-wrapper.h>
+
+#include "akmngr.hxx"
+
+#include <xmlsec/xmlsec.h>
+#include <xmlsec/keys.h>
+#include <xmlsec/keysmngr.h>
+#include <xmlsec/transforms.h>
+#include <xmlsec/errors.h>
+
+#include <xmlsec/mscng/crypto.h>
+#include <xmlsec/mscng/keysstore.h>
+#include <xmlsec/mscng/x509.h>
+#include <svl/cryptosign.hxx>
+
+namespace xmlsecurity
+{
+
+/**
+ * MSCryptoAppliedKeysMngrCreate:
+ *
+ * Create and load key store and certificate database into keys manager
+ *
+ * Returns keys manager pointer on success or NULL otherwise.
+ */
+xmlSecKeysMngrPtr MSCryptoAppliedKeysMngrCreate()
+{
+ xmlSecKeysMngrPtr keyMngr = nullptr ;
+ xmlSecKeyStorePtr keyStore = nullptr ;
+
+ keyStore = xmlSecKeyStoreCreate(xmlSecMSCngKeysStoreId);
+ if (keyStore == nullptr)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ nullptr,
+ "xmlSecKeyStoreCreate",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE) ;
+ return nullptr ;
+ }
+
+ keyMngr = xmlSecKeysMngrCreate() ;
+ if (keyMngr == nullptr)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ nullptr,
+ "xmlSecKeysMngrCreate",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE) ;
+
+ xmlSecKeyStoreDestroy(keyStore) ;
+ return nullptr ;
+ }
+
+ /*-
+ * Add key store to manager, from now on keys manager destroys the store if
+ * needed
+ */
+ if (xmlSecKeysMngrAdoptKeysStore(keyMngr, keyStore) < 0)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecKeyStoreGetName(keyStore)),
+ "xmlSecKeysMngrAdoptKeyStore",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE) ;
+
+ xmlSecKeyStoreDestroy(keyStore) ;
+ xmlSecKeysMngrDestroy(keyMngr) ;
+ return nullptr ;
+ }
+
+ /*-
+ * Initialize crypto library specific data in keys manager
+ */
+ if (xmlSecMSCngKeysMngrInit(keyMngr) < 0)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ nullptr,
+ "xmlSecMSCngKeysMngrInit",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+
+ xmlSecKeysMngrDestroy(keyMngr);
+ return nullptr;
+ }
+
+ /*-
+ * Set certificate database to X509 key data store
+ */
+ /*-
+ * At present, MS Crypto engine do not provide a way to setup a cert store.
+ */
+
+ /*-
+ * Set the getKey callback
+ */
+ keyMngr->getKey = xmlSecKeysMngrGetKey ;
+
+ return keyMngr ;
+}
+
+int
+MSCryptoAppliedKeysMngrAdoptKeyStore(
+ xmlSecKeysMngrPtr mngr,
+ HCERTSTORE keyStore
+)
+{
+ xmlSecKeyDataStorePtr x509Store ;
+
+ xmlSecAssert2(mngr != nullptr, -1) ;
+ xmlSecAssert2(keyStore != nullptr, -1) ;
+
+ x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecMSCngX509StoreId);
+ if (x509Store == nullptr)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ nullptr,
+ "xmlSecKeysMngrGetDataStore",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE) ;
+ return -1 ;
+ }
+
+ if (xmlSecMSCngX509StoreAdoptKeyStore(x509Store, keyStore) < 0)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(x509Store)),
+ "xmlSecMSCngX509StoreAdoptKeyStore",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return -1;
+ }
+
+ return 0 ;
+}
+
+int
+MSCryptoAppliedKeysMngrAdoptTrustedStore(
+ xmlSecKeysMngrPtr mngr,
+ HCERTSTORE trustedStore
+)
+{
+ xmlSecKeyDataStorePtr x509Store ;
+
+ xmlSecAssert2(mngr != nullptr, -1) ;
+ xmlSecAssert2(trustedStore != nullptr, -1) ;
+
+ x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecMSCngX509StoreId);
+ if (x509Store == nullptr)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ nullptr,
+ "xmlSecKeysMngrGetDataStore",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE) ;
+ return -1 ;
+ }
+
+ if (xmlSecMSCngX509StoreAdoptTrustedStore(x509Store, trustedStore) < 0)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(x509Store)),
+ "xmlSecMSCngX509StoreAdoptKeyStore",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return -1;
+ }
+
+ return 0 ;
+}
+
+int
+MSCryptoAppliedKeysMngrAdoptUntrustedStore(
+ xmlSecKeysMngrPtr mngr,
+ HCERTSTORE untrustedStore
+)
+{
+ xmlSecKeyDataStorePtr x509Store ;
+
+ xmlSecAssert2(mngr != nullptr, -1) ;
+ xmlSecAssert2(untrustedStore != nullptr, -1) ;
+
+ x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecMSCngX509StoreId);
+ if (x509Store == nullptr)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ nullptr,
+ "xmlSecKeysMngrGetDataStore",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE) ;
+ return -1 ;
+ }
+
+ if (xmlSecMSCngX509StoreAdoptUntrustedStore(x509Store, untrustedStore) < 0)
+ {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(x509Store)),
+ "xmlSecMSCngX509StoreAdoptKeyStore",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return -1;
+ }
+
+ return 0 ;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/akmngr.hxx b/xmlsecurity/source/xmlsec/mscrypt/akmngr.hxx
new file mode 100644
index 000000000..e5942ba2a
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/akmngr.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <xmlsec/xmlsec.h>
+#include <xmlsec/keys.h>
+#include <xmlsec/transforms.h>
+
+namespace xmlsecurity
+{
+
+xmlSecKeysMngrPtr MSCryptoAppliedKeysMngrCreate();
+
+int
+MSCryptoAppliedKeysMngrAdoptKeyStore(
+ xmlSecKeysMngrPtr mngr,
+ HCERTSTORE keyStore
+) ;
+
+int
+MSCryptoAppliedKeysMngrAdoptTrustedStore(
+ xmlSecKeysMngrPtr mngr,
+ HCERTSTORE trustedStore
+) ;
+
+int
+MSCryptoAppliedKeysMngrAdoptUntrustedStore(
+ xmlSecKeysMngrPtr mngr,
+ HCERTSTORE untrustedStore
+) ;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/oid.hxx b/xmlsecurity/source/xmlsec/mscrypt/oid.hxx
new file mode 100644
index 000000000..ce573d882
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/oid.hxx
@@ -0,0 +1,153 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/macros.h>
+
+typedef struct {
+ char const *oid;
+ char const *desc;
+} OIDItem;
+
+OIDItem OIDs[] = {
+ {"1.2.840.113549", "RSA Data Security Inc."},
+ {"1.2.840.113549.1", "PKCS"},
+ {"1.2.840.113549.2", "RSA digest algorithm"},
+ {"1.2.840.113549.3", "RSA cipher algorithm"},
+ {"1.2.840.113549.1.1", "PKCS #1"},
+ {"1.2.840.113549.1.2", "Unknown"},
+ {"1.2.840.113549.1.3", "Unknown"},
+ {"1.2.840.113549.1.4", "Unknown"},
+ {"1.2.840.113549.1.5", "PKCS #5"},
+ {"1.2.840.113549.1.6", "Unknown"},
+ {"1.2.840.113549.1.7", "PKCS #7"},
+ {"1.2.840.113549.1.8", "Unknown"},
+ {"1.2.840.113549.1.9", "PKCS #9"},
+ {"1.2.840.113549.1.10", "Unknown"},
+ {"1.2.840.113549.1.12", "PKCS #12"},
+ {"1.2.840.113549.1.1.2", "PKCS #1 MD2 With RSA Encryption"},
+ {"1.2.840.113549.1.1.3", "PKCS #1 MD4 With RSA Encryption"},
+ {"1.2.840.113549.1.1.4", "PKCS #1 MD5 With RSA Encryption"},
+ {"1.2.840.113549.1.1.1", "PKCS #1 RSA Encryption"},
+ {"1.2.840.113549.1.1.2", "PKCS #1 MD2 With RSA Encryption"},
+ {"1.2.840.113549.1.1.3", "PKCS #1 MD4 With RSA Encryption"},
+ {"1.2.840.113549.1.1.4", "PKCS #1 MD5 With RSA Encryption"},
+ {"1.2.840.113549.1.1.5", "PKCS #1 SHA-1 With RSA Encryption"},
+ {"1.2.840.113549.1.1.5", "PKCS #1 SHA-1 With RSA Encryption"},
+ {"1.2.840.113549.1.3.1", "Unknown"},
+ {"1.2.840.113549.1.7.1", "PKCS #7 Data"},
+ {"1.2.840.113549.1.7.2", "PKCS #7 Signed Data"},
+ {"1.2.840.113549.1.7.3", "PKCS #7 Enveloped Data"},
+ {"1.2.840.113549.1.7.4", "PKCS #7 Signed and Enveloped Data"},
+ {"1.2.840.113549.1.7.5", "PKCS #7 Digested Data"},
+ {"1.2.840.113549.1.7.6", "PKCS #7 Encrypted Data"},
+ {"1.2.840.113549.1.9.1", "PKCS #9 Email Address"},
+ {"1.2.840.113549.1.9.2", "PKCS #9 Unstructured Name"},
+ {"1.2.840.113549.1.9.3", "PKCS #9 Content Type"},
+ {"1.2.840.113549.1.9.4", "PKCS #9 Message Digest"},
+ {"1.2.840.113549.1.9.5", "PKCS #9 Signing Time"},
+ {"1.2.840.113549.1.9.6", "PKCS #9 Counter Signature"},
+ {"1.2.840.113549.1.9.7", "PKCS #9 Challenge Password"},
+ {"1.2.840.113549.1.9.8", "PKCS #9 Unstructured Address"},
+ {"1.2.840.113549.1.9.9", "PKCS #9 Extended Certificate Attributes"},
+ {"1.2.840.113549.1.9.15", "PKCS #9 S/MIME Capabilities"},
+ {"1.2.840.113549.1.9.15.1", "Unknown"},
+ {"1.2.840.113549.3.2", "RC2-CBC"},
+ {"1.2.840.113549.3.4", "RC4"},
+ {"1.2.840.113549.3.7", "DES-EDE3-CBC"},
+ {"1.2.840.113549.3.9", "RC5-CBCPad"},
+ {"1.2.840.10046", "ANSI X9.42"},
+ {"1.2.840.10046.2.1", "Diffie-Hellman Public Key Algorithm"},
+ {"1.2.840.10040", "ANSI X9.57"},
+ {"1.2.840.10040.4.1", "ANSI X9.57 DSA Signature"},
+ {"1.2.840.10040.4.3", "ANSI X9.57 Algorithm DSA Signature with SHA-1 Digest"},
+ {"2.5", "Directory"},
+ {"2.5.8", "X.500-defined algorithms"},
+ {"2.5.8.1", "X.500-defined encryption algorithms"},
+ {"2.5.8.2", "Unknown"},
+ {"2.5.8.3", "Unknown"},
+ {"2.5.8.1.1", "RSA Encryption Algorithm"},
+ {"1.3.14", "Open Systems Implementors Workshop"},
+ {"1.3.14.3.2", "OIW SECSIG Algorithm"},
+ {"1.3.14.3.2.2", "Unknown"},
+ {"1.3.14.3.2.3", "Unknown"},
+ {"1.3.14.3.2.4", "Unknown"},
+ {"1.3.14.3.2.6", "DES-ECB"},
+ {"1.3.14.3.2.7", "DES-CBC"},
+ {"1.3.14.3.2.8", "DES-OFB"},
+ {"1.3.14.3.2.9", "DES-CFB"},
+ {"1.3.14.3.2.10", "DES-MAC"},
+ {"1.3.14.3.2.11", "Unknown"},
+ {"1.3.14.3.2.12", "Unknown"},
+ {"1.3.14.3.2.13", "Unknown"},
+ {"1.3.14.3.2.14", "Unknown"},
+ {"1.3.14.3.2.15", "ISO SHA with RSA Signature"},
+ {"1.3.14.3.2.16", "Unknown"},
+ {"1.3.14.3.2.17", "DES-EDE"},
+ {"1.3.14.3.2.18", "Unknown"},
+ {"1.3.14.3.2.19", "Unknown"},
+ {"1.3.14.3.2.20", "Unknown"},
+ {"1.3.14.3.2.21", "Unknown"},
+ {"1.3.14.3.2.22", "Unknown"},
+ {"1.3.14.3.2.23", "Unknown"},
+ {"1.3.14.3.2.24", "Unknown"},
+ {"1.3.14.3.2.25", "Unknown"},
+ {"1.3.14.3.2.26", "SHA-1"},
+ {"1.3.14.3.2.27", "Forgezza DSA Signature with SHA-1 Digest"},
+ {"1.3.14.3.2.28", "Unknown"},
+ {"1.3.14.3.2.29", "Unknown"},
+ {"1.3.14.7.2", "Unknown"},
+ {"1.3.14.7.2.1", "Unknown"},
+ {"1.3.14.7.2.2", "Unknown"},
+ {"1.3.14.7.2.3", "Unknown"},
+ {"1.3.14.7.2.2.1", "Unknown"},
+ {"1.3.14.7.2.3.1", "Unknown"},
+ {"2.16.840.1.101.2.1", "US DOD Infosec"},
+ {"2.16.840.1.101.2.1.1.1", "Unknown"},
+ {"2.16.840.1.101.2.1.1.2", "MISSI DSS Algorithm (Old)"},
+ {"2.16.840.1.101.2.1.1.3", "Unknown"},
+ {"2.16.840.1.101.2.1.1.4", "Skipjack CBC64"},
+ {"2.16.840.1.101.2.1.1.5", "Unknown"},
+ {"2.16.840.1.101.2.1.1.6", "Unknown"},
+ {"2.16.840.1.101.2.1.1.7", "Unknown"},
+ {"2.16.840.1.101.2.1.1.8", "Unknown"},
+ {"2.16.840.1.101.2.1.1.9", "Unknown"},
+ {"2.16.840.1.101.2.1.1.10", "MISSI KEA Algorithm"},
+ {"2.16.840.1.101.2.1.1.11", "Unknown"},
+ {"2.16.840.1.101.2.1.1.12", "MISSI KEA and DSS Algorithm (Old)"},
+ {"2.16.840.1.101.2.1.1.13", "Unknown"},
+ {"2.16.840.1.101.2.1.1.14", "Unknown"},
+ {"2.16.840.1.101.2.1.1.15", "Unknown"},
+ {"2.16.840.1.101.2.1.1.16", "Unknown"},
+ {"2.16.840.1.101.2.1.1.17", "Unknown"},
+ {"2.16.840.1.101.2.1.1.18", "Unknown"},
+ {"2.16.840.1.101.2.1.1.19", "MISSI DSS Algorithm"},
+ {"2.16.840.1.101.2.1.1.20", "MISSI KEA and DSS Algorithm"},
+ {"2.16.840.1.101.2.1.1.21", "Unknown"},
+ {"1.2.643.2.2.35.0", "GOST_R_34.10-2001_Test"},
+ {"1.2.643.2.2.35.1", "GOST_R_34.10-2001_Sign_DH_PRO"},
+ {"1.2.643.2.2.35.2", "GOST_R_34.10-2001_Sign_DH_CARD"},
+ {"1.2.643.2.2.35.3", "GOST_R_34.10-2001_Sign_DH"},
+ {"1.2.643.2.2.36.0", "GOST_R_34.10-2001_Sign_DH_PRO"}
+};
+
+const int nOID = SAL_N_ELEMENTS(OIDs);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.cxx
new file mode 100644
index 000000000..6ded5fa0c
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.cxx
@@ -0,0 +1,131 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <rtl/uuid.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/security/ExtAltNameType.hpp>
+#include <com/sun/star/security/CertAltNameEntry.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <comphelper/sequence.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include "sanextension_mscryptimpl.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::security ;
+
+using ::com::sun::star::security::XCertificateExtension ;
+
+
+SanExtensionImpl::SanExtensionImpl() :
+ m_critical( false )
+{
+}
+
+SanExtensionImpl::~SanExtensionImpl() {
+}
+
+
+//Methods from XCertificateExtension
+sal_Bool SAL_CALL SanExtensionImpl::isCritical() {
+ return m_critical ;
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL SanExtensionImpl::getExtensionId() {
+ return m_xExtnId ;
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL SanExtensionImpl::getExtensionValue() {
+ return m_xExtnValue ;
+}
+
+//Methods from XSanExtension
+css::uno::Sequence< css::security::CertAltNameEntry > SAL_CALL SanExtensionImpl::getAlternativeNames(){
+
+ if (!m_Entries.hasElements())
+ {
+ CERT_ALT_NAME_INFO *subjectName;
+ DWORD size;
+ CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME, reinterpret_cast<unsigned char*>(m_xExtnValue.getArray()), m_xExtnValue.getLength(), CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, nullptr,&subjectName, &size);
+
+ auto arrCertAltNameEntry = std::make_unique<CertAltNameEntry[]>(subjectName->cAltEntry);
+
+ for (unsigned int i = 0; i < static_cast<unsigned int>(subjectName->cAltEntry); i++){
+ PCERT_ALT_NAME_ENTRY pEntry = &subjectName->rgAltEntry[i];
+
+ switch(pEntry->dwAltNameChoice) {
+ case CERT_ALT_NAME_OTHER_NAME :
+ {
+ arrCertAltNameEntry[i].Type = ExtAltNameType_OTHER_NAME;
+ PCERT_OTHER_NAME pOtherName = pEntry->pOtherName;
+
+ css::beans::NamedValue otherNameProp;
+ otherNameProp.Name = OUString::createFromAscii(pOtherName->pszObjId);
+
+ Sequence< sal_Int8 > otherName( comphelper::arrayToSequence<sal_Int8>(
+ pOtherName->Value.pbData, pOtherName->Value.cbData) );
+ otherNameProp.Value <<= otherName;
+
+ arrCertAltNameEntry[i].Value <<= otherNameProp;
+ break;
+ }
+ case CERT_ALT_NAME_RFC822_NAME :
+ arrCertAltNameEntry[i].Type = ExtAltNameType_RFC822_NAME;
+ arrCertAltNameEntry[i].Value <<= OUString(o3tl::toU(pEntry->pwszRfc822Name));
+ break;
+ case CERT_ALT_NAME_DNS_NAME :
+ arrCertAltNameEntry[i].Type = ExtAltNameType_DNS_NAME;
+ arrCertAltNameEntry[i].Value <<= OUString(o3tl::toU(pEntry->pwszDNSName));
+ break;
+ case CERT_ALT_NAME_DIRECTORY_NAME :
+ {
+ arrCertAltNameEntry[i].Type = ExtAltNameType_DIRECTORY_NAME;
+ break;
+ }
+ case CERT_ALT_NAME_URL :
+ arrCertAltNameEntry[i].Type = ExtAltNameType_URL;
+ arrCertAltNameEntry[i].Value <<= OUString(o3tl::toU(pEntry->pwszURL));
+ break;
+ case CERT_ALT_NAME_IP_ADDRESS :
+ {
+ arrCertAltNameEntry[i].Type = ExtAltNameType_IP_ADDRESS;
+
+ Sequence< sal_Int8 > ipAddress( comphelper::arrayToSequence<sal_Int8>(
+ pEntry->IPAddress.pbData, pEntry->IPAddress.cbData) );
+ arrCertAltNameEntry[i].Value <<= ipAddress;
+ break;
+ }
+ case CERT_ALT_NAME_REGISTERED_ID :
+ arrCertAltNameEntry[i].Type = ExtAltNameType_REGISTERED_ID;
+ arrCertAltNameEntry[i].Value <<= OUString::createFromAscii(pEntry->pszRegisteredID);
+ break;
+ }
+ }
+ m_Entries = ::comphelper::arrayToSequence< css::security::CertAltNameEntry >(arrCertAltNameEntry.get(), subjectName->cAltEntry);
+ }
+
+ return m_Entries;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.hxx b/xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.hxx
new file mode 100644
index 000000000..6c65c84f8
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/sanextension_mscryptimpl.hxx
@@ -0,0 +1,66 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <Windows.h>
+#include <WinCrypt.h>
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/SecurityException.hpp>
+#include <com/sun/star/security/XCertificateExtension.hpp>
+#include <com/sun/star/security/XSanExtension.hpp>
+#include <com/sun/star/security/CertAltNameEntry.hpp>
+
+class SanExtensionImpl : public ::cppu::WeakImplHelper<
+ css::security::XSanExtension >
+{
+ private:
+ bool m_critical ;
+ css::uno::Sequence< sal_Int8 > m_xExtnId ;
+ css::uno::Sequence< sal_Int8 > m_xExtnValue ;
+
+ css::uno::Sequence< css::security::CertAltNameEntry > m_Entries;
+
+ public:
+ SanExtensionImpl() ;
+ virtual ~SanExtensionImpl() override;
+
+ //Methods from XCertificateExtension
+ virtual sal_Bool SAL_CALL isCritical() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getExtensionId() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getExtensionValue() override;
+
+ //Methods from XSanExtension
+
+ virtual css::uno::Sequence< css::security::CertAltNameEntry > SAL_CALL getAlternativeNames() override;
+
+ //Helper method
+ void setCertExtn( unsigned char* value, unsigned int vlen, unsigned char* id, unsigned int idlen, bool critical ) ;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
new file mode 100644
index 000000000..f198f839f
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.cxx
@@ -0,0 +1,1118 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstddef>
+#include <string.h>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <Windows.h>
+#include <WinCrypt.h>
+#include <sal/macros.h>
+#include <osl/thread.h>
+#include "securityenvironment_mscryptimpl.hxx"
+
+#include "x509certificate_mscryptimpl.hxx"
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <xmlsec-wrapper.h>
+#include "akmngr.hxx"
+
+#include <biginteger.hxx>
+
+#include <comphelper/windowserrorstring.hxx>
+#include <sal/log.hxx>
+#include <rtl/locale.h>
+#include <rtl/ref.hxx>
+#include <osl/nlsupport.h>
+#include <osl/process.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <svl/cryptosign.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang ;
+using ::com::sun::star::lang::XMultiServiceFactory ;
+using ::com::sun::star::lang::XSingleServiceFactory ;
+
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::security::XCertificate ;
+
+static rtl::Reference<X509Certificate_MSCryptImpl> MswcryCertContextToXCert( PCCERT_CONTEXT cert ) ;
+
+namespace {
+
+struct CertErrorToString{
+ DWORD error;
+ char const * name;
+};
+
+}
+
+CertErrorToString const arErrStrings[] =
+{
+ { 0x00000000, "CERT_TRUST_NO_ERROR"},
+ { 0x00000001, "CERT_TRUST_IS_NOT_TIME_VALID"},
+ { 0x00000002, "CERT_TRUST_IS_NOT_TIME_NESTED"},
+ { 0x00000004, "CERT_TRUST_IS_REVOKED" },
+ { 0x00000008, "CERT_TRUST_IS_NOT_SIGNATURE_VALID" },
+ { 0x00000010, "CERT_TRUST_IS_NOT_SIGNATURE_VALID"},
+ { 0x00000020, "CERT_TRUST_IS_UNTRUSTED_ROOT"},
+ { 0x00000040, "CERT_TRUST_REVOCATION_STATUS_UNKNOWN"},
+ { 0x00000080, "CERT_TRUST_IS_CYCLIC"},
+ { 0x00000100, "CERT_TRUST_INVALID_EXTENSION"},
+ { 0x00000200, "CERT_TRUST_INVALID_POLICY_CONSTRAINTS"},
+ { 0x00000400, "CERT_TRUST_INVALID_BASIC_CONSTRAINTS"},
+ { 0x00000800, "CERT_TRUST_INVALID_NAME_CONSTRAINTS"},
+ { 0x00001000, "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT"},
+ { 0x00002000, "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT"},
+ { 0x00004000, "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT"},
+ { 0x00008000, "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT"},
+ { 0x01000000, "CERT_TRUST_IS_OFFLINE_REVOCATION"},
+ { 0x02000000, "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY"},
+ { 0x04000000, "CERT_TRUST_IS_EXPLICIT_DISTRUST"},
+ { 0x08000000, "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT"},
+ //Chain errors
+ { 0x00010000, "CERT_TRUST_IS_PARTIAL_CHAIN"},
+ { 0x00020000, "CERT_TRUST_CTL_IS_NOT_TIME_VALID"},
+ { 0x00040000, "CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID"},
+ { 0x00080000, "CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE"}
+};
+
+static void traceTrustStatus(DWORD err)
+{
+ if (err == 0)
+ SAL_INFO("xmlsecurity.xmlsec", " " << arErrStrings[0].name);
+ for (std::size_t i = 1; i < SAL_N_ELEMENTS(arErrStrings); i++)
+ {
+ if (arErrStrings[i].error & err)
+ SAL_INFO("xmlsecurity.xmlsec", " " << arErrStrings[i].name);
+ }
+}
+
+SecurityEnvironment_MSCryptImpl::SecurityEnvironment_MSCryptImpl( const uno::Reference< uno::XComponentContext >& xContext ) : m_hProv( 0 ) , m_pszContainer( nullptr ) , m_hKeyStore( nullptr ), m_hCertStore( nullptr ), m_hMySystemStore(nullptr), m_hRootSystemStore(nullptr), m_hTrustSystemStore(nullptr), m_hCaSystemStore(nullptr), m_bEnableDefault( false ){
+
+ m_xServiceManager.set(xContext, uno::UNO_QUERY);
+}
+
+SecurityEnvironment_MSCryptImpl::~SecurityEnvironment_MSCryptImpl() {
+
+ if( m_hProv != 0 ) {
+ CryptReleaseContext( m_hProv, 0 ) ;
+ m_hProv = 0 ;
+ }
+
+ if( m_pszContainer != nullptr ) {
+ //TODO: Don't know whether or not it should be released now.
+ m_pszContainer = nullptr ;
+ }
+
+ if( m_hCertStore != nullptr ) {
+ CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hCertStore = nullptr ;
+ }
+
+ if( m_hKeyStore != nullptr ) {
+ CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hKeyStore = nullptr ;
+ }
+
+ //i120675, close the store handles
+ if( m_hMySystemStore != nullptr ) {
+ CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hMySystemStore = nullptr ;
+ }
+
+ if( m_hRootSystemStore != nullptr ) {
+ CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hRootSystemStore = nullptr ;
+ }
+
+ if( m_hTrustSystemStore != nullptr ) {
+ CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hTrustSystemStore = nullptr ;
+ }
+
+ if( m_hCaSystemStore != nullptr ) {
+ CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hCaSystemStore = nullptr ;
+ }
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SecurityEnvironment_MSCryptImpl::getImplementationName() {
+ return "com.sun.star.xml.crypto.SecurityEnvironment";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SecurityEnvironment_MSCryptImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+/* XServiceInfo */
+uno::Sequence< OUString > SAL_CALL SecurityEnvironment_MSCryptImpl::getSupportedServiceNames() {
+ return { "com.sun.star.xml.crypto.SecurityEnvironment" };
+}
+
+/* XUnoTunnel */
+UNO3_GETIMPLEMENTATION_IMPL(SecurityEnvironment_MSCryptImpl);
+
+HCRYPTPROV SecurityEnvironment_MSCryptImpl::getCryptoProvider() {
+ return m_hProv ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setCryptoProvider( HCRYPTPROV aProv ) {
+ if( m_hProv != 0 ) {
+ CryptReleaseContext( m_hProv, 0 ) ;
+ m_hProv = 0 ;
+ }
+
+ if( aProv != 0 ) {
+ m_hProv = aProv ;
+ }
+}
+
+LPCTSTR SecurityEnvironment_MSCryptImpl::getKeyContainer() {
+ return m_pszContainer ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setKeyContainer( LPCTSTR aKeyContainer ) {
+ //TODO: Don't know whether or not it should be copied.
+ m_pszContainer = aKeyContainer ;
+}
+
+
+HCERTSTORE SecurityEnvironment_MSCryptImpl::getCryptoSlot() {
+ return m_hKeyStore ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setCryptoSlot( HCERTSTORE aSlot) {
+ if( m_hKeyStore != nullptr ) {
+ CertCloseStore( m_hKeyStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hKeyStore = nullptr ;
+ }
+
+ if( aSlot != nullptr ) {
+ m_hKeyStore = CertDuplicateStore( aSlot ) ;
+ }
+}
+
+HCERTSTORE SecurityEnvironment_MSCryptImpl::getCertDb() {
+ return m_hCertStore ;
+}
+
+void SecurityEnvironment_MSCryptImpl::setCertDb( HCERTSTORE aCertDb ) {
+ if( m_hCertStore != nullptr ) {
+ CertCloseStore( m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ m_hCertStore = nullptr ;
+ }
+
+ if( aCertDb != nullptr ) {
+ m_hCertStore = CertDuplicateStore( aCertDb ) ;
+ }
+}
+
+#ifdef SAL_LOG_INFO
+
+// Based on sample code from MSDN
+
+static OUString get_system_name(const void *pvSystemStore,
+ DWORD dwFlags)
+{
+ LPCWSTR ppwszSystemName;
+ if (dwFlags & CERT_SYSTEM_STORE_RELOCATE_FLAG)
+ {
+ _CERT_SYSTEM_STORE_RELOCATE_PARA const * pRelocatePara;
+ pRelocatePara = static_cast<_CERT_SYSTEM_STORE_RELOCATE_PARA const *>(pvSystemStore);
+ ppwszSystemName = pRelocatePara->pwszSystemStore;
+ }
+ else
+ {
+ ppwszSystemName = static_cast<LPCWSTR>(pvSystemStore);
+ }
+ return OUString(o3tl::toU(ppwszSystemName));
+}
+
+extern "C" {
+
+static BOOL WINAPI cert_enum_physical_store_callback(const void *,
+ DWORD dwFlags,
+ LPCWSTR pwszStoreName,
+ PCERT_PHYSICAL_STORE_INFO,
+ void *,
+ void *)
+{
+ OUString name(o3tl::toU(pwszStoreName));
+ if (dwFlags & CERT_PHYSICAL_STORE_PREDEFINED_ENUM_FLAG)
+ name += " (implicitly created)";
+ SAL_INFO("xmlsecurity.xmlsec", " Physical store: " << name);
+
+ return TRUE;
+}
+
+static BOOL WINAPI cert_enum_system_store_callback(const void *pvSystemStore,
+ DWORD dwFlags,
+ PCERT_SYSTEM_STORE_INFO,
+ void *,
+ void *)
+{
+ SAL_INFO("xmlsecurity.xmlsec", "System store: " << get_system_name(pvSystemStore, dwFlags));
+
+ if (!CertEnumPhysicalStore(pvSystemStore,
+ dwFlags,
+ nullptr,
+ cert_enum_physical_store_callback))
+ {
+ DWORD dwErr = GetLastError();
+ if (!(ERROR_FILE_NOT_FOUND == dwErr ||
+ ERROR_NOT_SUPPORTED == dwErr))
+ {
+ SAL_WARN("xmlsecurity.xmlsec", "CertEnumPhysicalStore failed:" << WindowsErrorString(GetLastError()));
+ }
+ }
+ return TRUE;
+}
+
+}
+
+#endif
+
+//Methods from XSecurityEnvironment
+uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::getPersonalCertificates()
+{
+ sal_Int32 length ;
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+ std::vector< rtl::Reference<X509Certificate_MSCryptImpl> > certsList ;
+ PCCERT_CONTEXT pCertContext = nullptr;
+
+ //firstly, we try to find private keys in given key store.
+ if( m_hKeyStore != nullptr ) {
+ pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
+ while (pCertContext)
+ {
+ xcert = MswcryCertContextToXCert( pCertContext ) ;
+ if( xcert.is() )
+ certsList.push_back( xcert ) ;
+ pCertContext = CertEnumCertificatesInStore( m_hKeyStore, pCertContext );
+ }
+ }
+
+ //Thirdly, we try to find certificate from system default key store.
+ if( m_bEnableDefault ) {
+ HCERTSTORE hSystemKeyStore ;
+ DWORD dwKeySpec;
+ NCRYPT_KEY_HANDLE hCryptKey;
+
+#ifdef SAL_LOG_INFO
+ CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, nullptr, nullptr, cert_enum_system_store_callback);
+#endif
+
+ hSystemKeyStore = CertOpenSystemStoreW( 0, L"MY" ) ;
+ if( hSystemKeyStore != nullptr ) {
+ pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
+ while (pCertContext)
+ {
+ // for checking whether the certificate is a personal certificate or not.
+ DWORD dwFlags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
+ HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hCryptKey;
+ if(!(CryptAcquireCertificatePrivateKey(pCertContext,
+ dwFlags,
+ nullptr,
+ phCryptProvOrNCryptKey,
+ &dwKeySpec,
+ nullptr)))
+ {
+ // Not Privatekey found. SKIP this one.
+ pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
+ continue;
+ }
+ // then TODO : Check the personal cert is valid or not.
+
+ xcert = MswcryCertContextToXCert( pCertContext ) ;
+ if( xcert.is() )
+ certsList.push_back( xcert ) ;
+ pCertContext = CertEnumCertificatesInStore( hSystemKeyStore, pCertContext );
+ }
+ }
+
+ CertCloseStore( hSystemKeyStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ }
+
+ length = certsList.size() ;
+ if( length != 0 ) {
+ int i = 0;
+ uno::Sequence< uno::Reference< XCertificate > > certSeq( length ) ;
+ auto pcertSeq = certSeq.getArray();
+
+ for( const auto& rXCert : certsList ) {
+ pcertSeq[i] = rXCert ;
+ ++i;
+ }
+
+ return certSeq ;
+ }
+
+ return uno::Sequence< uno::Reference< XCertificate > >() ;
+}
+
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const uno::Sequence< sal_Int8 >& serialNumber ) {
+ unsigned int i ;
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+ PCCERT_CONTEXT pCertContext = nullptr ;
+ HCERTSTORE hCertStore = nullptr ;
+ CRYPT_INTEGER_BLOB cryptSerialNumber ;
+ CERT_INFO certInfo ;
+
+ // for correct encoding
+ rtl_Locale *pLocale = nullptr ;
+ osl_getProcessLocale( &pLocale ) ;
+
+ //Create cert info from issue and serial
+ LPCWSTR pszName = o3tl::toW( issuerName.getStr() );
+
+ if( ! ( CertStrToNameW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ pszName ,
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
+ nullptr ,
+ nullptr ,
+ &certInfo.Issuer.cbData, nullptr ) )
+ ) {
+ return nullptr ;
+ }
+
+ certInfo.Issuer.pbData = static_cast<BYTE*>(malloc( certInfo.Issuer.cbData ));
+ if(!certInfo.Issuer.pbData)
+ throw uno::RuntimeException() ;
+
+ if( ! ( CertStrToNameW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ pszName ,
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG,
+ nullptr ,
+ certInfo.Issuer.pbData ,
+ &certInfo.Issuer.cbData, nullptr ) )
+ ) {
+ free( certInfo.Issuer.pbData ) ;
+ return nullptr ;
+ }
+
+ //Get the SerialNumber
+ cryptSerialNumber.cbData = serialNumber.getLength() ;
+ cryptSerialNumber.pbData = static_cast<BYTE*>(malloc( cryptSerialNumber.cbData));
+ if (!cryptSerialNumber.pbData)
+ {
+ free( certInfo.Issuer.pbData ) ;
+ throw uno::RuntimeException() ;
+ }
+ for( i = 0; i < cryptSerialNumber.cbData; i ++ )
+ cryptSerialNumber.pbData[i] = serialNumber[ cryptSerialNumber.cbData - i - 1 ] ;
+
+ certInfo.SerialNumber.cbData = cryptSerialNumber.cbData ;
+ certInfo.SerialNumber.pbData = cryptSerialNumber.pbData ;
+
+ // Get the Cert from all store.
+ for( i = 0 ; i < 6 ; i ++ )
+ {
+ switch(i)
+ {
+ case 0:
+ if(m_hKeyStore == nullptr) continue ;
+ hCertStore = m_hKeyStore ;
+ break;
+ case 1:
+ if(m_hCertStore == nullptr) continue ;
+ hCertStore = m_hCertStore ;
+ break;
+ case 2:
+ hCertStore = CertOpenSystemStoreW( 0, L"MY" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ case 3:
+ hCertStore = CertOpenSystemStoreW( 0, L"Root" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ case 4:
+ hCertStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ case 5:
+ hCertStore = CertOpenSystemStoreW( 0, L"CA" ) ;
+ if(hCertStore == nullptr || !m_bEnableDefault) continue ;
+ break;
+ default:
+ i=6;
+ continue;
+ }
+
+/*******************************************************************************
+ * This code reserved for remind us there are another way to find one cert by
+ * IssuerName&serialnumber. You can use the code to replaced the function
+ * CertFindCertificateInStore IF and ONLY IF you must find one special cert in
+ * certStore but can not be found by CertFindCertificateInStore , then , you
+ * should also change the same part in libxmlsec/.../src/mscrypto/x509vfy.c#875.
+ * By Chandler Peng(chandler.peng@sun.com)
+ *****/
+/*******************************************************************************
+ pCertContext = NULL ;
+ found = 0;
+ do{
+ // 1. enum the certs has same string in the issuer string.
+ pCertContext = CertEnumCertificatesInStore( hCertStore , pCertContext ) ;
+ if( pCertContext != NULL )
+ {
+ // 2. check the cert's issuer name .
+ char* issuer = NULL ;
+ DWORD cbIssuer = 0 ;
+
+ cbIssuer = CertNameToStr(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( pCertContext->pCertInfo->Issuer ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ NULL, 0
+ ) ;
+
+ if( cbIssuer == 0 ) continue ; // discard this cert;
+
+ issuer = (char *)malloc( cbIssuer ) ;
+ if( issuer == NULL ) // discard this cert;
+ {
+ free( cryptSerialNumber.pbData) ;
+ free( certInfo.Issuer.pbData ) ;
+ CertFreeCertificateContext( pCertContext ) ;
+ if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ throw RuntimeException() ;
+ }
+
+ cbIssuer = CertNameToStr(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( pCertContext->pCertInfo->Issuer ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ issuer, cbIssuer
+ ) ;
+
+ if( cbIssuer <= 0 )
+ {
+ free( issuer ) ;
+ continue ;// discard this cert;
+ }
+
+ if(strncmp(pszName , issuer , cbIssuer) != 0)
+ {
+ free( issuer ) ;
+ continue ;// discard this cert;
+ }
+ free( issuer ) ;
+
+ // 3. check the serial number.
+ if( memcmp( cryptSerialNumber.pbData , pCertContext->pCertInfo->SerialNumber.pbData , cryptSerialNumber.cbData ) != 0 )
+ {
+ continue ;// discard this cert;
+ }
+
+ // 4. confirm and break;
+ found = 1;
+ break ;
+ }
+
+ }while(pCertContext);
+
+ if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ if( found != 0 ) break; // Found the certificate.
+********************************************************************************/
+
+ pCertContext = CertFindCertificateInStore(
+ hCertStore,
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_CERT,
+ &certInfo,
+ nullptr
+ ) ;
+
+ if(i != 0 && i != 1) CertCloseStore( hCertStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ if( pCertContext != nullptr ) break ; // Found the certificate.
+
+ }
+
+ free(cryptSerialNumber.pbData);
+ free(certInfo.Issuer.pbData);
+
+ if( pCertContext != nullptr ) {
+ xcert = MswcryCertContextToXCert(pCertContext);
+ CertFreeCertificateContext(pCertContext);
+ }
+
+ return xcert ;
+}
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::getCertificate( const OUString& issuerName, const OUString& serialNumber ) {
+ uno::Sequence< sal_Int8 > serial = xmlsecurity::numericStringToBigInteger( serialNumber ) ;
+ return getCertificate( issuerName, serial ) ;
+}
+
+uno::Sequence< uno::Reference < XCertificate > > SecurityEnvironment_MSCryptImpl::buildCertificatePath( const uno::Reference< XCertificate >& begin ) {
+ PCCERT_CHAIN_CONTEXT pChainContext ;
+ PCCERT_CONTEXT pCertContext ;
+
+ CERT_ENHKEY_USAGE enhKeyUsage ;
+ CERT_USAGE_MATCH certUsage ;
+ CERT_CHAIN_PARA chainPara ;
+
+ enhKeyUsage.cUsageIdentifier = 0 ;
+ enhKeyUsage.rgpszUsageIdentifier = nullptr ;
+ certUsage.dwType = USAGE_MATCH_TYPE_AND ;
+ certUsage.Usage = enhKeyUsage ;
+ chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
+ chainPara.RequestedUsage = certUsage ;
+
+ uno::Reference< XUnoTunnel > xCertTunnel( begin, uno::UNO_QUERY_THROW ) ;
+ const auto* xcert = comphelper::getFromUnoTunnel<X509Certificate_MSCryptImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw uno::RuntimeException() ;
+ }
+
+ pCertContext = xcert->getMswcryCert() ;
+
+ pChainContext = nullptr ;
+
+ bool bChain = false;
+ if( pCertContext != nullptr )
+ {
+ HCERTSTORE hAdditionalStore = nullptr;
+ HCERTSTORE hCollectionStore = nullptr;
+ if (m_hCertStore && m_hKeyStore)
+ {
+ //Merge m_hCertStore and m_hKeyStore into one store.
+ hCollectionStore = CertOpenStore(
+ CERT_STORE_PROV_COLLECTION ,
+ 0 ,
+ 0 ,
+ 0 ,
+ nullptr
+ ) ;
+ if (hCollectionStore != nullptr)
+ {
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hCertStore ,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hKeyStore,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ hAdditionalStore = hCollectionStore;
+ }
+
+ }
+
+ //if the merge of both stores failed then we add only m_hCertStore
+ if (hAdditionalStore == nullptr && m_hCertStore)
+ hAdditionalStore = m_hCertStore;
+ else if (hAdditionalStore == nullptr && m_hKeyStore)
+ hAdditionalStore = m_hKeyStore;
+ else
+ hAdditionalStore = nullptr;
+
+ //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
+ bChain = CertGetCertificateChain(
+ nullptr ,
+ pCertContext ,
+ nullptr , //use current system time
+ hAdditionalStore,
+ &chainPara ,
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_TIMESTAMP_TIME ,
+ nullptr ,
+ &pChainContext);
+ if (!bChain)
+ pChainContext = nullptr;
+
+ //Close the additional store
+ CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ }
+
+ if(bChain && pChainContext != nullptr && pChainContext->cChain > 0 )
+ {
+ PCCERT_CONTEXT pCertInChain ;
+ PCERT_SIMPLE_CHAIN pCertChain ;
+ rtl::Reference<X509Certificate_MSCryptImpl> pCert ;
+
+ pCertChain = pChainContext->rgpChain[0] ;
+ if( pCertChain->cElement ) {
+ uno::Sequence< uno::Reference< XCertificate > > xCertChain( pCertChain->cElement ) ;
+ auto pxCertChain = xCertChain.getArray();
+
+ for( unsigned int i = 0 ; i < pCertChain->cElement ; i ++ ) {
+ if( pCertChain->rgpElement[i] )
+ pCertInChain = pCertChain->rgpElement[i]->pCertContext ;
+ else
+ pCertInChain = nullptr ;
+
+ if( pCertInChain != nullptr ) {
+ pCert = MswcryCertContextToXCert( pCertInChain ) ;
+ if( pCert.is() )
+ pxCertChain[i] = pCert ;
+ }
+ }
+
+ CertFreeCertificateChain( pChainContext ) ;
+ pChainContext = nullptr ;
+
+ return xCertChain ;
+ }
+ }
+ if (pChainContext)
+ CertFreeCertificateChain(pChainContext);
+
+ return uno::Sequence< uno::Reference < XCertificate > >();
+}
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromRaw( const uno::Sequence< sal_Int8 >& rawCertificate ) {
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+
+ if( rawCertificate.getLength() > 0 ) {
+ xcert = new X509Certificate_MSCryptImpl() ;
+ xcert->setRawCert( rawCertificate ) ;
+ }
+
+ return xcert ;
+}
+
+uno::Reference< XCertificate > SecurityEnvironment_MSCryptImpl::createCertificateFromAscii( const OUString& asciiCertificate ) {
+
+ OString oscert = OUStringToOString( asciiCertificate , RTL_TEXTENCODING_ASCII_US ) ;
+
+ xmlChar* chCert = xmlStrndup( reinterpret_cast<const xmlChar*>(oscert.getStr()), static_cast<int>(oscert.getLength()) ) ;
+
+ xmlSecSize certSize = xmlSecBase64Decode( chCert, chCert, xmlStrlen( chCert ) ) ;
+
+ uno::Sequence< sal_Int8 > rawCert( certSize ) ;
+ auto rawCertRange = asNonConstRange(rawCert);
+ for( xmlSecSize i = 0 ; i < certSize ; i ++ )
+ rawCertRange[i] = *( chCert + i ) ;
+
+ xmlFree( chCert ) ;
+
+ return createCertificateFromRaw( rawCert ) ;
+}
+
+
+static HCERTSTORE getCertStoreForIntermediatCerts(
+ const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
+{
+ HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, nullptr);
+ if (store == nullptr)
+ return nullptr;
+
+ for (int i = 0; i < seqCerts.getLength(); i++)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Added temporary certificate: " << seqCerts[i]->getSubjectName());
+
+ uno::Sequence<sal_Int8> data = seqCerts[i]->getEncoded();
+ PCCERT_CONTEXT cert = CertCreateCertificateContext(
+ X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(&data[0]), data.getLength());
+ //Adding the certificate creates a copy and not just increases the ref count
+ //Therefore we free later the certificate that we now add
+ CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_ALWAYS, nullptr);
+ CertFreeCertificateContext(cert);
+ }
+ return store;
+}
+
+static bool CheckUnitTestStore(PCCERT_CHAIN_CONTEXT const pChainContext, DWORD ignoreFlags)
+{
+ bool ret = false;
+ static char const*const pVar = getenv("LIBO_TEST_CRYPTOAPI_PKCS7");
+ if (!pVar)
+ {
+ return ret;
+ }
+ if (pChainContext->cChain == 0)
+ {
+ return ret;
+ }
+ PCERT_SIMPLE_CHAIN pSimpleChain = pChainContext->rgpChain[0];
+ // check if untrusted root is the only problem
+ if (pSimpleChain->TrustStatus.dwErrorStatus & ~(CERT_TRUST_IS_UNTRUSTED_ROOT | ignoreFlags))
+ {
+ return ret;
+ }
+
+ // leak this store, re-opening is a waste of time in tests
+ static HCERTSTORE const hExtra = CertOpenStore(
+ CERT_STORE_PROV_FILENAME_A,
+ PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ 0,
+ CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG,
+ OString(OString::Concat(pVar) + "/test.p7b").getStr());
+ assert(hExtra != nullptr);
+ if (pSimpleChain->cElement < 1)
+ {
+ SAL_WARN("xmlsecurity.xmlsec", "unexpected empty chain");
+ return ret;
+ }
+ PCCERT_CONTEXT const pRoot(pSimpleChain->rgpElement[pSimpleChain->cElement-1]->pCertContext);
+ PCCERT_CONTEXT const pIssuerCert = CertFindCertificateInStore(
+ hExtra,
+ PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_NAME,
+ &pRoot->pCertInfo->Subject,
+ nullptr);
+ if (pIssuerCert)
+ {
+ // check that it signed itself
+ DWORD flags = CERT_STORE_SIGNATURE_FLAG;
+ bool result = CertVerifySubjectCertificateContext(pRoot, pIssuerCert, &flags);
+ if (result && flags == 0)
+ {
+ ret = true;
+ }
+ }
+ CertFreeCertificateContext(pIssuerCert);
+ return ret;
+}
+
+//We return only valid or invalid, as long as the API documentation expresses
+//explicitly that all validation steps are carried out even if one or several
+//errors occur. See also
+//http://wiki.openoffice.org/wiki/Certificate_Path_Validation#Validation_status
+sal_Int32 SecurityEnvironment_MSCryptImpl::verifyCertificate(
+ const uno::Reference< css::security::XCertificate >& aCert,
+ const uno::Sequence< uno::Reference< css::security::XCertificate > >& seqCerts)
+{
+ sal_Int32 validity = css::security::CertificateValidity::INVALID;
+ PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
+ PCCERT_CONTEXT pCertContext = nullptr;
+
+ uno::Reference< XUnoTunnel > xCertTunnel( aCert, uno::UNO_QUERY_THROW ) ;
+
+ SAL_INFO("xmlsecurity.xmlsec", "Start verification of certificate: " << aCert->getSubjectName());
+
+ const auto* xcert = comphelper::getFromUnoTunnel<X509Certificate_MSCryptImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw uno::RuntimeException() ;
+ }
+
+ pCertContext = xcert->getMswcryCert() ;
+
+ CERT_ENHKEY_USAGE enhKeyUsage ;
+ CERT_USAGE_MATCH certUsage ;
+ CERT_CHAIN_PARA chainPara = {};
+
+ //Prepare parameter for CertGetCertificateChain
+ enhKeyUsage.cUsageIdentifier = 0 ;
+ enhKeyUsage.rgpszUsageIdentifier = nullptr ;
+ certUsage.dwType = USAGE_MATCH_TYPE_AND ;
+ certUsage.Usage = enhKeyUsage ;
+ chainPara.cbSize = sizeof( CERT_CHAIN_PARA ) ;
+ chainPara.RequestedUsage = certUsage ;
+
+
+ HCERTSTORE hCollectionStore = nullptr;
+ HCERTSTORE hIntermediateCertsStore = nullptr;
+ bool bChain = false;
+ if( pCertContext != nullptr )
+ {
+ hIntermediateCertsStore =
+ getCertStoreForIntermediatCerts(seqCerts);
+
+ //Merge m_hCertStore and m_hKeyStore and the store of the intermediate
+ //certificates into one store.
+ hCollectionStore = CertOpenStore(
+ CERT_STORE_PROV_COLLECTION ,
+ 0 ,
+ 0 ,
+ 0 ,
+ nullptr
+ ) ;
+ if (hCollectionStore != nullptr)
+ {
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hCertStore ,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ CertAddStoreToCollection (
+ hCollectionStore ,
+ m_hKeyStore,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG ,
+ 0) ;
+ CertAddStoreToCollection (
+ hCollectionStore,
+ hIntermediateCertsStore,
+ CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG,
+ 0);
+
+ }
+
+ //CertGetCertificateChain searches by default in MY, CA, ROOT and TRUST
+ //We do not check revocation of the root. In most cases there are none.
+ //Then we would get CERT_TRUST_REVOCATION_STATUS_UNKNOWN
+ SAL_INFO("xmlsecurity.xmlsec", "Verifying cert using revocation information.");
+ bChain = CertGetCertificateChain(
+ nullptr ,
+ pCertContext ,
+ nullptr , //use current system time
+ hCollectionStore,
+ &chainPara ,
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
+ nullptr ,
+ &pChainContext);
+
+ if (bChain && pChainContext->cChain > 0)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Overall error status (all chains):");
+ traceTrustStatus(pChainContext->TrustStatus.dwErrorStatus);
+ //highest quality chains come first
+ PCERT_SIMPLE_CHAIN pSimpleChain = pChainContext->rgpChain[0];
+ SAL_INFO("xmlsecurity.xmlsec", "Error status of first chain:");
+ traceTrustStatus(pSimpleChain->TrustStatus.dwErrorStatus);
+
+ //CERT_TRUST_REVOCATION_STATUS_UNKNOWN is also set if a certificate
+ //has no AIA(OCSP) or CRLDP extension and there is no CRL locally installed.
+ DWORD revocationFlags = CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
+ CERT_TRUST_IS_OFFLINE_REVOCATION;
+ DWORD otherErrorsMask = ~revocationFlags;
+ if (!(pSimpleChain->TrustStatus.dwErrorStatus & otherErrorsMask)
+ || CheckUnitTestStore(pChainContext, revocationFlags))
+
+ {
+ //No errors except maybe those caused by missing revocation information
+ //Check if there are errors
+ if ( pSimpleChain->TrustStatus.dwErrorStatus & revocationFlags)
+ {
+ //No revocation information. Because MSDN documentation is not
+ //clear about if all other tests are performed if an error occurs,
+ //we test again, without requiring revocation checking.
+ CertFreeCertificateChain(pChainContext);
+ pChainContext = nullptr;
+ SAL_INFO("xmlsecurity.xmlsec", "Checking again but without requiring revocation information.");
+ bChain = CertGetCertificateChain(
+ nullptr ,
+ pCertContext ,
+ nullptr , //use current system time
+ hCollectionStore,
+ &chainPara ,
+ 0,
+ nullptr ,
+ &pChainContext);
+ if (bChain
+ && pChainContext->cChain > 0
+ && pChainContext->rgpChain[0]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
+ validity = css::security::CertificateValidity::VALID;
+ }
+ else if (CheckUnitTestStore(pChainContext, 0))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "root certificate found in extra test store");
+ validity = css::security::CertificateValidity::VALID;
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
+ }
+ }
+ else
+ {
+ //valid and revocation information available
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
+ validity = css::security::CertificateValidity::VALID;
+ }
+ }
+ else
+ {
+ //invalid
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
+ validity = css::security::CertificateValidity::INVALID ;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CertGetCertificateChain failed.");
+ }
+ }
+
+ if (pChainContext)
+ {
+ CertFreeCertificateChain(pChainContext);
+ pChainContext = nullptr;
+ }
+
+ //Close the additional store, do not destroy the contained certs
+ CertCloseStore(hCollectionStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ //Close the temporary store containing the intermediate certificates and make
+ //sure all certificates are deleted.
+ CertCloseStore(hIntermediateCertsStore, CERT_CLOSE_STORE_CHECK_FLAG);
+
+ return validity ;
+}
+
+sal_Int32 SecurityEnvironment_MSCryptImpl::getCertificateCharacters( const css::uno::Reference< css::security::XCertificate >& aCert ) {
+ sal_Int32 characters ;
+ PCCERT_CONTEXT pCertContext ;
+
+ uno::Reference< XUnoTunnel > xCertTunnel( aCert, uno::UNO_QUERY_THROW ) ;
+ const auto* xcert = comphelper::getFromUnoTunnel<X509Certificate_MSCryptImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw uno::RuntimeException() ;
+ }
+
+ pCertContext = xcert->getMswcryCert() ;
+
+ characters = 0x00000000 ;
+
+ //Firstly, make sentence whether or not the cert is self-signed.
+ if( CertCompareCertificateName( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->Subject), &(pCertContext->pCertInfo->Issuer) ) ) {
+ characters |= css::security::CertificateCharacters::SELF_SIGNED ;
+ } else {
+ characters &= ~ css::security::CertificateCharacters::SELF_SIGNED ;
+ }
+
+ //Secondly, make sentence whether or not the cert has a private key.
+ {
+ BOOL fCallerFreeProv ;
+ DWORD dwKeySpec ;
+ NCRYPT_KEY_HANDLE hKey = 0;
+ DWORD dwFlags = CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
+ HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hKey;
+ if( CryptAcquireCertificatePrivateKey( pCertContext ,
+ dwFlags,
+ nullptr ,
+ phCryptProvOrNCryptKey,
+ &dwKeySpec,
+ &fCallerFreeProv )
+ ) {
+ characters |= css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+
+ if (hKey && fCallerFreeProv)
+ NCryptFreeObject(hKey);
+ } else {
+ characters &= ~ css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+ }
+ }
+ return characters ;
+}
+
+void SecurityEnvironment_MSCryptImpl::enableDefaultCrypt( bool enable ) {
+ m_bEnableDefault = enable ;
+}
+
+bool SecurityEnvironment_MSCryptImpl::defaultEnabled() {
+ return m_bEnableDefault ;
+}
+
+static rtl::Reference<X509Certificate_MSCryptImpl> MswcryCertContextToXCert( PCCERT_CONTEXT cert )
+{
+ rtl::Reference<X509Certificate_MSCryptImpl> xcert ;
+
+ if( cert != nullptr ) {
+ xcert = new X509Certificate_MSCryptImpl() ;
+ xcert->setMswcryCert( cert ) ;
+ }
+
+ return xcert ;
+}
+
+OUString SecurityEnvironment_MSCryptImpl::getSecurityEnvironmentInformation()
+{
+ return "Microsoft Crypto API";
+}
+
+xmlSecKeysMngrPtr SecurityEnvironment_MSCryptImpl::createKeysManager() {
+
+ /*-
+ * The following lines is based on the of xmlsec-mscrypto crypto engine
+ */
+ xmlSecKeysMngrPtr pKeysMngr = xmlsecurity::MSCryptoAppliedKeysMngrCreate() ;
+ if( pKeysMngr == nullptr )
+ throw uno::RuntimeException() ;
+
+ /*-
+ * Adopt system default certificate store.
+ */
+ if( defaultEnabled() ) {
+ //Add system key store into the keys manager.
+ m_hMySystemStore = CertOpenSystemStoreW( 0, L"MY" ) ;
+ if( m_hMySystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptKeyStore( pKeysMngr, m_hMySystemStore ) < 0 ) {
+ CertCloseStore( m_hMySystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hMySystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hMySystemStore = nullptr;
+ }
+
+ //Add system root store into the keys manager.
+ m_hRootSystemStore = CertOpenSystemStoreW( 0, L"Root" ) ;
+ if( m_hRootSystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptTrustedStore( pKeysMngr, m_hRootSystemStore ) < 0 ) {
+ CertCloseStore( m_hRootSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hRootSystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hRootSystemStore = nullptr;
+ }
+
+ //Add system trusted store into the keys manager.
+ m_hTrustSystemStore = CertOpenSystemStoreW( 0, L"Trust" ) ;
+ if( m_hTrustSystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hTrustSystemStore ) < 0 ) {
+ CertCloseStore( m_hTrustSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hTrustSystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hTrustSystemStore = nullptr;
+ }
+
+ //Add system CA store into the keys manager.
+ m_hCaSystemStore = CertOpenSystemStoreW( 0, L"CA" ) ;
+ if( m_hCaSystemStore != nullptr ) {
+ if( xmlsecurity::MSCryptoAppliedKeysMngrAdoptUntrustedStore( pKeysMngr, m_hCaSystemStore ) < 0 ) {
+ CertCloseStore( m_hCaSystemStore, CERT_CLOSE_STORE_CHECK_FLAG ) ;
+ m_hCaSystemStore = nullptr;
+ throw uno::RuntimeException() ;
+ }
+ m_hCaSystemStore = nullptr;
+ }
+ }
+
+ return pKeysMngr ;
+}
+void SecurityEnvironment_MSCryptImpl::destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) {
+ if( pKeysMngr != nullptr ) {
+ xmlSecKeysMngrDestroy( pKeysMngr ) ;
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_SecurityEnvironment_get_implementation(
+ uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SecurityEnvironment_MSCryptImpl(pCtx));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.hxx b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.hxx
new file mode 100644
index 000000000..a4ae45742
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/securityenvironment_mscryptimpl.hxx
@@ -0,0 +1,173 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <wincrypt.h>
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/security/CertificateCharacters.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+
+#include <vector>
+#include <xmlsec-wrapper.h>
+
+#include <sal/types.h>
+
+
+class SecurityEnvironment_MSCryptImpl : public ::cppu::WeakImplHelper<
+ css::xml::crypto::XSecurityEnvironment ,
+ css::lang::XServiceInfo,
+ css::lang::XUnoTunnel >
+{
+ private:
+ //crypto provider and key container
+ HCRYPTPROV m_hProv ;
+ LPCTSTR m_pszContainer ;
+
+ //Key store
+ HCERTSTORE m_hKeyStore ;
+
+ //Certificate store
+ HCERTSTORE m_hCertStore ;
+
+ // i120675, save the store handles
+ HCERTSTORE m_hMySystemStore;
+ HCERTSTORE m_hRootSystemStore;
+ HCERTSTORE m_hTrustSystemStore;
+ HCERTSTORE m_hCaSystemStore;
+
+ //Enable default system cryptography setting
+ bool m_bEnableDefault ;
+
+ //Service manager
+ css::uno::Reference< css::lang::XMultiServiceFactory > m_xServiceManager ;
+
+ public:
+ explicit SecurityEnvironment_MSCryptImpl( const css::uno::Reference< css::uno::XComponentContext >& xContext ) ;
+ virtual ~SecurityEnvironment_MSCryptImpl() override;
+
+ //Methods from XSecurityEnvironment
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getPersonalCertificates() override;
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getAllCertificates() override
+ { return css::uno::Sequence< css::uno::Reference< css::security::XCertificate > >(); }
+
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL getCertificate(
+ const OUString& issuerName,
+ const css::uno::Sequence< sal_Int8 >& serialNumber ) override;
+
+ /// @throws css::uno::SecurityException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Reference< css::security::XCertificate > getCertificate(
+ const OUString& issuerName,
+ const OUString& serialNumber ) ;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL buildCertificatePath(
+ const css::uno::Reference< css::security::XCertificate >& beginCert ) override;
+
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromRaw(
+ const css::uno::Sequence< sal_Int8 >& rawCertificate ) override;
+
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromAscii(
+ const OUString& asciiCertificate ) override;
+
+ virtual ::sal_Int32 SAL_CALL verifyCertificate(
+ const css::uno::Reference< css::security::XCertificate >& xCert,
+ const css::uno::Sequence< css::uno::Reference<
+ css::security::XCertificate > >& intermediateCertificates) override;
+
+ virtual ::sal_Int32 SAL_CALL getCertificateCharacters(
+ const css::uno::Reference< css::security::XCertificate >& xCert ) override;
+
+ virtual OUString SAL_CALL getSecurityEnvironmentInformation( ) override;
+
+
+ //Methods from XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& ServiceName
+ ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ //Methods from XUnoTunnel
+ UNO3_GETIMPLEMENTATION_DECL(SecurityEnvironment_MSCryptImpl)
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ HCRYPTPROV getCryptoProvider() ;
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void setCryptoProvider( HCRYPTPROV aProv ) ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ LPCTSTR getKeyContainer() ;
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void setKeyContainer( LPCTSTR aKeyContainer ) ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ HCERTSTORE getCryptoSlot() ;
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void setCryptoSlot( HCERTSTORE aKeyStore ) ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ HCERTSTORE getCertDb() ;
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void setCertDb( HCERTSTORE aCertDb ) ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void enableDefaultCrypt( bool enable ) ;
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ bool defaultEnabled() ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ xmlSecKeysMngrPtr createKeysManager() ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ static void destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) ;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.cxx
new file mode 100644
index 000000000..08143f992
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.cxx
@@ -0,0 +1,173 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <xmlsec-wrapper.h>
+
+#include "seinitializer_mscryptimpl.hxx"
+
+#include "securityenvironment_mscryptimpl.hxx"
+
+#include <xmlsec/mscng/app.h>
+#include <com/sun/star/xml/crypto/SecurityEnvironment.hpp>
+#include <com/sun/star/xml/crypto/XMLSecurityContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <svl/cryptosign.hxx>
+
+using namespace com::sun::star;
+namespace cssl = com::sun::star::lang;
+namespace cssxc = com::sun::star::xml::crypto;
+
+SEInitializer_MSCryptImpl::SEInitializer_MSCryptImpl(
+ const uno::Reference< uno::XComponentContext > &rxContext)
+ :mxContext( rxContext )
+{
+}
+
+SEInitializer_MSCryptImpl::~SEInitializer_MSCryptImpl()
+{
+}
+
+/* XSEInitializer */
+uno::Reference< cssxc::XXMLSecurityContext > SAL_CALL
+ SEInitializer_MSCryptImpl::createSecurityContext(
+ const OUString& sCertDB )
+{
+ const char* n_pCertStore ;
+ HCERTSTORE n_hStoreHandle ;
+ OString sCertDir;
+
+ //Initialize the crypto engine
+ if( sCertDB.getLength() > 0 )
+ {
+ sCertDir = OString(sCertDB.getStr(), sCertDB.getLength(), RTL_TEXTENCODING_ASCII_US);
+ n_pCertStore = sCertDir.getStr();
+ n_hStoreHandle = CertOpenSystemStoreW( 0, o3tl::toW(sCertDB.getStr())) ;
+ if( n_hStoreHandle == nullptr )
+ {
+ return nullptr;
+ }
+ }
+ else
+ {
+ n_pCertStore = nullptr ;
+ n_hStoreHandle = nullptr ;
+ }
+
+ xmlSecMSCngAppInit(n_pCertStore);
+
+ try {
+ /* Build Security Environment */
+ uno::Reference< cssxc::XSecurityEnvironment > xSecEnv = cssxc::SecurityEnvironment::create( mxContext );
+
+ /* Setup key slot and certDb */
+ uno::Reference< cssl::XUnoTunnel > xSecEnvTunnel( xSecEnv, uno::UNO_QUERY_THROW );
+ SecurityEnvironment_MSCryptImpl* pSecEnv = comphelper::getFromUnoTunnel<SecurityEnvironment_MSCryptImpl>(xSecEnvTunnel);
+ if( pSecEnv == nullptr )
+ {
+ if( n_hStoreHandle != nullptr )
+ {
+ CertCloseStore( n_hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ }
+
+ xmlSecMSCngAppShutdown();
+ return nullptr;
+ }
+
+ if( n_hStoreHandle != nullptr )
+ {
+ pSecEnv->setCryptoSlot( n_hStoreHandle ) ;
+ pSecEnv->setCertDb( n_hStoreHandle ) ;
+ }
+ else
+ {
+ pSecEnv->enableDefaultCrypt( true ) ;
+ }
+
+ /* Build XML Security Context */
+ uno::Reference< cssxc::XXMLSecurityContext > xSecCtx = cssxc::XMLSecurityContext::create( mxContext );
+
+ xSecCtx->setDefaultSecurityEnvironmentIndex(xSecCtx->addSecurityEnvironment( xSecEnv )) ;
+ return xSecCtx;
+ }
+ catch( uno::Exception& )
+ {
+ if( n_hStoreHandle != nullptr )
+ {
+ CertCloseStore( n_hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ }
+
+ xmlSecMSCngAppShutdown();
+ return nullptr;
+ }
+}
+
+void SAL_CALL SEInitializer_MSCryptImpl::freeSecurityContext( const uno::Reference< cssxc::XXMLSecurityContext >&)
+{
+ /*
+ uno::Reference< cssxc::XSecurityEnvironment > xSecEnv
+ = securityContext->getSecurityEnvironment();
+
+ if( xSecEnv.is() )
+ {
+ uno::Reference< cssl::XUnoTunnel > xEnvTunnel( xSecEnv , uno::UNO_QUERY ) ;
+ if (auto pSecEnv = comphelper::getFromUnoTunnel<SecurityEnvironment_MSCryptImpl>(xEnvTunnel))
+ {
+ HCERTSTORE n_hStoreHandle = pSecEnv->getCryptoSlot();
+
+ if( n_hStoreHandle != NULL )
+ {
+ CertCloseStore( n_hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG ) ;
+ pSecEnv->setCryptoSlot( NULL ) ;
+ pSecEnv->setCertDb( NULL ) ;
+ }
+
+ xmlSecMSCryptoAppShutdown() ;
+ }
+ }
+ */
+
+ xmlSecMSCngAppShutdown();
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SEInitializer_MSCryptImpl::getImplementationName()
+{
+ return "com.sun.star.xml.crypto.SEInitializer";
+}
+
+sal_Bool SAL_CALL SEInitializer_MSCryptImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL SEInitializer_MSCryptImpl::getSupportedServiceNames()
+{
+ return { "com.sun.star.xml.crypto.SEInitializer" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_SEInitializer_get_implementation(
+ uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SEInitializer_MSCryptImpl(pCtx));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.hxx b/xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.hxx
new file mode 100644
index 000000000..182ba5e5a
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/seinitializer_mscryptimpl.hxx
@@ -0,0 +1,71 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
+#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <libxml/tree.h>
+
+class SEInitializer_MSCryptImpl : public cppu::WeakImplHelper
+<
+ css::xml::crypto::XSEInitializer,
+ css::lang::XServiceInfo
+>
+/****** SEInitializer_MSCryptImpl.hxx/CLASS SEInitializer_MSCryptImpl ***********
+ *
+ * NAME
+ * SEInitializer_MSCryptImpl -- Class to initialize a Security Context
+ * instance
+ *
+ * FUNCTION
+ * Use this class to initialize a XmlSec based Security Context
+ * instance. After this instance is used up, use this class to free this
+ * instance.
+ ******************************************************************************/
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > mxContext;
+
+public:
+ explicit SEInitializer_MSCryptImpl(const css::uno::Reference< css::uno::XComponentContext > &rxContext);
+ virtual ~SEInitializer_MSCryptImpl() override;
+
+ /* XSEInitializer */
+ virtual css::uno::Reference< css::xml::crypto::XXMLSecurityContext >
+ SAL_CALL createSecurityContext( const OUString& certDB ) override;
+
+ virtual void SAL_CALL freeSecurityContext( const css::uno::Reference<
+ css::xml::crypto::XXMLSecurityContext >& securityContext ) override;
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx
new file mode 100644
index 000000000..5e0ca9094
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx
@@ -0,0 +1,786 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/windowserrorstring.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include "x509certificate_mscryptimpl.hxx"
+#include <certificateextension_xmlsecimpl.hxx>
+#include <biginteger.hxx>
+#include "sanextension_mscryptimpl.hxx"
+
+#include "oid.hxx"
+
+#include <rtl/locale.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/nlsupport.h>
+#include <osl/process.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+#include <tools/time.hxx>
+#include <svl/sigstruct.hxx>
+
+using namespace com::sun::star;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::security ;
+
+using ::com::sun::star::security::XCertificate ;
+using ::com::sun::star::util::DateTime ;
+
+/*Returns the index within rRawString where sTypeName starts and where it ends.
+ The starting index is pair.first. The ending index in pair.second points
+ one char after the last character of the type.
+ sTypeName can be
+ "S" or "CN" (without ""). Do not use spaces at the beginning of the type name.
+ If the type name is not found then pair.first and pair.second are -1.
+*/
+static std::pair< sal_Int32, sal_Int32 >
+findTypeInDN(const OUString& rRawString, std::u16string_view sTypeName)
+{
+ std::pair< sal_Int32, sal_Int32 > retVal;
+ bool bInEscape = false;
+ bool bInValue = false;
+ bool bFound = false;
+ sal_Int32 nTypeNameStart = 0;
+ sal_Int32 length = rRawString.getLength();
+
+ for (sal_Int32 i = 0; i < length; i++)
+ {
+ sal_Unicode c = rRawString[i];
+
+ if (c == '=')
+ {
+ if (! bInValue)
+ {
+ OUString sType = rRawString.copy(nTypeNameStart, i - nTypeNameStart);
+ sType = sType.trim();
+ if (sType.equalsIgnoreAsciiCase(sTypeName))
+ {
+ bFound = true;
+ break;
+ }
+ }
+ }
+ else if (c == '"')
+ {
+ if (!bInEscape)
+ {
+ //If this is the quote is the first of the couple which enclose the
+ //whole value, because the value contains special characters
+ //then we just drop it. That is, this character must be followed by
+ //a character which is not '"'.
+ if ( i + 1 < length && rRawString[i+1] == '"')
+ bInEscape = true;
+ else
+ bInValue = !bInValue; //value is enclosed in " "
+ }
+ else
+ {
+ //This quote is escaped by a preceding quote and therefore is
+ //part of the value
+ bInEscape = false;
+ }
+ }
+ else if (c == ',' || c == '+')
+ {
+ //The comma separate the attribute value pairs.
+ //If the comma is not part of a value (the value would then be enclosed in '"'),
+ //then we have reached the end of the value
+ if (!bInValue)
+ {
+ //The next char is the start of the new type
+ nTypeNameStart = i + 1;
+ }
+ }
+ }
+
+ //Found the Type Name, but there can still be spaces after the last comma
+ //and the beginning of the type.
+ if (bFound)
+ {
+ while (true)
+ {
+ sal_Unicode c = rRawString[nTypeNameStart];
+ if (c != ' ' && c != '\t')
+ //found
+ break;
+ nTypeNameStart ++;
+ }
+ // search end (one after last letter)
+ sal_Int32 nTypeNameEnd = nTypeNameStart;
+ nTypeNameEnd++;
+ while (true)
+ {
+ sal_Unicode c = rRawString[nTypeNameEnd];
+ if (c == ' ' || c == '\t' || c == '=')
+ break;
+ nTypeNameEnd++;
+ }
+ retVal = std::make_pair(nTypeNameStart, nTypeNameEnd);
+ }
+ else
+ {
+ retVal = std::make_pair(-1, -1);
+ }
+ return retVal;
+}
+
+
+/*
+ MS Crypto uses the 'S' tag (equal to the 'ST' tag in NSS), but the NSS can't recognise
+ it, so the 'S' tag should be changed to 'ST' tag. However I am not sure if this is necessary
+ anymore, because we provide always the signers certificate when signing. So libmlsec can find
+ the private key based on the provided certificate (X509Certificate element) and does not need
+ the issuer name (X509IssuerName element). The issuer name in the xml signature has also no
+ effect for the signature nor the certificate validation.
+ In many RFCs, for example 4519, on speaks of 'ST'. However, the certificate does not contain
+ strings for type names. Instead it uses OIDs.
+ */
+
+static OUString replaceTagSWithTagST(OUString const & oldDN)
+{
+ std::pair<sal_Int32, sal_Int32 > pairIndex = findTypeInDN(oldDN, u"S");
+
+ if (pairIndex.first != -1)
+ {
+ return OUString::Concat(oldDN.subView(0, pairIndex.first))+"ST"
+ +oldDN.subView(pairIndex.second);
+ }
+ return oldDN;
+}
+/* end */
+
+X509Certificate_MSCryptImpl::X509Certificate_MSCryptImpl() :
+ m_pCertContext( nullptr )
+{
+}
+
+X509Certificate_MSCryptImpl::~X509Certificate_MSCryptImpl() {
+ if( m_pCertContext != nullptr ) {
+ CertFreeCertificateContext( m_pCertContext ) ;
+ }
+}
+
+//Methods from XCertificate
+sal_Int16 SAL_CALL X509Certificate_MSCryptImpl::getVersion() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
+ return static_cast<char>(m_pCertContext->pCertInfo->dwVersion) ;
+ } else {
+ return -1 ;
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSerialNumber() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
+ Sequence< sal_Int8 > serial( m_pCertContext->pCertInfo->SerialNumber.cbData ) ;
+ auto serialRange = asNonConstRange(serial);
+ for( unsigned int i = 0 ; i < m_pCertContext->pCertInfo->SerialNumber.cbData ; i ++ )
+ serialRange[i] = *( m_pCertContext->pCertInfo->SerialNumber.pbData + m_pCertContext->pCertInfo->SerialNumber.cbData - i - 1 ) ;
+
+ return serial ;
+ } else {
+ return Sequence< sal_Int8 >();
+ }
+}
+
+OUString SAL_CALL X509Certificate_MSCryptImpl::getIssuerName() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
+ DWORD cchIssuer = CertNameToStrW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( m_pCertContext->pCertInfo->Issuer ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ nullptr, 0
+ ) ;
+
+ // Here the cbIssuer count the last 0x00 , take care.
+ if( cchIssuer != 0 ) {
+ auto issuer = std::make_unique<wchar_t[]>(cchIssuer);
+
+ cchIssuer = CertNameToStrW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( m_pCertContext->pCertInfo->Issuer ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ issuer.get(), cchIssuer
+ ) ;
+
+ if( cchIssuer <= 0 ) {
+ throw RuntimeException() ;
+ }
+
+ if(issuer.get()[cchIssuer -1] == 0) cchIssuer--; //delimit the last 0x00;
+ OUString xIssuer(o3tl::toU(issuer.get()), cchIssuer) ;
+
+ return replaceTagSWithTagST(xIssuer);
+ } else {
+ return OUString() ;
+ }
+ } else {
+ return OUString() ;
+ }
+}
+
+OUString SAL_CALL X509Certificate_MSCryptImpl::getSubjectName()
+{
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
+ {
+ DWORD cchSubject = CertNameToStrW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( m_pCertContext->pCertInfo->Subject ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ nullptr, 0
+ ) ;
+
+ if( cchSubject != 0 )
+ {
+ auto subject = std::make_unique<wchar_t[]>(cchSubject);
+
+ cchSubject = CertNameToStrW(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING ,
+ &( m_pCertContext->pCertInfo->Subject ),
+ CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG ,
+ subject.get(), cchSubject
+ ) ;
+
+ if( cchSubject <= 0 ) {
+ throw RuntimeException() ;
+ }
+
+ OUString xSubject(o3tl::toU(subject.get()));
+
+ return replaceTagSWithTagST(xSubject);
+ } else
+ {
+ return OUString() ;
+ }
+ }
+ else
+ {
+ return OUString() ;
+ }
+}
+
+css::util::DateTime SAL_CALL X509Certificate_MSCryptImpl::getNotValidBefore() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
+ SYSTEMTIME explTime ;
+ DateTime dateTime ;
+ FILETIME localFileTime;
+
+ if (FileTimeToLocalFileTime(&( m_pCertContext->pCertInfo->NotBefore ), &localFileTime))
+ {
+ if( FileTimeToSystemTime( &localFileTime, &explTime ) ) {
+ //Convert the time to readable local time
+ dateTime.NanoSeconds = explTime.wMilliseconds * ::tools::Time::nanoPerMilli ;
+ dateTime.Seconds = explTime.wSecond ;
+ dateTime.Minutes = explTime.wMinute ;
+ dateTime.Hours = explTime.wHour ;
+ dateTime.Day = explTime.wDay ;
+ dateTime.Month = explTime.wMonth ;
+ dateTime.Year = explTime.wYear ;
+ }
+ }
+
+ return dateTime ;
+ } else {
+ return DateTime() ;
+ }
+}
+
+css::util::DateTime SAL_CALL X509Certificate_MSCryptImpl::getNotValidAfter() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
+ SYSTEMTIME explTime ;
+ DateTime dateTime ;
+ FILETIME localFileTime;
+
+ if (FileTimeToLocalFileTime(&( m_pCertContext->pCertInfo->NotAfter ), &localFileTime))
+ {
+ if( FileTimeToSystemTime( &localFileTime, &explTime ) ) {
+ //Convert the time to readable local time
+ dateTime.NanoSeconds = explTime.wMilliseconds * ::tools::Time::nanoPerMilli ;
+ dateTime.Seconds = explTime.wSecond ;
+ dateTime.Minutes = explTime.wMinute ;
+ dateTime.Hours = explTime.wHour ;
+ dateTime.Day = explTime.wDay ;
+ dateTime.Month = explTime.wMonth ;
+ dateTime.Year = explTime.wYear ;
+ }
+ }
+
+ return dateTime ;
+ } else {
+ return DateTime() ;
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getIssuerUniqueID() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
+ Sequence< sal_Int8 > issuerUid( m_pCertContext->pCertInfo->IssuerUniqueId.cbData ) ;
+ auto issuerUidRange = asNonConstRange(issuerUid);
+ for( unsigned int i = 0 ; i < m_pCertContext->pCertInfo->IssuerUniqueId.cbData; i ++ )
+ issuerUidRange[i] = *( m_pCertContext->pCertInfo->IssuerUniqueId.pbData + i ) ;
+
+ return issuerUid ;
+ } else {
+ return Sequence< sal_Int8 >();
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSubjectUniqueID() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr ) {
+ Sequence< sal_Int8 > subjectUid( m_pCertContext->pCertInfo->SubjectUniqueId.cbData ) ;
+ auto subjectUidRange = asNonConstRange(subjectUid);
+ for( unsigned int i = 0 ; i < m_pCertContext->pCertInfo->SubjectUniqueId.cbData; i ++ )
+ subjectUidRange[i] = *( m_pCertContext->pCertInfo->SubjectUniqueId.pbData + i ) ;
+
+ return subjectUid ;
+ } else {
+ return Sequence< sal_Int8 >();
+ }
+}
+
+css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > SAL_CALL X509Certificate_MSCryptImpl::getExtensions() {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr && m_pCertContext->pCertInfo->cExtension != 0 ) {
+ rtl::Reference<CertificateExtension_XmlSecImpl> xExtn ;
+ Sequence< Reference< XCertificateExtension > > xExtns( m_pCertContext->pCertInfo->cExtension ) ;
+ auto pExtns = xExtns.getArray();
+
+ for( unsigned int i = 0; i < m_pCertContext->pCertInfo->cExtension; i++ ) {
+ CERT_EXTENSION* pExtn = &(m_pCertContext->pCertInfo->rgExtension[i]) ;
+
+
+ OUString objId = OUString::createFromAscii( pExtn->pszObjId );
+
+ if ( objId == "2.5.29.17" )
+ xExtn = reinterpret_cast<CertificateExtension_XmlSecImpl*>(new SanExtensionImpl());
+ else
+ xExtn = new CertificateExtension_XmlSecImpl;
+
+ xExtn->setCertExtn( pExtn->Value.pbData, pExtn->Value.cbData, reinterpret_cast<unsigned char*>(pExtn->pszObjId), strlen( pExtn->pszObjId ), pExtn->fCritical ) ;
+
+ pExtns[i] = xExtn ;
+ }
+
+ return xExtns ;
+ } else {
+ return Sequence< Reference< XCertificateExtension > >();
+ }
+}
+
+css::uno::Reference< css::security::XCertificateExtension > SAL_CALL X509Certificate_MSCryptImpl::findCertificateExtension( const css::uno::Sequence< sal_Int8 >& /*oid*/ ) {
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr && m_pCertContext->pCertInfo->cExtension != 0 ) {
+ rtl::Reference<CertificateExtension_XmlSecImpl> xExtn ;
+
+ for( unsigned int i = 0; i < m_pCertContext->pCertInfo->cExtension; i++ ) {
+ CERT_EXTENSION* pExtn = &( m_pCertContext->pCertInfo->rgExtension[i] ) ;
+
+ //TODO: Compare the oid
+ if( false ) {
+ xExtn = new CertificateExtension_XmlSecImpl;
+ xExtn->setCertExtn( pExtn->Value.pbData, pExtn->Value.cbData, reinterpret_cast<unsigned char*>(pExtn->pszObjId), strlen( pExtn->pszObjId ), pExtn->fCritical ) ;
+ }
+ }
+
+ return xExtn ;
+ } else {
+ return nullptr ;
+ }
+}
+
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getEncoded() {
+ if( m_pCertContext != nullptr && m_pCertContext->cbCertEncoded > 0 ) {
+ Sequence< sal_Int8 > rawCert( m_pCertContext->cbCertEncoded ) ;
+ auto prawCert = rawCert.getArray();
+
+ for( unsigned int i = 0 ; i < m_pCertContext->cbCertEncoded ; i ++ )
+ prawCert[i] = *( m_pCertContext->pbCertEncoded + i ) ;
+
+ return rawCert ;
+ } else {
+ return Sequence< sal_Int8 >();
+ }
+}
+
+//Helper methods
+void X509Certificate_MSCryptImpl::setMswcryCert( const CERT_CONTEXT* cert ) {
+ if( m_pCertContext != nullptr ) {
+ CertFreeCertificateContext( m_pCertContext ) ;
+ m_pCertContext = nullptr ;
+ }
+
+ if( cert != nullptr ) {
+ m_pCertContext = CertDuplicateCertificateContext( cert ) ;
+ }
+}
+
+const CERT_CONTEXT* X509Certificate_MSCryptImpl::getMswcryCert() const {
+ if( m_pCertContext != nullptr ) {
+ return m_pCertContext ;
+ } else {
+ return nullptr ;
+ }
+}
+
+void X509Certificate_MSCryptImpl::setRawCert( Sequence< sal_Int8 > const & rawCert ) {
+ if( m_pCertContext != nullptr ) {
+ CertFreeCertificateContext( m_pCertContext ) ;
+ m_pCertContext = nullptr ;
+ }
+
+ if( rawCert.getLength() != 0 ) {
+ m_pCertContext = CertCreateCertificateContext( X509_ASN_ENCODING, reinterpret_cast<const sal_uInt8*>(&rawCert[0]), rawCert.getLength() ) ;
+ }
+}
+
+/* XUnoTunnel */
+UNO3_GETIMPLEMENTATION_IMPL(X509Certificate_MSCryptImpl);
+
+static OUString findOIDDescription(char const *oid)
+{
+ OUString ouOID = OUString::createFromAscii( oid );
+ for (int i=0; i<nOID; i++)
+ {
+ OUString item = OUString::createFromAscii( OIDs[i].oid );
+ if (ouOID == item)
+ {
+ return OUString::createFromAscii( OIDs[i].desc );
+ }
+ }
+
+ return OUString() ;
+}
+
+static css::uno::Sequence< sal_Int8 > getThumbprint(const CERT_CONTEXT* pCertContext, DWORD dwPropId)
+{
+ if( pCertContext != nullptr )
+ {
+ DWORD cbData = dwPropId == CERT_SHA256_HASH_PROP_ID ? 32 : 20;
+ unsigned char fingerprint[32];
+ if (CertGetCertificateContextProperty(pCertContext, dwPropId, fingerprint, &cbData))
+ {
+ Sequence< sal_Int8 > thumbprint( cbData ) ;
+ auto pthumbprint = thumbprint.getArray();
+ for( unsigned int i = 0 ; i < cbData ; i ++ )
+ {
+ pthumbprint[i] = fingerprint[i];
+ }
+
+ return thumbprint;
+ }
+ else
+ {
+ DWORD e = GetLastError();
+ cbData = e;
+ }
+ }
+
+ return Sequence< sal_Int8 >();
+}
+
+OUString SAL_CALL X509Certificate_MSCryptImpl::getSubjectPublicKeyAlgorithm()
+{
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
+ {
+ CRYPT_ALGORITHM_IDENTIFIER algorithm = m_pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm;
+ return findOIDDescription( algorithm.pszObjId ) ;
+ }
+ else
+ {
+ return OUString() ;
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSubjectPublicKeyValue()
+{
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
+ {
+ CRYPT_BIT_BLOB publicKey = m_pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey;
+
+ Sequence< sal_Int8 > key( publicKey.cbData ) ;
+ auto keyRange = asNonConstRange(key);
+ for( unsigned int i = 0 ; i < publicKey.cbData ; i++ )
+ {
+ keyRange[i] = *(publicKey.pbData + i) ;
+ }
+
+ return key;
+ }
+ else
+ {
+ return Sequence< sal_Int8 >();
+ }
+}
+
+OUString SAL_CALL X509Certificate_MSCryptImpl::getSignatureAlgorithm()
+{
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr )
+ {
+ CRYPT_ALGORITHM_IDENTIFIER algorithm = m_pCertContext->pCertInfo->SignatureAlgorithm;
+ return findOIDDescription( algorithm.pszObjId ) ;
+ }
+ else
+ {
+ return OUString() ;
+ }
+}
+
+uno::Sequence<sal_Int8> X509Certificate_MSCryptImpl::getSHA256Thumbprint()
+{
+ return getThumbprint(m_pCertContext, CERT_SHA256_HASH_PROP_ID);
+}
+
+svl::crypto::SignatureMethodAlgorithm X509Certificate_MSCryptImpl::getSignatureMethodAlgorithm()
+{
+ svl::crypto::SignatureMethodAlgorithm nRet = svl::crypto::SignatureMethodAlgorithm::RSA;
+
+ if (!m_pCertContext || !m_pCertContext->pCertInfo)
+ return nRet;
+
+ CRYPT_ALGORITHM_IDENTIFIER algorithm = m_pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm;
+ OString aObjId(algorithm.pszObjId);
+ if (aObjId == szOID_ECC_PUBLIC_KEY)
+ nRet = svl::crypto::SignatureMethodAlgorithm::ECDSA;
+
+ return nRet;
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getSHA1Thumbprint()
+{
+ return getThumbprint(m_pCertContext, CERT_SHA1_HASH_PROP_ID);
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_MSCryptImpl::getMD5Thumbprint()
+{
+ return getThumbprint(m_pCertContext, CERT_MD5_HASH_PROP_ID);
+}
+
+CertificateKind SAL_CALL X509Certificate_MSCryptImpl::getCertificateKind()
+{
+ return CertificateKind_X509;
+}
+
+sal_Int32 SAL_CALL X509Certificate_MSCryptImpl::getCertificateUsage( )
+{
+ sal_Int32 usage =
+ CERT_DATA_ENCIPHERMENT_KEY_USAGE |
+ CERT_DIGITAL_SIGNATURE_KEY_USAGE |
+ CERT_KEY_AGREEMENT_KEY_USAGE |
+ CERT_KEY_CERT_SIGN_KEY_USAGE |
+ CERT_KEY_ENCIPHERMENT_KEY_USAGE |
+ CERT_NON_REPUDIATION_KEY_USAGE |
+ CERT_OFFLINE_CRL_SIGN_KEY_USAGE;
+
+ if( m_pCertContext != nullptr && m_pCertContext->pCertInfo != nullptr && m_pCertContext->pCertInfo->cExtension != 0 )
+ {
+ CERT_EXTENSION* pExtn = CertFindExtension(
+ szOID_KEY_USAGE,
+ m_pCertContext->pCertInfo->cExtension,
+ m_pCertContext->pCertInfo->rgExtension);
+
+ if (pExtn != nullptr)
+ {
+ DWORD length = 0;
+ bool rc = CryptDecodeObject(
+ X509_ASN_ENCODING,
+ X509_KEY_USAGE,
+ pExtn->Value.pbData,
+ pExtn->Value.cbData,
+ 0,
+ nullptr,
+ &length);
+
+ if (!rc)
+ SAL_WARN("xmlsecurity.xmlsec", "CryptDecodeObject failed: " << WindowsErrorString(GetLastError()));
+ else
+ {
+ std::vector<char>buffer(length);
+
+ rc = CryptDecodeObject(
+ X509_ASN_ENCODING,
+ X509_KEY_USAGE,
+ pExtn->Value.pbData,
+ pExtn->Value.cbData,
+ 0,
+ buffer.data(),
+ &length);
+
+ CRYPT_BIT_BLOB *blob = reinterpret_cast<CRYPT_BIT_BLOB*>(buffer.data());
+ if (!rc)
+ SAL_WARN("xmlsecurity.xmlsec", "CryptDecodeObject failed: " << WindowsErrorString(GetLastError()));
+ else if (blob->cbData == 1)
+ usage = blob->pbData[0];
+ else
+ SAL_WARN("xmlsecurity.xmlsec", "CryptDecodeObject(X509_KEY_USAGE) returned unexpected amount of data: " << blob->cbData);
+ }
+ }
+ }
+
+ return usage;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL X509Certificate_MSCryptImpl::getImplementationName()
+{
+ return "com.sun.star.xml.security.gpg.XCertificate_MsCryptImpl";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL X509Certificate_MSCryptImpl::supportsService(const OUString& serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence<OUString> SAL_CALL X509Certificate_MSCryptImpl::getSupportedServiceNames()
+{
+ return { OUString() };
+}
+
+namespace xmlsecurity {
+
+// based on some guesswork and:
+// https://datatracker.ietf.org/doc/html/rfc1485
+// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certnametostra#CERT_X500_NAME_STR
+// the main problem appears to be that in values NSS uses \ escapes but CryptoAPI requires " quotes around value
+static OUString CompatDNNSS(OUString const& rDN)
+{
+ OUStringBuffer buf(rDN.getLength());
+ enum { DEFAULT, INVALUE, INQUOTE } state(DEFAULT);
+ for (sal_Int32 i = 0; i < rDN.getLength(); ++i)
+ {
+ if (state == DEFAULT)
+ {
+ buf.append(rDN[i]);
+ if (rDN[i] == '=')
+ {
+ if (rDN.getLength() == i+1)
+ {
+ break; // invalid?
+ }
+ else
+ {
+ buf.append('"');
+ state = INVALUE;
+ }
+ }
+ }
+ else if (state == INVALUE)
+ {
+ if (rDN[i] == '+' || rDN[i] == ',' || rDN[i] == ';')
+ {
+ buf.append('"');
+ buf.append(rDN[i]);
+ state = DEFAULT;
+ }
+ else if (rDN[i] == '\\')
+ {
+ if (rDN.getLength() == i+1)
+ {
+ break; // invalid?
+ }
+ if (rDN[i+1] == '"')
+ {
+ buf.append('"');
+ }
+ buf.append(rDN[i+1]);
+ ++i;
+ }
+ else
+ {
+ buf.append(rDN[i]);
+ }
+ if (i+1 == rDN.getLength())
+ {
+ buf.append('"');
+ state = DEFAULT;
+ }
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+static bool EncodeDistinguishedName(std::u16string_view const rName, CERT_NAME_BLOB & rBlob)
+{
+ LPCWSTR pszError;
+ if (!CertStrToNameW(X509_ASN_ENCODING,
+ reinterpret_cast<LPCWSTR>(rName.data()), CERT_X500_NAME_STR,
+ nullptr, nullptr, &rBlob.cbData, &pszError))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << OUString(o3tl::toU(pszError)));
+ return false;
+ }
+ rBlob.pbData = new BYTE[rBlob.cbData];
+ if (!CertStrToNameW(X509_ASN_ENCODING,
+ reinterpret_cast<LPCWSTR>(rName.data()), CERT_X500_NAME_STR,
+ nullptr, rBlob.pbData, &rBlob.cbData, &pszError))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << OUString(o3tl::toU(pszError)));
+ return false;
+ }
+ return true;
+}
+
+bool EqualDistinguishedNames(
+ std::u16string_view const rName1, std::u16string_view const rName2,
+ EqualMode const eMode)
+{
+ if (eMode == COMPAT_BOTH && !rName1.empty() && rName1 == rName2)
+ { // handle case where both need to be converted
+ return true;
+ }
+ CERT_NAME_BLOB blob1;
+ if (!EncodeDistinguishedName(rName1, blob1))
+ {
+ return false;
+ }
+ CERT_NAME_BLOB blob2;
+ bool ret(false);
+ if (EncodeDistinguishedName(rName2, blob2))
+ {
+ ret = CertCompareCertificateName(X509_ASN_ENCODING,
+ &blob1, &blob2) == TRUE;
+ delete[] blob2.pbData;
+ }
+ if (!ret && eMode == COMPAT_2ND)
+ {
+ CERT_NAME_BLOB blob2compat;
+ if (!EncodeDistinguishedName(CompatDNNSS(OUString(rName2)), blob2compat))
+ {
+ delete[] blob1.pbData;
+ return false;
+ }
+ ret = CertCompareCertificateName(X509_ASN_ENCODING,
+ &blob1, &blob2compat) == TRUE;
+ delete[] blob2compat.pbData;
+ }
+ delete[] blob1.pbData;
+ return ret;
+}
+
+} // namespace xmlsecurity
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.hxx b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.hxx
new file mode 100644
index 000000000..9bd6a34ce
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.hxx
@@ -0,0 +1,96 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <Windows.h>
+#include <WinCrypt.h>
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/SecurityException.hpp>
+#include <com/sun/star/security/CertificateKind.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <certificate.hxx>
+
+class X509Certificate_MSCryptImpl : public ::cppu::WeakImplHelper<
+ css::security::XCertificate ,
+ css::lang::XUnoTunnel,
+ css::lang::XServiceInfo > , public xmlsecurity::Certificate
+{
+ private:
+ const CERT_CONTEXT* m_pCertContext ;
+
+ public:
+ X509Certificate_MSCryptImpl() ;
+ virtual ~X509Certificate_MSCryptImpl() override;
+
+ //Methods from XCertificate
+ virtual sal_Int16 SAL_CALL getVersion() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSerialNumber() override;
+ virtual OUString SAL_CALL getIssuerName() override;
+ virtual OUString SAL_CALL getSubjectName() override;
+ virtual css::util::DateTime SAL_CALL getNotValidBefore() override;
+ virtual css::util::DateTime SAL_CALL getNotValidAfter() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getIssuerUniqueID() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectUniqueID() override;
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > SAL_CALL getExtensions() override;
+ virtual css::uno::Reference< css::security::XCertificateExtension > SAL_CALL findCertificateExtension( const css::uno::Sequence< sal_Int8 >& oid ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getEncoded() override;
+ virtual OUString SAL_CALL getSubjectPublicKeyAlgorithm() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectPublicKeyValue() override;
+ virtual OUString SAL_CALL getSignatureAlgorithm() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSHA1Thumbprint() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getMD5Thumbprint() override;
+ virtual css::security::CertificateKind SAL_CALL getCertificateKind() override;
+
+
+ virtual sal_Int32 SAL_CALL getCertificateUsage( ) override;
+
+ //Methods from XUnoTunnel
+ UNO3_GETIMPLEMENTATION_DECL(X509Certificate_MSCryptImpl)
+
+ /// @see xmlsecurity::Certificate::getSHA256Thumbprint().
+ virtual css::uno::Sequence<sal_Int8> getSHA256Thumbprint() override;
+
+ /// @see xmlsecurity::Certificate::getSignatureMethodAlgorithm().
+ virtual svl::crypto::SignatureMethodAlgorithm getSignatureMethodAlgorithm() override;
+
+ //Helper methods
+ void setMswcryCert( const CERT_CONTEXT* cert ) ;
+ const CERT_CONTEXT* getMswcryCert() const ;
+ /// @throws css::uno::RuntimeException
+ void setRawCert( css::uno::Sequence< sal_Int8 > const & rawCert ) ;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/xmlsecuritycontext_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/xmlsecuritycontext_mscryptimpl.cxx
new file mode 100644
index 000000000..7d71d4863
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/xmlsecuritycontext_mscryptimpl.cxx
@@ -0,0 +1,155 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <xmlsec-wrapper.h>
+
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
+#include "securityenvironment_mscryptimpl.hxx"
+
+#include <xmlsec/xmlstreamio.hxx>
+#include "akmngr.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang ;
+using ::com::sun::star::lang::XMultiServiceFactory ;
+using ::com::sun::star::lang::XSingleServiceFactory ;
+
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::xml::crypto::XXMLSecurityContext ;
+
+namespace {
+
+class XMLSecurityContext_MSCryptImpl : public ::cppu::WeakImplHelper<
+ css::xml::crypto::XXMLSecurityContext ,
+ css::lang::XServiceInfo >
+{
+ private:
+ //xmlSecKeysMngrPtr m_pKeysMngr ;
+ css::uno::Reference< css::xml::crypto::XSecurityEnvironment > m_xSecurityEnvironment ;
+
+ public:
+ XMLSecurityContext_MSCryptImpl();
+
+ //Methods from XXMLSecurityContext
+ virtual sal_Int32 SAL_CALL addSecurityEnvironment(
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& aSecurityEnvironment
+ ) override;
+
+ virtual ::sal_Int32 SAL_CALL getSecurityEnvironmentNumber( ) override;
+
+ virtual css::uno::Reference<
+ css::xml::crypto::XSecurityEnvironment > SAL_CALL
+ getSecurityEnvironmentByIndex( ::sal_Int32 index ) override;
+
+ virtual css::uno::Reference<
+ css::xml::crypto::XSecurityEnvironment > SAL_CALL
+ getSecurityEnvironment( ) override;
+
+ virtual ::sal_Int32 SAL_CALL getDefaultSecurityEnvironmentIndex( ) override;
+
+ virtual void SAL_CALL setDefaultSecurityEnvironmentIndex( sal_Int32 nDefaultEnvIndex ) override;
+
+
+ //Methods from XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& ServiceName
+ ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+XMLSecurityContext_MSCryptImpl::XMLSecurityContext_MSCryptImpl()
+{
+}
+
+sal_Int32 SAL_CALL XMLSecurityContext_MSCryptImpl::addSecurityEnvironment(
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& aSecurityEnvironment)
+{
+ if( !aSecurityEnvironment.is() )
+ {
+ throw uno::RuntimeException() ;
+ }
+
+ m_xSecurityEnvironment = aSecurityEnvironment;
+
+ return 0;
+}
+
+
+sal_Int32 SAL_CALL XMLSecurityContext_MSCryptImpl::getSecurityEnvironmentNumber( )
+{
+ return 1;
+}
+
+css::uno::Reference< css::xml::crypto::XSecurityEnvironment > SAL_CALL
+ XMLSecurityContext_MSCryptImpl::getSecurityEnvironmentByIndex( sal_Int32 index )
+{
+ if (index != 0)
+ {
+ throw uno::RuntimeException() ;
+ }
+ return m_xSecurityEnvironment;
+}
+
+css::uno::Reference< css::xml::crypto::XSecurityEnvironment > SAL_CALL
+ XMLSecurityContext_MSCryptImpl::getSecurityEnvironment( )
+{
+ return m_xSecurityEnvironment;
+}
+
+sal_Int32 SAL_CALL XMLSecurityContext_MSCryptImpl::getDefaultSecurityEnvironmentIndex( )
+{
+ return 0;
+}
+
+void SAL_CALL XMLSecurityContext_MSCryptImpl::setDefaultSecurityEnvironmentIndex( sal_Int32 /*nDefaultEnvIndex*/ )
+{
+ //dummy
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSecurityContext_MSCryptImpl::getImplementationName() {
+ return "com.sun.star.xml.crypto.XMLSecurityContext" ;
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSecurityContext_MSCryptImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+uno::Sequence< OUString > SAL_CALL XMLSecurityContext_MSCryptImpl::getSupportedServiceNames() {
+ return { "com.sun.star.xml.crypto.XMLSecurityContext" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_XMLSecurityContext_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new XMLSecurityContext_MSCryptImpl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx
new file mode 100644
index 000000000..87af0f040
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx
@@ -0,0 +1,312 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <rtl/uuid.h>
+#include <xmlsec-wrapper.h>
+
+#include <xmlsec/mscng/x509.h>
+
+#include <com/sun/star/xml/crypto/SecurityOperationStatus.hpp>
+#include <com/sun/star/xml/crypto/XXMLSignature.hpp>
+
+#include "securityenvironment_mscryptimpl.hxx"
+
+#include <xmlsec/xmldocumentwrapper_xmlsecimpl.hxx>
+#include <xmlelementwrapper_xmlsecimpl.hxx>
+#include <xmlsec/xmlstreamio.hxx>
+#include <xmlsec/errorcallback.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::lang ;
+using ::com::sun::star::lang::XMultiServiceFactory ;
+using ::com::sun::star::lang::XSingleServiceFactory ;
+
+using ::com::sun::star::xml::wrapper::XXMLElementWrapper ;
+using ::com::sun::star::xml::wrapper::XXMLDocumentWrapper ;
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::xml::crypto::XXMLSignature ;
+using ::com::sun::star::xml::crypto::XXMLSignatureTemplate ;
+using ::com::sun::star::xml::crypto::XXMLSecurityContext ;
+using ::com::sun::star::xml::crypto::XUriBinding ;
+
+namespace {
+
+class XMLSignature_MSCryptImpl : public ::cppu::WeakImplHelper<
+ css::xml::crypto::XXMLSignature ,
+ css::lang::XServiceInfo >
+{
+ public:
+ explicit XMLSignature_MSCryptImpl();
+
+ //Methods from XXMLSignature
+ virtual css::uno::Reference< css::xml::crypto::XXMLSignatureTemplate > SAL_CALL generate(
+ const css::uno::Reference< css::xml::crypto::XXMLSignatureTemplate >& aTemplate ,
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& aEnvironment
+ ) override;
+
+ virtual css::uno::Reference< css::xml::crypto::XXMLSignatureTemplate > SAL_CALL validate(
+ const css::uno::Reference< css::xml::crypto::XXMLSignatureTemplate >& aTemplate ,
+ const css::uno::Reference< css::xml::crypto::XXMLSecurityContext >& aContext
+ ) override;
+
+ //Methods from XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& ServiceName
+ ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+} ;
+
+}
+
+XMLSignature_MSCryptImpl::XMLSignature_MSCryptImpl() {
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_MSCryptImpl::generate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XSecurityEnvironment >& aEnvironment
+)
+{
+ xmlSecKeysMngrPtr pMngr = nullptr ;
+ xmlSecDSigCtxPtr pDsigCtx = nullptr ;
+ xmlNodePtr pNode = nullptr ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aEnvironment.is() )
+ throw RuntimeException() ;
+
+ //Get Keys Manager
+ Reference< XUnoTunnel > xSecTunnel( aEnvironment , UNO_QUERY_THROW ) ;
+ SecurityEnvironment_MSCryptImpl* pSecEnv = comphelper::getFromUnoTunnel<SecurityEnvironment_MSCryptImpl>(xSecTunnel);
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() ) {
+ throw RuntimeException() ;
+ }
+
+ Reference< XUnoTunnel > xNodTunnel( xElement , UNO_QUERY_THROW ) ;
+ XMLElementWrapper_XmlSecImpl* pElement = comphelper::getFromUnoTunnel<XMLElementWrapper_XmlSecImpl>(xNodTunnel);
+ if( pElement == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ setErrorRecorder( );
+
+ pMngr = pSecEnv->createKeysManager();
+ if( !pMngr ) {
+ throw RuntimeException() ;
+ }
+
+ //Create Signature context
+ pDsigCtx = xmlSecDSigCtxCreate( pMngr ) ;
+ if( pDsigCtx == nullptr )
+ {
+ //throw XMLSignatureException() ;
+ SecurityEnvironment_MSCryptImpl::destroyKeysManager( pMngr );
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ //Sign the template
+ if( xmlSecDSigCtxSign( pDsigCtx , pNode ) == 0 )
+ {
+ if (pDsigCtx->status == xmlSecDSigStatusSucceeded)
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED);
+ else
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN);
+ }
+ else
+ {
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN);
+ }
+
+
+ xmlSecDSigCtxDestroy( pDsigCtx ) ;
+ SecurityEnvironment_MSCryptImpl::destroyKeysManager( pMngr );
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+ clearErrorRecorder();
+ return aTemplate ;
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_MSCryptImpl::validate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XXMLSecurityContext >& aSecurityCtx
+) {
+ xmlSecKeysMngrPtr pMngr = nullptr ;
+ xmlSecDSigCtxPtr pDsigCtx = nullptr ;
+ xmlNodePtr pNode = nullptr ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aSecurityCtx.is() )
+ throw RuntimeException() ;
+
+ //Get Keys Manager
+ Reference< XSecurityEnvironment > xSecEnv
+ = aSecurityCtx->getSecurityEnvironmentByIndex(
+ aSecurityCtx->getDefaultSecurityEnvironmentIndex());
+ Reference< XUnoTunnel > xSecTunnel( xSecEnv , UNO_QUERY_THROW ) ;
+ SecurityEnvironment_MSCryptImpl* pSecEnv = comphelper::getFromUnoTunnel<SecurityEnvironment_MSCryptImpl>(xSecTunnel);
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() )
+ throw RuntimeException() ;
+
+ Reference< XUnoTunnel > xNodTunnel( xElement , UNO_QUERY_THROW ) ;
+ XMLElementWrapper_XmlSecImpl* pElement = comphelper::getFromUnoTunnel<XMLElementWrapper_XmlSecImpl>(xNodTunnel);
+ if( pElement == nullptr )
+ throw RuntimeException() ;
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ setErrorRecorder( );
+
+ pMngr = pSecEnv->createKeysManager();
+ if( !pMngr ) {
+ throw RuntimeException() ;
+ }
+
+ //Create Signature context
+ pDsigCtx = xmlSecDSigCtxCreate( pMngr ) ;
+ if( pDsigCtx == nullptr )
+ {
+ SecurityEnvironment_MSCryptImpl::destroyKeysManager( pMngr );
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ // We do certificate verification ourselves.
+ pDsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
+
+ // limit possible key data to valid X509 certificates only, no KeyValues
+ if (xmlSecPtrListAdd(&(pDsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecMSCngKeyDataX509GetKlass()) < 0)
+ throw RuntimeException("failed to limit allowed key data");
+
+ //Verify signature
+ //The documentation says that the signature is only valid if the return value is 0 (that is, not < 0)
+ //AND pDsigCtx->status == xmlSecDSigStatusSucceeded. That is, we must not make any assumptions, if
+ //the return value is < 0. Then we must regard the signature as INVALID. We cannot use the
+ //error recorder feature to get the ONE error that made the verification fail, because there is no
+ //documentation/specification as to how to interpret the number of recorded errors and what is the initial
+ //error.
+ int rs = xmlSecDSigCtxVerify(pDsigCtx , pNode);
+
+ // Also verify manifest: this is empty for ODF, but contains everything (except signature metadata) for OOXML.
+ xmlSecSize nReferenceCount = xmlSecPtrListGetSize(&pDsigCtx->manifestReferences);
+ // Require that all manifest references are also good.
+ xmlSecSize nReferenceGood = 0;
+ for (xmlSecSize nReference = 0; nReference < nReferenceCount; ++nReference)
+ {
+ xmlSecDSigReferenceCtxPtr pReference = static_cast<xmlSecDSigReferenceCtxPtr>(xmlSecPtrListGetItem(&pDsigCtx->manifestReferences, nReference));
+ if (pReference)
+ {
+ if (pReference->status == xmlSecDSigStatusSucceeded)
+ ++nReferenceGood;
+ }
+ }
+ SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount);
+
+ if (rs == 0 && nReferenceCount == nReferenceGood)
+ {
+ if (pDsigCtx->status == xmlSecDSigStatusSucceeded)
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED);
+ else
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN);
+ }
+ else
+ {
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN);
+ }
+
+ xmlSecDSigCtxDestroy( pDsigCtx ) ;
+ SecurityEnvironment_MSCryptImpl::destroyKeysManager( pMngr );
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+
+ clearErrorRecorder();
+ return aTemplate;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSignature_MSCryptImpl::getImplementationName() {
+ return "com.sun.star.xml.crypto.XMLSignature";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSignature_MSCryptImpl::supportsService( const OUString& serviceName) {
+return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL XMLSignature_MSCryptImpl::getSupportedServiceNames() {
+ return { "com.sun.star.xml.crypto.XMLSignature" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_XMLSignature_get_implementation(uno::XComponentContext* /*pCtx*/,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new XMLSignature_MSCryptImpl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/certerrors.h b/xmlsecurity/source/xmlsec/nss/certerrors.h
new file mode 100644
index 000000000..e8b7b3873
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/certerrors.h
@@ -0,0 +1,385 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+{SEC_ERROR_IO, "An I/O error occurred during security authorization."},
+
+{SEC_ERROR_LIBRARY_FAILURE, "security library failure."},
+
+{SEC_ERROR_BAD_DATA, "security library: received bad data."},
+
+{SEC_ERROR_OUTPUT_LEN, "security library: output length error."},
+
+{SEC_ERROR_INPUT_LEN, "security library has experienced an input length error."},
+
+{SEC_ERROR_INVALID_ARGS, "security library: invalid arguments."},
+
+{SEC_ERROR_INVALID_ALGORITHM, "security library: invalid algorithm."},
+
+{SEC_ERROR_INVALID_AVA, "security library: invalid AVA."},
+
+{SEC_ERROR_INVALID_TIME, "Improperly formatted time string."},
+
+{SEC_ERROR_BAD_DER, "security library: improperly formatted DER-encoded message."},
+
+{SEC_ERROR_BAD_SIGNATURE, "Peer's certificate has an invalid signature."},
+
+{SEC_ERROR_EXPIRED_CERTIFICATE, "Peer's Certificate has expired."},
+
+{SEC_ERROR_REVOKED_CERTIFICATE, "Peer's Certificate has been revoked."},
+
+{SEC_ERROR_UNKNOWN_ISSUER, "Peer's Certificate issuer is not recognized."},
+
+{SEC_ERROR_BAD_KEY, "Peer's public key is invalid."},
+
+{SEC_ERROR_BAD_PASSWORD, "The security password entered is incorrect."},
+
+{SEC_ERROR_RETRY_PASSWORD, "New password entered incorrectly. Please try again."},
+
+{SEC_ERROR_NO_NODELOCK, "security library: no nodelock."},
+
+{SEC_ERROR_BAD_DATABASE, "security library: bad database."},
+
+{SEC_ERROR_NO_MEMORY, "security library: memory allocation failure."},
+
+{SEC_ERROR_UNTRUSTED_ISSUER, "Peer's certificate issuer has been marked as not trusted by the user."},
+
+{SEC_ERROR_UNTRUSTED_CERT, "Peer's certificate has been marked as not trusted by the user."},
+
+{SEC_ERROR_DUPLICATE_CERT, "Certificate already exists in your database."},
+
+{SEC_ERROR_DUPLICATE_CERT_NAME, "Downloaded certificate's name duplicates one already in your database."},
+
+{SEC_ERROR_ADDING_CERT, "Error adding certificate to database."},
+
+{SEC_ERROR_FILING_KEY, "Error refiling the key for this certificate."},
+
+{SEC_ERROR_NO_KEY, "The private key for this certificate cannot be found in key database"},
+
+{SEC_ERROR_CERT_VALID, "This certificate is valid."},
+
+{SEC_ERROR_CERT_NOT_VALID, "This certificate is not valid."},
+
+{SEC_ERROR_CERT_NO_RESPONSE, "Cert Library: No Response"},
+
+{SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE, "The certificate issuer's certificate has expired. Check your system date and time."},
+
+{SEC_ERROR_CRL_EXPIRED, "The CRL for the certificate's issuer has expired. Update it or check your system date and time."},
+
+{SEC_ERROR_CRL_BAD_SIGNATURE, "The CRL for the certificate's issuer has an invalid signature."},
+
+{SEC_ERROR_CRL_INVALID, "New CRL has an invalid format."},
+
+{SEC_ERROR_EXTENSION_VALUE_INVALID, "Certificate extension value is invalid."},
+
+{SEC_ERROR_EXTENSION_NOT_FOUND, "Certificate extension not found."},
+
+{SEC_ERROR_CA_CERT_INVALID, "Issuer certificate is invalid."},
+
+{SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID, "Certificate path length constraint is invalid."},
+
+{SEC_ERROR_CERT_USAGES_INVALID, "Certificate usages field is invalid."},
+
+{SEC_INTERNAL_ONLY, "**Internal ONLY module**"},
+
+{SEC_ERROR_INVALID_KEY, "The key does not support the requested operation."},
+
+{SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, "Certificate contains unknown critical extension."},
+
+{SEC_ERROR_OLD_CRL, "New CRL is not later than the current one."},
+
+{SEC_ERROR_NO_EMAIL_CERT, "Not encrypted or signed: you do not yet have an email certificate."},
+
+{SEC_ERROR_NO_RECIPIENT_CERTS_QUERY, "Not encrypted: you do not have certificates for each of the recipients."},
+
+{SEC_ERROR_NOT_A_RECIPIENT, "Cannot decrypt: you are not a recipient, or matching certificate and \
+private key not found."},
+
+{SEC_ERROR_PKCS7_KEYALG_MISMATCH, "Cannot decrypt: key encryption algorithm does not match your certificate."},
+
+{SEC_ERROR_PKCS7_BAD_SIGNATURE, "Signature verification failed: no signer found, too many signers found, \
+or improper or corrupted data."},
+
+{SEC_ERROR_UNSUPPORTED_KEYALG, "Unsupported or unknown key algorithm."},
+
+{SEC_ERROR_DECRYPTION_DISALLOWED, "Cannot decrypt: encrypted using a disallowed algorithm or key size."},
+
+
+/* Fortezza Alerts */
+{XP_SEC_FORTEZZA_BAD_CARD, "Fortezza card has not been properly initialized. \
+Please remove it and return it to your issuer."},
+
+{XP_SEC_FORTEZZA_NO_CARD, "No Fortezza cards Found"},
+
+{XP_SEC_FORTEZZA_NONE_SELECTED, "No Fortezza card selected"},
+
+{XP_SEC_FORTEZZA_MORE_INFO, "Please select a personality to get more info on"},
+
+{XP_SEC_FORTEZZA_PERSON_NOT_FOUND, "Personality not found"},
+
+{XP_SEC_FORTEZZA_NO_MORE_INFO, "No more information on that Personality"},
+
+{XP_SEC_FORTEZZA_BAD_PIN, "Invalid Pin"},
+
+{XP_SEC_FORTEZZA_PERSON_ERROR, "Couldn't initialize Fortezza personalities."},
+/* end fortezza alerts. */
+
+{SEC_ERROR_NO_KRL, "No KRL for this site's certificate has been found."},
+
+{SEC_ERROR_KRL_EXPIRED, "The KRL for this site's certificate has expired."},
+
+{SEC_ERROR_KRL_BAD_SIGNATURE, "The KRL for this site's certificate has an invalid signature."},
+
+{SEC_ERROR_REVOKED_KEY, "The key for this site's certificate has been revoked."},
+
+{SEC_ERROR_KRL_INVALID, "New KRL has an invalid format."},
+
+{SEC_ERROR_NEED_RANDOM, "security library: need random data."},
+
+{SEC_ERROR_NO_MODULE, "security library: no security module can perform the requested operation."},
+
+{SEC_ERROR_NO_TOKEN, "The security card or token does not exist, needs to be initialized, or has been removed."},
+
+{SEC_ERROR_READ_ONLY, "security library: read-only database."},
+
+{SEC_ERROR_NO_SLOT_SELECTED, "No slot or token was selected."},
+
+{SEC_ERROR_CERT_NICKNAME_COLLISION, "A certificate with the same nickname already exists."},
+
+{SEC_ERROR_KEY_NICKNAME_COLLISION, "A key with the same nickname already exists."},
+
+{SEC_ERROR_SAFE_NOT_CREATED, "error while creating safe object"},
+
+{SEC_ERROR_BAGGAGE_NOT_CREATED, "error while creating baggage object"},
+
+{XP_JAVA_REMOVE_PRINCIPAL_ERROR, "Couldn't remove the principal"},
+
+{XP_JAVA_DELETE_PRIVILEGE_ERROR, "Couldn't delete the privilege"},
+
+{XP_JAVA_CERT_NOT_EXISTS_ERROR, "This principal doesn't have a certificate"},
+
+{SEC_ERROR_BAD_EXPORT_ALGORITHM, "Required algorithm is not allowed."},
+
+{SEC_ERROR_EXPORTING_CERTIFICATES, "Error attempting to export certificates."},
+
+{SEC_ERROR_IMPORTING_CERTIFICATES, "Error attempting to import certificates."},
+
+{SEC_ERROR_PKCS12_DECODING_PFX, "Unable to import. Decoding error. File not valid."},
+
+{SEC_ERROR_PKCS12_INVALID_MAC, "Unable to import. Invalid MAC. Incorrect password or corrupt file."},
+
+{SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM, "Unable to import. MAC algorithm not supported."},
+
+{SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE, "Unable to import. Only password integrity and privacy modes supported."},
+
+{SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE, "Unable to import. File structure is corrupt."},
+
+{SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM, "Unable to import. Encryption algorithm not supported."},
+
+{SEC_ERROR_PKCS12_UNSUPPORTED_VERSION, "Unable to import. File version not supported."},
+
+{SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT, "Unable to import. Incorrect privacy password."},
+
+{SEC_ERROR_PKCS12_CERT_COLLISION, "Unable to import. Same nickname already exists in database."},
+
+{SEC_ERROR_USER_CANCELLED, "The user pressed cancel."},
+
+{SEC_ERROR_PKCS12_DUPLICATE_DATA, "Not imported, already in database."},
+
+{SEC_ERROR_MESSAGE_SEND_ABORTED, "Message not sent."},
+
+{SEC_ERROR_INADEQUATE_KEY_USAGE, "Certificate key usage inadequate for attempted operation."},
+
+{SEC_ERROR_INADEQUATE_CERT_TYPE, "Certificate type not approved for application."},
+
+{SEC_ERROR_CERT_ADDR_MISMATCH, "Address in signing certificate does not match address in message headers."},
+
+{SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY, "Unable to import. Error attempting to import private key."},
+
+{SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN, "Unable to import. Error attempting to import certificate chain."},
+
+{SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME, "Unable to export. Unable to locate certificate or key by nickname."},
+
+{SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY, "Unable to export. Private Key could not be located and exported."},
+
+{SEC_ERROR_PKCS12_UNABLE_TO_WRITE, "Unable to export. Unable to write the export file."},
+
+{SEC_ERROR_PKCS12_UNABLE_TO_READ, "Unable to import. Unable to read the import file."},
+
+{SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED, "Unable to export. Key database corrupt or deleted."},
+
+{SEC_ERROR_KEYGEN_FAIL, "Unable to generate public/private key pair."},
+
+{SEC_ERROR_INVALID_PASSWORD, "Password entered is invalid. Please pick a different one."},
+
+{SEC_ERROR_RETRY_OLD_PASSWORD, "Old password entered incorrectly. Please try again."},
+
+{SEC_ERROR_BAD_NICKNAME, "Certificate nickname already in use."},
+
+{SEC_ERROR_NOT_FORTEZZA_ISSUER, "Peer FORTEZZA chain has a non-FORTEZZA Certificate."},
+
+{SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY, "A sensitive key cannot be moved to the slot where it is needed."},
+
+{SEC_ERROR_JS_INVALID_MODULE_NAME, "Invalid module name."},
+
+{SEC_ERROR_JS_INVALID_DLL, "Invalid module path/filename"},
+
+{SEC_ERROR_JS_ADD_MOD_FAILURE, "Unable to add module"},
+
+{SEC_ERROR_JS_DEL_MOD_FAILURE, "Unable to delete module"},
+
+{SEC_ERROR_OLD_KRL, "New KRL is not later than the current one."},
+
+{SEC_ERROR_CKL_CONFLICT, "New CKL has different issuer than current CKL. Delete current CKL."},
+
+{SEC_ERROR_CERT_NOT_IN_NAME_SPACE, "The Certifying Authority for this certificate is not permitted to issue a \
+certificate with this name."},
+
+{SEC_ERROR_KRL_NOT_YET_VALID, "The key revocation list for this certificate is not yet valid."},
+
+{SEC_ERROR_CRL_NOT_YET_VALID, "The certificate revocation list for this certificate is not yet valid."},
+
+{SEC_ERROR_UNKNOWN_CERT, "The requested certificate could not be found."},
+
+{SEC_ERROR_UNKNOWN_SIGNER, "The signer's certificate could not be found."},
+
+{SEC_ERROR_CERT_BAD_ACCESS_LOCATION, "The location for the certificate status server has invalid format."},
+
+{SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE, "The OCSP response cannot be fully decoded; it is of an unknown type."},
+
+{SEC_ERROR_OCSP_BAD_HTTP_RESPONSE, "The OCSP server returned unexpected/invalid HTTP data."},
+
+{SEC_ERROR_OCSP_MALFORMED_REQUEST, "The OCSP server found the request to be corrupted or improperly formed."},
+
+{SEC_ERROR_OCSP_SERVER_ERROR, "The OCSP server experienced an internal error."},
+
+{SEC_ERROR_OCSP_TRY_SERVER_LATER, "The OCSP server suggests trying again later."},
+
+{SEC_ERROR_OCSP_REQUEST_NEEDS_SIG, "The OCSP server requires a signature on this request."},
+
+{SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST, "The OCSP server has refused this request as unauthorized."},
+
+{SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, "The OCSP server returned an unrecognizable status."},
+
+{SEC_ERROR_OCSP_UNKNOWN_CERT, "The OCSP server has no status for the certificate."},
+
+{SEC_ERROR_OCSP_NOT_ENABLED, "You must enable OCSP before performing this operation."},
+
+{SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER, "You must set the OCSP default responder before performing this operation."},
+
+{SEC_ERROR_OCSP_MALFORMED_RESPONSE, "The response from the OCSP server was corrupted or improperly formed."},
+
+{SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE, "The signer of the OCSP response is not authorized to give status for \
+this certificate."},
+
+{SEC_ERROR_OCSP_FUTURE_RESPONSE, "The OCSP response is not yet valid (contains a date in the future},."},
+
+{SEC_ERROR_OCSP_OLD_RESPONSE, "The OCSP response contains out-of-date information."},
+
+{SEC_ERROR_DIGEST_NOT_FOUND, "The CMS or PKCS #7 Digest was not found in signed message."},
+
+{SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE, "The CMS or PKCS #7 Message type is unsupported."},
+
+{SEC_ERROR_MODULE_STUCK, "PKCS #11 module could not be removed because it is still in use."},
+
+{SEC_ERROR_BAD_TEMPLATE, "Could not decode ASN.1 data. Specified template was invalid."},
+
+{SEC_ERROR_CRL_NOT_FOUND, "No matching CRL was found."},
+
+{SEC_ERROR_REUSED_ISSUER_AND_SERIAL, "You are attempting to import a cert with the same issuer/serial as \
+an existing cert, but that is not the same cert."},
+
+{SEC_ERROR_BUSY, "NSS could not shutdown. Objects are still in use."},
+
+{SEC_ERROR_EXTRA_INPUT, "DER-encoded message contained extra unused data."},
+
+{SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE, "Unsupported elliptic curve."},
+
+{SEC_ERROR_UNSUPPORTED_EC_POINT_FORM, "Unsupported elliptic curve point form."},
+
+{SEC_ERROR_UNRECOGNIZED_OID, "Unrecognized Object Identifier."},
+
+{SEC_ERROR_OCSP_INVALID_SIGNING_CERT, "Invalid OCSP signing certificate in OCSP response."},
+
+{SEC_ERROR_REVOKED_CERTIFICATE_CRL, "Certificate is revoked in issuer's certificate revocation list."},
+
+{SEC_ERROR_REVOKED_CERTIFICATE_OCSP, "Issuer's OCSP responder reports certificate is revoked."},
+
+{SEC_ERROR_CRL_INVALID_VERSION, "Issuer's Certificate Revocation List has an unknown version number."},
+
+{SEC_ERROR_CRL_V1_CRITICAL_EXTENSION, "Issuer's V1 Certificate Revocation List has a critical extension."},
+
+{SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION, "Issuer's V2 Certificate Revocation List has an unknown critical extension."},
+
+{SEC_ERROR_UNKNOWN_OBJECT_TYPE, "Unknown object type specified."},
+
+{SEC_ERROR_INCOMPATIBLE_PKCS11, "PKCS #11 driver violates the spec in an incompatible way."},
+
+{SEC_ERROR_NO_EVENT, "No new slot event is available at this time."},
+
+{SEC_ERROR_CRL_ALREADY_EXISTS, "CRL already exists."},
+
+{SEC_ERROR_NOT_INITIALIZED, "NSS is not initialized."},
+
+{SEC_ERROR_TOKEN_NOT_LOGGED_IN, "The operation failed because the PKCS#11 token is not logged in."},
+
+{SEC_ERROR_OCSP_RESPONDER_CERT_INVALID, "Configured OCSP responder's certificate is invalid."},
+
+{SEC_ERROR_OCSP_BAD_SIGNATURE, "OCSP response has an invalid signature."},
+
+{SEC_ERROR_OUT_OF_SEARCH_LIMITS, "Cert validation search is out of search limits"},
+
+{SEC_ERROR_INVALID_POLICY_MAPPING, "Policy mapping contains anypolicy"},
+
+{SEC_ERROR_POLICY_VALIDATION_FAILED, "Cert chain fails policy validation"},
+
+{SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE, "Unknown location type in cert AIA extension"},
+
+{SEC_ERROR_BAD_HTTP_RESPONSE, "Server returned bad HTTP response"},
+
+{SEC_ERROR_BAD_LDAP_RESPONSE, "Server returned bad LDAP response"},
+
+{SEC_ERROR_FAILED_TO_ENCODE_DATA, "Failed to encode data with ASN1 encoder"},
+
+{SEC_ERROR_BAD_INFO_ACCESS_LOCATION, "Bad information access location in cert extension"},
+
+{SEC_ERROR_LIBPKIX_INTERNAL, "Libpkix internal error occurred during cert validation."},
+
+#if ( NSS_VMAJOR > 3 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR > 12 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH > 2 )
+// following 3 errors got first used in NSS 3.12.3
+// they were in the header even in 3.12.2 but there was missing the mapping in pk11err.c
+// see also https://bugzilla.mozilla.org/show_bug.cgi?id=453364
+
+{SEC_ERROR_PKCS11_GENERAL_ERROR, "A PKCS #11 module returned CKR_GENERAL_ERROR, indicating that an unrecoverable error has occurred."},
+
+{SEC_ERROR_PKCS11_FUNCTION_FAILED, "A PKCS #11 module returned CKR_FUNCTION_FAILED, indicating that the requested function could not be performed. Trying the same operation again might succeed."},
+
+{SEC_ERROR_PKCS11_DEVICE_ERROR, "A PKCS #11 module returned CKR_DEVICE_ERROR, indicating that a problem has occurred with the token or slot."},
+
+#endif
+
+#if ( NSS_VMAJOR > 3 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR > 12 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH > 3 )
+// following 2 errors got added in NSS 3.12.4
+
+{SEC_ERROR_BAD_INFO_ACCESS_METHOD, "Unknown information access method in certificate extension."},
+
+{SEC_ERROR_CRL_IMPORT_FAILED, "Error attempting to import a CRL."},
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx b/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx
new file mode 100644
index 000000000..ff3441249
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx
@@ -0,0 +1,265 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <osl/diagnose.h>
+#include <rtl/random.h>
+#include <rtl/ref.hxx>
+
+#include "ciphercontext.hxx"
+#include <pk11pub.h>
+
+using namespace ::com::sun::star;
+
+uno::Reference< xml::crypto::XCipherContext > OCipherContext::Create( CK_MECHANISM_TYPE nNSSCipherID, const uno::Sequence< ::sal_Int8 >& aKey, const uno::Sequence< ::sal_Int8 >& aInitializationVector, bool bEncryption, bool bW3CPadding )
+{
+ ::rtl::Reference< OCipherContext > xResult = new OCipherContext;
+
+ xResult->m_pSlot = PK11_GetBestSlot( nNSSCipherID, nullptr );
+ if ( xResult->m_pSlot )
+ {
+ SECItem aKeyItem = { siBuffer, const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( aKey.getConstArray() ) ), sal::static_int_cast<unsigned>( aKey.getLength() ) };
+ xResult->m_pSymKey = PK11_ImportSymKey( xResult->m_pSlot, nNSSCipherID, PK11_OriginDerive, bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, &aKeyItem, nullptr );
+ if ( xResult->m_pSymKey )
+ {
+ SECItem aIVItem = { siBuffer, const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( aInitializationVector.getConstArray() ) ), sal::static_int_cast<unsigned>( aInitializationVector.getLength() ) };
+ xResult->m_pSecParam = PK11_ParamFromIV( nNSSCipherID, &aIVItem );
+ if ( xResult->m_pSecParam )
+ {
+ xResult->m_pContext = PK11_CreateContextBySymKey( nNSSCipherID, bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, xResult->m_pSymKey, xResult->m_pSecParam);
+ if ( xResult->m_pContext )
+ {
+ xResult->m_bEncryption = bEncryption;
+ xResult->m_bW3CPadding = bW3CPadding;
+ xResult->m_bPadding = bW3CPadding || ( PK11_GetPadMechanism( nNSSCipherID ) == nNSSCipherID );
+ xResult->m_nBlockSize = PK11_GetBlockSize( nNSSCipherID, xResult->m_pSecParam );
+ if ( xResult->m_nBlockSize <= SAL_MAX_INT8 )
+ return xResult;
+ }
+ }
+ }
+ }
+
+ return uno::Reference< xml::crypto::XCipherContext >();
+}
+
+void OCipherContext::Dispose()
+{
+ if ( m_pContext )
+ {
+ PK11_DestroyContext( m_pContext, PR_TRUE );
+ m_pContext = nullptr;
+ }
+
+ if ( m_pSecParam )
+ {
+ SECITEM_FreeItem( m_pSecParam, PR_TRUE );
+ m_pSecParam = nullptr;
+ }
+
+ if ( m_pSymKey )
+ {
+ PK11_FreeSymKey( m_pSymKey );
+ m_pSymKey = nullptr;
+ }
+
+ if ( m_pSlot )
+ {
+ PK11_FreeSlot( m_pSlot );
+ m_pSlot = nullptr;
+ }
+
+ m_bDisposed = true;
+}
+
+uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::convertWithCipherContext( const uno::Sequence< ::sal_Int8 >& aData )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bBroken )
+ throw uno::RuntimeException();
+
+ if ( m_bDisposed )
+ throw lang::DisposedException();
+
+ uno::Sequence< sal_Int8 > aToConvert;
+ if ( aData.hasElements() )
+ {
+ sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength();
+ OSL_ENSURE( nOldLastBlockLen <= m_nBlockSize, "Unexpected last block size!" );
+
+ sal_Int32 nAvailableData = nOldLastBlockLen + aData.getLength();
+ sal_Int32 nToConvertLen;
+ if ( m_bEncryption || !m_bW3CPadding )
+ {
+ if ( nAvailableData % m_nBlockSize == 0 )
+ nToConvertLen = nAvailableData;
+ else if ( nAvailableData < m_nBlockSize )
+ nToConvertLen = 0;
+ else
+ nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize;
+ }
+ else
+ {
+ // decryption with W3C padding needs at least one block for finalizing
+ if ( nAvailableData < m_nBlockSize * 2 )
+ nToConvertLen = 0;
+ else
+ nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize - m_nBlockSize;
+ }
+
+ aToConvert.realloc( nToConvertLen );
+ if ( nToConvertLen == 0 )
+ {
+ m_aLastBlock.realloc( nOldLastBlockLen + aData.getLength() );
+ memcpy( m_aLastBlock.getArray() + nOldLastBlockLen, aData.getConstArray(), aData.getLength() );
+ // aToConvert stays empty
+ }
+ else if ( nToConvertLen < nOldLastBlockLen )
+ {
+ memcpy( aToConvert.getArray(), m_aLastBlock.getConstArray(), nToConvertLen );
+ memcpy( m_aLastBlock.getArray(), m_aLastBlock.getConstArray() + nToConvertLen, nOldLastBlockLen - nToConvertLen );
+ m_aLastBlock.realloc( nOldLastBlockLen - nToConvertLen + aData.getLength() );
+ memcpy( m_aLastBlock.getArray() + nOldLastBlockLen - nToConvertLen, aData.getConstArray(), aData.getLength() );
+ }
+ else
+ {
+ memcpy( aToConvert.getArray(), m_aLastBlock.getConstArray(), nOldLastBlockLen );
+ if ( nToConvertLen > nOldLastBlockLen )
+ memcpy( aToConvert.getArray() + nOldLastBlockLen, aData.getConstArray(), nToConvertLen - nOldLastBlockLen );
+ m_aLastBlock.realloc( nAvailableData - nToConvertLen );
+ memcpy( m_aLastBlock.getArray(), aData.getConstArray() + nToConvertLen - nOldLastBlockLen, nAvailableData - nToConvertLen );
+ }
+ }
+
+ uno::Sequence< sal_Int8 > aResult;
+ OSL_ENSURE( aToConvert.getLength() % m_nBlockSize == 0, "Unexpected size of the data to encrypt!" );
+ if ( aToConvert.hasElements() )
+ {
+ int nResultLen = 0;
+ aResult.realloc( aToConvert.getLength() + m_nBlockSize );
+ if ( PK11_CipherOp( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() ), &nResultLen, aResult.getLength(), reinterpret_cast< const unsigned char* >( aToConvert.getConstArray() ), aToConvert.getLength() ) != SECSuccess )
+ {
+ m_bBroken = true;
+ Dispose();
+ throw uno::RuntimeException();
+ }
+
+ m_nConverted += aToConvert.getLength();
+ aResult.realloc( nResultLen );
+ }
+
+ return aResult;
+}
+
+uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::finalizeCipherContextAndDispose()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bBroken )
+ throw uno::RuntimeException();
+
+ if ( m_bDisposed )
+ throw lang::DisposedException();
+
+ OSL_ENSURE( m_nBlockSize <= SAL_MAX_INT8, "Unexpected block size!" );
+ OSL_ENSURE( m_nConverted % m_nBlockSize == 0, "Unexpected amount of bytes is already converted!" );
+ sal_Int32 nSizeForPadding = ( m_nConverted + m_aLastBlock.getLength() ) % m_nBlockSize;
+
+ // if it is decryption, the amount of data should be rounded to the block size even in case of padding
+ if ( ( !m_bPadding || !m_bEncryption ) && nSizeForPadding )
+ throw uno::RuntimeException("The data should contain complete blocks only." );
+
+ if ( m_bW3CPadding && m_bEncryption )
+ {
+ // in this case the last block should be smaller than standard block
+ // it will be increased with the padding
+ OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize, "Unexpected size of cashed incomplete last block!" );
+
+ // W3CPadding handling for encryption
+ sal_Int32 nPaddingSize = m_nBlockSize - nSizeForPadding;
+ sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength();
+ m_aLastBlock.realloc( nOldLastBlockLen + nPaddingSize );
+ auto pLastBlock = m_aLastBlock.getArray();
+
+ if ( nPaddingSize > 1 )
+ {
+ rtlRandomPool aRandomPool = rtl_random_createPool();
+ rtl_random_getBytes( aRandomPool, pLastBlock + nOldLastBlockLen, nPaddingSize - 1 );
+ rtl_random_destroyPool ( aRandomPool );
+ }
+ pLastBlock[m_aLastBlock.getLength() - 1] = static_cast< sal_Int8 >( nPaddingSize );
+ }
+
+ // finally should the last block be smaller than two standard blocks
+ OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize * 2 , "Unexpected size of cashed incomplete last block!" );
+
+ uno::Sequence< sal_Int8 > aResult;
+ if ( m_aLastBlock.hasElements() )
+ {
+ int nPrefResLen = 0;
+ aResult.realloc( m_aLastBlock.getLength() + m_nBlockSize );
+ if ( PK11_CipherOp( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() ), &nPrefResLen, aResult.getLength(), reinterpret_cast< const unsigned char* >( m_aLastBlock.getConstArray() ), m_aLastBlock.getLength() ) != SECSuccess )
+ {
+ m_bBroken = true;
+ Dispose();
+ throw uno::RuntimeException();
+ }
+
+ aResult.realloc( nPrefResLen );
+ m_aLastBlock.realloc( 0 );
+ }
+
+ sal_Int32 nPrefixLen = aResult.getLength();
+ aResult.realloc( nPrefixLen + m_nBlockSize * 2 );
+ unsigned nFinalLen = 0;
+ if ( PK11_DigestFinal( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() + nPrefixLen ), &nFinalLen, aResult.getLength() - nPrefixLen ) != SECSuccess )
+ {
+ m_bBroken = true;
+ Dispose();
+ throw uno::RuntimeException();
+ }
+
+ aResult.realloc( nPrefixLen + nFinalLen );
+
+ if ( m_bW3CPadding && !m_bEncryption )
+ {
+ // W3CPadding handling for decryption
+ // aResult should have enough data, since we let m_aLastBlock be big enough in case of decryption
+ OSL_ENSURE( aResult.getLength() >= m_nBlockSize, "Not enough data to handle the padding!" );
+
+ sal_Int8 nBytesToRemove = aResult[aResult.getLength() - 1];
+ if ( nBytesToRemove <= 0 || nBytesToRemove > aResult.getLength() )
+ {
+ m_bBroken = true;
+ Dispose();
+ throw uno::RuntimeException();
+ }
+
+ aResult.realloc( aResult.getLength() - nBytesToRemove );
+ }
+
+ Dispose();
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/ciphercontext.hxx b/xmlsecurity/source/xmlsec/nss/ciphercontext.hxx
new file mode 100644
index 000000000..40b610ef5
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/ciphercontext.hxx
@@ -0,0 +1,80 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+#include <seccomon.h>
+#include <secmodt.h>
+
+class OCipherContext : public cppu::WeakImplHelper< css::xml::crypto::XCipherContext >
+{
+private:
+ std::mutex m_aMutex;
+
+ PK11SlotInfo* m_pSlot;
+ PK11SymKey* m_pSymKey;
+ SECItem* m_pSecParam;
+ PK11Context* m_pContext;
+
+ sal_Int32 m_nBlockSize;
+ css::uno::Sequence< sal_Int8 > m_aLastBlock;
+
+ bool m_bEncryption;
+ bool m_bPadding;
+ bool m_bW3CPadding;
+ sal_Int64 m_nConverted;
+
+ bool m_bDisposed;
+ bool m_bBroken;
+
+ void Dispose();
+
+ OCipherContext()
+ : m_pSlot( nullptr )
+ , m_pSymKey( nullptr )
+ , m_pSecParam( nullptr )
+ , m_pContext( nullptr )
+ , m_nBlockSize( 0 )
+ , m_bEncryption( false )
+ , m_bPadding( false )
+ , m_bW3CPadding( false )
+ , m_nConverted( 0 )
+ , m_bDisposed( false )
+ , m_bBroken( false )
+ {}
+
+public:
+
+ virtual ~OCipherContext() override
+ {
+ Dispose();
+ }
+
+ static css::uno::Reference< css::xml::crypto::XCipherContext > Create( CK_MECHANISM_TYPE nNSSCipherID, const css::uno::Sequence< ::sal_Int8 >& aKey, const css::uno::Sequence< ::sal_Int8 >& aInitializationVector, bool bEncryption, bool bW3CPadding );
+
+ // XCipherContext
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL convertWithCipherContext( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL finalizeCipherContextAndDispose( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/digestcontext.cxx b/xmlsecurity/source/xmlsec/nss/digestcontext.cxx
new file mode 100644
index 000000000..63c128e70
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/digestcontext.cxx
@@ -0,0 +1,94 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+
+#include <pk11pub.h>
+#include "digestcontext.hxx"
+
+using namespace ::com::sun::star;
+
+ODigestContext::~ODigestContext()
+{
+ if ( m_pContext )
+ {
+ PK11_DestroyContext( m_pContext, PR_TRUE );
+ m_pContext = nullptr;
+ }
+}
+
+void SAL_CALL ODigestContext::updateDigest( const uno::Sequence< ::sal_Int8 >& aData )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( m_bBroken )
+ throw uno::RuntimeException();
+
+ if ( m_bDisposed )
+ throw lang::DisposedException();
+
+ if (m_b1KData && m_nDigested >= 1024)
+ return;
+
+ uno::Sequence< sal_Int8 > aToDigest = aData;
+ if ( m_b1KData && m_nDigested + aData.getLength() > 1024 )
+ aToDigest.realloc( 1024 - m_nDigested );
+
+ if ( PK11_DigestOp( m_pContext, reinterpret_cast< const unsigned char* >( aToDigest.getConstArray() ), aToDigest.getLength() ) != SECSuccess )
+ {
+ PK11_DestroyContext( m_pContext, PR_TRUE );
+ m_pContext = nullptr;
+ m_bBroken = true;
+ throw uno::RuntimeException();
+ }
+
+ m_nDigested += aToDigest.getLength();
+}
+
+uno::Sequence< ::sal_Int8 > SAL_CALL ODigestContext::finalizeDigestAndDispose()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( m_bBroken )
+ throw uno::RuntimeException();
+
+ if ( m_bDisposed )
+ throw lang::DisposedException();
+
+ uno::Sequence< sal_Int8 > aResult( m_nDigestLength );
+ unsigned int nResultLen = 0;
+ if ( PK11_DigestFinal( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() ), &nResultLen, aResult.getLength() ) != SECSuccess )
+ {
+ PK11_DestroyContext( m_pContext, PR_TRUE );
+ m_pContext = nullptr;
+ m_bBroken = true;
+ throw uno::RuntimeException();
+ }
+
+ PK11_DestroyContext( m_pContext, PR_TRUE );
+ m_pContext = nullptr;
+ m_bDisposed = true;
+
+ aResult.realloc( nResultLen );
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/digestcontext.hxx b/xmlsecurity/source/xmlsec/nss/digestcontext.hxx
new file mode 100644
index 000000000..72e186485
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/digestcontext.hxx
@@ -0,0 +1,59 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+#include <secmodt.h>
+
+class ODigestContext : public cppu::WeakImplHelper< css::xml::crypto::XDigestContext >
+{
+private:
+ std::mutex m_aMutex;
+
+ PK11Context* m_pContext;
+ sal_Int32 const m_nDigestLength;
+ bool const m_b1KData;
+ sal_Int32 m_nDigested;
+
+ bool m_bDisposed;
+ bool m_bBroken;
+
+public:
+ ODigestContext( PK11Context* pContext, sal_Int32 nDigestLength, bool b1KData )
+ : m_pContext( pContext )
+ , m_nDigestLength( nDigestLength )
+ , m_b1KData( b1KData )
+ , m_nDigested( 0 )
+ , m_bDisposed( false )
+ , m_bBroken( false )
+ {}
+
+ virtual ~ODigestContext() override;
+
+
+ // XDigestContext
+ virtual void SAL_CALL updateDigest( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL finalizeDigestAndDispose() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/nssinitializer.cxx b/xmlsecurity/source/xmlsec/nss/nssinitializer.cxx
new file mode 100644
index 000000000..29b26734e
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/nssinitializer.cxx
@@ -0,0 +1,638 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/mozilla/XMozillaBootstrap.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/NSSInitializer.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <sal/types.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/string.hxx>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/tempfile.hxx>
+#include <salhelper/singletonref.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <nss/nssinitializer.hxx>
+
+#include "digestcontext.hxx"
+#include "ciphercontext.hxx"
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include <nss.h>
+#include <pk11pub.h>
+#include <secmod.h>
+#include <prerror.h>
+#include <prinit.h>
+
+namespace cssu = css::uno;
+namespace cssl = css::lang;
+
+using namespace com::sun::star;
+
+#define ROOT_CERTS "Root Certs for OpenOffice.org"
+
+extern "C" {
+
+static void nsscrypto_finalize();
+
+}
+
+namespace
+{
+
+class InitNSSPrivate
+{
+private:
+ std::unique_ptr<utl::TempFile> m_pTempFileDatabaseDirectory;
+
+public:
+ OUString getTempDatabasePath()
+ {
+ if (!m_pTempFileDatabaseDirectory)
+ {
+ m_pTempFileDatabaseDirectory.reset(new utl::TempFile(nullptr, true));
+ m_pTempFileDatabaseDirectory->EnableKillingFile();
+ }
+ return m_pTempFileDatabaseDirectory->GetFileName();
+ }
+
+ void reset()
+ {
+ if (m_pTempFileDatabaseDirectory)
+ {
+ m_pTempFileDatabaseDirectory.reset();
+ }
+ }
+};
+
+salhelper::SingletonRef<InitNSSPrivate>* getInitNSSPrivate()
+{
+ static salhelper::SingletonRef<InitNSSPrivate> aInitNSSPrivate;
+ return &aInitNSSPrivate;
+}
+
+bool nsscrypto_initialize( const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool & out_nss_init );
+
+#ifdef XMLSEC_CRYPTO_NSS
+
+void deleteRootsModule()
+{
+ SECMODModule *RootsModule = nullptr;
+ SECMODModuleList *list = SECMOD_GetDefaultModuleList();
+ SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
+ SECMOD_GetReadLock(lock);
+
+ while (!RootsModule && list)
+ {
+ SECMODModule *module = list->module;
+
+ for (int i=0; i < module->slotCount; i++)
+ {
+ PK11SlotInfo *slot = module->slots[i];
+ if (PK11_IsPresent(slot))
+ {
+ if (PK11_HasRootCerts(slot))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "The root certificates module \"" << module->commonName << "\" is already loaded: " << module->dllName);
+
+ RootsModule = SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ }
+ list = list->next;
+ }
+ SECMOD_ReleaseReadLock(lock);
+
+ if (!RootsModule)
+ return;
+
+ PRInt32 modType;
+ if (SECSuccess == SECMOD_DeleteModule(RootsModule->commonName, &modType))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Deleted module \"" << RootsModule->commonName << "\".");
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Failed to delete \"" << RootsModule->commonName << "\": " << RootsModule->dllName);
+ }
+ SECMOD_DestroyModule(RootsModule);
+ RootsModule = nullptr;
+}
+
+#endif
+
+bool lcl_pathExists(const OUString& sPath)
+{
+ if (sPath.isEmpty())
+ return false;
+
+ ::osl::DirectoryItem aPathItem;
+ OUString sURL;
+ osl::FileBase::getFileURLFromSystemPath(sPath, sURL);
+ if (::osl::FileBase::E_None == ::osl::DirectoryItem::get(sURL, aPathItem))
+ {
+ ::osl::FileStatus aStatus = osl_FileStatus_Mask_Validate;
+ if (::osl::FileBase::E_None == aPathItem.getFileStatus(aStatus))
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+const OUString & ONSSInitializer::getMozillaCurrentProfile(const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool bSetActive)
+{
+ if (m_bIsNSSinitialized)
+ return m_sNSSPath;
+ if (bSetActive)
+ m_bIsNSSinitialized = true;
+
+ // first, try to get the profile from "MOZILLA_CERTIFICATE_FOLDER"
+ const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
+ if (pEnv)
+ {
+ SAL_INFO(
+ "xmlsecurity.xmlsec",
+ "Using Mozilla profile from MOZILLA_CERTIFICATE_FOLDER=" << pEnv);
+ m_sNSSPath = OStringToOUString(pEnv, osl_getThreadTextEncoding());
+ }
+
+ // second, try to get saved user-preference
+ if (m_sNSSPath.isEmpty())
+ {
+ try
+ {
+ OUString sUserSetCertPath =
+ officecfg::Office::Common::Security::Scripting::CertDir::get().value_or(OUString());
+
+ if (lcl_pathExists(sUserSetCertPath))
+ {
+ SAL_INFO(
+ "xmlsecurity.xmlsec",
+ "Using Mozilla profile from /org.openoffice.Office.Common/"
+ "Security/Scripting/CertDir: " << sUserSetCertPath);
+ m_sNSSPath = sUserSetCertPath;
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("xmlsecurity.xmlsec", "getMozillaCurrentProfile:");
+ }
+ }
+
+ // third, dig around to see if there's one default available
+ mozilla::MozillaProductType productTypes[3] = {
+ mozilla::MozillaProductType_Thunderbird,
+ mozilla::MozillaProductType_Firefox,
+ mozilla::MozillaProductType_Mozilla };
+
+ uno::Reference<uno::XInterface> xInstance = rxContext->getServiceManager()->createInstanceWithContext("com.sun.star.mozilla.MozillaBootstrap", rxContext);
+ OSL_ENSURE( xInstance.is(), "failed to create instance" );
+
+ uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap(xInstance,uno::UNO_QUERY);
+ OSL_ENSURE( xMozillaBootstrap.is(), "failed to create instance" );
+
+ if (xMozillaBootstrap.is())
+ {
+ for (std::size_t i=0; i<SAL_N_ELEMENTS(productTypes); ++i)
+ {
+ OUString profile = xMozillaBootstrap->getDefaultProfile(productTypes[i]);
+
+ if (!profile.isEmpty())
+ {
+ OUString sProfilePath = xMozillaBootstrap->getProfilePath(productTypes[i], profile);
+ if (m_sNSSPath.isEmpty())
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Using Mozilla profile " << sProfilePath);
+ m_sNSSPath = sProfilePath;
+ }
+ break;
+ }
+ }
+ }
+
+ SAL_INFO_IF(m_sNSSPath.isEmpty(), "xmlsecurity.xmlsec", "No Mozilla profile found");
+ return m_sNSSPath;
+}
+
+css::uno::Sequence<css::xml::crypto::NSSProfile> SAL_CALL ONSSInitializer::getNSSProfiles()
+{
+ ONSSInitializer::getMozillaCurrentProfile(m_xContext);
+
+ std::vector<xml::crypto::NSSProfile> aProfileList;
+ aProfileList.reserve(10);
+
+ mozilla::MozillaProductType productTypes[3] = {
+ mozilla::MozillaProductType_Thunderbird,
+ mozilla::MozillaProductType_Firefox,
+ mozilla::MozillaProductType_Mozilla };
+
+ uno::Reference<uno::XInterface> xInstance = m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.mozilla.MozillaBootstrap", m_xContext);
+ OSL_ENSURE(xInstance.is(), "failed to create instance" );
+
+ uno::Reference<mozilla::XMozillaBootstrap> xMozillaBootstrap(xInstance,uno::UNO_QUERY);
+
+ if (xMozillaBootstrap.is())
+ {
+ for (std::size_t i=0; i<SAL_N_ELEMENTS(productTypes); ++i)
+ {
+ uno::Sequence<OUString> aProductProfileList;
+ xMozillaBootstrap->getProfileList(productTypes[i], aProductProfileList);
+ for (const auto& sProfile : std::as_const(aProductProfileList))
+ aProfileList.push_back({sProfile, xMozillaBootstrap->getProfilePath(productTypes[i], sProfile), productTypes[i]});
+ }
+ }
+
+ OUString sUserSelect;
+ try
+ {
+ sUserSelect = officecfg::Office::Common::Security::Scripting::CertDir::get().value_or(OUString());;
+ if (!lcl_pathExists(sUserSelect))
+ sUserSelect = OUString();
+ }
+ catch (const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("xmlsecurity.xmlsec", "getMozillaCurrentProfile:");
+ }
+ aProfileList.push_back({"MANUAL", sUserSelect, mozilla::MozillaProductType_Default});
+
+ const char* pEnv = getenv("MOZILLA_CERTIFICATE_FOLDER");
+ aProfileList.push_back({"MOZILLA_CERTIFICATE_FOLDER",
+ pEnv ? OStringToOUString(pEnv, osl_getThreadTextEncoding()) : OUString(),
+ mozilla::MozillaProductType_Default});
+
+ return comphelper::containerToSequence(aProfileList);
+}
+
+bool ONSSInitializer::m_bIsNSSinitialized = false;
+OUString ONSSInitializer::m_sNSSPath;
+
+OUString SAL_CALL ONSSInitializer::getNSSPath()
+{
+ ONSSInitializer::getMozillaCurrentProfile(m_xContext);
+ return m_sNSSPath;
+};
+
+sal_Bool SAL_CALL ONSSInitializer::getIsNSSinitialized()
+{
+ return m_bIsNSSinitialized;
+}
+
+ONSSInitializer::ONSSInitializer(const css::uno::Reference< css::uno::XComponentContext > &rxContext)
+ : m_xContext(rxContext)
+{
+}
+
+ONSSInitializer::ONSSInitializer()
+{
+}
+
+namespace
+{
+
+//Older versions of Firefox (FF), for example FF2, and Thunderbird (TB) 2 write
+//the roots certificate module (libnssckbi.so), which they use, into the
+//profile. This module will then already be loaded during NSS_Init (and the
+//other init functions). This fails in two cases. First, FF3 was used to create
+//the profile, or possibly used that profile before, and second the profile was
+//used on a different platform.
+//
+//Then one needs to add the roots module oneself. This should be done with
+//SECMOD_LoadUserModule rather than SECMOD_AddNewModule. The latter would write
+//the location of the roots module to the profile, which makes FF2 and TB2 use
+//it instead of their own module.
+//
+//When using SYSTEM_NSS then the libnss3.so lib is typically found in /usr/lib.
+//This folder may, however, NOT contain the roots certificate module. That is,
+//just providing the library name in SECMOD_LoadUserModule or
+//SECMOD_AddNewModule will FAIL to load the mozilla unless the LD_LIBRARY_PATH
+//contains an FF or TB installation.
+//ATTENTION: DO NOT call this function directly instead use initNSS
+//return true - whole initialization was successful
+//param out_nss_init = true: at least the NSS initialization (NSS_InitReadWrite
+//was successful and therefore NSS_Shutdown should be called when terminating.
+bool nsscrypto_initialize(css::uno::Reference<css::uno::XComponentContext> const & rxContext, bool & out_nss_init)
+{
+ // this method must be called only once, no need for additional lock
+ OString sCertDir;
+
+#ifdef XMLSEC_CRYPTO_NSS
+ sCertDir = OUStringToOString(ONSSInitializer::getMozillaCurrentProfile(rxContext, true), osl_getThreadTextEncoding());
+#else
+ (void) rxContext;
+#endif
+ SAL_INFO("xmlsecurity.xmlsec", "Using profile: " << sCertDir );
+
+ PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 1 ) ;
+
+ bool bSuccess = false;
+ // there might be no profile
+ if (!sCertDir.isEmpty())
+ {
+ if (sCertDir.indexOf(':') == -1) //might be env var with explicit prefix
+ {
+ OUString sCertDirURL;
+ osl::FileBase::getFileURLFromSystemPath(
+ OStringToOUString(sCertDir, osl_getThreadTextEncoding()),
+ sCertDirURL);
+ osl::DirectoryItem item;
+ if (osl::FileBase::E_NOENT != osl::DirectoryItem::get(sCertDirURL + "/cert8.db", item) &&
+ osl::FileBase::E_NOENT == osl::DirectoryItem::get(sCertDirURL + "/cert9.db", item))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "nsscrypto_initialize: trying to avoid profile migration");
+ sCertDir = "dbm:" + sCertDir;
+ }
+ }
+ if (NSS_InitReadWrite(sCertDir.getStr()) != SECSuccess)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with profile failed.");
+ int errlen = PR_GetErrorTextLength();
+ if (errlen > 0)
+ {
+ std::unique_ptr<char[]> const error(new char[errlen + 1]);
+ PR_GetErrorText(error.get());
+ SAL_INFO("xmlsecurity.xmlsec", error.get());
+ }
+ }
+ else
+ {
+ bSuccess = true;
+ }
+ }
+
+ if (!bSuccess) // Try to create a database in temp dir
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with a temporary profile.");
+ OUString rString = (*getInitNSSPrivate())->getTempDatabasePath();
+
+ if (NSS_InitReadWrite(rString.toUtf8().getStr()) != SECSuccess)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Initializing NSS with a temporary profile.");
+ int errlen = PR_GetErrorTextLength();
+ if(errlen > 0)
+ {
+ std::unique_ptr<char[]> const error(new char[errlen + 1]);
+ PR_GetErrorText(error.get());
+ SAL_INFO("xmlsecurity.xmlsec", error.get());
+ }
+ return false;
+ }
+ }
+
+ // Initialize and set empty password if needed
+ // note: it's possible that the first NSS_InitReadWrite() succeeds by
+ // creating a new DB; in this case it may also be necessary to call
+ // PK11_InitPin()
+ PK11SlotInfo* pSlot = PK11_GetInternalKeySlot();
+ if (pSlot)
+ {
+ if (PK11_NeedUserInit(pSlot))
+ PK11_InitPin(pSlot, nullptr, nullptr);
+ PK11_FreeSlot(pSlot);
+ }
+
+ out_nss_init = true;
+
+#ifdef XMLSEC_CRYPTO_NSS
+ bool return_value = true;
+
+#if defined SYSTEM_NSS || defined IOS // The statically linked nss on iOS acts as a "system" nss in this regards
+ if (!SECMOD_HasRootCerts())
+#endif
+ {
+ deleteRootsModule();
+
+#ifdef IOS // Use statically linked NSS
+ OUString rootModulePath("NSSCKBI");
+
+ if (true)
+#else
+#if defined SYSTEM_NSS || defined ANDROID
+ OUString rootModule("libnssckbi" SAL_DLLEXTENSION);
+#else
+ OUString rootModule("${LO_LIB_DIR}/libnssckbi" SAL_DLLEXTENSION);
+#endif
+ ::rtl::Bootstrap::expandMacros(rootModule);
+
+ OUString rootModulePath;
+ if (::osl::File::E_None == ::osl::File::getSystemPathFromFileURL(rootModule, rootModulePath))
+#endif
+ {
+ OString ospath = OUStringToOString(rootModulePath, osl_getThreadTextEncoding());
+ OString aStr = "name=\"" ROOT_CERTS "\" library=\"" + ospath + "\"";
+
+ SECMODModule * RootsModule =
+ SECMOD_LoadUserModule(
+ const_cast<char*>(aStr.getStr()),
+ nullptr, // no parent
+ PR_FALSE); // do not recurse
+
+ if (RootsModule)
+ {
+
+ bool found = RootsModule->loaded;
+
+ SECMOD_DestroyModule(RootsModule);
+ RootsModule = nullptr;
+ if (found)
+ SAL_INFO("xmlsecurity.xmlsec", "Added new root certificate module " ROOT_CERTS " contained in " << ospath);
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "FAILED to load the new root certificate module " ROOT_CERTS "contained in " << ospath);
+ return_value = false;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "FAILED to add new root certificate module " ROOT_CERTS " contained in " << ospath);
+ return_value = false;
+
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Adding new root certificate module failed.");
+ return_value = false;
+ }
+ }
+
+ return return_value;
+#else
+ return true;
+#endif
+}
+
+} // namespace
+
+// must be extern "C" because we pass the function pointer to atexit
+extern "C" void nsscrypto_finalize()
+{
+ SECMODModule *RootsModule = SECMOD_FindModule(ROOT_CERTS);
+
+ if (RootsModule)
+ {
+
+ if (SECSuccess == SECMOD_UnloadUserModule(RootsModule))
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Unloaded module \"" ROOT_CERTS "\".");
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Failed unloading module \"" ROOT_CERTS "\".");
+ }
+ SECMOD_DestroyModule(RootsModule);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Unloading module \"" ROOT_CERTS "\" failed because it was not found.");
+ }
+ PK11_LogoutAll();
+ (void)NSS_Shutdown();
+
+ (*getInitNSSPrivate())->reset();
+}
+
+
+ONSSInitializer::~ONSSInitializer()
+{
+}
+
+bool ONSSInitializer::initNSS( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
+{
+ static bool gbInitialized = [&rxContext]()
+ {
+ bool bNSSInit = false;
+ bool bInitialized = nsscrypto_initialize( rxContext, bNSSInit );
+ if (bNSSInit)
+ atexit(nsscrypto_finalize);
+ return bInitialized;
+ }();
+ return gbInitialized;
+}
+
+css::uno::Reference< css::xml::crypto::XDigestContext > SAL_CALL ONSSInitializer::getDigestContext( ::sal_Int32 nDigestID, const css::uno::Sequence< css::beans::NamedValue >& aParams )
+{
+ SECOidTag nNSSDigestID = SEC_OID_UNKNOWN;
+ sal_Int32 nDigestLength = 0;
+ bool b1KData = false;
+ if ( nDigestID == css::xml::crypto::DigestID::SHA256
+ || nDigestID == css::xml::crypto::DigestID::SHA256_1K )
+ {
+ nNSSDigestID = SEC_OID_SHA256;
+ nDigestLength = 32;
+ b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA256_1K );
+ }
+ else if ( nDigestID == css::xml::crypto::DigestID::SHA1
+ || nDigestID == css::xml::crypto::DigestID::SHA1_1K )
+ {
+ nNSSDigestID = SEC_OID_SHA1;
+ nDigestLength = 20;
+ b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA1_1K );
+ }
+ else if ( nDigestID == css::xml::crypto::DigestID::SHA512
+ || nDigestID == css::xml::crypto::DigestID::SHA512_1K )
+ {
+ nNSSDigestID = SEC_OID_SHA512;
+ nDigestLength = 64;
+ b1KData = ( nDigestID == css::xml::crypto::DigestID::SHA512_1K );
+ }
+ else
+ throw css::lang::IllegalArgumentException("Unexpected digest requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
+
+ if ( aParams.hasElements() )
+ throw css::lang::IllegalArgumentException("Unexpected arguments provided for digest creation.", css::uno::Reference< css::uno::XInterface >(), 2 );
+
+ css::uno::Reference< css::xml::crypto::XDigestContext > xResult;
+ if( initNSS( m_xContext ) )
+ {
+ PK11Context* pContext = PK11_CreateDigestContext( nNSSDigestID );
+ if ( pContext && PK11_DigestBegin( pContext ) == SECSuccess )
+ xResult = new ODigestContext( pContext, nDigestLength, b1KData );
+ }
+
+ return xResult;
+}
+
+css::uno::Reference< css::xml::crypto::XCipherContext > SAL_CALL ONSSInitializer::getCipherContext( ::sal_Int32 nCipherID, const css::uno::Sequence< ::sal_Int8 >& aKey, const css::uno::Sequence< ::sal_Int8 >& aInitializationVector, sal_Bool bEncryption, const css::uno::Sequence< css::beans::NamedValue >& aParams )
+{
+ CK_MECHANISM_TYPE nNSSCipherID = 0;
+ bool bW3CPadding = false;
+ if ( nCipherID != css::xml::crypto::CipherID::AES_CBC_W3C_PADDING )
+ throw css::lang::IllegalArgumentException("Unexpected cipher requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
+
+ nNSSCipherID = CKM_AES_CBC;
+ bW3CPadding = true;
+
+ if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
+ throw css::lang::IllegalArgumentException("Unexpected key length.", css::uno::Reference< css::uno::XInterface >(), 2 );
+
+ if ( aParams.hasElements() )
+ throw css::lang::IllegalArgumentException("Unexpected arguments provided for cipher creation.", css::uno::Reference< css::uno::XInterface >(), 5 );
+
+ css::uno::Reference< css::xml::crypto::XCipherContext > xResult;
+ if( initNSS( m_xContext ) )
+ {
+ if ( aInitializationVector.getLength() != PK11_GetIVLength( nNSSCipherID ) )
+ throw css::lang::IllegalArgumentException("Unexpected length of initialization vector.", css::uno::Reference< css::uno::XInterface >(), 3 );
+
+ xResult = OCipherContext::Create( nNSSCipherID, aKey, aInitializationVector, bEncryption, bW3CPadding );
+ }
+
+ return xResult;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL ONSSInitializer::getImplementationName()
+{
+ return "com.sun.star.xml.crypto.NSSInitializer";
+}
+
+sal_Bool SAL_CALL ONSSInitializer::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+cssu::Sequence< OUString > SAL_CALL ONSSInitializer::getSupportedServiceNames( )
+{
+ return { NSS_SERVICE_NAME };
+}
+
+#ifndef XMLSEC_CRYPTO_NSS
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_NSSInitializer_get_implementation(
+ uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new ONSSInitializer(pCtx));
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/nssinitializer.hxx b/xmlsecurity/source/xmlsec/nss/nssinitializer.hxx
new file mode 100644
index 000000000..fa861c298
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/nssinitializer.hxx
@@ -0,0 +1,68 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/XNSSInitializer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+inline constexpr OUStringLiteral NSS_SERVICE_NAME = u"com.sun.star.xml.crypto.NSSInitializer";
+
+class ONSSInitializer : public cppu::WeakImplHelper
+<
+ css::xml::crypto::XNSSInitializer,
+ css::lang::XServiceInfo
+>
+{
+protected:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ static OUString m_sNSSPath;
+ static bool m_bIsNSSinitialized;
+
+ ONSSInitializer();
+
+public:
+ explicit ONSSInitializer(const css::uno::Reference<css::uno::XComponentContext> &rxContext);
+ virtual ~ONSSInitializer() override;
+
+ static bool initNSS( const css::uno::Reference< css::uno::XComponentContext > &rxContext );
+ static const OUString & getMozillaCurrentProfile(const css::uno::Reference< css::uno::XComponentContext > &rxContext, bool bSetActive = false);
+
+ /* XNSSInitializer */
+ virtual OUString SAL_CALL getNSSPath() override;
+ virtual sal_Bool SAL_CALL getIsNSSinitialized() override;
+ virtual css::uno::Sequence<css::xml::crypto::NSSProfile> SAL_CALL getNSSProfiles() override;
+
+ /* XDigestContextSupplier */
+ virtual css::uno::Reference< css::xml::crypto::XDigestContext > SAL_CALL getDigestContext( ::sal_Int32 nDigestID, const css::uno::Sequence< css::beans::NamedValue >& aParams ) override;
+
+ /* XCipherContextSupplier */
+ virtual css::uno::Reference< css::xml::crypto::XCipherContext > SAL_CALL getCipherContext( ::sal_Int32 nCipherID, const css::uno::Sequence< ::sal_Int8 >& aKey, const css::uno::Sequence< ::sal_Int8 >& aInitializationVector, sal_Bool bEncryption, const css::uno::Sequence< css::beans::NamedValue >& aParams ) override;
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/nssrenam.h b/xmlsecurity/source/xmlsec/nss/nssrenam.h
new file mode 100644
index 000000000..47280408b
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/nssrenam.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 2001 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#pragma once
+
+#define CERT_NewTempCertificate __CERT_NewTempCertificate
+#define PK11_GetKeyData __PK11_GetKeyData
+#define CERT_DecodeDERCertificate __CERT_DecodeDERCertificate
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.cxx
new file mode 100644
index 000000000..e37a5473c
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.cxx
@@ -0,0 +1,164 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/security/ExtAltNameType.hpp>
+#include <com/sun/star/security/CertAltNameEntry.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/sequence.hxx>
+#include <seccomon.h>
+#include <cert.h>
+#include <certt.h>
+
+#include "sanextension_nssimpl.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::security ;
+
+namespace {
+ // Helper functions from nss/lib/certdb/genname.c
+ int GetNamesLength(CERTGeneralName *names)
+ {
+ int length = 0;
+ CERTGeneralName *first;
+
+ first = names;
+ if (names != nullptr) {
+ do {
+ length++;
+ names = CERT_GetNextGeneralName(names);
+ } while (names != first);
+ }
+ return length;
+ }
+
+}
+
+//Methods from XSanExtension
+css::uno::Sequence< css::security::CertAltNameEntry > SAL_CALL SanExtensionImpl::getAlternativeNames()
+{
+ if (m_Entries.empty())
+ {
+ SECItem item;
+
+ item.type = siDERCertBuffer;
+ item.data = reinterpret_cast<unsigned char*>(m_Extn.m_xExtnValue.getArray());
+ item.len = m_Extn.m_xExtnValue.getLength();
+
+ PRArenaPool *arena;
+ CERTGeneralName *nameList;
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+
+ if (!arena)
+ return css::uno::Sequence<css::security::CertAltNameEntry>();
+
+ nameList = CERT_DecodeAltNameExtension(arena, &item);
+
+ CERTGeneralName* current = nameList;
+
+ int size = GetNamesLength(nameList);
+ m_Entries.resize(size);
+ for(int i = 0; i < size; ++i){
+ switch (current->type) {
+ case certOtherName: {
+ m_Entries[i].Type = ExtAltNameType_OTHER_NAME;
+ css::beans::PropertyValue otherNameProp;
+ otherNameProp.Name = OUString::createFromAscii(CERT_GetOidString(&current->name.OthName.oid));
+
+ Sequence< sal_Int8 > otherName( current->name.OthName.name.len ) ;
+ auto otherNameRange = asNonConstRange(otherName);
+ for( unsigned int r = 0; r < current->name.OthName.name.len ; r ++ )
+ otherNameRange[r] = *( current->name.OthName.name.data + r ) ;
+
+ otherNameProp.Value <<= otherName;
+
+ m_Entries[i].Value <<= otherNameProp;
+ break;
+ }
+ case certRFC822Name:
+ m_Entries[i].Type = ExtAltNameType_RFC822_NAME;
+ m_Entries[i].Value <<= OUString(reinterpret_cast<char*>(current->name.other.data), current->name.other.len, RTL_TEXTENCODING_ASCII_US);
+ break;
+ case certDNSName:
+ m_Entries[i].Type = ExtAltNameType_DNS_NAME;
+ m_Entries[i].Value <<= OUString(reinterpret_cast<char*>(current->name.other.data), current->name.other.len, RTL_TEXTENCODING_ASCII_US);
+ break;
+ case certX400Address: {
+ // unsupported
+ m_Entries[i].Type = ExtAltNameType_X400_ADDRESS;
+ break;
+ }
+ case certDirectoryName: {
+ // unsupported
+ m_Entries[i].Type = ExtAltNameType_DIRECTORY_NAME;
+ break;
+ }
+ case certEDIPartyName: {
+ // unsupported
+ m_Entries[i].Type = ExtAltNameType_EDI_PARTY_NAME;
+ break;
+ }
+ case certURI:
+ m_Entries[i].Type = ExtAltNameType_URL;
+ m_Entries[i].Value <<= OUString(reinterpret_cast<char*>(current->name.other.data), current->name.other.len, RTL_TEXTENCODING_ASCII_US);
+ break;
+ case certIPAddress: {
+ m_Entries[i].Type = ExtAltNameType_IP_ADDRESS;
+
+ Sequence< sal_Int8 > ipAddress( current->name.other.len ) ;
+ auto ipAddressRange = asNonConstRange(ipAddress);
+ for( unsigned int r = 0; r < current->name.other.len ; r ++ )
+ ipAddressRange[r] = *( current->name.other.data + r ) ;
+
+ m_Entries[i].Value <<= ipAddress;
+ break;
+ }
+ case certRegisterID:
+ m_Entries[i].Type = ExtAltNameType_REGISTERED_ID;
+
+
+ OString nssOid(CERT_GetOidString(&current->name.other));
+ OString unoOid = removeOIDFromString(nssOid);
+ m_Entries[i].Value <<= OStringToOUString( unoOid, RTL_TEXTENCODING_ASCII_US );
+ break;
+ }
+ current = CERT_GetNextGeneralName(current);
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return comphelper::containerToSequence<css::security::CertAltNameEntry>(m_Entries);
+}
+
+OString SanExtensionImpl::removeOIDFromString( const OString &oidString)
+{
+ OString objID;
+ OString oid("OID.");
+ if (oidString.match(oid))
+ objID = oidString.copy(oid.getLength());
+ else
+ objID = oidString;
+ return objID;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.hxx b/xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.hxx
new file mode 100644
index 000000000..3716d414e
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/sanextension_nssimpl.hxx
@@ -0,0 +1,65 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/security/XSanExtension.hpp>
+#include <com/sun/star/security/CertAltNameEntry.hpp>
+#include <certificateextension_certextn.hxx>
+#include <vector>
+
+class SanExtensionImpl : public ::cppu::WeakImplHelper<
+ css::security::XSanExtension >
+{
+ private:
+ CertificateExtension_CertExtn m_Extn;
+ std::vector<css::security::CertAltNameEntry> m_Entries;
+
+ static OString removeOIDFromString( const OString &oid);
+
+ public:
+ //Methods from XCertificateExtension
+ virtual sal_Bool SAL_CALL isCritical() override
+ {
+ return m_Extn.m_critical;
+ }
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getExtensionId() override
+ {
+ return m_Extn.m_xExtnId;
+ }
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getExtensionValue() override
+ {
+ return m_Extn.m_xExtnValue;
+ }
+
+ void setCertExtn(unsigned char const * value, unsigned int vlen, unsigned char const * id, unsigned int idlen, bool critical)
+ {
+ m_Extn.setCertExtn(value, vlen, id, idlen, critical);
+ }
+
+ //Methods from XSanExtension
+
+ virtual css::uno::Sequence< css::security::CertAltNameEntry > SAL_CALL getAlternativeNames() override ;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/secerror.cxx b/xmlsecurity/source/xmlsec/nss/secerror.cxx
new file mode 100644
index 000000000..b7e623ce0
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/secerror.cxx
@@ -0,0 +1,152 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <secerr.h>
+#include "secerror.hxx"
+#include <nss.h>
+#include <certt.h>
+#include <sal/log.hxx>
+
+namespace {
+
+struct ErrDesc {
+ PRErrorCode const errNum;
+ const char * errString;
+};
+
+}
+
+const ErrDesc allDesc[] = {
+
+#include "certerrors.h"
+
+};
+
+
+/* Returns a UTF-8 encoded constant error string for "errNum".
+ * Returns NULL of errNum is unknown.
+ */
+const char *
+getCertError(PRErrorCode errNum)
+{
+ for (const ErrDesc& i : allDesc)
+ {
+ if (i.errNum == errNum)
+ return i.errString;
+ }
+
+ return "";
+}
+
+void
+printChainFailure(CERTVerifyLog *log)
+{
+ unsigned int depth = static_cast<unsigned int>(-1);
+ CERTVerifyLogNode *node = nullptr;
+
+ if (log->count > 0)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Bad certification path:");
+ unsigned long errorFlags = 0;
+ for (node = log->head; node; node = node->next)
+ {
+ if (depth != node->depth)
+ {
+ depth = node->depth;
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate: " << depth <<
+ node->cert->subjectName << ": " <<
+ (depth ? "[Certificate Authority]": ""));
+ }
+ SAL_INFO("xmlsecurity.xmlsec", " ERROR " << node->error << ": " <<
+ getCertError(node->error));
+ const char * specificError = nullptr;
+ const char * issuer = nullptr;
+ switch (node->error)
+ {
+ case SEC_ERROR_INADEQUATE_KEY_USAGE:
+ errorFlags = reinterpret_cast<unsigned long>(node->arg);
+ switch (errorFlags)
+ {
+ case KU_DIGITAL_SIGNATURE:
+ specificError = "Certificate cannot sign.";
+ break;
+ case KU_KEY_ENCIPHERMENT:
+ specificError = "Certificate cannot encrypt.";
+ break;
+ case KU_KEY_CERT_SIGN:
+ specificError = "Certificate cannot sign other certs.";
+ break;
+ default:
+ specificError = "[unknown usage].";
+ break;
+ }
+ break;
+ case SEC_ERROR_INADEQUATE_CERT_TYPE:
+ errorFlags = reinterpret_cast<unsigned long>(node->arg);
+ switch (errorFlags)
+ {
+ case NS_CERT_TYPE_SSL_CLIENT:
+ case NS_CERT_TYPE_SSL_SERVER:
+ specificError = "Certificate cannot be used for SSL.";
+ break;
+ case NS_CERT_TYPE_SSL_CA:
+ specificError = "Certificate cannot be used as an SSL CA.";
+ break;
+ case NS_CERT_TYPE_EMAIL:
+ specificError = "Certificate cannot be used for SMIME.";
+ break;
+ case NS_CERT_TYPE_EMAIL_CA:
+ specificError = "Certificate cannot be used as an SMIME CA.";
+ break;
+ case NS_CERT_TYPE_OBJECT_SIGNING:
+ specificError = "Certificate cannot be used for object signing.";
+ break;
+ case NS_CERT_TYPE_OBJECT_SIGNING_CA:
+ specificError = "Certificate cannot be used as an object signing CA.";
+ break;
+ default:
+ specificError = "[unknown usage].";
+ break;
+ }
+ break;
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ specificError = "Unknown issuer:";
+ issuer = node->cert->issuerName;
+ break;
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ specificError = "Untrusted issuer:";
+ issuer = node->cert->issuerName;
+ break;
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ specificError = "Expired issuer certificate:";
+ issuer = node->cert->issuerName;
+ break;
+ default:
+ break;
+ }
+ if (specificError)
+ SAL_INFO("xmlsecurity.xmlsec", specificError);
+ if (issuer)
+ SAL_INFO("xmlsecurity.xmlsec", issuer);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/secerror.hxx b/xmlsecurity/source/xmlsec/nss/secerror.hxx
new file mode 100644
index 000000000..bd5907826
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/secerror.hxx
@@ -0,0 +1,31 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <certt.h>
+#include <prerror.h>
+
+const char* getCertError(PRErrorCode errNum);
+
+void printChainFailure(CERTVerifyLog* log);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx
new file mode 100644
index 000000000..f6c35257f
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.cxx
@@ -0,0 +1,890 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "nssrenam.h"
+#include <cert.h>
+#include <secerr.h>
+#include <ocsp.h>
+
+#include <sal/config.h>
+#include <sal/macros.h>
+#include <osl/diagnose.h>
+#include "securityenvironment_nssimpl.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <xmlsec-wrapper.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/docpasswordrequest.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <vector>
+#include <memory>
+#include <osl/thread.h>
+#include <comphelper/sequence.hxx>
+
+#include "x509certificate_nssimpl.hxx"
+#include "secerror.hxx"
+#include <prerror.h>
+#include <keyhi.h>
+
+// added for password exception
+#include <com/sun/star/security/NoPasswordException.hpp>
+#include <com/sun/star/security/CertificateCharacters.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+
+namespace csss = ::com::sun::star::security;
+using namespace ::com::sun::star::security;
+using namespace com::sun::star;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::lang ;
+
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::security::XCertificate ;
+
+namespace std
+{
+template <> struct default_delete<PRArenaPool>
+{
+ void operator()(PRArenaPool* ptr) { PORT_FreeArena(ptr, PR_FALSE); }
+};
+}
+
+static rtl::Reference<X509Certificate_NssImpl> NssCertToXCert( CERTCertificate* cert ) ;
+static rtl::Reference<X509Certificate_NssImpl> NssPrivKeyToXCert( SECKEYPrivateKey* ) ;
+
+namespace {
+
+struct UsageDescription
+{
+ SECCertificateUsage usage;
+ char const* description;
+
+ UsageDescription()
+ : usage( certificateUsageCheckAllUsages )
+ , description( nullptr )
+ {}
+
+ UsageDescription( SECCertificateUsage i_usage, char const* i_description )
+ : usage( i_usage )
+ , description( i_description )
+ {}
+};
+
+}
+
+static char* GetPasswordFunction( PK11SlotInfo* pSlot, PRBool bRetry, void* /*arg*/ )
+{
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference < task::XInteractionHandler2 > xInteractionHandler(
+ task::InteractionHandler::createWithParent(xContext, nullptr) );
+
+ task::PasswordRequestMode eMode = bRetry ? task::PasswordRequestMode_PASSWORD_REENTER : task::PasswordRequestMode_PASSWORD_ENTER;
+ rtl::Reference<::comphelper::DocPasswordRequest> pPasswordRequest = new ::comphelper::DocPasswordRequest(
+ ::comphelper::DocPasswordRequestType::Standard, eMode, OUString::createFromAscii(PK11_GetTokenName(pSlot)) );
+
+ xInteractionHandler->handle( pPasswordRequest );
+
+ if ( pPasswordRequest->isPassword() )
+ {
+ OString aPassword(OUStringToOString(
+ pPasswordRequest->getPassword(),
+ osl_getThreadTextEncoding()));
+ sal_Int32 nLen = aPassword.getLength();
+ char* pPassword = static_cast<char*>(PORT_Alloc( nLen+1 ) );
+ pPassword[nLen] = 0;
+ memcpy( pPassword, aPassword.getStr(), nLen );
+ return pPassword;
+ }
+ return nullptr;
+}
+
+SecurityEnvironment_NssImpl::SecurityEnvironment_NssImpl() :
+m_pHandler( nullptr ) {
+ PK11_SetPasswordFunc( GetPasswordFunction ) ;
+}
+
+SecurityEnvironment_NssImpl::~SecurityEnvironment_NssImpl() {
+
+ PK11_SetPasswordFunc( nullptr ) ;
+
+ for (auto& slot : m_Slots)
+ {
+ PK11_FreeSlot(slot);
+ }
+
+ for( auto& symKey : m_tSymKeyList )
+ PK11_FreeSymKey( symKey ) ;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SecurityEnvironment_NssImpl::getImplementationName() {
+ return "com.sun.star.xml.crypto.SecurityEnvironment";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SecurityEnvironment_NssImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SecurityEnvironment_NssImpl::getSupportedServiceNames() {
+ Sequence<OUString> seqServiceNames{ "com.sun.star.xml.crypto.SecurityEnvironment" };
+ return seqServiceNames;
+}
+
+/* XUnoTunnel */
+sal_Int64 SAL_CALL SecurityEnvironment_NssImpl::getSomething( const Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XUnoTunnel extension */
+
+const Sequence< sal_Int8>& SecurityEnvironment_NssImpl::getUnoTunnelId() {
+ static const comphelper::UnoIdInit theSecurityEnvironment_NssImplUnoTunnelId;
+ return theSecurityEnvironment_NssImplUnoTunnelId.getSeq();
+}
+
+OUString SecurityEnvironment_NssImpl::getSecurityEnvironmentInformation()
+{
+ OUStringBuffer buff;
+ for (auto& slot : m_Slots)
+ {
+ buff.append(OUString::createFromAscii(PK11_GetTokenName(slot)));
+ buff.append("\n");
+ }
+ return buff.makeStringAndClear();
+}
+
+void SecurityEnvironment_NssImpl::addCryptoSlot( PK11SlotInfo* aSlot)
+{
+ PK11_ReferenceSlot(aSlot);
+ m_Slots.push_back(aSlot);
+}
+
+//Could we have multiple cert dbs?
+void SecurityEnvironment_NssImpl::setCertDb( CERTCertDBHandle* aCertDb ) {
+ m_pHandler = aCertDb ;
+}
+
+void SecurityEnvironment_NssImpl::adoptSymKey( PK11SymKey* aSymKey ) {
+ if( aSymKey == nullptr ) return;
+
+ //First try to find the key in the list
+ if (std::find(m_tSymKeyList.begin(), m_tSymKeyList.end(), aSymKey) != m_tSymKeyList.end())
+ return;
+
+ //If we do not find the key in the list, add a new node
+ PK11SymKey* symkey = PK11_ReferenceSymKey( aSymKey ) ;
+ if( symkey == nullptr )
+ throw RuntimeException() ;
+
+ try {
+ m_tSymKeyList.push_back( symkey ) ;
+ } catch ( Exception& ) {
+ PK11_FreeSymKey( symkey ) ;
+ }
+}
+
+void SecurityEnvironment_NssImpl::updateSlots()
+{
+ //In case new tokens are present then we can obtain the corresponding slot
+ std::scoped_lock guard(m_mutex);
+
+ m_Slots.clear();
+ m_tSymKeyList.clear();
+
+ PK11SlotList * slotList = PK11_GetAllTokens( CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr ) ;
+ if( slotList == nullptr )
+ return;
+
+ for (PK11SlotListElement* slotEle = slotList->head ; slotEle != nullptr; slotEle = slotEle->next)
+ {
+ PK11SlotInfo * pSlot = slotEle->slot ;
+
+ if(pSlot != nullptr)
+ {
+ SAL_INFO(
+ "xmlsecurity.xmlsec",
+ "Found a slot: SlotName=" << PK11_GetSlotName(pSlot)
+ << ", TokenName=" << PK11_GetTokenName(pSlot));
+
+//The following code which is commented out checks if a slot, that is a smart card for example, is
+// able to generate a symmetric key of type CKM_DES3_CBC. If this fails then this token
+// will not be used. This key is possibly used for the encryption service. However, all
+// interfaces and services used for public key signature and encryption are not published
+// and the encryption is not used in OOo. Therefore it does not do any harm to remove
+// this code, hence allowing smart cards which cannot generate this type of key.
+//
+// By doing this, the encryption may fail if a smart card is being used which does not
+// support this key generation.
+//
+ PK11SymKey * pSymKey = PK11_KeyGen( pSlot , CKM_DES3_CBC, nullptr, 128, nullptr ) ;
+// if( pSymKey == NULL )
+// {
+// PK11_FreeSlot( pSlot ) ;
+// SAL_INFO( "xmlsecurity", "XMLSEC: Error - pSymKey is NULL" );
+// continue;
+// }
+ addCryptoSlot(pSlot);
+
+ if (pSymKey != nullptr)
+ {
+ adoptSymKey( pSymKey ) ;
+ PK11_FreeSymKey( pSymKey ) ;
+ pSymKey = nullptr;
+ }
+
+ }// end of if(pSlot != NULL)
+ }// end of for
+
+ PK11_FreeSlotList(slotList);
+}
+
+Sequence< Reference < XCertificate > >
+SecurityEnvironment_NssImpl::getPersonalCertificates()
+{
+ std::vector< rtl::Reference<X509Certificate_NssImpl> > certsList ;
+
+ updateSlots();
+ //firstly, we try to find private keys in slot
+ for (auto& slot : m_Slots)
+ {
+ SECKEYPrivateKeyList* priKeyList ;
+
+ if( PK11_NeedLogin(slot ) ) {
+ SECStatus nRet = PK11_Authenticate(slot, PR_TRUE, nullptr);
+ //PK11_Authenticate may fail in case the a slot has not been initialized.
+ //this is the case if the user has a new profile, so that they have never
+ //added a personal certificate.
+ if( nRet != SECSuccess && PORT_GetError() != SEC_ERROR_IO) {
+ throw NoPasswordException();
+ }
+ }
+
+ priKeyList = PK11_ListPrivateKeysInSlot(slot) ;
+ if( priKeyList != nullptr )
+ {
+ for (SECKEYPrivateKeyListNode* curPri = PRIVKEY_LIST_HEAD(priKeyList);
+ !PRIVKEY_LIST_END( curPri, priKeyList ) && curPri != nullptr;
+ curPri = PRIVKEY_LIST_NEXT(curPri))
+ {
+ rtl::Reference<X509Certificate_NssImpl> xcert = NssPrivKeyToXCert( curPri->key ) ;
+ if( xcert != nullptr )
+ certsList.push_back( xcert ) ;
+ }
+ SECKEY_DestroyPrivateKeyList( priKeyList ) ;
+ }
+
+
+ }
+
+ if( certsList.size() != 0 ) {
+ return comphelper::containerToSequence<Reference< XCertificate >>(certsList) ;
+ }
+
+ return Sequence< Reference < XCertificate > > ();
+}
+
+Reference< XCertificate > SecurityEnvironment_NssImpl::getCertificate( const OUString& issuerName, const Sequence< sal_Int8 >& serialNumber )
+{
+ if( !m_pHandler )
+ return nullptr;
+
+ rtl::Reference<X509Certificate_NssImpl> xcert;
+ CERTIssuerAndSN issuerAndSN ;
+ CERTCertificate* cert ;
+ CERTName* nmIssuer ;
+ char* chIssuer ;
+ SECItem* derIssuer ;
+ std::unique_ptr<PRArenaPool> arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if( arena == nullptr )
+ throw RuntimeException() ;
+
+ // Create cert info from issue and serial
+ OString ostr = OUStringToOString( issuerName , RTL_TEXTENCODING_UTF8 ) ;
+ chIssuer = PL_strndup( ostr.getStr(), static_cast<int>(ostr.getLength()) ) ;
+ nmIssuer = CERT_AsciiToName( chIssuer ) ;
+ if( nmIssuer == nullptr ) {
+ PL_strfree( chIssuer ) ;
+ return nullptr; // no need for exception cf. i40394
+ }
+
+ derIssuer = SEC_ASN1EncodeItem( arena.get(), nullptr, static_cast<void*>(nmIssuer), SEC_ASN1_GET( CERT_NameTemplate ) ) ;
+ if( derIssuer == nullptr ) {
+ PL_strfree( chIssuer ) ;
+ CERT_DestroyName( nmIssuer ) ;
+ throw RuntimeException() ;
+ }
+
+ memset( &issuerAndSN, 0, sizeof( issuerAndSN ) ) ;
+
+ issuerAndSN.derIssuer.data = derIssuer->data ;
+ issuerAndSN.derIssuer.len = derIssuer->len ;
+
+ issuerAndSN.serialNumber.data = reinterpret_cast<unsigned char *>(const_cast<sal_Int8 *>(serialNumber.getConstArray()));
+ issuerAndSN.serialNumber.len = serialNumber.getLength() ;
+
+ cert = CERT_FindCertByIssuerAndSN( m_pHandler, &issuerAndSN ) ;
+ if( cert != nullptr ) {
+ xcert = NssCertToXCert( cert ) ;
+ }
+
+ PL_strfree( chIssuer ) ;
+ CERT_DestroyName( nmIssuer ) ;
+ //SECITEM_FreeItem( derIssuer, PR_FALSE ) ;
+ CERT_DestroyCertificate( cert ) ;
+
+ return xcert ;
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironment_NssImpl::buildCertificatePath( const Reference< XCertificate >& begin ) {
+ // Remember the signing certificate.
+ m_xSigningCertificate = begin;
+
+ Reference< XUnoTunnel > xCertTunnel( begin, UNO_QUERY_THROW ) ;
+ const X509Certificate_NssImpl* xcert = comphelper::getFromUnoTunnel<X509Certificate_NssImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ const CERTCertificate* cert = xcert->getNssCert() ;
+ if (!cert)
+ return {};
+
+ //Get the system clock time
+ int64 timeboundary = PR_Now() ;
+ CERTCertList* certChain = CERT_GetCertChainFromCert( const_cast<CERTCertificate*>(cert), timeboundary, certUsageAnyCA ) ;
+
+ if( !certChain )
+ return {};
+
+ std::vector<uno::Reference<security::XCertificate>> aCertChain;
+
+ for (CERTCertListNode* node = CERT_LIST_HEAD(certChain); !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
+ rtl::Reference<X509Certificate_NssImpl> pCert = new X509Certificate_NssImpl();
+ if( pCert == nullptr ) {
+ CERT_DestroyCertList( certChain ) ;
+ throw RuntimeException() ;
+ }
+
+ pCert->setCert( node->cert ) ;
+
+ aCertChain.push_back(pCert);
+ }
+
+ CERT_DestroyCertList( certChain ) ;
+
+ return comphelper::containerToSequence(aCertChain);
+}
+
+rtl::Reference<X509Certificate_NssImpl> SecurityEnvironment_NssImpl::createAndAddCertificateFromPackage(
+ const css::uno::Sequence<sal_Int8>& raDERCertificate,
+ std::u16string_view raString)
+{
+ auto pCertificateBytes = reinterpret_cast<char *>(const_cast<sal_Int8 *>(raDERCertificate.getConstArray()));
+ CERTCertificate* pCERTCertificate = CERT_DecodeCertFromPackage(pCertificateBytes, raDERCertificate.getLength());
+
+ if (!pCERTCertificate)
+ return nullptr;
+
+ SECStatus aStatus;
+
+ OString aTrustString = OUStringToOString(raString, RTL_TEXTENCODING_ASCII_US);
+ CERTCertTrust aTrust;
+
+ aStatus = CERT_DecodeTrustString(&aTrust, aTrustString.getStr());
+
+ if (aStatus != SECSuccess)
+ {
+ PRIntn err = PR_GetError();
+ SAL_WARN("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+ return nullptr;
+ }
+
+ PK11SlotInfo* pSlot = PK11_GetInternalKeySlot();
+
+ if (!pSlot)
+ return nullptr;
+
+ aStatus = PK11_ImportCert(pSlot, pCERTCertificate, CK_INVALID_HANDLE, nullptr, PR_FALSE);
+
+ if (aStatus != SECSuccess)
+ {
+ PRIntn err = PR_GetError();
+ SAL_WARN("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+ return nullptr;
+ }
+
+ aStatus = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), pCERTCertificate, &aTrust);
+
+ if (aStatus != SECSuccess)
+ {
+ PRIntn err = PR_GetError();
+ SAL_WARN("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+ return nullptr;
+ }
+
+ PK11_FreeSlot(pSlot);
+
+ rtl::Reference<X509Certificate_NssImpl> pX509Certificate = new X509Certificate_NssImpl();
+ pX509Certificate->setCert(pCERTCertificate);
+ return pX509Certificate;
+}
+
+rtl::Reference<X509Certificate_NssImpl> SecurityEnvironment_NssImpl::createX509CertificateFromDER(const css::uno::Sequence<sal_Int8>& aDerCertificate)
+{
+ rtl::Reference<X509Certificate_NssImpl> pX509Certificate;
+
+ if (aDerCertificate.hasElements())
+ {
+ pX509Certificate = new X509Certificate_NssImpl();
+ if (pX509Certificate == nullptr)
+ throw RuntimeException();
+ pX509Certificate->setRawCert(aDerCertificate);
+ }
+ return pX509Certificate;
+}
+
+Reference<XCertificate> SecurityEnvironment_NssImpl::createCertificateFromRaw(const Sequence< sal_Int8 >& rawCertificate)
+{
+ return createX509CertificateFromDER(rawCertificate);
+}
+
+Reference< XCertificate > SecurityEnvironment_NssImpl::createCertificateFromAscii( const OUString& asciiCertificate )
+{
+ OString oscert = OUStringToOString( asciiCertificate , RTL_TEXTENCODING_ASCII_US ) ;
+ xmlChar* chCert = xmlStrndup( reinterpret_cast<const xmlChar*>(oscert.getStr()), static_cast<int>(oscert.getLength()) ) ;
+ int certSize = xmlSecBase64Decode( chCert, reinterpret_cast<xmlSecByte*>(chCert), xmlStrlen( chCert ) ) ;
+ if (certSize == 0)
+ return nullptr;
+
+ Sequence< sal_Int8 > rawCert(comphelper::arrayToSequence<sal_Int8>(chCert, certSize)) ;
+
+ xmlFree( chCert ) ;
+
+ return createCertificateFromRaw( rawCert ) ;
+}
+
+sal_Int32 SecurityEnvironment_NssImpl ::
+verifyCertificate( const Reference< csss::XCertificate >& aCert,
+ const Sequence< Reference< csss::XCertificate > >& intermediateCerts )
+{
+ sal_Int32 validity = csss::CertificateValidity::INVALID;
+ const CERTCertificate* cert ;
+ Reference< XUnoTunnel > xCertTunnel( aCert, UNO_QUERY_THROW ) ;
+
+ SAL_INFO("xmlsecurity.xmlsec", "Start verification of certificate: " << aCert->getSubjectName());
+
+ const X509Certificate_NssImpl* xcert = comphelper::getFromUnoTunnel<X509Certificate_NssImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ //CERT_PKIXVerifyCert does not take a db as argument. It will therefore
+ //internally use CERT_GetDefaultCertDB
+ //Make sure m_pHandler is the default DB
+ OSL_ASSERT(m_pHandler == CERT_GetDefaultCertDB());
+ CERTCertDBHandle * certDb = m_pHandler != nullptr ? m_pHandler : CERT_GetDefaultCertDB();
+ cert = xcert->getNssCert() ;
+ if( !cert )
+ return css::security::CertificateValidity::INVALID;
+
+
+ ::std::vector<CERTCertificate*> vecTmpNSSCertificates;
+
+ //prepare the intermediate certificates
+ for (const auto& rIntermediateCert : intermediateCerts)
+ {
+ Sequence<sal_Int8> der = rIntermediateCert->getEncoded();
+ SECItem item;
+ item.type = siBuffer;
+ item.data = reinterpret_cast<unsigned char*>(der.getArray());
+ item.len = der.getLength();
+
+ CERTCertificate* certTmp = CERT_NewTempCertificate(certDb, &item,
+ nullptr /* nickname */,
+ PR_FALSE /* isPerm */,
+ PR_TRUE /* copyDER */);
+ if (!certTmp)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Failed to add a temporary certificate: " << rIntermediateCert->getIssuerName());
+
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Added temporary certificate: " <<
+ (certTmp->subjectName ? certTmp->subjectName : ""));
+ vecTmpNSSCertificates.push_back(certTmp);
+ }
+ }
+
+
+ SECStatus status ;
+
+ CERTVerifyLog log;
+ log.arena = PORT_NewArena(512);
+ log.head = log.tail = nullptr;
+ log.count = 0;
+
+ CERT_EnableOCSPChecking(certDb);
+ CERT_DisableOCSPDefaultResponder(certDb);
+ CERTValOutParam cvout[5];
+ CERTValInParam cvin[3];
+ int ncvinCount=0;
+
+#if ( NSS_VMAJOR > 3 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR > 12 ) || ( NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH > 0 )
+ cvin[ncvinCount].type = cert_pi_useAIACertFetch;
+ cvin[ncvinCount].value.scalar.b = PR_TRUE;
+ ncvinCount++;
+#endif
+
+ PRUint64 revFlagsLeaf[2];
+ PRUint64 revFlagsChain[2];
+ CERTRevocationFlags rev;
+ rev.leafTests.number_of_defined_methods = 2;
+ rev.leafTests.cert_rev_flags_per_method = revFlagsLeaf;
+ //the flags are defined in cert.h
+ //We check both leaf and chain.
+ //It is enough if one revocation method has fresh info,
+ //but at least one must have some. Otherwise validation fails.
+ //!!! using leaf test and CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
+ // when validating a root certificate will result in "revoked". Usually
+ //there is no revocation information available for the root cert because
+ //it must be trusted anyway and it does itself issue revocation information.
+ //When we use the flag here and OOo shows the certification path then the root
+ //cert is invalid while all other can be valid. It would probably best if
+ //this interface method returned the whole chain.
+ //Otherwise we need to check if the certificate is self-signed and if it is
+ //then not use the flag when doing the leaf-test.
+ rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.leafTests.number_of_preferred_methods = 0;
+ rev.leafTests.preferred_methods = nullptr;
+ rev.leafTests.cert_rev_method_independent_flags =
+ CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
+
+ rev.chainTests.number_of_defined_methods = 2;
+ rev.chainTests.cert_rev_flags_per_method = revFlagsChain;
+ rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] =
+ CERT_REV_M_TEST_USING_THIS_METHOD
+ | CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
+ rev.chainTests.number_of_preferred_methods = 0;
+ rev.chainTests.preferred_methods = nullptr;
+ rev.chainTests.cert_rev_method_independent_flags =
+ CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
+
+
+ cvin[ncvinCount].type = cert_pi_revocationFlags;
+ cvin[ncvinCount].value.pointer.revocation = &rev;
+ ncvinCount++;
+ // does not work, not implemented yet in 3.12.4
+// cvin[ncvinCount].type = cert_pi_keyusage;
+// cvin[ncvinCount].value.scalar.ui = KU_DIGITAL_SIGNATURE;
+// ncvinCount++;
+ cvin[ncvinCount].type = cert_pi_end;
+
+ cvout[0].type = cert_po_trustAnchor;
+ cvout[0].value.pointer.cert = nullptr;
+ cvout[1].type = cert_po_errorLog;
+ cvout[1].value.pointer.log = &log;
+ cvout[2].type = cert_po_end;
+
+ // We check SSL server certificates, CA certificates and signing certificates.
+ //
+ // ToDo check keyusage, looking at CERT_KeyUsageAndTypeForCertUsage (
+ // mozilla/security/nss/lib/certdb/certdb.c indicates that
+ // certificateUsageSSLClient, certificateUsageSSLServer and certificateUsageSSLCA
+ // are sufficient. They cover the key usages for digital signature, key agreement
+ // and encipherment and certificate signature
+
+ //never use the following usages because they are not checked properly
+ // certificateUsageUserCertImport
+ // certificateUsageVerifyCA
+ // certificateUsageAnyCA
+ // certificateUsageProtectedObjectSigner
+
+ UsageDescription arUsages[5];
+ arUsages[0] = UsageDescription( certificateUsageSSLClient, "certificateUsageSSLClient" );
+ arUsages[1] = UsageDescription( certificateUsageSSLServer, "certificateUsageSSLServer" );
+ arUsages[2] = UsageDescription( certificateUsageSSLCA, "certificateUsageSSLCA" );
+ arUsages[3] = UsageDescription( certificateUsageEmailSigner, "certificateUsageEmailSigner" );
+ arUsages[4] = UsageDescription( certificateUsageEmailRecipient, "certificateUsageEmailRecipient" );
+
+ int numUsages = SAL_N_ELEMENTS(arUsages);
+ for (int i = 0; i < numUsages; i++)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Testing usage " << i+1 <<
+ " of " << numUsages << ": " <<
+ arUsages[i].description <<
+ " (0x" << std::hex << static_cast<int>(arUsages[i].usage) << ")" << std::dec);
+
+ status = CERT_PKIXVerifyCert(const_cast<CERTCertificate *>(cert), arUsages[i].usage,
+ cvin, cvout, nullptr);
+ if( status == SECSuccess )
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "CERT_PKIXVerifyCert returned SECSuccess.");
+ //When an intermediate or root certificate is checked then we expect the usage
+ //certificateUsageSSLCA. This, however, will be only set when in the trust settings dialog
+ //the button "This certificate can identify websites" is checked. If for example only
+ //"This certificate can identify mail users" is set then the end certificate can
+ //be validated and the returned usage will contain certificateUsageEmailRecipient.
+ //But checking directly the root or intermediate certificate will fail. In the
+ //certificate path view the end certificate will be shown as valid but the others
+ //will be displayed as invalid.
+
+ validity = csss::CertificateValidity::VALID;
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is valid.");
+ CERTCertificate * issuerCert = cvout[0].value.pointer.cert;
+ if (issuerCert)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Root certificate: " << issuerCert->subjectName);
+ CERT_DestroyCertificate(issuerCert);
+ };
+
+ break;
+ }
+ else
+ {
+ PRIntn err = PR_GetError();
+ SAL_INFO("xmlsecurity.xmlsec", "Error: " << err << ": " << getCertError(err));
+
+ /* Display validation results */
+ if ( log.count > 0)
+ {
+ CERTVerifyLogNode *node = nullptr;
+ printChainFailure(&log);
+
+ for (node = log.head; node; node = node->next) {
+ if (node->cert)
+ CERT_DestroyCertificate(node->cert);
+ }
+ log.head = log.tail = nullptr;
+ log.count = 0;
+ }
+ SAL_INFO("xmlsecurity.xmlsec", "Certificate is invalid.");
+ }
+ }
+
+ //Destroying the temporary certificates
+ for (auto& tmpCert : vecTmpNSSCertificates)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "Destroying temporary certificate");
+ CERT_DestroyCertificate(tmpCert);
+ }
+ PORT_FreeArena(log.arena, true);
+ return validity ;
+}
+
+sal_Int32 SecurityEnvironment_NssImpl::getCertificateCharacters(
+ const css::uno::Reference< css::security::XCertificate >& aCert ) {
+ sal_Int32 characters ;
+ const CERTCertificate* cert ;
+
+ Reference< XUnoTunnel > xCertTunnel( aCert, UNO_QUERY_THROW ) ;
+ const X509Certificate_NssImpl* xcert = comphelper::getFromUnoTunnel<X509Certificate_NssImpl>(xCertTunnel);
+ if( xcert == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ cert = xcert->getNssCert() ;
+
+ characters = 0x00000000 ;
+
+ //Firstly, find out whether or not the cert is self-signed.
+ if( SECITEM_CompareItem( &(cert->derIssuer), &(cert->derSubject) ) == SECEqual ) {
+ characters |= css::security::CertificateCharacters::SELF_SIGNED ;
+ } else {
+ characters &= ~ css::security::CertificateCharacters::SELF_SIGNED ;
+ }
+
+ //Secondly, find out whether or not the cert has a private key.
+
+ /*
+ * i40394
+ *
+ * mmi : need to check whether the cert's slot is valid first
+ */
+ SECKEYPrivateKey* priKey = nullptr;
+
+ if (cert->slot != nullptr)
+ {
+ priKey = PK11_FindPrivateKeyFromCert( cert->slot, const_cast<CERTCertificate*>(cert), nullptr ) ;
+ }
+ if(priKey == nullptr)
+ {
+ for (auto& slot : m_Slots)
+ {
+ priKey = PK11_FindPrivateKeyFromCert(slot, const_cast<CERTCertificate*>(cert), nullptr);
+ if (priKey)
+ break;
+ }
+ }
+ if( priKey != nullptr ) {
+ characters |= css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+
+ SECKEY_DestroyPrivateKey( priKey ) ;
+ } else {
+ characters &= ~ css::security::CertificateCharacters::HAS_PRIVATE_KEY ;
+ }
+
+ return characters ;
+}
+
+rtl::Reference<X509Certificate_NssImpl> NssCertToXCert( CERTCertificate* cert )
+{
+ if( cert != nullptr ) {
+ rtl::Reference<X509Certificate_NssImpl> xcert = new X509Certificate_NssImpl() ;
+ xcert->setCert( cert ) ;
+ return xcert;
+ }
+
+ return nullptr;
+}
+
+rtl::Reference<X509Certificate_NssImpl> NssPrivKeyToXCert( SECKEYPrivateKey* priKey )
+{
+ if( priKey != nullptr ) {
+ rtl::Reference<X509Certificate_NssImpl> xcert;
+ CERTCertificate* cert = PK11_GetCertFromPrivateKey( priKey ) ;
+
+ if( cert != nullptr ) {
+ xcert = NssCertToXCert( cert ) ;
+ }
+
+ CERT_DestroyCertificate( cert ) ;
+ return xcert;
+ }
+
+ return nullptr;
+}
+
+xmlSecKeysMngrPtr SecurityEnvironment_NssImpl::createKeysManager() {
+
+ /*-
+ * The following lines is based on the private version of xmlSec-NSS
+ * crypto engine
+ */
+ int cSlots = m_Slots.size();
+ std::unique_ptr<PK11SlotInfo*[]> sarSlots(new PK11SlotInfo*[cSlots]);
+ PK11SlotInfo** slots = sarSlots.get();
+ int count = 0;
+ for (const auto& slot : m_Slots)
+ {
+ slots[count] = slot;
+ ++count;
+ }
+
+ xmlSecKeysMngrPtr pKeysMngr = xmlSecKeysMngrCreate();
+ if (!pKeysMngr)
+ throw RuntimeException();
+
+ if (xmlSecNssAppDefaultKeysMngrInit(pKeysMngr) < 0)
+ throw RuntimeException();
+
+ // Adopt the private key of the signing certificate, if it has any.
+ if (auto pCertificate = dynamic_cast<X509Certificate_NssImpl*>(m_xSigningCertificate.get()))
+ {
+ SECKEYPrivateKey* pPrivateKey = SECKEY_CopyPrivateKey(pCertificate->getPrivateKey());
+ if (pPrivateKey)
+ {
+ xmlSecKeyDataPtr pKeyData = xmlSecNssPKIAdoptKey(pPrivateKey, nullptr);
+ xmlSecKeyPtr pKey = xmlSecKeyCreate();
+ xmlSecKeySetValue(pKey, pKeyData);
+ xmlSecNssAppDefaultKeysMngrAdoptKey(pKeysMngr, pKey);
+ }
+ else
+ {
+ SAL_WARN("xmlsecurity.xmlsec", "Can't get the private key from the certificate.");
+ }
+ }
+
+ return pKeysMngr;
+}
+
+void SecurityEnvironment_NssImpl::destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) {
+ if( pKeysMngr != nullptr ) {
+ xmlSecKeysMngrDestroy( pKeysMngr ) ;
+ }
+}
+
+SECKEYPrivateKey* SecurityEnvironment_NssImpl::insertPrivateKey(css::uno::Sequence<sal_Int8> const & raPrivateKey)
+{
+ PK11SlotInfo* pSlot = PK11_GetInternalKeySlot();
+
+ if (!pSlot)
+ return nullptr;
+
+ SECItem aDerPrivateKeyInfo;
+ aDerPrivateKeyInfo.data = reinterpret_cast<unsigned char *>(const_cast<sal_Int8 *>(raPrivateKey.getConstArray()));
+ aDerPrivateKeyInfo.len = raPrivateKey.getLength();
+
+ const unsigned int aKeyUsage = KU_ALL;
+ SECKEYPrivateKey* pPrivateKey = nullptr;
+
+ bool bPermanent = PR_FALSE;
+ bool bPrivate = PR_TRUE;
+
+ SECStatus nStatus = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ pSlot, &aDerPrivateKeyInfo, nullptr, nullptr, bPermanent, bPrivate,
+ aKeyUsage, &pPrivateKey, nullptr);
+
+ if (nStatus != SECSuccess)
+ return nullptr;
+
+ PK11_FreeSlot(pSlot);
+
+ return pPrivateKey;
+}
+
+uno::Reference<security::XCertificate> SecurityEnvironment_NssImpl::createDERCertificateWithPrivateKey(
+ Sequence<sal_Int8> const & raDERCertificate, Sequence<sal_Int8> const & raPrivateKey)
+{
+ SECKEYPrivateKey* pPrivateKey = insertPrivateKey(raPrivateKey);
+
+ if (!pPrivateKey)
+ return uno::Reference<security::XCertificate>();
+
+ return createAndAddCertificateFromPackage(raDERCertificate, u"TCu,TCu,TCu");
+}
+
+uno::Reference<security::XCertificate> SecurityEnvironment_NssImpl::addDERCertificateToTheDatabase(
+ uno::Sequence<sal_Int8> const & raDERCertificate, OUString const & raTrustString)
+{
+ return createAndAddCertificateFromPackage(raDERCertificate, raTrustString);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_SecurityEnvironment_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SecurityEnvironment_NssImpl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.hxx b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.hxx
new file mode 100644
index 000000000..123103720
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/securityenvironment_nssimpl.hxx
@@ -0,0 +1,146 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+
+#include <mutex>
+
+#include <keythi.h>
+#include <certt.h>
+
+#include <string_view>
+#include <vector>
+
+#include <xmlsec-wrapper.h>
+
+namespace com::sun::star::security { class XCertificate; }
+class X509Certificate_NssImpl;
+
+class SecurityEnvironment_NssImpl : public ::cppu::WeakImplHelper<
+ css::xml::crypto::XSecurityEnvironment,
+ css::xml::crypto::XCertificateCreator,
+ css::lang::XServiceInfo,
+ css::lang::XUnoTunnel >
+{
+private:
+
+ std::vector< PK11SlotInfo* > m_Slots;
+ /// The last used certificate which has the private key for signing.
+ css::uno::Reference<css::security::XCertificate> m_xSigningCertificate;
+
+ std::mutex m_mutex;
+
+ CERTCertDBHandle* m_pHandler ;
+ std::vector< PK11SymKey* > m_tSymKeyList ;
+
+ public:
+ SecurityEnvironment_NssImpl();
+ virtual ~SecurityEnvironment_NssImpl() override;
+
+ //Methods from XSecurityEnvironment
+
+ //Methods from XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override ;
+
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& ServiceName
+ ) override ;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override ;
+
+ virtual ::sal_Int32 SAL_CALL verifyCertificate(
+ const css::uno::Reference<
+ css::security::XCertificate >& xCert,
+ const css::uno::Sequence<
+ css::uno::Reference< css::security::XCertificate > > &
+ intermediateCerts) override ;
+
+ virtual ::sal_Int32 SAL_CALL getCertificateCharacters( const css::uno::Reference< css::security::XCertificate >& xCert ) override ;
+
+ virtual OUString SAL_CALL getSecurityEnvironmentInformation( ) override;
+
+ //Methods from XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId() ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void setCertDb( CERTCertDBHandle* aCertDb ) ;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void adoptSymKey( PK11SymKey* aSymKey ) ;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getPersonalCertificates() override ;
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getAllCertificates() override
+ { return css::uno::Sequence< css::uno::Reference< css::security::XCertificate > >(); }
+
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL getCertificate( const OUString& issuerName, const css::uno::Sequence< sal_Int8 >& serialNumber ) override ;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL buildCertificatePath( const css::uno::Reference< css::security::XCertificate >& beginCert ) override ;
+
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromRaw( const css::uno::Sequence< sal_Int8 >& rawCertificate ) override ;
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromAscii( const OUString& asciiCertificate ) override ;
+
+ // Methods of XCertificateCreator
+ css::uno::Reference<css::security::XCertificate> SAL_CALL addDERCertificateToTheDatabase(
+ css::uno::Sequence<sal_Int8> const & raDERCertificate,
+ OUString const & raTrustString) override;
+
+ css::uno::Reference<css::security::XCertificate> SAL_CALL createDERCertificateWithPrivateKey(
+ css::uno::Sequence<sal_Int8> const & raDERCertificate,
+ css::uno::Sequence<sal_Int8> const & raPrivateKey) override;
+
+ //Native methods
+ /// @throws css::uno::RuntimeException
+ xmlSecKeysMngrPtr createKeysManager() ;
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ static void destroyKeysManager(xmlSecKeysMngrPtr pKeysMngr) ;
+
+private:
+
+ void updateSlots();
+
+ static rtl::Reference<X509Certificate_NssImpl> createAndAddCertificateFromPackage(
+ const css::uno::Sequence<sal_Int8>& raDerCertificate,
+ std::u16string_view raString);
+ static SECKEYPrivateKey* insertPrivateKey(css::uno::Sequence<sal_Int8> const & raPrivateKey);
+
+ static rtl::Reference<X509Certificate_NssImpl> createX509CertificateFromDER(const css::uno::Sequence<sal_Int8>& raDerCertificate);
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void addCryptoSlot( PK11SlotInfo* aSlot ) ;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.cxx
new file mode 100644
index 000000000..6ada0522b
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.cxx
@@ -0,0 +1,144 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+#include <com/sun/star/xml/crypto/SecurityEnvironment.hpp>
+#include <com/sun/star/xml/crypto/XMLSecurityContext.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "seinitializer_nssimpl.hxx"
+#include "securityenvironment_nssimpl.hxx"
+
+#include <cert.h>
+
+
+using namespace com::sun::star;
+
+
+SEInitializer_NssImpl::SEInitializer_NssImpl( const css::uno::Reference< css::uno::XComponentContext > &rxContext )
+{
+ m_xContext = rxContext;
+}
+
+SEInitializer_NssImpl::~SEInitializer_NssImpl()
+{
+}
+
+/* XSEInitializer */
+uno::Reference< css::xml::crypto::XXMLSecurityContext > SAL_CALL
+ SEInitializer_NssImpl::createSecurityContext( const OUString& )
+{
+ CERTCertDBHandle *pCertHandle = nullptr ;
+
+ if( !initNSS( m_xContext ) )
+ return nullptr;
+
+ pCertHandle = CERT_GetDefaultCertDB() ;
+
+ try
+ {
+ /* Build XML Security Context */
+ uno::Reference< css::xml::crypto::XXMLSecurityContext > xSecCtx = css::xml::crypto::XMLSecurityContext::create( m_xContext );
+
+ uno::Reference< css::xml::crypto::XSecurityEnvironment > xSecEnv = css::xml::crypto::SecurityEnvironment::create( m_xContext );
+ uno::Reference< lang::XUnoTunnel > xSecEnvTunnel(xSecEnv, uno::UNO_QUERY_THROW);
+ SecurityEnvironment_NssImpl* pSecEnv = comphelper::getFromUnoTunnel<SecurityEnvironment_NssImpl>(xSecEnvTunnel);
+ pSecEnv->setCertDb(pCertHandle);
+
+ sal_Int32 n = xSecCtx->addSecurityEnvironment(xSecEnv);
+ //originally the SecurityEnvironment with the internal slot was set as default
+ xSecCtx->setDefaultSecurityEnvironmentIndex( n );
+ return xSecCtx;
+ }
+ catch( const uno::Exception& )
+ {
+ //PK11_LogoutAll();
+ //NSS_Shutdown();
+ return nullptr;
+ }
+}
+
+void SAL_CALL SEInitializer_NssImpl::freeSecurityContext( const uno::Reference< css::xml::crypto::XXMLSecurityContext >& )
+{
+ /*
+ * because the security context will free all its content when it
+ * is destructed, so here no free process for the security context
+ * is needed.
+ */
+ //PK11_LogoutAll();
+ //NSS_Shutdown();
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SEInitializer_NssImpl::getImplementationName( )
+{
+ return "com.sun.star.xml.crypto.SEInitializer";
+}
+sal_Bool SAL_CALL SEInitializer_NssImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+uno::Sequence< OUString > SAL_CALL SEInitializer_NssImpl::getSupportedServiceNames( )
+{
+ return { "com.sun.star.xml.crypto.SEInitializer" };
+}
+
+namespace {
+
+class NSSInitializer_NssImpl : public SEInitializer_NssImpl
+{
+public:
+ explicit NSSInitializer_NssImpl(const uno::Reference<uno::XComponentContext>& xContext);
+ OUString SAL_CALL getImplementationName() override;
+ uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+NSSInitializer_NssImpl::NSSInitializer_NssImpl(const uno::Reference<uno::XComponentContext>& xContext)
+ : SEInitializer_NssImpl(xContext)
+{
+}
+
+OUString NSSInitializer_NssImpl::getImplementationName()
+{
+ return "com.sun.star.xml.crypto.NSSInitializer";
+}
+
+uno::Sequence<OUString> SAL_CALL NSSInitializer_NssImpl::getSupportedServiceNames()
+{
+ return { "com.sun.star.xml.crypto.NSSInitializer" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_NSSInitializer_get_implementation(
+ uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new NSSInitializer_NssImpl(pCtx));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_SEInitializer_get_implementation(
+ uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SEInitializer_NssImpl(pCtx));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.hxx b/xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.hxx
new file mode 100644
index 000000000..f078f387a
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/seinitializer_nssimpl.hxx
@@ -0,0 +1,55 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include "nssinitializer.hxx"
+
+namespace com::sun::star::xml::crypto { class XXMLSecurityContext; }
+
+class SEInitializer_NssImpl : public cppu::ImplInheritanceHelper
+<
+ ONSSInitializer,
+ css::xml::crypto::XSEInitializer
+>
+{
+public:
+ explicit SEInitializer_NssImpl(const css::uno::Reference<css::uno::XComponentContext > &rxContext);
+ virtual ~SEInitializer_NssImpl() override;
+
+ /* XSEInitializer */
+ virtual css::uno::Reference< css::xml::crypto::XXMLSecurityContext >
+ SAL_CALL createSecurityContext( const OUString& ) override;
+
+ virtual void SAL_CALL freeSecurityContext( const css::uno::Reference<
+ css::xml::crypto::XXMLSecurityContext >& securityContext ) override;
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx
new file mode 100644
index 000000000..7e28cbc61
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx
@@ -0,0 +1,614 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "nssrenam.h"
+#include <secder.h>
+
+#include <cert.h>
+#include <pk11pub.h>
+#include <hasht.h>
+
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include "x509certificate_nssimpl.hxx"
+
+#include <biginteger.hxx>
+#include <certificateextension_xmlsecimpl.hxx>
+
+#include "sanextension_nssimpl.hxx"
+#include <tools/time.hxx>
+#include <svl/sigstruct.hxx>
+
+using ::css::util::DateTime;
+
+X509Certificate_NssImpl::X509Certificate_NssImpl() :
+ m_pCert(nullptr)
+{
+}
+
+X509Certificate_NssImpl::~X509Certificate_NssImpl() {
+ if( m_pCert != nullptr ) {
+ CERT_DestroyCertificate( m_pCert ) ;
+ }
+}
+
+//Methods from XCertificate
+sal_Int16 SAL_CALL X509Certificate_NssImpl::getVersion() {
+ if( m_pCert != nullptr ) {
+ if( m_pCert->version.len > 0 ) {
+ return static_cast<char>(*( m_pCert->version.data )) ;
+ } else
+ return 0 ;
+ } else {
+ return -1 ;
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_NssImpl::getSerialNumber() {
+ if( m_pCert != nullptr && m_pCert->serialNumber.len > 0 ) {
+ return comphelper::arrayToSequence<sal_Int8>(m_pCert->serialNumber.data,
+ m_pCert->serialNumber.len) ;
+ } else {
+ return css::uno::Sequence< sal_Int8 >();
+ }
+}
+
+OUString SAL_CALL X509Certificate_NssImpl::getIssuerName() {
+ if( m_pCert != nullptr ) {
+ return OUString(m_pCert->issuerName , PL_strlen(m_pCert->issuerName) , RTL_TEXTENCODING_UTF8) ;
+ } else {
+ return OUString() ;
+ }
+}
+
+OUString SAL_CALL X509Certificate_NssImpl::getSubjectName() {
+ if( m_pCert != nullptr ) {
+ return OUString(m_pCert->subjectName , PL_strlen(m_pCert->subjectName) , RTL_TEXTENCODING_UTF8);
+ } else {
+ return OUString() ;
+ }
+}
+
+css::util::DateTime SAL_CALL X509Certificate_NssImpl::getNotValidBefore() {
+ if( m_pCert != nullptr ) {
+ SECStatus rv ;
+ PRTime notBefore ;
+ PRExplodedTime explTime ;
+ DateTime dateTime ;
+
+ rv = DER_DecodeTimeChoice( &notBefore, &m_pCert->validity.notBefore ) ;
+ if( rv != SECStatus::SECSuccess ) {
+ return DateTime() ;
+ }
+
+ //Convert the time to readable local time
+ PR_ExplodeTime( notBefore, PR_LocalTimeParameters, &explTime ) ;
+
+ dateTime.NanoSeconds = static_cast< sal_Int32 >( explTime.tm_usec * ::tools::Time::nanoPerMicro );
+ dateTime.Seconds = static_cast< sal_Int16 >( explTime.tm_sec );
+ dateTime.Minutes = static_cast< sal_Int16 >( explTime.tm_min );
+ dateTime.Hours = static_cast< sal_Int16 >( explTime.tm_hour );
+ dateTime.Day = static_cast< sal_Int16 >( explTime.tm_mday );
+ dateTime.Month = static_cast< sal_Int16 >( explTime.tm_month+1 );
+ dateTime.Year = static_cast< sal_Int16 >( explTime.tm_year );
+
+ return dateTime ;
+ } else {
+ return DateTime() ;
+ }
+}
+
+css::util::DateTime SAL_CALL X509Certificate_NssImpl::getNotValidAfter() {
+ if( m_pCert != nullptr ) {
+ SECStatus rv ;
+ PRTime notAfter ;
+ PRExplodedTime explTime ;
+ DateTime dateTime ;
+
+ rv = DER_DecodeTimeChoice( &notAfter, &m_pCert->validity.notAfter ) ;
+ if( rv != SECStatus::SECSuccess ) {
+ return DateTime() ;
+ }
+
+ //Convert the time to readable local time
+ PR_ExplodeTime( notAfter, PR_LocalTimeParameters, &explTime ) ;
+
+ dateTime.NanoSeconds = static_cast< sal_Int16 >( explTime.tm_usec * ::tools::Time::nanoPerMicro );
+ dateTime.Seconds = static_cast< sal_Int16 >( explTime.tm_sec );
+ dateTime.Minutes = static_cast< sal_Int16 >( explTime.tm_min );
+ dateTime.Hours = static_cast< sal_Int16 >( explTime.tm_hour );
+ dateTime.Day = static_cast< sal_Int16 >( explTime.tm_mday );
+ dateTime.Month = static_cast< sal_Int16 >( explTime.tm_month+1 );
+ dateTime.Year = static_cast< sal_Int16 >( explTime.tm_year );
+
+ return dateTime ;
+ } else {
+ return DateTime() ;
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_NssImpl::getIssuerUniqueID() {
+ if( m_pCert != nullptr && m_pCert->issuerID.len > 0 ) {
+ return comphelper::arrayToSequence<sal_Int8>(m_pCert->issuerID.data, m_pCert->issuerID.len) ;
+ } else {
+ return css::uno::Sequence< sal_Int8 >();
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_NssImpl::getSubjectUniqueID() {
+ if( m_pCert != nullptr && m_pCert->subjectID.len > 0 ) {
+ return comphelper::arrayToSequence<sal_Int8>(m_pCert->subjectID.data,
+ m_pCert->subjectID.len) ;
+ } else {
+ return css::uno::Sequence< sal_Int8 >();
+ }
+}
+
+css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > SAL_CALL X509Certificate_NssImpl::getExtensions() {
+ if( m_pCert != nullptr && m_pCert->extensions != nullptr ) {
+ CERTCertExtension** extns ;
+ int len ;
+
+ for( len = 0, extns = m_pCert->extensions; *extns != nullptr; len ++, extns ++ ) ;
+ css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > xExtns( len ) ;
+ auto xExtnsRange = asNonConstRange(xExtns);
+
+ for( extns = m_pCert->extensions, len = 0; *extns != nullptr; extns ++, len ++ ) {
+ const SECItem id = (*extns)->id;
+ OString oidString(CERT_GetOidString(&id));
+
+ bool crit;
+ if( (*extns)->critical.data == nullptr )
+ crit = false ;
+ else
+ crit = (*extns)->critical.data[0] == 0xFF;
+
+ // remove "OID." prefix if existing
+ OString objID;
+ OString oid("OID.");
+ if (oidString.match(oid))
+ objID = oidString.copy(oid.getLength());
+ else
+ objID = oidString;
+
+ unsigned char* value = (*extns)->value.data;
+ unsigned int vlen = (*extns)->value.len;
+ unsigned char* objid = reinterpret_cast<unsigned char *>(const_cast<char *>(objID.getStr()));
+ unsigned int objidlen = objID.getLength();
+
+ if (objID == "2.5.29.17")
+ {
+ rtl::Reference<SanExtensionImpl> pExtn = new SanExtensionImpl;
+ pExtn->setCertExtn(value, vlen, objid, objidlen, crit);
+ xExtnsRange[len] = pExtn ;
+ }
+ else
+ {
+ rtl::Reference<CertificateExtension_XmlSecImpl> pExtn = new CertificateExtension_XmlSecImpl;
+ pExtn->setCertExtn(value, vlen, objid, objidlen, crit);
+ xExtnsRange[len] = pExtn;
+ }
+ }
+
+ return xExtns ;
+ } else {
+ return css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > ();
+ }
+}
+
+css::uno::Reference< css::security::XCertificateExtension > SAL_CALL X509Certificate_NssImpl::findCertificateExtension( const css::uno::Sequence< sal_Int8 >& oid ) {
+ if( m_pCert != nullptr && m_pCert->extensions != nullptr ) {
+ CERTCertExtension** extns ;
+ SECItem idItem ;
+
+ idItem.data = reinterpret_cast<unsigned char *>(const_cast<sal_Int8 *>(oid.getConstArray()));
+ idItem.len = oid.getLength() ;
+
+ css::uno::Reference<css::security::XCertificateExtension> xExtn;
+ for( extns = m_pCert->extensions; *extns != nullptr; extns ++ ) {
+ if( SECITEM_CompareItem( &idItem, &(*extns)->id ) == SECEqual ) {
+ const SECItem id = (*extns)->id;
+ OString objId(CERT_GetOidString(&id));
+
+ bool crit;
+ if( (*extns)->critical.data == nullptr )
+ crit = false ;
+ else
+ crit = (*extns)->critical.data[0] == 0xFF;
+
+ unsigned char* value = (*extns)->value.data;
+ unsigned int vlen = (*extns)->value.len;
+ unsigned char* objid = (*extns)->id.data;
+ unsigned int objidlen = (*extns)->id.len;
+
+ if ( objId == "OID.2.5.29.17" )
+ {
+ rtl::Reference<SanExtensionImpl> xSanImpl(
+ new SanExtensionImpl);
+ xSanImpl->setCertExtn(value, vlen, objid, objidlen, crit);
+ xExtn = xSanImpl.get();
+ }
+ else
+ {
+ rtl::Reference<CertificateExtension_XmlSecImpl> xSecImpl(
+ new CertificateExtension_XmlSecImpl);
+ xSecImpl->setCertExtn(value, vlen, objid, objidlen, crit);
+ xExtn = xSecImpl.get();
+ }
+ break;
+ }
+ }
+
+ return xExtn;
+ } else {
+ return nullptr ;
+ }
+}
+
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_NssImpl::getEncoded() {
+ if( m_pCert != nullptr && m_pCert->derCert.len > 0 ) {
+ return comphelper::arrayToSequence<sal_Int8>(m_pCert->derCert.data, m_pCert->derCert.len) ;
+ } else {
+ return css::uno::Sequence< sal_Int8 >();
+ }
+}
+
+//Helper methods
+void X509Certificate_NssImpl::setCert( CERTCertificate* cert ) {
+ if( m_pCert != nullptr ) {
+ CERT_DestroyCertificate( m_pCert ) ;
+ m_pCert = nullptr ;
+ }
+
+ if( cert != nullptr ) {
+ m_pCert = CERT_DupCertificate( cert ) ;
+ }
+}
+
+const CERTCertificate* X509Certificate_NssImpl::getNssCert() const {
+ if( m_pCert != nullptr ) {
+ return m_pCert ;
+ } else {
+ return nullptr ;
+ }
+}
+
+void X509Certificate_NssImpl::setRawCert( const css::uno::Sequence< sal_Int8 >& rawCert ) {
+ CERTCertificate* cert ;
+ SECItem certItem ;
+
+ certItem.data = reinterpret_cast<unsigned char *>(const_cast<sal_Int8 *>(rawCert.getConstArray()));
+ certItem.len = rawCert.getLength() ;
+
+ cert = CERT_DecodeDERCertificate( &certItem, PR_TRUE, nullptr ) ;
+ if( cert == nullptr )
+ throw css::uno::RuntimeException() ;
+
+ if( m_pCert != nullptr ) {
+ CERT_DestroyCertificate( m_pCert ) ;
+ m_pCert = nullptr ;
+ }
+
+ m_pCert = cert ;
+}
+
+SECKEYPrivateKey* X509Certificate_NssImpl::getPrivateKey()
+{
+ if (m_pCert && m_pCert->slot)
+ {
+ SECKEYPrivateKey* pPrivateKey = PK11_FindPrivateKeyFromCert(m_pCert->slot, m_pCert, nullptr);
+ if (pPrivateKey)
+ return pPrivateKey;
+ pPrivateKey = PK11_FindKeyByDERCert(m_pCert->slot, m_pCert, nullptr);
+ if (pPrivateKey)
+ {
+ SAL_INFO("xmlsecurity.xmlsec", "fallback from PK11_FindPrivateKeyFromCert to PK11_FindKeyByDERCert needed");
+ return pPrivateKey;
+ }
+ SAL_WARN("xmlsecurity.xmlsec", "X509Certificate_NssImpl::getPrivateKey() cannot find private key");
+ }
+ return nullptr;
+}
+
+/* XUnoTunnel */
+sal_Int64 SAL_CALL X509Certificate_NssImpl::getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) {
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XUnoTunnel extension */
+
+const css::uno::Sequence< sal_Int8>& X509Certificate_NssImpl::getUnoTunnelId() {
+ static const comphelper::UnoIdInit theX509Certificate_NssImplUnoTunnelId;
+ return theX509Certificate_NssImplUnoTunnelId.getSeq();
+}
+
+static OUString getAlgorithmDescription(SECAlgorithmID const *aid)
+{
+ SECOidTag tag;
+ tag = SECOID_GetAlgorithmTag(aid);
+
+ const char *pDesc = SECOID_FindOIDTagDescription(tag);
+
+ return OUString::createFromAscii( pDesc ) ;
+}
+
+static css::uno::Sequence< sal_Int8 > getThumbprint(CERTCertificate const *pCert, SECOidTag id)
+{
+ if( pCert != nullptr )
+ {
+ SECStatus rv;
+ unsigned char fingerprint[32];
+ int length = 0;
+ switch (id)
+ {
+ case SEC_OID_MD5:
+ length = MD5_LENGTH;
+ break;
+ case SEC_OID_SHA1:
+ length = SHA1_LENGTH;
+ break;
+ case SEC_OID_SHA256:
+ length = SHA256_LENGTH;
+ break;
+ default:
+ break;
+ }
+
+ memset(fingerprint, 0, sizeof fingerprint);
+ rv = PK11_HashBuf(id, fingerprint, pCert->derCert.data, pCert->derCert.len);
+ if(rv == SECStatus::SECSuccess)
+ {
+ return comphelper::arrayToSequence<sal_Int8>(fingerprint, length);
+ }
+ }
+ return css::uno::Sequence< sal_Int8 >();
+}
+
+OUString SAL_CALL X509Certificate_NssImpl::getSubjectPublicKeyAlgorithm()
+{
+ if( m_pCert != nullptr )
+ {
+ return getAlgorithmDescription(&(m_pCert->subjectPublicKeyInfo.algorithm));
+ }
+ else
+ {
+ return OUString() ;
+ }
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_NssImpl::getSubjectPublicKeyValue()
+{
+ if( m_pCert != nullptr )
+ {
+ SECItem spk = m_pCert->subjectPublicKeyInfo.subjectPublicKey;
+ DER_ConvertBitString(&spk);
+
+ if ( spk.len>0)
+ {
+ return comphelper::arrayToSequence<sal_Int8>(spk.data, spk.len) ;
+ }
+ }
+
+ return css::uno::Sequence< sal_Int8 >();
+}
+
+OUString SAL_CALL X509Certificate_NssImpl::getSignatureAlgorithm()
+{
+ if( m_pCert != nullptr )
+ {
+ return getAlgorithmDescription(&(m_pCert->signature));
+ }
+ else
+ {
+ return OUString() ;
+ }
+}
+
+svl::crypto::SignatureMethodAlgorithm X509Certificate_NssImpl::getSignatureMethodAlgorithm()
+{
+ svl::crypto::SignatureMethodAlgorithm nRet = svl::crypto::SignatureMethodAlgorithm::RSA;
+
+ if (!m_pCert)
+ return nRet;
+
+ SECOidTag eTag = SECOID_GetAlgorithmTag(&m_pCert->subjectPublicKeyInfo.algorithm);
+ if (eTag == SEC_OID_ANSIX962_EC_PUBLIC_KEY)
+ nRet = svl::crypto::SignatureMethodAlgorithm::ECDSA;
+
+ return nRet;
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_NssImpl::getSHA1Thumbprint()
+{
+ return getThumbprint(m_pCert, SEC_OID_SHA1);
+}
+
+css::uno::Sequence<sal_Int8> X509Certificate_NssImpl::getSHA256Thumbprint()
+{
+ return getThumbprint(m_pCert, SEC_OID_SHA256);
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL X509Certificate_NssImpl::getMD5Thumbprint()
+{
+ return getThumbprint(m_pCert, SEC_OID_MD5);
+}
+
+css::security::CertificateKind SAL_CALL X509Certificate_NssImpl::getCertificateKind()
+{
+ return css::security::CertificateKind_X509;
+}
+
+sal_Int32 SAL_CALL X509Certificate_NssImpl::getCertificateUsage( )
+{
+ SECStatus rv;
+ SECItem tmpitem;
+ sal_Int32 usage;
+
+ rv = CERT_FindKeyUsageExtension(m_pCert, &tmpitem);
+ if ( rv == SECStatus::SECSuccess )
+ {
+ usage = tmpitem.data[0];
+ PORT_Free(tmpitem.data);
+ tmpitem.data = nullptr;
+ }
+ else
+ {
+ usage = KU_ALL;
+ }
+
+ /*
+ * to make the nss implementation compatible with MSCrypto,
+ * the following usage is ignored
+ *
+ *
+ if ( CERT_GovtApprovedBitSet(m_pCert) )
+ {
+ usage |= KU_NS_GOVT_APPROVED;
+ }
+ */
+
+ return usage;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL X509Certificate_NssImpl::getImplementationName()
+{
+ return "com.sun.star.xml.security.gpg.XCertificate_NssImpl";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL X509Certificate_NssImpl::supportsService(const OUString& serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+css::uno::Sequence<OUString> SAL_CALL X509Certificate_NssImpl::getSupportedServiceNames() { return { OUString() }; }
+
+namespace xmlsecurity {
+
+// based on some guesswork and:
+// https://datatracker.ietf.org/doc/html/rfc1485
+// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certnametostra#CERT_X500_NAME_STR
+// the main problem appears to be that in values " is escaped as "" vs. \"
+static OUString CompatDNCryptoAPI(OUString const& rDN)
+{
+ OUStringBuffer buf(rDN.getLength());
+ enum { DEFAULT, INVALUE, INQUOTE } state(DEFAULT);
+ for (sal_Int32 i = 0; i < rDN.getLength(); ++i)
+ {
+ if (state == DEFAULT)
+ {
+ buf.append(rDN[i]);
+ if (rDN[i] == '=')
+ {
+ if (rDN.getLength() == i+1)
+ {
+ break; // invalid?
+ }
+ else if (rDN[i+1] == '"')
+ {
+ buf.append(rDN[i+1]);
+ ++i;
+ state = INQUOTE;
+ }
+ else
+ {
+ state = INVALUE;
+ }
+ }
+ }
+ else if (state == INVALUE)
+ {
+ if (rDN[i] == '+' || rDN[i] == ',' || rDN[i] == ';')
+ {
+ state = DEFAULT;
+ }
+ buf.append(rDN[i]);
+ }
+ else
+ {
+ assert(state == INQUOTE);
+ if (rDN[i] == '"')
+ {
+ if (rDN.getLength() != i+1 && rDN[i+1] == '"')
+ {
+ buf.append('\\');
+ buf.append(rDN[i+1]);
+ ++i;
+ }
+ else
+ {
+ buf.append(rDN[i]);
+ state = DEFAULT;
+ }
+ }
+ else
+ {
+ buf.append(rDN[i]);
+ }
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+bool EqualDistinguishedNames(
+ std::u16string_view const rName1, std::u16string_view const rName2,
+ EqualMode const eMode)
+{
+ if (eMode == COMPAT_BOTH && !rName1.empty() && rName1 == rName2)
+ { // handle case where both need to be converted
+ return true;
+ }
+ CERTName *const pName1(CERT_AsciiToName(OUStringToOString(rName1, RTL_TEXTENCODING_UTF8).getStr()));
+ if (pName1 == nullptr)
+ {
+ return false;
+ }
+ CERTName *const pName2(CERT_AsciiToName(OUStringToOString(rName2, RTL_TEXTENCODING_UTF8).getStr()));
+ bool ret(false);
+ if (pName2)
+ {
+ ret = (CERT_CompareName(pName1, pName2) == SECEqual);
+ CERT_DestroyName(pName2);
+ }
+ if (!ret && eMode == COMPAT_2ND)
+ {
+ CERTName *const pName2Compat(CERT_AsciiToName(OUStringToOString(
+ CompatDNCryptoAPI(OUString(rName2)), RTL_TEXTENCODING_UTF8).getStr()));
+ if (pName2Compat == nullptr)
+ {
+ CERT_DestroyName(pName1);
+ return false;
+ }
+ ret = CERT_CompareName(pName1, pName2Compat) == SECEqual;
+ CERT_DestroyName(pName2Compat);
+ }
+ CERT_DestroyName(pName1);
+ return ret;
+}
+
+} // namespace xmlsecurity
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.hxx b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.hxx
new file mode 100644
index 000000000..dbfb6c6b0
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.hxx
@@ -0,0 +1,104 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/security/CertificateKind.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#include <certificate.hxx>
+#include <certt.h>
+#include <keythi.h>
+
+class X509Certificate_NssImpl : public ::cppu::WeakImplHelper<
+ css::security::XCertificate ,
+ css::lang::XUnoTunnel,
+ css::lang::XServiceInfo > , public xmlsecurity::Certificate
+{
+ private:
+ CERTCertificate* m_pCert;
+
+ public:
+ X509Certificate_NssImpl() ;
+ virtual ~X509Certificate_NssImpl() override ;
+
+ //Methods from XCertificate
+ virtual sal_Int16 SAL_CALL getVersion( ) override ;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSerialNumber( ) override ;
+
+ virtual OUString SAL_CALL getIssuerName( ) override ;
+ virtual OUString SAL_CALL getSubjectName( ) override ;
+
+ virtual css::util::DateTime SAL_CALL getNotValidBefore( ) override ;
+ virtual css::util::DateTime SAL_CALL getNotValidAfter( ) override ;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getIssuerUniqueID( ) override ;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectUniqueID( ) override ;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > SAL_CALL getExtensions( ) override ;
+
+ virtual css::uno::Reference< css::security::XCertificateExtension > SAL_CALL findCertificateExtension( const css::uno::Sequence< sal_Int8 >& oid ) override ;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getEncoded( ) override ;
+
+ virtual OUString SAL_CALL getSubjectPublicKeyAlgorithm() override ;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectPublicKeyValue() override ;
+
+ virtual OUString SAL_CALL getSignatureAlgorithm() override ;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSHA1Thumbprint() override ;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getMD5Thumbprint() override ;
+ virtual css::security::CertificateKind SAL_CALL getCertificateKind() override;
+
+ virtual sal_Int32 SAL_CALL getCertificateUsage( ) override ;
+
+ //Methods from XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ /// @see xmlsecurity::Certificate::getSHA256Thumbprint().
+ virtual css::uno::Sequence<sal_Int8> getSHA256Thumbprint() override;
+
+ /// @see xmlsecurity::Certificate::getSignatureMethodAlgorithm().
+ virtual svl::crypto::SignatureMethodAlgorithm getSignatureMethodAlgorithm() override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId() ;
+
+ //Helper methods
+ void setCert( CERTCertificate* cert ) ;
+ const CERTCertificate* getNssCert() const ;
+
+ /// @throws css::uno::RuntimeException
+ void setRawCert( const css::uno::Sequence< sal_Int8 >& rawCert ) ;
+ SECKEYPrivateKey* getPrivateKey();
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/xmlsecuritycontext_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/xmlsecuritycontext_nssimpl.cxx
new file mode 100644
index 000000000..9ccf0ab98
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/xmlsecuritycontext_nssimpl.cxx
@@ -0,0 +1,150 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <vector>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
+#include <o3tl/safeint.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang ;
+
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::xml::crypto::XXMLSecurityContext ;
+
+namespace {
+
+class XMLSecurityContext_NssImpl
+ : public ::cppu::WeakImplHelper<xml::crypto::XXMLSecurityContext, lang::XServiceInfo>
+{
+private:
+ std::vector<uno::Reference<xml::crypto::XSecurityEnvironment>> m_vSecurityEnvironments;
+
+ sal_Int32 m_nDefaultEnvIndex;
+
+public:
+ XMLSecurityContext_NssImpl();
+
+ //XXMLSecurityContext
+ virtual sal_Int32 SAL_CALL addSecurityEnvironment(
+ const uno::Reference<xml::crypto::XSecurityEnvironment>& aSecurityEnvironment) override;
+
+ virtual ::sal_Int32 SAL_CALL getSecurityEnvironmentNumber() override;
+
+ virtual uno::Reference<xml::crypto::XSecurityEnvironment>
+ SAL_CALL getSecurityEnvironmentByIndex(::sal_Int32 index) override;
+
+ virtual uno::Reference<xml::crypto::XSecurityEnvironment>
+ SAL_CALL getSecurityEnvironment() override;
+
+ virtual ::sal_Int32 SAL_CALL getDefaultSecurityEnvironmentIndex() override;
+
+ virtual void SAL_CALL setDefaultSecurityEnvironmentIndex(sal_Int32 nDefaultEnvIndex) override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+
+ virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+XMLSecurityContext_NssImpl::XMLSecurityContext_NssImpl()
+ : m_nDefaultEnvIndex(-1)
+{
+}
+
+sal_Int32 SAL_CALL XMLSecurityContext_NssImpl::addSecurityEnvironment(
+ const uno::Reference< xml::crypto::XSecurityEnvironment >& aSecurityEnvironment)
+{
+ if( !aSecurityEnvironment.is() )
+ {
+ throw uno::RuntimeException() ;
+ }
+
+ m_vSecurityEnvironments.push_back( aSecurityEnvironment );
+
+ return m_vSecurityEnvironments.size() - 1 ;
+}
+
+
+sal_Int32 SAL_CALL XMLSecurityContext_NssImpl::getSecurityEnvironmentNumber( )
+{
+ return m_vSecurityEnvironments.size();
+}
+
+uno::Reference< xml::crypto::XSecurityEnvironment > SAL_CALL
+ XMLSecurityContext_NssImpl::getSecurityEnvironmentByIndex( sal_Int32 index )
+{
+ if (index < 0 || o3tl::make_unsigned(index) >= m_vSecurityEnvironments.size())
+ throw uno::RuntimeException();
+
+ uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnvironment = m_vSecurityEnvironments[index];
+ return xSecurityEnvironment;
+}
+
+uno::Reference< xml::crypto::XSecurityEnvironment > SAL_CALL
+ XMLSecurityContext_NssImpl::getSecurityEnvironment( )
+{
+ if (m_nDefaultEnvIndex < 0 || o3tl::make_unsigned(m_nDefaultEnvIndex) >= m_vSecurityEnvironments.size())
+ throw uno::RuntimeException();
+
+ return getSecurityEnvironmentByIndex(m_nDefaultEnvIndex);
+}
+
+sal_Int32 SAL_CALL XMLSecurityContext_NssImpl::getDefaultSecurityEnvironmentIndex( )
+{
+ return m_nDefaultEnvIndex ;
+}
+
+void SAL_CALL XMLSecurityContext_NssImpl::setDefaultSecurityEnvironmentIndex( sal_Int32 nDefaultEnvIndex )
+{
+ m_nDefaultEnvIndex = nDefaultEnvIndex;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSecurityContext_NssImpl::getImplementationName() {
+ return "com.sun.star.xml.crypto.XMLSecurityContext";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSecurityContext_NssImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+uno::Sequence< OUString > SAL_CALL XMLSecurityContext_NssImpl::getSupportedServiceNames() {
+ return { "com.sun.star.xml.crypto.XMLSecurityContext" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_XMLSecurityContext_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new XMLSecurityContext_NssImpl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx
new file mode 100644
index 000000000..94e84a71b
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx
@@ -0,0 +1,325 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <xmlsec-wrapper.h>
+
+#include <xmlsec/nss/x509.h>
+
+#include <xmlelementwrapper_xmlsecimpl.hxx>
+#include <xmlsec/xmlstreamio.hxx>
+#include <xmlsec/errorcallback.hxx>
+
+#include "securityenvironment_nssimpl.hxx"
+
+#include <comphelper/servicehelper.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/xml/crypto/XXMLSignature.hpp>
+#include <memory>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::lang ;
+
+using ::com::sun::star::xml::wrapper::XXMLElementWrapper ;
+using ::com::sun::star::xml::crypto::XSecurityEnvironment ;
+using ::com::sun::star::xml::crypto::XXMLSignature ;
+using ::com::sun::star::xml::crypto::XXMLSignatureTemplate ;
+using ::com::sun::star::xml::crypto::XXMLSecurityContext ;
+using ::com::sun::star::xml::crypto::XUriBinding ;
+
+namespace std
+{
+template <> struct default_delete<xmlSecKeysMngr>
+{
+ void operator()(xmlSecKeysMngrPtr ptr) { SecurityEnvironment_NssImpl::destroyKeysManager(ptr); }
+};
+template <> struct default_delete<xmlSecDSigCtx>
+{
+ void operator()(xmlSecDSigCtxPtr ptr) { xmlSecDSigCtxDestroy(ptr); }
+};
+}
+
+namespace {
+
+class XMLSignature_NssImpl
+ : public ::cppu::WeakImplHelper<xml::crypto::XXMLSignature, lang::XServiceInfo>
+{
+public:
+ explicit XMLSignature_NssImpl();
+
+ //Methods from XXMLSignature
+ virtual uno::Reference<xml::crypto::XXMLSignatureTemplate> SAL_CALL
+ generate(const uno::Reference<xml::crypto::XXMLSignatureTemplate>& aTemplate,
+ const uno::Reference<xml::crypto::XSecurityEnvironment>& aEnvironment) override;
+
+ virtual uno::Reference<xml::crypto::XXMLSignatureTemplate> SAL_CALL
+ validate(const uno::Reference<xml::crypto::XXMLSignatureTemplate>& aTemplate,
+ const uno::Reference<xml::crypto::XXMLSecurityContext>& aContext) override;
+
+ //Methods from XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+
+ virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+XMLSignature_NssImpl::XMLSignature_NssImpl() {
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_NssImpl::generate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XSecurityEnvironment >& aEnvironment
+)
+{
+ xmlNodePtr pNode = nullptr ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aEnvironment.is() )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() ) {
+ throw RuntimeException() ;
+ }
+
+ Reference< XUnoTunnel > xNodTunnel( xElement , UNO_QUERY_THROW ) ;
+ XMLElementWrapper_XmlSecImpl* pElement
+ = comphelper::getFromUnoTunnel<XMLElementWrapper_XmlSecImpl>(xNodTunnel);
+ if( pElement == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ //Get Keys Manager
+ Reference< XUnoTunnel > xSecTunnel( aEnvironment , UNO_QUERY_THROW ) ;
+
+ // the key manager should be retrieved from SecurityEnvironment, instead of SecurityContext
+ SecurityEnvironment_NssImpl* pSecEnv
+ = comphelper::getFromUnoTunnel<SecurityEnvironment_NssImpl>(xSecTunnel);
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ setErrorRecorder();
+
+ std::unique_ptr<xmlSecKeysMngr> pMngr(pSecEnv->createKeysManager());
+ if( !pMngr ) {
+ throw RuntimeException() ;
+ }
+
+ //Create Signature context
+ std::unique_ptr<xmlSecDSigCtx> pDsigCtx(xmlSecDSigCtxCreate(pMngr.get()));
+ if( pDsigCtx == nullptr )
+ {
+ //throw XMLSignatureException() ;
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ //Sign the template
+ if( xmlSecDSigCtxSign( pDsigCtx.get() , pNode ) == 0 )
+ {
+ if (pDsigCtx->status == xmlSecDSigStatusSucceeded)
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED);
+ else
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN);
+ }
+ else
+ {
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN);
+ }
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+ clearErrorRecorder();
+ return aTemplate ;
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_NssImpl::validate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XXMLSecurityContext >& aSecurityCtx
+) {
+ xmlNodePtr pNode = nullptr ;
+ //sal_Bool valid ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aSecurityCtx.is() )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() )
+ throw RuntimeException() ;
+
+ Reference< XUnoTunnel > xNodTunnel( xElement , UNO_QUERY_THROW ) ;
+ XMLElementWrapper_XmlSecImpl* pElement
+ = comphelper::getFromUnoTunnel<XMLElementWrapper_XmlSecImpl>(xNodTunnel);
+ if( pElement == nullptr )
+ throw RuntimeException() ;
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ setErrorRecorder();
+
+ sal_Int32 nSecurityEnvironment = aSecurityCtx->getSecurityEnvironmentNumber();
+ sal_Int32 i;
+
+ for (i=0; i<nSecurityEnvironment; ++i)
+ {
+ Reference< XSecurityEnvironment > aEnvironment = aSecurityCtx->getSecurityEnvironmentByIndex(i);
+
+ //Get Keys Manager
+ Reference< XUnoTunnel > xSecTunnel( aEnvironment , UNO_QUERY_THROW ) ;
+ SecurityEnvironment_NssImpl* pSecEnv
+ = comphelper::getFromUnoTunnel<SecurityEnvironment_NssImpl>(xSecTunnel);
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ std::unique_ptr<xmlSecKeysMngr> pMngr(pSecEnv->createKeysManager());
+ if( !pMngr ) {
+ throw RuntimeException() ;
+ }
+
+ //Create Signature context
+ std::unique_ptr<xmlSecDSigCtx> pDsigCtx(xmlSecDSigCtxCreate(pMngr.get()));
+ if( pDsigCtx == nullptr )
+ {
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ // We do certificate verification ourselves.
+ pDsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
+
+ // limit possible key data to valid X509 certificates only, no KeyValues
+ if (xmlSecPtrListAdd(&(pDsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecNssKeyDataX509GetKlass()) < 0)
+ throw RuntimeException("failed to limit allowed key data");
+
+ xmlBufferPtr pBuf = xmlBufferCreate();
+ xmlNodeDump(pBuf, nullptr, pNode, 0, 0);
+ SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify input XML node is '"
+ << reinterpret_cast<const char*>(xmlBufferContent(pBuf))
+ << "'");
+ xmlBufferFree(pBuf);
+
+ //Verify signature
+ int rs = xmlSecDSigCtxVerify( pDsigCtx.get() , pNode );
+
+ // Also verify manifest: this is empty for ODF, but contains everything (except signature metadata) for OOXML.
+ xmlSecSize nReferenceCount = xmlSecPtrListGetSize(&pDsigCtx->manifestReferences);
+ // Require that all manifest references are also good.
+ xmlSecSize nReferenceGood = 0;
+ for (xmlSecSize nReference = 0; nReference < nReferenceCount; ++nReference)
+ {
+ xmlSecDSigReferenceCtxPtr pReference = static_cast<xmlSecDSigReferenceCtxPtr>(xmlSecPtrListGetItem(&pDsigCtx->manifestReferences, nReference));
+ if (pReference)
+ {
+ if (pReference->status == xmlSecDSigStatusSucceeded)
+ ++nReferenceGood;
+ }
+ }
+ SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount);
+
+ if (rs == 0 && pDsigCtx->status == xmlSecDSigStatusSucceeded && nReferenceCount == nReferenceGood)
+ {
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED);
+ break;
+ }
+ else
+ {
+ aTemplate->setStatus(css::xml::crypto::SecurityOperationStatus_UNKNOWN);
+ }
+ }
+
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+ //return valid ;
+ clearErrorRecorder();
+ return aTemplate;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSignature_NssImpl::getImplementationName()
+{
+ return "com.sun.star.xml.crypto.XMLSignature";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSignature_NssImpl::supportsService(const OUString& rServiceName)
+{
+ const css::uno::Sequence<OUString> aServiceNames = getSupportedServiceNames();
+ for (OUString const & rCurrentServiceName : aServiceNames)
+ {
+ if (rCurrentServiceName == rServiceName)
+ return true;
+ }
+ return false;
+}
+
+/* XServiceInfo */
+Sequence<OUString> SAL_CALL XMLSignature_NssImpl::getSupportedServiceNames()
+{
+ return { "com.sun.star.xml.crypto.XMLSignature" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_crypto_XMLSignature_get_implementation(uno::XComponentContext* /*pCtx*/,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new XMLSignature_NssImpl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/saxhelper.cxx b/xmlsecurity/source/xmlsec/saxhelper.cxx
new file mode 100644
index 000000000..ff576db49
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/saxhelper.cxx
@@ -0,0 +1,364 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <rtl/ustring.hxx>
+
+#include <xmlsec/saxhelper.hxx>
+#include <libxml/parserInternals.h>
+
+#include <com/sun/star/xml/csax/XMLAttribute.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#ifndef XMLSEC_NO_XSLT
+#include "libxslt/xslt.h"
+#endif
+
+/**
+ * The return value is NULL terminated. The application has the responsibility to
+ * deallocate the return value.
+ */
+static xmlChar* ous_to_xmlstr( std::u16string_view oustr )
+{
+ OString ostr = OUStringToOString( oustr , RTL_TEXTENCODING_UTF8 ) ;
+ return xmlStrndup( reinterpret_cast<xmlChar const *>(ostr.getStr()), static_cast<int>(ostr.getLength()) ) ;
+}
+
+/**
+ * The return value is NULL terminated. The application has the responsibility to
+ * deallocate the return value.
+ */
+static xmlChar* ous_to_nxmlstr( std::u16string_view oustr, int& length )
+{
+ OString ostr = OUStringToOString( oustr , RTL_TEXTENCODING_UTF8 ) ;
+ length = ostr.getLength();
+
+ return xmlStrndup( reinterpret_cast<xmlChar const *>(ostr.getStr()), length ) ;
+}
+
+/**
+ * The return value and the referenced value must be NULL terminated.
+ * The application has the responsibility to deallocate the return value.
+ */
+static const xmlChar** attrlist_to_nxmlstr( const css::uno::Sequence< css::xml::csax::XMLAttribute >& aAttributes )
+{
+ xmlChar* attname = nullptr ;
+ xmlChar* attvalue = nullptr ;
+ const xmlChar** attrs = nullptr ;
+
+ sal_Int32 nLength = aAttributes.getLength();
+
+ if( nLength != 0 )
+ {
+ attrs = static_cast<const xmlChar**>(xmlMalloc( ( nLength * 2 + 2 ) * sizeof( xmlChar* ) ));
+ }
+ else
+ {
+ return nullptr ;
+ }
+
+ int i = 0;
+ for( const auto& rAttr : aAttributes )
+ {
+ attname = ous_to_xmlstr( rAttr.sName ) ;
+ attvalue = ous_to_xmlstr( rAttr.sValue ) ;
+
+ if( attname != nullptr && attvalue != nullptr )
+ {
+ attrs[i++] = attname ;
+ attrs[i++] = attvalue ;
+ attrs[i] = nullptr ;
+ attrs[i+1] = nullptr ;
+ }
+ else
+ {
+ if( attname != nullptr )
+ xmlFree( attname ) ;
+ if( attvalue != nullptr )
+ xmlFree( attvalue ) ;
+ }
+ }
+
+ return attrs ;
+}
+
+/**
+ * Constructor
+ *
+ * In this constructor, a libxml sax parser context is initialized. a libxml
+ * default sax handler is initialized with the context.
+ */
+SAXHelper::SAXHelper( )
+ : m_pParserCtxt( nullptr ),
+ m_pSaxHandler( nullptr )
+{
+ xmlInitParser() ;
+ LIBXML_TEST_VERSION ;
+
+ /*
+ * compile error:
+ * xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS ;
+ */
+ xmlSubstituteEntitiesDefault(0) ;
+
+#ifndef XMLSEC_NO_XSLT
+ xmlIndentTreeOutput = 1 ;
+#endif /* XMLSEC_NO_XSLT */
+
+ m_pParserCtxt = xmlNewParserCtxt() ;
+
+ if( m_pParserCtxt == nullptr )
+ {
+// see issue i74334, we cannot call xmlCleanupParser when libxml is still used
+// in other parts of the office.
+// xmlCleanupParser() ;
+// and neither can we call xsltCleanupGlobals()
+ throw css::uno::RuntimeException() ;
+ }
+
+ xmlSAXVersion(m_pParserCtxt->sax, 1);
+
+ if (m_pParserCtxt->inputTab != nullptr)
+ {
+ m_pParserCtxt->inputTab[0] = nullptr ;
+ }
+
+ if( m_pParserCtxt->sax == nullptr )
+ {
+ xmlFreeParserCtxt( m_pParserCtxt ) ;
+
+// see issue i74334, we cannot call xmlCleanupParser when libxml is still used
+// in other parts of the office.
+// xmlCleanupParser() ;
+// and neither can we call xsltCleanupGlobals()
+ m_pParserCtxt = nullptr ;
+ throw css::uno::RuntimeException() ;
+ }
+ else
+ {
+ m_pSaxHandler = m_pParserCtxt->sax ;
+
+ //Adjust the context
+ m_pParserCtxt->recovery = 1 ;
+ }
+}
+
+/**
+ * Destructor
+ *
+ * In this destructor, a libxml sax parser context is destructed. The XML tree
+ * in the context is not deallocated because the tree is bind with a document
+ * model by the setTargetDocument method, which delegate the target document to
+ * destruct the xml tree.
+ */
+SAXHelper::~SAXHelper() {
+ if( m_pParserCtxt != nullptr )
+ {
+ /*
+ * In the situation that no object refer the Document, this destructor
+ * must deallocate the Document memory
+ */
+ if( m_pSaxHandler == m_pParserCtxt->sax )
+ {
+ m_pSaxHandler = nullptr ;
+ }
+
+ xmlFreeParserCtxt( m_pParserCtxt ) ;
+ m_pParserCtxt = nullptr ;
+ }
+
+ if( m_pSaxHandler != nullptr )
+ {
+ xmlFree( m_pSaxHandler ) ;
+ m_pSaxHandler = nullptr ;
+ }
+// see issue i74334, we cannot call xmlCleanupParser when libxml is still used
+// in other parts of the office.
+// xmlCleanupParser() ;
+}
+
+
+void SAXHelper::setCurrentNode(const xmlNodePtr pNode)
+{
+ /*
+ * This is really a black trick.
+ * When the current node is replaced, the nodeTab
+ * stack's top has to been replaced with the same
+ * node, in order to make compatibility.
+ */
+ m_pParserCtxt->nodeTab[m_pParserCtxt->nodeNr - 1]
+ = m_pParserCtxt->node
+ = pNode;
+}
+
+
+/**
+ * XDocumentHandler -- start an xml document
+ */
+void SAXHelper::startDocument()
+{
+ if( m_pParserCtxt == nullptr)
+ {
+ throw css::uno::RuntimeException() ;
+ }
+ /*
+ * Adjust inputTab
+ */
+ xmlParserInputPtr pInput = xmlNewInputStream( m_pParserCtxt ) ;
+
+ if( m_pParserCtxt->inputTab != nullptr && m_pParserCtxt->inputMax != 0 )
+ {
+ m_pParserCtxt->inputTab[0] = pInput ;
+ m_pParserCtxt->input = pInput ;
+ }
+
+ m_pSaxHandler->startDocument( m_pParserCtxt ) ;
+
+ if( m_pParserCtxt->myDoc == nullptr )
+ {
+ throw css::uno::RuntimeException() ;
+ }
+}
+
+/**
+ * XDocumentHandler -- end an xml document
+ */
+void SAXHelper::endDocument()
+{
+ m_pSaxHandler->endDocument( m_pParserCtxt ) ;
+}
+
+/**
+ * XDocumentHandler -- start an xml element
+ */
+void SAXHelper::startElement(
+ std::u16string_view aName,
+ const css::uno::Sequence< css::xml::csax::XMLAttribute >& aAttributes )
+{
+ const xmlChar* fullName = nullptr ;
+ const xmlChar** attrs = nullptr ;
+
+ fullName = ous_to_xmlstr( aName ) ;
+ attrs = attrlist_to_nxmlstr( aAttributes ) ;
+
+ if( fullName != nullptr || attrs != nullptr )
+ {
+ m_pSaxHandler->startElement( m_pParserCtxt , fullName , attrs ) ;
+ }
+
+ if( fullName != nullptr )
+ {
+ xmlFree( const_cast<xmlChar*>(fullName) ) ;
+ fullName = nullptr ;
+ }
+
+ if( attrs != nullptr )
+ {
+ for( int i = 0 ; attrs[i] != nullptr ; ++i )
+ {
+ xmlFree( const_cast<xmlChar*>(attrs[i]) ) ;
+ attrs[i] = nullptr ;
+ }
+
+ xmlFree( static_cast<void*>(attrs) ) ;
+ attrs = nullptr ;
+ }
+}
+
+/**
+ * XDocumentHandler -- end an xml element
+ */
+void SAXHelper::endElement( std::u16string_view aName )
+{
+ xmlChar* fullname = ous_to_xmlstr( aName ) ;
+ m_pSaxHandler->endElement( m_pParserCtxt , fullname ) ;
+
+ if( fullname != nullptr )
+ {
+ xmlFree( fullname ) ;
+ fullname = nullptr ;
+ }
+}
+
+/**
+ * XDocumentHandler -- an xml element or cdata characters
+ */
+void SAXHelper::characters( std::u16string_view aChars )
+{
+ const xmlChar* chars = nullptr ;
+ int length = 0 ;
+
+ chars = ous_to_nxmlstr( aChars, length ) ;
+ m_pSaxHandler->characters( m_pParserCtxt , chars , length ) ;
+
+ if( chars != nullptr )
+ {
+ xmlFree( const_cast<xmlChar*>(chars) ) ;
+ }
+}
+
+/**
+ * XDocumentHandler -- ignorable xml white space
+ */
+void SAXHelper::ignorableWhitespace( std::u16string_view aWhitespaces )
+{
+ const xmlChar* chars = nullptr ;
+ int length = 0 ;
+
+ chars = ous_to_nxmlstr( aWhitespaces, length ) ;
+ m_pSaxHandler->ignorableWhitespace( m_pParserCtxt , chars , length ) ;
+
+ if( chars != nullptr )
+ {
+ xmlFree( const_cast<xmlChar*>(chars) ) ;
+ }
+}
+
+/**
+ * XDocumentHandler -- preprocessing instruction
+ */
+void SAXHelper::processingInstruction(
+ std::u16string_view aTarget,
+ std::u16string_view aData )
+{
+ xmlChar* target = nullptr ;
+ xmlChar* data = nullptr ;
+
+ target = ous_to_xmlstr( aTarget ) ;
+ data = ous_to_xmlstr( aData ) ;
+
+ m_pSaxHandler->processingInstruction( m_pParserCtxt , target , data ) ;
+
+ if( target != nullptr )
+ {
+ xmlFree( target ) ;
+ target = nullptr ;
+ }
+
+ if( data != nullptr )
+ {
+ xmlFree( data ) ;
+ data = nullptr ;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/xmldocumentwrapper_xmlsecimpl.cxx b/xmlsecurity/source/xmlsec/xmldocumentwrapper_xmlsecimpl.cxx
new file mode 100644
index 000000000..1694e30c8
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/xmldocumentwrapper_xmlsecimpl.cxx
@@ -0,0 +1,902 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/diagnose.h>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/crypto/sax/XSAXEventKeeper.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <xmlsec/xmldocumentwrapper_xmlsecimpl.hxx>
+#include "xmlelementwrapper_xmlsecimpl.hxx"
+
+#include <xmloff/attrlist.hxx>
+#include <rtl/ref.hxx>
+
+#ifdef UNX
+#define stricmp strcasecmp
+#endif
+
+using namespace com::sun::star;
+
+#define STRXMLNS "xmlns"
+
+/* used by the recursiveDelete method */
+#define NODE_REMOVED 0
+#define NODE_NOTREMOVED 1
+#define NODE_STOPPED 2
+
+XMLDocumentWrapper_XmlSecImpl::XMLDocumentWrapper_XmlSecImpl()
+ : m_nCurrentPosition(0)
+ , m_pStopAtNode(nullptr)
+ , m_pCurrentReservedNode(nullptr)
+ , m_nReservedNodeIndex(0)
+{
+ saxHelper.startDocument();
+ m_pDocument = saxHelper.getDocument();
+
+ /*
+ * creates the virtual root element
+ */
+ saxHelper.startElement(u"root", uno::Sequence<css::xml::csax::XMLAttribute>());
+
+ m_pRootElement = saxHelper.getCurrentNode();
+ m_pCurrentElement = m_pRootElement;
+}
+
+XMLDocumentWrapper_XmlSecImpl::~XMLDocumentWrapper_XmlSecImpl()
+{
+ saxHelper.endDocument();
+ xmlFreeDoc(m_pDocument);
+}
+
+void XMLDocumentWrapper_XmlSecImpl::getNextSAXEvent()
+/****** XMLDocumentWrapper_XmlSecImpl/getNextSAXEvent *************************
+ *
+ * NAME
+ * getNextSAXEvent -- Prepares the next SAX event to be manipulate
+ *
+ * FUNCTION
+ * When converting the document into SAX events, this method is used to
+ * decide the next SAX event to be generated.
+ * Two member variables are checked to make the decision, the
+ * m_pCurrentElement and the m_nCurrentPosition.
+ * The m_pCurrentElement represents the node which have been covered, and
+ * the m_nCurrentPosition represents the event which have been sent.
+ * For example, suppose that the m_pCurrentElement
+ * points to element A, and the m_nCurrentPosition equals to
+ * NODEPOSITION_STARTELEMENT, then the next SAX event should be the
+ * endElement for element A if A has no child, or startElement for the
+ * first child element of element A otherwise.
+ * The m_nCurrentPosition can be one of following values:
+ * NODEPOSITION_STARTELEMENT for startElement;
+ * NODEPOSITION_ENDELEMENT for endElement;
+ * NODEPOSITION_NORMAL for other SAX events;
+ ******************************************************************************/
+{
+ OSL_ASSERT( m_pCurrentElement != nullptr );
+
+ /*
+ * Get the next event through tree order.
+ *
+ * if the current event is a startElement, then the next
+ * event depends on whether or not the current node has
+ * children.
+ */
+ if (m_nCurrentPosition == NODEPOSITION_STARTELEMENT)
+ {
+ /*
+ * If the current node has children, then its first child
+ * should be next current node, and the next event will be
+ * startElement or characters(PI) based on that child's node
+ * type. Otherwise, the endElement of current node is the
+ * next event.
+ */
+ if (m_pCurrentElement->children != nullptr)
+ {
+ m_pCurrentElement = m_pCurrentElement->children;
+ m_nCurrentPosition
+ = (m_pCurrentElement->type == XML_ELEMENT_NODE)?
+ NODEPOSITION_STARTELEMENT:NODEPOSITION_NORMAL;
+ }
+ else
+ {
+ m_nCurrentPosition = NODEPOSITION_ENDELEMENT;
+ }
+ }
+ /*
+ * if the current event is a not startElement, then the next
+ * event depends on whether or not the current node has
+ * following sibling.
+ */
+ else if (m_nCurrentPosition == NODEPOSITION_ENDELEMENT || m_nCurrentPosition == NODEPOSITION_NORMAL)
+ {
+ xmlNodePtr pNextSibling = m_pCurrentElement->next;
+
+ /*
+ * If the current node has following sibling, that sibling
+ * should be next current node, and the next event will be
+ * startElement or characters(PI) based on that sibling's node
+ * type. Otherwise, the endElement of current node's parent
+ * becomes the next event.
+ */
+ if (pNextSibling != nullptr)
+ {
+ m_pCurrentElement = pNextSibling;
+ m_nCurrentPosition
+ = (m_pCurrentElement->type == XML_ELEMENT_NODE)?
+ NODEPOSITION_STARTELEMENT:NODEPOSITION_NORMAL;
+ }
+ else
+ {
+ m_pCurrentElement = m_pCurrentElement->parent;
+ m_nCurrentPosition = NODEPOSITION_ENDELEMENT;
+ }
+ }
+}
+
+void XMLDocumentWrapper_XmlSecImpl::sendStartElement(
+ const uno::Reference< css::xml::sax::XDocumentHandler >& xHandler,
+ const uno::Reference< css::xml::sax::XDocumentHandler >& xHandler2,
+ const xmlNodePtr pNode)
+/****** XMLDocumentWrapper_XmlSecImpl/sendStartElement ************************
+ *
+ * NAME
+ * sendStartElement -- Constructs a startElement SAX event
+ *
+ * FUNCTION
+ * Used when converting the document into SAX event stream.
+ * This method constructs a startElement SAX event for a particular
+ * element, then calls the startElement methods of the XDocumentHandlers.
+ *
+ * INPUTS
+ * xHandler - the first XDocumentHandler interface to receive the
+ * startElement SAX event. It can be NULL.
+ * xHandler2 - the second XDocumentHandler interface to receive the
+ * startElement SAX event. It can't be NULL.
+ * pNode - the node on which the startElement should be generated.
+ * This node must be an element type.
+ ******************************************************************************/
+{
+ rtl::Reference<SvXMLAttributeList> pAttributeList = new SvXMLAttributeList();
+
+ xmlNsPtr pNsDef = pNode->nsDef;
+
+ while (pNsDef != nullptr)
+ {
+ const xmlChar* pNsPrefix = pNsDef->prefix;
+ const xmlChar* pNsHref = pNsDef->href;
+
+ if (pNsDef->prefix == nullptr)
+ {
+ pAttributeList->AddAttribute(
+ STRXMLNS,
+ OUString::fromUtf8(reinterpret_cast<char const *>(pNsHref)));
+ }
+ else
+ {
+ pAttributeList->AddAttribute(
+ STRXMLNS ":"
+ +OUString::fromUtf8(reinterpret_cast<char const *>(pNsPrefix)),
+ OUString::fromUtf8(reinterpret_cast<char const *>(pNsHref)));
+ }
+
+ pNsDef = pNsDef->next;
+ }
+
+ xmlAttrPtr pAttr = pNode->properties;
+
+ while (pAttr != nullptr)
+ {
+ const xmlChar* pAttrName = pAttr->name;
+ xmlNsPtr pAttrNs = pAttr->ns;
+
+ OUString ouAttrName;
+ if (pAttrNs == nullptr)
+ {
+ ouAttrName = OUString::fromUtf8(reinterpret_cast<char const *>(pAttrName));
+ }
+ else
+ {
+ ouAttrName = OUString::fromUtf8(reinterpret_cast<char const *>(pAttrNs->prefix))
+ + ":" + OUString::fromUtf8(reinterpret_cast<char const *>(pAttrName));
+ }
+
+ pAttributeList->AddAttribute(
+ ouAttrName,
+ OUString::fromUtf8(reinterpret_cast<char*>(pAttr->children->content)));
+ pAttr = pAttr->next;
+ }
+
+ OString sNodeName = getNodeQName(pNode);
+
+ if (xHandler.is())
+ {
+ xHandler->startElement(
+ OUString::fromUtf8(sNodeName),
+ pAttributeList);
+ }
+
+ xHandler2->startElement(
+ OUString::fromUtf8(sNodeName),
+ pAttributeList);
+}
+
+void XMLDocumentWrapper_XmlSecImpl::sendEndElement(
+ const uno::Reference< css::xml::sax::XDocumentHandler >& xHandler,
+ const uno::Reference< css::xml::sax::XDocumentHandler >& xHandler2,
+ const xmlNodePtr pNode)
+/****** XMLDocumentWrapper_XmlSecImpl/sendEndElement **************************
+ *
+ * NAME
+ * sendEndElement -- Constructs an endElement SAX event
+ *
+ * FUNCTION
+ * Used when converting the document into SAX event stream.
+ * This method constructs an endElement SAX event for a particular
+ * element, then calls the endElement methods of the XDocumentHandlers.
+ *
+ * INPUTS
+ * xHandler - the first XDocumentHandler interface to receive the
+ * endElement SAX event. It can be NULL.
+ * xHandler2 - the second XDocumentHandler interface to receive the
+ * endElement SAX event. It can't be NULL.
+ * pNode - the node on which the endElement should be generated.
+ * This node must be an element type.
+ ******************************************************************************/
+{
+ OString sNodeName = getNodeQName(pNode);
+
+ if (xHandler.is())
+ {
+ xHandler->endElement(OUString::fromUtf8(sNodeName));
+ }
+
+ xHandler2->endElement(OUString::fromUtf8(sNodeName));
+}
+
+void XMLDocumentWrapper_XmlSecImpl::sendNode(
+ const uno::Reference< css::xml::sax::XDocumentHandler >& xHandler,
+ const uno::Reference< css::xml::sax::XDocumentHandler >& xHandler2,
+ const xmlNodePtr pNode)
+/****** XMLDocumentWrapper_XmlSecImpl/sendNode ********************************
+ *
+ * NAME
+ * sendNode -- Constructs a characters SAX event or a
+ * processingInstruction SAX event
+ *
+ * FUNCTION
+ * Used when converting the document into SAX event stream.
+ * This method constructs a characters SAX event or a
+ * processingInstructionfor SAX event based on the type of a particular
+ * element, then calls the corresponding methods of the XDocumentHandlers.
+ *
+ * INPUTS
+ * xHandler - the first XDocumentHandler interface to receive the
+ * SAX event. It can be NULL.
+ * xHandler2 - the second XDocumentHandler interface to receive the
+ * SAX event. It can't be NULL.
+ * pNode - the node on which the endElement should be generated.
+ * If it is a text node, then a characters SAX event is
+ * generated; if it is a PI node, then a
+ * processingInstructionfor SAX event is generated.
+ ******************************************************************************/
+{
+ xmlElementType type = pNode->type;
+
+ if (type == XML_TEXT_NODE)
+ {
+ if (xHandler.is())
+ {
+ xHandler->characters(OUString::fromUtf8(reinterpret_cast<char*>(pNode->content)));
+ }
+
+ xHandler2->characters(OUString::fromUtf8(reinterpret_cast<char*>(pNode->content)));
+ }
+ else if (type == XML_PI_NODE)
+ {
+ if (xHandler.is())
+ {
+ xHandler->processingInstruction(
+ OUString::fromUtf8(reinterpret_cast<char const *>(pNode->name)),
+ OUString::fromUtf8(reinterpret_cast<char const *>(pNode->content)));
+ }
+
+ xHandler2->processingInstruction(
+ OUString::fromUtf8(reinterpret_cast<char const *>(pNode->name)),
+ OUString::fromUtf8(reinterpret_cast<char*>(pNode->content)));
+ }
+}
+
+OString XMLDocumentWrapper_XmlSecImpl::getNodeQName(const xmlNodePtr pNode)
+/****** XMLDocumentWrapper_XmlSecImpl/getNodeQName ****************************
+ *
+ * NAME
+ * getNodeQName -- Retrieves the qualified name of a node
+ *
+ * INPUTS
+ * pNode - the node whose name will be retrieved
+ *
+ * RESULT
+ * name - the node's qualified name
+ ******************************************************************************/
+{
+ OString sNodeName(reinterpret_cast<const char*>(pNode->name));
+ if (pNode->ns != nullptr)
+ {
+ xmlNsPtr pNs = pNode->ns;
+
+ if (pNs->prefix != nullptr)
+ {
+ OString sPrefix(reinterpret_cast<const char*>(pNs->prefix));
+ sNodeName = sPrefix + ":" + sNodeName;
+ }
+ }
+
+ return sNodeName;
+}
+
+xmlNodePtr XMLDocumentWrapper_XmlSecImpl::checkElement( const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& xXMLElement)
+/****** XMLDocumentWrapper_XmlSecImpl/checkElement ****************************
+ *
+ * NAME
+ * checkElement -- Retrieves the node wrapped by an XXMLElementWrapper
+ * interface
+ *
+ * INPUTS
+ * xXMLElement - the XXMLElementWrapper interface wrapping a node
+ *
+ * RESULT
+ * node - the node wrapped in the XXMLElementWrapper interface
+ ******************************************************************************/
+{
+ xmlNodePtr rc = nullptr;
+
+ if (xXMLElement.is())
+ {
+ uno::Reference< css::lang::XUnoTunnel > xNodTunnel( xXMLElement, uno::UNO_QUERY_THROW ) ;
+ XMLElementWrapper_XmlSecImpl* pElement
+ = comphelper::getFromUnoTunnel<XMLElementWrapper_XmlSecImpl>(xNodTunnel);
+
+ if( pElement == nullptr ) {
+ throw uno::RuntimeException() ;
+ }
+
+ rc = pElement->getNativeElement();
+ }
+
+ return rc;
+}
+
+sal_Int32 XMLDocumentWrapper_XmlSecImpl::recursiveDelete(
+ const xmlNodePtr pNode)
+/****** XMLDocumentWrapper_XmlSecImpl/recursiveDelete *************************
+ *
+ * NAME
+ * recursiveDelete -- Deletes a particular node with its branch.
+ *
+ * FUNCTION
+ * Deletes a particular node with its branch, while reserving the nodes
+ * (and their branches) listed in the m_aReservedNodes.
+ * The deletion process is performed in the tree order, that is, a node
+ * is deleted after its previous sibling node is deleted, a parent node
+ * is deleted after its branch is deleted.
+ * During the deletion process when the m_pStopAtNode is reached, the
+ * progress is interrupted at once.
+ *
+ * INPUTS
+ * pNode - the node to be deleted
+ *
+ * RESULT
+ * result - the result of the deletion process, can be one of following
+ * values:
+ * NODE_STOPPED - the process is interrupted by meeting the
+ * m_pStopAtNode
+ * NODE_NOTREMOVED - the pNode is not completely removed
+ * because there is its descendant in the
+ * m_aReservedNodes list
+ * NODE_REMOVED - the pNode and its branch are completely
+ * removed
+ *
+ * NOTES
+ * The node in the m_aReservedNodes list must be in the tree order, otherwise
+ * the result is unpredictable.
+ ******************************************************************************/
+{
+ if (pNode == m_pStopAtNode)
+ {
+ return NODE_STOPPED;
+ }
+
+ if (pNode != m_pCurrentReservedNode)
+ {
+ xmlNodePtr pChild = pNode->children;
+
+ xmlNodePtr pNextSibling;
+ bool bIsRemoved = true;
+ sal_Int32 nResult;
+
+ while( pChild != nullptr )
+ {
+ pNextSibling = pChild->next;
+ nResult = recursiveDelete(pChild);
+
+ switch (nResult)
+ {
+ case NODE_STOPPED:
+ return NODE_STOPPED;
+ case NODE_NOTREMOVED:
+ bIsRemoved = false;
+ break;
+ case NODE_REMOVED:
+ removeNode(pChild);
+ break;
+ default:
+ throw uno::RuntimeException();
+ }
+
+ pChild = pNextSibling;
+ }
+
+ if (pNode == m_pCurrentElement)
+ {
+ bIsRemoved = false;
+ }
+
+ return bIsRemoved?NODE_REMOVED:NODE_NOTREMOVED;
+ }
+ else
+ {
+ getNextReservedNode();
+ return NODE_NOTREMOVED;
+ }
+}
+
+void XMLDocumentWrapper_XmlSecImpl::getNextReservedNode()
+/****** XMLDocumentWrapper_XmlSecImpl/getNextReservedNode *********************
+ *
+ * NAME
+ * getNextReservedNode -- Highlights the next reserved node in the
+ * reserved node list
+ *
+ * FUNCTION
+ * The m_aReservedNodes array holds a node list, while the
+ * m_pCurrentReservedNode points to the one currently highlighted.
+ * This method is used to highlight the next node in the node list.
+ * This method is called at the time when the current highlighted node
+ * has been already processed, and the next node should be ready.
+ ******************************************************************************/
+{
+ if (m_nReservedNodeIndex < m_aReservedNodes.getLength())
+ {
+ m_pCurrentReservedNode = checkElement( m_aReservedNodes[m_nReservedNodeIndex] );
+ m_nReservedNodeIndex ++;
+ }
+ else
+ {
+ m_pCurrentReservedNode = nullptr;
+ }
+}
+
+void XMLDocumentWrapper_XmlSecImpl::removeNode(const xmlNodePtr pNode) const
+/****** XMLDocumentWrapper_XmlSecImpl/removeNode ******************************
+ *
+ * NAME
+ * removeNode -- Deletes a node with its branch unconditionally
+ *
+ * FUNCTION
+ * Delete the node along with its branch from the document.
+ *
+ * INPUTS
+ * pNode - the node to be deleted
+ ******************************************************************************/
+{
+ /* you can't remove the current node */
+ OSL_ASSERT( m_pCurrentElement != pNode );
+
+ xmlAttrPtr pAttr = pNode->properties;
+
+ while (pAttr != nullptr)
+ {
+ if (!stricmp(reinterpret_cast<char const *>(pAttr->name), "id"))
+ {
+ xmlRemoveID(m_pDocument, pAttr);
+ }
+
+ pAttr = pAttr->next;
+ }
+
+ xmlUnlinkNode(pNode);
+ xmlFreeNode(pNode);
+}
+
+void XMLDocumentWrapper_XmlSecImpl::buildIDAttr(xmlNodePtr pNode) const
+/****** XMLDocumentWrapper_XmlSecImpl/buildIDAttr *****************************
+ *
+ * NAME
+ * buildIDAttr -- build the ID attribute of a node
+ *
+ * INPUTS
+ * pNode - the node whose id attribute will be built
+ ******************************************************************************/
+{
+ xmlAttrPtr idAttr = xmlHasProp( pNode, reinterpret_cast<const unsigned char *>("id") );
+ if (idAttr == nullptr)
+ {
+ idAttr = xmlHasProp( pNode, reinterpret_cast<const unsigned char *>("Id") );
+ }
+
+ if (idAttr != nullptr)
+ {
+ xmlChar* idValue = xmlNodeListGetString( m_pDocument, idAttr->children, 1 ) ;
+ xmlAddID( nullptr, m_pDocument, idValue, idAttr );
+ }
+}
+
+void XMLDocumentWrapper_XmlSecImpl::rebuildIDLink(xmlNodePtr pNode) const
+/****** XMLDocumentWrapper_XmlSecImpl/rebuildIDLink ***************************
+ *
+ * NAME
+ * rebuildIDLink -- rebuild the ID link for the branch
+ *
+ * INPUTS
+ * pNode - the node, from which the branch will be rebuilt
+ ******************************************************************************/
+{
+ if (pNode != nullptr && pNode->type == XML_ELEMENT_NODE)
+ {
+ buildIDAttr( pNode );
+
+ xmlNodePtr child = pNode->children;
+ while (child != nullptr)
+ {
+ rebuildIDLink(child);
+ child = child->next;
+ }
+ }
+}
+
+/* XXMLDocumentWrapper */
+uno::Reference< css::xml::wrapper::XXMLElementWrapper > SAL_CALL XMLDocumentWrapper_XmlSecImpl::getCurrentElement( )
+{
+ return new XMLElementWrapper_XmlSecImpl(m_pCurrentElement);
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::setCurrentElement( const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& element )
+{
+ m_pCurrentElement = checkElement( element );
+ saxHelper.setCurrentNode( m_pCurrentElement );
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::removeCurrentElement( )
+{
+ OSL_ASSERT( m_pCurrentElement != nullptr );
+
+ xmlNodePtr pOldCurrentElement = m_pCurrentElement;
+
+ /*
+ * pop the top node in the parser context's
+ * nodeTab stack, then the parent of that node will
+ * automatically become the new stack top, and
+ * the current node as well.
+ */
+ saxHelper.endElement(OUString::fromUtf8(reinterpret_cast<char const *>(pOldCurrentElement->name)));
+ m_pCurrentElement = saxHelper.getCurrentNode();
+
+ /*
+ * remove the node
+ */
+ removeNode(pOldCurrentElement);
+}
+
+sal_Bool SAL_CALL XMLDocumentWrapper_XmlSecImpl::isCurrent( const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& node )
+{
+ xmlNodePtr pNode = checkElement(node);
+ return (pNode == m_pCurrentElement);
+}
+
+sal_Bool SAL_CALL XMLDocumentWrapper_XmlSecImpl::isCurrentElementEmpty( )
+{
+ bool rc = false;
+
+ if (m_pCurrentElement->children == nullptr)
+ {
+ rc = true;
+ }
+
+ return rc;
+}
+
+OUString SAL_CALL XMLDocumentWrapper_XmlSecImpl::getNodeName( const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& node )
+{
+ xmlNodePtr pNode = checkElement(node);
+ return OUString::fromUtf8(reinterpret_cast<char const *>(pNode->name));
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::clearUselessData(
+ const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& node,
+ const uno::Sequence< uno::Reference< css::xml::wrapper::XXMLElementWrapper > >& reservedDescendants,
+ const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& stopAtNode )
+{
+ xmlNodePtr pTargetNode = checkElement(node);
+
+ m_pStopAtNode = checkElement(stopAtNode);
+ m_aReservedNodes = reservedDescendants;
+ m_nReservedNodeIndex = 0;
+
+ getNextReservedNode();
+
+ recursiveDelete(pTargetNode);
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::collapse( const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& node )
+{
+ xmlNodePtr pTargetNode = checkElement(node);
+ xmlNodePtr pParent;
+
+ while (pTargetNode != nullptr)
+ {
+ if (pTargetNode->children != nullptr || pTargetNode == m_pCurrentElement)
+ {
+ break;
+ }
+
+ pParent = pTargetNode->parent;
+ removeNode(pTargetNode);
+ pTargetNode = pParent;
+ }
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::getTree( const uno::Reference< css::xml::sax::XDocumentHandler >& handler )
+{
+ if (m_pRootElement == nullptr)
+ return;
+
+ xmlNodePtr pTempCurrentElement = m_pCurrentElement;
+ sal_Int32 nTempCurrentPosition = m_nCurrentPosition;
+
+ m_pCurrentElement = m_pRootElement;
+
+ m_nCurrentPosition = NODEPOSITION_STARTELEMENT;
+
+ while(true)
+ {
+ switch (m_nCurrentPosition)
+ {
+ case NODEPOSITION_STARTELEMENT:
+ sendStartElement(nullptr, handler, m_pCurrentElement);
+ break;
+ case NODEPOSITION_ENDELEMENT:
+ sendEndElement(nullptr, handler, m_pCurrentElement);
+ break;
+ case NODEPOSITION_NORMAL:
+ sendNode(nullptr, handler, m_pCurrentElement);
+ break;
+ }
+
+ if ( (m_pCurrentElement == m_pRootElement) && (m_nCurrentPosition == NODEPOSITION_ENDELEMENT ))
+ {
+ break;
+ }
+
+ getNextSAXEvent();
+ }
+
+ m_pCurrentElement = pTempCurrentElement;
+ m_nCurrentPosition = nTempCurrentPosition;
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::generateSAXEvents(
+ const uno::Reference< css::xml::sax::XDocumentHandler >& handler,
+ const uno::Reference< css::xml::sax::XDocumentHandler >& xEventKeeperHandler,
+ const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& startNode,
+ const uno::Reference< css::xml::wrapper::XXMLElementWrapper >& endNode )
+{
+ /*
+ * The first SAX event is the startElement of the startNode
+ * element.
+ */
+ bool bHasCurrentElementChild = (m_pCurrentElement->children != nullptr);
+
+ xmlNodePtr pTempCurrentElement = m_pCurrentElement;
+
+ m_pCurrentElement = checkElement(startNode);
+
+ if (m_pCurrentElement->type == XML_ELEMENT_NODE)
+ {
+ m_nCurrentPosition = NODEPOSITION_STARTELEMENT;
+ }
+ else
+ {
+ m_nCurrentPosition = NODEPOSITION_NORMAL;
+ }
+
+ xmlNodePtr pEndNode = checkElement(endNode);
+
+ uno::Reference < css::xml::crypto::sax::XSAXEventKeeper > xSAXEventKeeper( xEventKeeperHandler, uno::UNO_QUERY );
+
+ uno::Reference< css::xml::sax::XDocumentHandler > xHandler = handler;
+
+ while(true)
+ {
+ switch (m_nCurrentPosition)
+ {
+ case NODEPOSITION_STARTELEMENT:
+ sendStartElement(xHandler, xEventKeeperHandler, m_pCurrentElement);
+ break;
+ case NODEPOSITION_ENDELEMENT:
+ sendEndElement(xHandler, xEventKeeperHandler, m_pCurrentElement);
+ break;
+ case NODEPOSITION_NORMAL:
+ sendNode(xHandler, xEventKeeperHandler, m_pCurrentElement);
+ break;
+ default:
+ throw uno::RuntimeException();
+ }
+
+ if (xSAXEventKeeper->isBlocking())
+ {
+ xHandler = nullptr;
+ }
+
+ if (pEndNode == nullptr &&
+ ((bHasCurrentElementChild && m_pCurrentElement == xmlGetLastChild(pTempCurrentElement) && m_nCurrentPosition != NODEPOSITION_STARTELEMENT) ||
+ (!bHasCurrentElementChild && m_pCurrentElement == pTempCurrentElement && m_nCurrentPosition == NODEPOSITION_STARTELEMENT)))
+ {
+ break;
+ }
+
+ getNextSAXEvent();
+
+ /*
+ * If there is an end point specified, then check whether
+ * the current node equals to the end point. If so, stop
+ * generating.
+ */
+ if (pEndNode != nullptr && m_pCurrentElement == pEndNode)
+ {
+ break;
+ }
+ }
+
+ m_pCurrentElement = pTempCurrentElement;
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::rebuildIDLink(
+ const css::uno::Reference< css::xml::wrapper::XXMLElementWrapper >& node )
+{
+ xmlNodePtr pNode = checkElement( node );
+ rebuildIDLink(pNode);
+}
+
+
+/* css::xml::sax::XDocumentHandler */
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::startDocument( )
+{
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::endDocument( )
+{
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::startElement( const OUString& aName, const uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ sal_Int32 nLength = xAttribs->getLength();
+ uno::Sequence< css::xml::csax::XMLAttribute > aAttributes (nLength);
+ auto aAttributesRange = asNonConstRange(aAttributes);
+
+ for (int i = 0; i < nLength; ++i)
+ {
+ aAttributesRange[i].sName = xAttribs->getNameByIndex(static_cast<short>(i));
+ aAttributesRange[i].sValue =xAttribs->getValueByIndex(static_cast<short>(i));
+ }
+
+ compressedStartElement(aName, aAttributes);
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::endElement( const OUString& aName )
+{
+ saxHelper.endElement(aName);
+ m_pCurrentElement = saxHelper.getCurrentNode();
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::characters( const OUString& aChars )
+{
+ saxHelper.characters(aChars);
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::ignorableWhitespace( const OUString& aWhitespaces )
+{
+ saxHelper.ignorableWhitespace(aWhitespaces);
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::processingInstruction( const OUString& aTarget, const OUString& aData )
+{
+ saxHelper.processingInstruction(aTarget, aData);
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::setDocumentLocator( const uno::Reference< css::xml::sax::XLocator >& )
+{
+}
+
+/* XCompressedDocumentHandler */
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedStartDocument( )
+{
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedEndDocument( )
+{
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedStartElement( const OUString& aName, const uno::Sequence< css::xml::csax::XMLAttribute >& aAttributes )
+{
+ saxHelper.startElement(aName, aAttributes);
+ m_pCurrentElement = saxHelper.getCurrentNode();
+
+ buildIDAttr( m_pCurrentElement );
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedEndElement( const OUString& aName )
+{
+ endElement( aName );
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedCharacters( const OUString& aChars )
+{
+ characters( aChars );
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedIgnorableWhitespace( const OUString& aWhitespaces )
+{
+ ignorableWhitespace( aWhitespaces );
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedProcessingInstruction( const OUString& aTarget, const OUString& aData )
+{
+ processingInstruction( aTarget, aData );
+}
+
+void SAL_CALL XMLDocumentWrapper_XmlSecImpl::compressedSetDocumentLocator( sal_Int32 /*columnNumber*/, sal_Int32 /*lineNumber*/, const OUString& /*publicId*/, const OUString& /*systemId*/ )
+{
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLDocumentWrapper_XmlSecImpl::getImplementationName( )
+{
+ return "com.sun.star.xml.wrapper.XMLDocumentWrapper";
+}
+
+sal_Bool SAL_CALL XMLDocumentWrapper_XmlSecImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL XMLDocumentWrapper_XmlSecImpl::getSupportedServiceNames( )
+{
+ return { "com.sun.star.xml.wrapper.XMLDocumentWrapper" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_wrapper_XMLDocumentWrapper_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new XMLDocumentWrapper_XmlSecImpl());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.cxx b/xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.cxx
new file mode 100644
index 000000000..910496618
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.cxx
@@ -0,0 +1,70 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+
+#include "xmlelementwrapper_xmlsecimpl.hxx"
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace com::sun::star;
+
+XMLElementWrapper_XmlSecImpl::XMLElementWrapper_XmlSecImpl(const xmlNodePtr pNode)
+ : m_pElement( pNode )
+{
+}
+
+/* XUnoTunnel */
+const uno::Sequence< sal_Int8 > & XMLElementWrapper_XmlSecImpl::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit implId;
+ return implId.getSeq();
+}
+
+sal_Int64 SAL_CALL XMLElementWrapper_XmlSecImpl::getSomething( const uno::Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLElementWrapper_XmlSecImpl::getImplementationName( )
+{
+ return "com.sun.star.xml.wrapper.XMLElementWrapper";
+}
+
+sal_Bool SAL_CALL XMLElementWrapper_XmlSecImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL XMLElementWrapper_XmlSecImpl::getSupportedServiceNames( )
+{
+ return { "com.sun.star.xml.wrapper.XMLElementWrapper" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_wrapper_XMLElementWrapper_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new XMLElementWrapper_XmlSecImpl(nullptr));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.hxx b/xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.hxx
new file mode 100644
index 000000000..de833d25a
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/xmlelementwrapper_xmlsecimpl.hxx
@@ -0,0 +1,79 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/wrapper/XXMLElementWrapper.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <libxml/tree.h>
+#include <xsecxmlsecdllapi.h>
+
+class XMLElementWrapper_XmlSecImpl : public cppu::WeakImplHelper
+<
+ css::xml::wrapper::XXMLElementWrapper,
+ css::lang::XUnoTunnel,
+ css::lang::XServiceInfo
+>
+/****** XMLElementWrapper_XmlSecImpl.hxx/CLASS XMLElementWrapper_XmlSecImpl ***
+ *
+ * NAME
+ * XMLElementWrapper_XmlSecImpl -- Class to wrap a libxml2 node
+ *
+ * FUNCTION
+ * Used as a wrapper class to transfer a libxml2 node structure
+ * between different UNO components.
+ ******************************************************************************/
+{
+private:
+ /* the libxml2 node wrapped by this object */
+ xmlNodePtr const m_pElement;
+
+public:
+ explicit XMLElementWrapper_XmlSecImpl(const xmlNodePtr pNode);
+
+ /* XXMLElementWrapper */
+
+ /* css::lang::XUnoTunnel */
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+ /// @throws css::uno::RuntimeException
+ static const css::uno::Sequence < sal_Int8 > & getUnoTunnelId();
+
+ /* css::lang::XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+public:
+ /*
+ * NAME
+ * getNativeElement -- Retrieves the libxml2 node wrapped by this object
+ *
+ * SYNOPSIS
+ * pNode = getNativeElement();
+ *
+ * RESULT
+ * pNode - the libxml2 node wrapped by this object
+ */
+ xmlNodePtr getNativeElement( ) const { return m_pElement;}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/xmlsec_init.cxx b/xmlsecurity/source/xmlsec/xmlsec_init.cxx
new file mode 100644
index 000000000..410408ed2
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/xmlsec_init.cxx
@@ -0,0 +1,73 @@
+/* -*- 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 <sal/config.h>
+#include <xmlsec-wrapper.h>
+
+#include <xmlsec/xmlsec_init.hxx>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+
+#include <xmlsec/xmlstreamio.hxx>
+#ifdef XMLSEC_CRYPTO_MSCRYPTO
+#include <xmlsec/mscng/crypto.h>
+#endif
+#ifdef XMLSEC_CRYPTO_NSS
+#include <xmlsec/nss/crypto.h>
+#endif
+
+using namespace css::uno;
+
+XSECXMLSEC_DLLPUBLIC void initXmlSec()
+{
+ //Init xmlsec library
+ if( xmlSecInit() < 0 ) {
+ throw RuntimeException() ;
+ }
+
+ //Init xmlsec crypto engine library
+#ifdef XMLSEC_CRYPTO_MSCRYPTO
+ if( xmlSecMSCngInit() < 0 ) {
+ xmlSecShutdown();
+ throw RuntimeException();
+ }
+#endif
+#ifdef XMLSEC_CRYPTO_NSS
+ if( xmlSecNssInit() < 0 ) {
+ xmlSecShutdown();
+ throw RuntimeException();
+ }
+#endif
+
+ //Enable external stream handlers
+ if( xmlEnableStreamInputCallbacks() < 0 ) {
+#ifdef XMLSEC_CRYPTO_MSCRYPTO
+ xmlSecMSCngShutdown();
+#endif
+#ifdef XMLSEC_CRYPTO_NSS
+ xmlSecNssShutdown();
+#endif
+ xmlSecShutdown() ;
+ throw RuntimeException() ;
+ }
+}
+
+XSECXMLSEC_DLLPUBLIC void deInitXmlSec()
+{
+ xmlDisableStreamInputCallbacks();
+#ifdef XMLSEC_CRYPTO_MSCRYPTO
+ xmlSecMSCngShutdown();
+#endif
+#ifdef XMLSEC_CRYPTO_NSS
+ xmlSecNssShutdown();
+#endif
+ xmlSecShutdown();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/xmlsec/xmlstreamio.cxx b/xmlsecurity/source/xmlsec/xmlstreamio.cxx
new file mode 100644
index 000000000..b3ee76712
--- /dev/null
+++ b/xmlsecurity/source/xmlsec/xmlstreamio.cxx
@@ -0,0 +1,251 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <xmlsec-wrapper.h>
+
+/*
+ * Implementation of the I/O interfaces based on stream and URI binding
+ */
+#include <xmlsec/xmlstreamio.hxx>
+#include <xmlsec/errorcallback.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/uri.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/xml/crypto/XUriBinding.hpp>
+
+static bool g_bInputCallbacksEnabled = false;
+static bool g_bInputCallbacksRegistered = false;
+
+static css::uno::Reference< css::xml::crypto::XUriBinding > m_xUriBinding ;
+
+extern "C" {
+
+static int xmlStreamMatch( const char* uri )
+{
+ css::uno::Reference< css::io::XInputStream > xInputStream ;
+
+ if (g_bInputCallbacksEnabled && g_bInputCallbacksRegistered)
+ {
+ if( uri == nullptr || !m_xUriBinding.is() )
+ return 0 ;
+ //XMLSec first unescapes the uri and calls this function. For example, we pass the Uri
+ //ObjectReplacements/Object%201 then XMLSec passes ObjectReplacements/Object 1
+ //first. If this failed it would try this
+ //again with the original escaped string. However, it does not get this far, because there
+ //is another callback registered by libxml which claims to be able to handle this uri.
+ OUString sUri =
+ ::rtl::Uri::encode( OUString::createFromAscii( uri ),
+ rtl_UriCharClassUric, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8);
+ xInputStream = m_xUriBinding->getUriBinding( sUri ) ;
+ if (!xInputStream.is())
+ {
+ //Try the passed in uri directly.
+ //For old documents prior OOo 3.0. We did not use URIs then.
+ xInputStream = m_xUriBinding->getUriBinding(
+ OUString::createFromAscii(uri));
+ }
+ }
+ SAL_INFO("xmlsecurity.xmlsec",
+ "xmlStreamMath: uri is '" << uri << "', returning " << xInputStream.is());
+ if (xInputStream.is())
+ return 1;
+ else
+ return 0 ;
+}
+
+static void* xmlStreamOpen( const char* uri )
+{
+ css::uno::Reference< css::io::XInputStream > xInputStream ;
+
+ if (g_bInputCallbacksEnabled && g_bInputCallbacksRegistered)
+ {
+ if( uri == nullptr || !m_xUriBinding.is() )
+ return nullptr ;
+
+ //see xmlStreamMatch
+ OUString sUri =
+ ::rtl::Uri::encode( OUString::createFromAscii( uri ),
+ rtl_UriCharClassUric, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8);
+ xInputStream = m_xUriBinding->getUriBinding( sUri ) ;
+ if (!xInputStream.is())
+ {
+ //For old documents.
+ //try the passed in uri directly.
+ xInputStream = m_xUriBinding->getUriBinding(
+ OUString::createFromAscii(uri));
+ }
+
+ if( xInputStream.is() ) {
+ css::io::XInputStream* pInputStream ;
+ pInputStream = xInputStream.get() ;
+ pInputStream->acquire() ;
+ SAL_INFO("xmlsecurity.xmlsec",
+ "xmlStreamOpen: uri is '" << uri << "', returning context " << pInputStream);
+ return static_cast<void*>(pInputStream) ;
+ }
+ }
+
+ return nullptr ;
+}
+
+static int xmlStreamRead( void* context, char* buffer, int len )
+{
+ int numbers ;
+ css::uno::Reference< css::io::XInputStream > xInputStream ;
+ css::uno::Sequence< sal_Int8 > outSeqs( len ) ;
+
+ numbers = 0 ;
+ if (g_bInputCallbacksEnabled && g_bInputCallbacksRegistered)
+ {
+ if( context != nullptr ) {
+ xInputStream = static_cast<css::io::XInputStream*>(context);
+ if( !xInputStream.is() )
+ return 0 ;
+
+ numbers = xInputStream->readBytes( outSeqs, len ) ;
+ const sal_Int8* readBytes = outSeqs.getArray() ;
+ for( int i = 0 ; i < numbers ; i ++ )
+ *( buffer + i ) = *( readBytes + i ) ;
+ }
+ }
+
+ SAL_INFO("xmlsecurity.xmlsec", "xmlStreamRead: context is " << context << ", buffer is now '"
+ << OString(buffer, numbers) << "'");
+ return numbers ;
+}
+
+static int xmlStreamClose( void * context )
+{
+ if (g_bInputCallbacksEnabled && g_bInputCallbacksRegistered)
+ {
+ if( context != nullptr ) {
+ css::io::XInputStream* pInputStream ;
+ pInputStream = static_cast<css::io::XInputStream*>(context);
+ pInputStream->release() ;
+ SAL_INFO("xmlsecurity.xmlsec", "xmlStreamRead: closed context " << context);
+ }
+ }
+
+ return 0 ;
+}
+
+}
+
+int xmlEnableStreamInputCallbacks()
+{
+ if (!g_bInputCallbacksEnabled)
+ {
+ //Register the callbacks into xmlSec
+ //In order to make the xmlsec io finding the callbacks firstly,
+ //I put the callbacks at the very beginning.
+
+ //Cleanup the older callbacks.
+ //Notes: all none default callbacks will lose.
+ xmlSecIOCleanupCallbacks() ;
+
+ // Make sure that errors are reported via SAL_WARN().
+ setErrorRecorder();
+ comphelper::ScopeGuard g([] { clearErrorRecorder(); });
+
+ // Newer xmlsec wants the callback order in the opposite direction.
+ if (xmlSecCheckVersionExt(1, 2, 26, xmlSecCheckVersionABICompatible))
+ {
+ //Register the default callbacks.
+ //Notes: the error will cause xmlsec working problems.
+ int cbs = xmlSecIORegisterDefaultCallbacks() ;
+ if( cbs < 0 ) {
+ return -1 ;
+ }
+
+ //Register my classbacks.
+ cbs = xmlSecIORegisterCallbacks(
+ xmlStreamMatch,
+ xmlStreamOpen,
+ xmlStreamRead,
+ xmlStreamClose ) ;
+ if( cbs < 0 ) {
+ return -1 ;
+ }
+ }
+ else
+ {
+ //Register my classbacks.
+ int cbs = xmlSecIORegisterCallbacks(
+ xmlStreamMatch,
+ xmlStreamOpen,
+ xmlStreamRead,
+ xmlStreamClose ) ;
+ if( cbs < 0 ) {
+ return -1 ;
+ }
+
+ //Register the default callbacks.
+ //Notes: the error will cause xmlsec working problems.
+ cbs = xmlSecIORegisterDefaultCallbacks() ;
+ if( cbs < 0 ) {
+ return -1 ;
+ }
+ }
+
+ g_bInputCallbacksEnabled = true;
+ }
+
+ return 0 ;
+}
+
+int xmlRegisterStreamInputCallbacks(
+ css::uno::Reference< css::xml::crypto::XUriBinding > const & aUriBinding
+) {
+ if (!g_bInputCallbacksEnabled)
+ {
+ if( xmlEnableStreamInputCallbacks() < 0 )
+ return -1 ;
+ }
+
+ if (!g_bInputCallbacksRegistered)
+ g_bInputCallbacksRegistered = true;
+
+ m_xUriBinding = aUriBinding ;
+
+ return 0 ;
+}
+
+int xmlUnregisterStreamInputCallbacks()
+{
+ if (g_bInputCallbacksRegistered)
+ {
+ //Clear the uri-stream binding
+ m_xUriBinding.clear() ;
+
+ //disable the registered flag
+ g_bInputCallbacksRegistered = false;
+ }
+
+ return 0 ;
+}
+
+void xmlDisableStreamInputCallbacks() {
+ xmlUnregisterStreamInputCallbacks() ;
+ g_bInputCallbacksEnabled = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */