comphelper/source/misc/storagehelper.cxx                 |   24 +-
 comphelper/source/misc/xmlsechelper.cxx                  |   16 -
 cui/source/inc/cuioptgenrl.hxx                           |   13 +
 cui/source/options/optgenrl.cxx                          |  154 ++++++++-------
 cui/uiconfig/ui/optuserpage.ui                           |  135 +++++++++----
 include/comphelper/xmlsechelper.hxx                      |    2 
 include/unotools/useroptions.hxx                         |    6 
 officecfg/registry/schema/org/openoffice/UserProfile.xcs |   12 +
 unotools/source/config/useroptions.cxx                   |    6 
 xmlsecurity/qa/uitest/gpg/encrypt.py                     |   37 ++-
 xmlsecurity/source/dialogs/certificatechooser.cxx        |    5 
 xmlsecurity/source/gpg/CertificateImpl.cxx               |    6 
 12 files changed, 273 insertions(+), 143 deletions(-)

New commits:
commit 9786f8ea59ccc9225871888d23b5af9ed16a5e5f
Author:     Sarper Akdemir <sarper.akde...@allotropia.de>
AuthorDate: Wed Jul 31 11:03:13 2024 +0200
Commit:     Sarper Akdemir <sarper.akde...@allotropia.de>
CommitDate: Mon Aug 5 13:15:31 2024 +0200

    tdf#159040 tdf#162206: better the user key selection and test sign with 
default
    
    fix GPG CertificateImpl::getSHA1Thumbprint not returning a
    sequence of bytes as the thumbprint. (that is what the
    documented API states, and what all of the code assumed.)
    
    now /org.openoffice.UserProfile/Data/signingkey and
    encryptionkey stores the key's SHA1Thumbprint for better
    identification of keys. Previously on the key name was being
    used.
    
    fix annoying NSS Certificate Database password prompt
    appearing on Tools->Options if the certificate database is
    password protected.
    
    improve Tools->Options->UserData user key selection and
    display of information.
    
    add a ui test for "Sign with default certificate"
    interaction in the save dialog.
    
    Change-Id: I1036856003f58f494838e0f81ca0fe18e821f528
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171395
    Tested-by: Jenkins
    Reviewed-by: Sarper Akdemir <sarper.akde...@allotropia.de>

diff --git a/comphelper/source/misc/storagehelper.cxx 
b/comphelper/source/misc/storagehelper.cxx
index 8d7786205d79..29e9512c2e9e 100644
--- a/comphelper/source/misc/storagehelper.cxx
+++ b/comphelper/source/misc/storagehelper.cxx
@@ -55,6 +55,7 @@
 #include <comphelper/propertyvalue.hxx>
 #include <comphelper/storagehelper.hxx>
 #include <comphelper/sequence.hxx>
+#include <comphelper/xmlsechelper.hxx>
 #include <cppuhelper/exc_hlp.hxx>
 #include <o3tl/string_view.hxx>
 
@@ -485,16 +486,15 @@ OStorageHelper::CreateGpgPackageEncryptionData(const 
css::uno::Reference<css::aw
             ctx->setArmor(false);
         }
 
-        uno::Sequence < sal_Int8 > aKeyID;
+        OString aKeyID;
         if (cert.is())
