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()