-            aKeyID = cert->getSHA1Thumbprint();
-
-        std::vector<GpgME::Key> keys
         {
-            ctx->key(
-                reinterpret_cast<const char*>(aKeyID.getConstArray()),
-                err, false)
-        };
+            aKeyID
+                = 
OUStringToOString(comphelper::xmlsec::GetHexString(cert->getSHA1Thumbprint(), 
""),
+                                    RTL_TEXTENCODING_UTF8);
+        }
+
+        std::vector<GpgME::Key> keys{ ctx->key(aKeyID.getStr(), err, false) };
 
         // ctx is setup now, let's encrypt the lot!
         GpgME::Data plain(
@@ -549,9 +549,11 @@ OStorageHelper::CreateGpgPackageEncryptionData(const 
css::uno::Reference<css::aw
 
         SAL_INFO("comphelper.crypto", "Generated gpg crypto of length: " << 
len);
 
-        uno::Sequence< beans::NamedValue > aGpgEncryptionEntry{
-            { u"KeyId"_ustr, uno::Any(aKeyID) },
-            { u"KeyPacket"_ustr, uno::Any(aKeyID) },
+        uno::Sequence<sal_Int8> aKeyIdSequence
+            = comphelper::arrayToSequence<sal_Int8>(aKeyID.getStr(), 
aKeyID.getLength() + 1);
+        uno::Sequence<beans::NamedValue> aGpgEncryptionEntry{
+            { u"KeyId"_ustr, uno::Any(aKeyIdSequence) },
+            { u"KeyPacket"_ustr, uno::Any(aKeyIdSequence) },
             { u"CipherValue"_ustr, uno::Any(aCipherValue) }
         };
 
diff --git a/comphelper/source/misc/xmlsechelper.cxx 
b/comphelper/source/misc/xmlsechelper.cxx
index 69dd3b6571bb..f810de47af69 100644
--- a/comphelper/source/misc/xmlsechelper.cxx
+++ b/comphelper/source/misc/xmlsechelper.cxx
@@ -312,7 +312,7 @@ std::vector< std::pair< OUString, OUString> > 
parseDN(std::u16string_view rRawSt
 
     css::uno::Reference<css::security::XCertificate> FindCertInContext(
         const css::uno::Reference<css::xml::crypto::XXMLSecurityContext>& 
xSecurityContext,
-        const OUString& rContentPart)
+        const OUString& rSHA1Thumbprint)
     {
         if (!xSecurityContext.is())
             return {};
@@ -325,13 +325,13 @@ std::vector< std::pair< OUString, OUString> > 
parseDN(std::u16string_view rRawSt
         auto aCertsIter = asNonConstRange(xCertificates);
 
         auto pxCert
-            = std::find_if(aCertsIter.begin(), aCertsIter.end(),
-                           [&rContentPart](auto& xCert)
-                           {
-                               return comphelper::xmlsec::GetContentPart(
-                                          xCert->getSubjectName(), 
xCert->getCertificateKind())
-                                      == rContentPart;
-                           });
+                = std::find_if(aCertsIter.begin(), aCertsIter.end(),
+                               [&rSHA1Thumbprint](auto& xCert)
+                               {
+                                   return rSHA1Thumbprint
+                                           == 
GetHexString(xCert->getSHA1Thumbprint(), "");
+                               });
+
         if (pxCert == aCertsIter.end())
             return {};
 
diff --git a/cui/source/inc/cuioptgenrl.hxx b/cui/source/inc/cuioptgenrl.hxx
index 0ee8cacb6c50..ffebb3acc1d7 100644
--- a/cui/source/inc/cuioptgenrl.hxx
+++ b/cui/source/inc/cuioptgenrl.hxx
@@ -34,12 +34,16 @@ private:
     std::unique_ptr<weld::CheckButton> m_xUseDataCB;
     std::unique_ptr<weld::Widget> m_xUseDataImg;
     std::unique_ptr<weld::Widget> m_xCryptoFrame;
-    std::unique_ptr<weld::ComboBox> m_xSigningKeyLB;
+    std::unique_ptr<weld::Entry> m_xSigningKeyLB;
     std::unique_ptr<weld::Label> m_xSigningKeyFT;
     std::unique_ptr<weld::Widget> m_xSigningKeyImg;
-    std::unique_ptr<weld::ComboBox> m_xEncryptionKeyLB;
+    std::unique_ptr<weld::Button> m_xSigningKeyButton;
+    std::unique_ptr<weld::Button> m_xRemoveSigningKeyButton;
+    std::unique_ptr<weld::Entry> m_xEncryptionKeyLB;
     std::unique_ptr<weld::Label> m_xEncryptionKeyFT;
     std::unique_ptr<weld::Widget> m_xEncryptionKeyImg;
+    std::unique_ptr<weld::Button> m_xEncryptionKeyButton;
+    std::unique_ptr<weld::Button> m_xRemoveEncryptionKeyButton;
     std::unique_ptr<weld::CheckButton> m_xEncryptToSelfCB;
     std::unique_ptr<weld::Widget> m_xEncryptToSelfImg;
     // rows
@@ -52,7 +56,12 @@ private:
     unsigned nNameRow;
     unsigned nShortNameField;
 
+    OUString msCurrentSigningKey;
+    OUString msCurrentEncryptionKey;
+
     DECL_LINK( ModifyHdl_Impl, weld::Entry&, void );
+    DECL_LINK( ChooseKeyButtonHdl, weld::Button&, void );
+    DECL_LINK( RemoveKeyButtonHdl, weld::Button&, void );
 
     bool                GetData_Impl();
     void                SetData_Impl();
diff --git a/cui/source/options/optgenrl.cxx b/cui/source/options/optgenrl.cxx
index ff2216b6e41c..c956a9eb1ed8 100644
--- a/cui/source/options/optgenrl.cxx
+++ b/cui/source/options/optgenrl.cxx
@@ -17,6 +17,7 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <comphelper/diagnose_ex.hxx>
 #include <comphelper/string.hxx>
 #include <comphelper/processfactory.hxx>
 
@@ -27,6 +28,7 @@
 #endif
 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
 #include <comphelper/xmlsechelper.hxx>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
 
 #include <i18nlangtag/languagetag.hxx>
 #include <i18nlangtag/mslangid.hxx>
@@ -219,12 +221,16 @@ SvxGeneralTabPage::SvxGeneralTabPage(weld::Container* 
pPage, weld::DialogControl
     , m_xUseDataCB(m_xBuilder->weld_check_button(u"usefordocprop"_ustr))
     , m_xUseDataImg(m_xBuilder->weld_widget(u"lockusefordocprop"_ustr))
     , m_xCryptoFrame(m_xBuilder->weld_widget( u"cryptography"_ustr))
-    , m_xSigningKeyLB(m_xBuilder->weld_combo_box(u"signingkey"_ustr))
+    , m_xSigningKeyLB(m_xBuilder->weld_entry(u"signingkey"_ustr))
     , m_xSigningKeyFT(m_xBuilder->weld_label(u"signingkeylabel"_ustr))
     , m_xSigningKeyImg(m_xBuilder->weld_widget(u"locksigningkey"_ustr))
-    , m_xEncryptionKeyLB(m_xBuilder->weld_combo_box(u"encryptionkey"_ustr))
+    , m_xSigningKeyButton(m_xBuilder->weld_button(u"picksigningkey"_ustr))
+    , 
m_xRemoveSigningKeyButton(m_xBuilder->weld_button(u"removesigningkey"_ustr))
+    , m_xEncryptionKeyLB(m_xBuilder->weld_entry(u"encryptionkey"_ustr))
     , m_xEncryptionKeyFT(m_xBuilder->weld_label(u"encryptionkeylabel"_ustr))
     , m_xEncryptionKeyImg(m_xBuilder->weld_widget(u"lockencryptionkey"_ustr))
+    , 
m_xEncryptionKeyButton(m_xBuilder->weld_button(u"pickencryptionkey"_ustr))
+    , 
m_xRemoveEncryptionKeyButton(m_xBuilder->weld_button(u"removeencryptionkey"_ustr))
     , m_xEncryptToSelfCB(m_xBuilder->weld_check_button(u"encrypttoself"_ustr))
     , m_xEncryptToSelfImg(m_xBuilder->weld_widget(u"lockencrypttoself"_ustr))
 {
@@ -301,68 +307,89 @@ void SvxGeneralTabPage::InitCryptography()
 {
 #if HAVE_FEATURE_GPGME
     m_xCryptoFrame->show();
+    m_xSigningKeyButton->connect_clicked(LINK(this, SvxGeneralTabPage, 
ChooseKeyButtonHdl));
+    m_xEncryptionKeyButton->connect_clicked(LINK(this, SvxGeneralTabPage, 
ChooseKeyButtonHdl));
 
+    m_xRemoveSigningKeyButton->connect_clicked(LINK(this, SvxGeneralTabPage, 
RemoveKeyButtonHdl));
+    m_xRemoveEncryptionKeyButton->connect_clicked(LINK(this, 
SvxGeneralTabPage, RemoveKeyButtonHdl));
+#endif
+
+}
+
+IMPL_LINK(SvxGeneralTabPage, ChooseKeyButtonHdl, weld::Button&, rButton, void)
+{
     try
     {
-        uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
-            = 
xml::crypto::SEInitializer::create(comphelper::getProcessComponentContext())
-                  ->createSecurityContext("");
-        uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContextGPG
-            = 
xml::crypto::GPGSEInitializer::create(comphelper::getProcessComponentContext())
-                  ->createSecurityContext("");
-        if (xSecurityContextGPG.is())
-        {
-            uno::Reference<xml::crypto::XSecurityEnvironment> xSE = 
xSecurityContextGPG->getSecurityEnvironment();
-            uno::Sequence<uno::Reference<security::XCertificate>> 
xCertificates = xSE->getPersonalCertificates();
+        uno::Reference<security::XDocumentDigitalSignatures> xD(
+            security::DocumentDigitalSignatures::createDefault(
+                comphelper::getProcessComponentContext()));
+        xD->setParentWindow(GetDialogController()->getDialog()->GetXWindow());
 
-            if (xCertificates.hasElements())
-            {
-                for (auto& xCert : asNonConstRange(xCertificates))
-                {
-                    const auto aIssuer = comphelper::xmlsec::GetContentPart(
-                        xCert->getSubjectName(), xCert->getCertificateKind());
-                    m_xSigningKeyLB->append_text(aIssuer);
-                    m_xEncryptionKeyLB->append_text(aIssuer);
-                }
-            }
-        }
+        OUString aDescription;
 
-        if (xSecurityContext.is())
+        uno::Reference<security::XCertificate> xCertificate;
+        if (m_xSigningKeyButton.get() == &rButton)
+        {
+            xCertificate = xD->selectSigningCertificate(aDescription);
+        }
+        else if (m_xEncryptionKeyButton.get() == &rButton)
         {
-            uno::Reference<xml::crypto::XSecurityEnvironment> xSE = 
xSecurityContext->getSecurityEnvironment();
-            uno::Sequence<uno::Reference<security::XCertificate>> xCertificates
-                = xSE->getPersonalCertificates();
+            auto xCerts = xD->chooseEncryptionCertificate();
+            if(xCerts.hasElements())
+                xCertificate = xCerts[0];
+        }
 
-            if (xCertificates.hasElements())
-            {
-                for (auto& xCert : asNonConstRange(xCertificates))
-                {
-                    const auto aIssuer
-                        = 
comphelper::xmlsec::GetContentPart(xCert->getSubjectName(),
-                                                             
xCert->getCertificateKind());
-                    m_xSigningKeyLB->append_text(aIssuer);
-                }
-            }
+        if(!xCertificate.is())
+            return;
+
+        OUString aKeyThumbprint
+            = 
comphelper::xmlsec::GetHexString(xCertificate->getSHA1Thumbprint(), "");
+        OUString aIssuer = 
comphelper::xmlsec::GetContentPart(xCertificate->getIssuerName(),
+                                                              
xCertificate->getCertificateKind());
+        OUString aSubject = 
comphelper::xmlsec::GetContentPart(xCertificate->getSubjectName(),
+                                                               
xCertificate->getCertificateKind());
+        OUString aKeyDisplayName;
+        switch (xCertificate->getCertificateKind())
+        {
+            case security::CertificateKind::CertificateKind_X509:
+                aKeyDisplayName = u"(X.509) "_ustr + aIssuer + u" "_ustr + 
aSubject;
+                break;
+            case security::CertificateKind::CertificateKind_OPENPGP:
+                aKeyDisplayName = u"(OpenPGP) "_ustr + aIssuer;
+                break;
+            default:
+                break;
         }
 
-        if (xSecurityContext.is() || xSecurityContextGPG.is())
+        if (m_xSigningKeyButton.get() == &rButton)
         {
-            //tdf#115015: wrap checkbox text and listboxes if necessary
-            int nPrefWidth(m_xEncryptToSelfCB->get_preferred_size().Width());
-            int nMaxWidth = m_xEncryptToSelfCB->get_approximate_digit_width() 
* 40;
-            if (nPrefWidth > nMaxWidth)
-            {
-                 m_xSigningKeyLB->set_size_request(nMaxWidth, -1);
-                 m_xEncryptionKeyLB->set_size_request(nMaxWidth, -1);
-                 m_xEncryptToSelfCB->set_label_wrap(true);
-                 m_xEncryptToSelfCB->set_size_request(nMaxWidth, -1);
-            }
+            msCurrentSigningKey = aKeyThumbprint;
+            m_xSigningKeyLB->set_text(aKeyDisplayName);
+        }
+        else if (m_xEncryptionKeyButton.get() == &rButton)
+        {
+            msCurrentEncryptionKey = aKeyThumbprint;
+            m_xEncryptionKeyLB->set_text(aKeyDisplayName);
         }
     }
-    catch ( uno::Exception const & )
-    {}
-#endif
+    catch (const css::uno::Exception&)
+    {
+        TOOLS_WARN_EXCEPTION("cui.options", "" );
+    }
+}
 
+IMPL_LINK(SvxGeneralTabPage, RemoveKeyButtonHdl, weld::Button&, rButton, void)
+{
+        if (m_xRemoveSigningKeyButton.get() == &rButton)
+        {
+            msCurrentSigningKey.clear();
+            m_xSigningKeyLB->set_text(u""_ustr);
+        }
+        else if (m_xRemoveEncryptionKeyButton.get() == &rButton)
+        {
+            msCurrentEncryptionKey.clear();
+            m_xEncryptionKeyLB->set_text(u""_ustr);
+        }
 }
 
 void SvxGeneralTabPage::SetLinks ()
@@ -497,13 +524,10 @@ bool SvxGeneralTabPage::GetData_Impl()
     }
 
 #if HAVE_FEATURE_GPGME
-    OUString aSK = m_xSigningKeyLB->get_active() == 0 ? OUString() //i.e. no 
key
-                       : m_xSigningKeyLB->get_active_text();
-    OUString aEK = m_xEncryptionKeyLB->get_active() == 0 ? OUString()
-                       : m_xEncryptionKeyLB->get_active_text();
-
-    aUserOpt.SetToken( UserOptToken::SigningKey, aSK );
-    aUserOpt.SetToken( UserOptToken::EncryptionKey, aEK );
+    aUserOpt.SetToken( UserOptToken::SigningKey, msCurrentSigningKey );
+    aUserOpt.SetToken( UserOptToken::SigningKeyDisplayName, 
m_xSigningKeyLB->get_text() );
+    aUserOpt.SetToken( UserOptToken::EncryptionKey, msCurrentEncryptionKey );
+    aUserOpt.SetToken( UserOptToken::EncryptionKeyDisplayName, 
m_xEncryptionKeyLB->get_text() );
     aUserOpt.SetBoolValue( UserOptToken::EncryptToSelf, 
m_xEncryptToSelfCB->get_active() );
 
     bModified |= m_xSigningKeyLB->get_value_changed_from_saved() ||
@@ -550,12 +574,12 @@ void SvxGeneralTabPage::SetData_Impl()
 
 #if HAVE_FEATURE_GPGME
     bEnable = !aUserOpt.IsTokenReadonly(UserOptToken::SigningKey);
-    m_xSigningKeyLB->set_sensitive(bEnable);
+    m_xSigningKeyButton->set_sensitive(bEnable);
     m_xSigningKeyFT->set_sensitive(bEnable);
     m_xSigningKeyImg->set_visible(!bEnable);
 
     bEnable = !aUserOpt.IsTokenReadonly(UserOptToken::EncryptionKey);
-    m_xEncryptionKeyLB->set_sensitive(bEnable);
+    m_xEncryptionKeyButton->set_sensitive(bEnable);
     m_xEncryptionKeyFT->set_sensitive(bEnable);
     m_xEncryptionKeyImg->set_visible(!bEnable);
 
@@ -563,13 +587,11 @@ void SvxGeneralTabPage::SetData_Impl()
     m_xEncryptToSelfCB->set_sensitive(bEnable);
     m_xEncryptToSelfImg->set_visible(!bEnable);
 
-    OUString aSK = aUserOpt.GetToken(UserOptToken::SigningKey);
-    aSK.isEmpty() ? m_xSigningKeyLB->set_active( 0 ) //i.e. 'No Key'
-                  : m_xSigningKeyLB->set_active_text( aSK );
+    msCurrentSigningKey = aUserOpt.GetToken(UserOptToken::SigningKey);
+    
m_xSigningKeyLB->set_text(aUserOpt.GetToken(UserOptToken::SigningKeyDisplayName));
 
-    OUString aEK = aUserOpt.GetToken(UserOptToken::EncryptionKey);
-    aEK.isEmpty() ? m_xEncryptionKeyLB->set_active( 0 ) //i.e. 'No Key'
-                  : m_xEncryptionKeyLB->set_active_text( aEK );
+    msCurrentEncryptionKey = aUserOpt.GetToken(UserOptToken::EncryptionKey);
+    
m_xEncryptionKeyLB->set_text(aUserOpt.GetToken(UserOptToken::EncryptionKeyDisplayName));
 
     m_xEncryptToSelfCB->set_active( aUserOpt.GetEncryptToSelf() );
 #endif
diff --git a/cui/uiconfig/ui/optuserpage.ui b/cui/uiconfig/ui/optuserpage.ui
index 0c69fb176123..f486669fd4e5 100644
--- a/cui/uiconfig/ui/optuserpage.ui
+++ b/cui/uiconfig/ui/optuserpage.ui
@@ -1,7 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.38.2 -->
+<!-- Generated with glade 3.40.0 -->
 <interface domain="cui">
   <requires lib="gtk+" version="3.20"/>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="icon-name">sfx2/res/deleterow.png</property>
+  </object>
+  <object class="GtkImage" id="image2">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="icon-name">sfx2/res/deleterow.png</property>
+  </object>
   <object class="GtkBox" id="OptUserPage">
     <property name="visible">True</property>
     <property name="can-focus">False</property>
@@ -1086,7 +1096,7 @@
         <property name="label-xalign">0</property>
         <property name="shadow-type">none</property>
         <child>
-          <!-- n-columns=3 n-rows=3 -->
+          <!-- n-columns=5 n-rows=3 -->
           <object class="GtkGrid" id="grid14">
             <property name="visible">True</property>
             <property name="can-focus">False</property>
@@ -1122,44 +1132,6 @@
                 <property name="top-attach">1</property>
               </packing>
             </child>
-            <child>
-              <object class="GtkComboBoxText" id="encryptionkey">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="hexpand">True</property>
-                <items>
-                  <item translatable="yes" context="optuserpage|liststore1">No 
key</item>
-                </items>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="encryptionkey-atkobject">
-                    <property name="AtkObject::accessible-description" 
translatable="yes" context="extended tip | encryptionkey">Select your OpenPGP 
key from the drop-down list for encrypting ODF documents.</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="left-attach">2</property>
-                <property name="top-attach">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkComboBoxText" id="signingkey">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="hexpand">True</property>
-                <items>
-                  <item translatable="yes" context="optuserpage|liststore1">No 
key</item>
-                </items>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="signingkey-atkobject">
-                    <property name="AtkObject::accessible-description" 
translatable="yes" context="extended tip | signingkey">Select your key from the 
drop-down list for signing ODF documents.</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="left-attach">2</property>
-                <property name="top-attach">0</property>
-              </packing>
-            </child>
             <child>
               <object class="GtkCheckButton" id="encrypttoself">
                 <property name="label" translatable="yes" 
context="optuserpage|encrypttoself">When _encrypting documents, always encrypt 
to self</property>
@@ -1177,7 +1149,7 @@
               <packing>
                 <property name="left-attach">1</property>
                 <property name="top-attach">2</property>
-                <property name="width">2</property>
+                <property name="width">3</property>
               </packing>
             </child>
             <child>
@@ -1219,6 +1191,87 @@
                 <property name="top-attach">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkEntry" id="signingkey">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="editable">False</property>
+                <property name="placeholder-text" translatable="yes" 
context="optuserpage|signingkey">No key</property>
+              </object>
+              <packing>
+                <property name="left-attach">2</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="encryptionkey">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="editable">False</property>
+                <property name="placeholder-text" translatable="yes" 
context="optuserpage|encryptionkey">No key</property>
+              </object>
+              <packing>
+                <property name="left-attach">2</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="picksigningkey">
+                <property name="label" translatable="yes" 
context="optuserpage|picksigningkey">Select...</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">4</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="pickencryptionkey">
+                <property name="label" translatable="yes" 
context="optuserpage|pickencryptionkey">Select...</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">4</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="removesigningkey">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="tooltip-text" translatable="yes" 
context="optuserpage|removesigningkey">Clear Signing Key</property>
+                <property name="image">image1</property>
+                <property name="always-show-image">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">3</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="removeencryptionkey">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="tooltip-text" translatable="yes" 
context="optuserpage|removeencryptionkey">Clear Encryption Key</property>
+                <property name="image">image2</property>
+                <property name="always-show-image">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">3</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
           </object>
         </child>
         <child type="label">
diff --git a/include/comphelper/xmlsechelper.hxx 
b/include/comphelper/xmlsechelper.hxx
index 40c5599c8b57..94795c3b9361 100644
--- a/include/comphelper/xmlsechelper.hxx
+++ b/include/comphelper/xmlsechelper.hxx
@@ -53,7 +53,7 @@ COMPHELPER_DLLPUBLIC OUString GetHexString(const 
css::uno::Sequence<sal_Int8>& _
 
 COMPHELPER_DLLPUBLIC css::uno::Reference<css::security::XCertificate> 
FindCertInContext(
     const css::uno::Reference<css::xml::crypto::XXMLSecurityContext>& 
xSecurityContext,
-    const OUString& rContentPart);
+    const OUString& rSHA1Thumbprint);
 }
 
 #endif
diff --git a/include/unotools/useroptions.hxx b/include/unotools/useroptions.hxx
index d21482dcca2d..cd44524e6cba 100644
--- a/include/unotools/useroptions.hxx
+++ b/include/unotools/useroptions.hxx
@@ -49,7 +49,9 @@ enum class UserOptToken
     SigningKey         = 17,
     EncryptionKey      = 18,
     EncryptToSelf      = 19,
-    LAST               = EncryptToSelf,
+    SigningKeyDisplayName = 20,
+    EncryptionKeyDisplayName = 21,
+    LAST               = EncryptionKeyDisplayName,
 };
 
 // class SvtUserOptions --------------------------------------------------
@@ -79,6 +81,8 @@ public:
     OUString GetSigningKey     () const;
     OUString GetEncryptionKey  () const;
     bool GetEncryptToSelf      () const;
+    OUString GetSigningKeyDisplayName () const;
+    OUString GetEncryptionKeyDisplayName () const;
 
     OUString GetFullName       () const;
 
diff --git a/officecfg/registry/schema/org/openoffice/UserProfile.xcs 
b/officecfg/registry/schema/org/openoffice/UserProfile.xcs
index 3b0927d1341e..8d488509f508 100644
--- a/officecfg/registry/schema/org/openoffice/UserProfile.xcs
+++ b/officecfg/registry/schema/org/openoffice/UserProfile.xcs
@@ -188,6 +188,18 @@
         </info>
         <value>true</value>
       </prop>
+      <prop oor:name="signingkeydisplayname" oor:type="xs:string" 
oor:nillable="false">
+        <info>
+          <desc>Cached signing key display name.</desc>
+        </info>
+        <value/>
+      </prop>
+      <prop oor:name="encryptionkeydisplayname" oor:type="xs:string" 
oor:nillable="false">
+        <info>
+          <desc>Cached encryption key display name.</desc>
+        </info>
+        <value/>
+      </prop>
     </group>
     <group oor:name="WinUserInfo">
       <info>
diff --git a/unotools/source/config/useroptions.cxx 
b/unotools/source/config/useroptions.cxx
index c26f85f3d748..7ea5a3fd415f 100644
--- a/unotools/source/config/useroptions.cxx
+++ b/unotools/source/config/useroptions.cxx
@@ -65,7 +65,9 @@ constexpr o3tl::enumarray<UserOptToken, OUString> 
vOptionNames = {
     u"apartment"_ustr,                 // UserOptToken::Apartment
     u"signingkey"_ustr,                // UserOptToken::SigningKey
     u"encryptionkey"_ustr,             // UserOptToken::EncryptionKey
-    u"encrypttoself"_ustr              // UserOptToken::EncryptToSelf
+    u"encrypttoself"_ustr,             // UserOptToken::EncryptToSelf
+    u"signingkeydisplayname"_ustr,     // UserOptToken::SigningKeyDisplayName
+    u"encryptionkeydisplayname"_ustr,  // 
UserOptToken::EncryptionKeyDisplayName
 };
 
 std::weak_ptr<SvtUserOptions::Impl> SvtUserOptions::xSharedImpl;
@@ -306,6 +308,8 @@ OUString SvtUserOptions::GetFax            () const { 
return GetToken(UserOptTok
 OUString SvtUserOptions::GetEmail          () const { return 
GetToken(UserOptToken::Email); }
 OUString SvtUserOptions::GetSigningKey     () const { return 
GetToken(UserOptToken::SigningKey); }
 OUString SvtUserOptions::GetEncryptionKey  () const { return 
GetToken(UserOptToken::EncryptionKey); }
+OUString SvtUserOptions::GetSigningKeyDisplayName () const { return 
GetToken(UserOptToken::SigningKeyDisplayName); }
+OUString SvtUserOptions::GetEncryptionKeyDisplayName () const { return 
GetToken(UserOptToken::EncryptionKeyDisplayName); }
 
 bool SvtUserOptions::IsTokenReadonly (UserOptToken nToken) const
 {
diff --git a/xmlsecurity/qa/uitest/gpg/encrypt.py 
b/xmlsecurity/qa/uitest/gpg/encrypt.py
index 70c9c0e9ffc1..a3b0bc3f958d 100644
--- a/xmlsecurity/qa/uitest/gpg/encrypt.py
+++ b/xmlsecurity/qa/uitest/gpg/encrypt.py
@@ -8,6 +8,7 @@
 #
 
 from uitest.framework import UITestCase
+from uitest.uihelper.common import get_state_as_dict
 from libreoffice.uno.propertyvalue import mkPropertyValues
 import pyuno
 
@@ -23,20 +24,19 @@ import os.path
 # See solenv/gbuild/UITest.mk
 class GpgEncryptTest(UITestCase):
 
-    # should this be setUp() or setUpClass()?
-    # as setUp(), any test's change to the files should be overwritten before
-    # the next test, which could be an advantage.
-    def setUp(self):
+    @classmethod
+    def setUpClass(cls):
         testdir = os.getenv("TestUserDir")
         certdir = urljoin(testdir, "signing-keys")
         # this sets GNUPGHOME so do it before starting soffice
         pyuno.private_initTestEnvironmentGPG(certdir)
         # ugly: set var here again because "os.environ" is a copy :(
         os.environ["GNUPGHOME"] = url2pathname(urlparse(certdir).path)
-        super().setUp()
+        super().setUpClass()
 
-    def tearDown(self):
-        super().tearDown()
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
         pyuno.private_deinitTestEnvironmentGPG()
 
     def test_gpg_encrypt(self):
@@ -72,4 +72,27 @@ class GpgEncryptTest(UITestCase):
                     self.assertTrue(os.path.isfile(xFilePath))
 
 
+    def test_gpg_sign_default(self):
+        with 
self.ui_test.set_config('/org.openoffice.UserProfile/Data/signingkey', 
r"93F7584031D9B74A57BB89DFC468A04FCA526A9F"):
+            with TemporaryDirectory() as tempdir:
+                xFilePath = os.path.join(tempdir, "testfile.odt")
+                with self.ui_test.create_doc_in_start_center("writer") as 
document:
+                    with 
self.ui_test.execute_dialog_through_command(".uno:Save", close_button="") as 
xSaveDialog:
+                        xFileName = xSaveDialog.getChild("file_name")
+                        xFileName.executeAction("TYPE", 
mkPropertyValues({"KEYCODE":"CTRL+A"}))
+                        xFileName.executeAction("TYPE", 
mkPropertyValues({"KEYCODE":"BACKSPACE"}))
+                        xFileName.executeAction("TYPE", 
mkPropertyValues({"TEXT": xFilePath}))
+
+                        gpgsign = xSaveDialog.getChild("gpgsign")
+                        # make sure the checkbox is enabled when there's a 
matching signing key
+                        self.assertEqual('true', 
get_state_as_dict(gpgsign)['Enabled'])
+                        gpgsign.executeAction("CLICK",tuple())
+
+                        xOpen = xSaveDialog.getChild("open")
+                        self.assertFalse(os.path.isfile(xFilePath))
+                        with self.ui_test.execute_dialog_through_action(xOpen, 
"CLICK", close_button="") as xSignOnConsecutiveSaveDialog:
+                            
xSignOnConsecutiveSaveDialog.getChild("no").executeAction("CLICK", tuple())
+
+                        # confirm signing was sucessful
+                        
self.assertTrue(self.ui_test.get_component().getPropertyValue("HasValidSignatures"))
 # vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/xmlsecurity/source/dialogs/certificatechooser.cxx 
b/xmlsecurity/source/dialogs/certificatechooser.cxx
index 5b3d80291297..178ffe777c7b 100644
--- a/xmlsecurity/source/dialogs/certificatechooser.cxx
+++ b/xmlsecurity/source/dialogs/certificatechooser.cxx
@@ -248,9 +248,8 @@ void CertificateChooser::ImplInitialize(bool mbSearch)
             m_xCertLB->set_id(nRow, sId);
 
 #if HAVE_FEATURE_GPGME
-            // only GPG has preferred keys
-            if ( !sIssuer.isEmpty() && !msPreferredKey.isEmpty() ) {
-                if ( sIssuer == msPreferredKey )
+            if ( !msPreferredKey.isEmpty() ) {
+                if ( xmlsec::GetHexString(xCert->getSHA1Thumbprint(), "") == 
msPreferredKey )
                 {
                     if ( meAction == CertificateChooserUserAction::Sign || 
meAction == CertificateChooserUserAction::SelectSign )
                     {
diff --git a/xmlsecurity/source/gpg/CertificateImpl.cxx 
b/xmlsecurity/source/gpg/CertificateImpl.cxx
index 7e3d942313d6..b13faa8696de 100644
--- a/xmlsecurity/source/gpg/CertificateImpl.cxx
+++ b/xmlsecurity/source/gpg/CertificateImpl.cxx
@@ -15,6 +15,7 @@
 #include <com/sun/star/security/KeyUsage.hpp>
 #include <officecfg/Office/Common.hxx>
 #include <svl/sigstruct.hxx>
+#include <svl/cryptosign.hxx>
 
 #include <context.h>
 #include <data.h>
@@ -184,8 +185,9 @@ 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);
+
+    // get hex fingerprint as byte array
+    return 
comphelper::containerToSequence<sal_Int8>(svl::crypto::DecodeHexString(keyId));
 }
 
 Sequence<sal_Int8> CertificateImpl::getSHA256Thumbprint()

Reply via email to