1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <digitalsignaturesdialog.hxx>
25 #include <certificatechooser.hxx>
26 #include <certificateviewer.hxx>
27 #include <biginteger.hxx>
28 #include <sax/tools/converter.hxx>
29 #include <tools/diagnose_ex.h>
30 
31 #include <com/sun/star/embed/XStorage.hpp>
32 #include <com/sun/star/embed/ElementModes.hpp>
33 #include <com/sun/star/embed/StorageFormats.hpp>
34 #include <com/sun/star/container/XNameAccess.hpp>
35 #include <com/sun/star/lang/XComponent.hpp>
36 #include <com/sun/star/security/NoPasswordException.hpp>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/security/CertificateValidity.hpp>
40 #include <com/sun/star/packages/WrongPasswordException.hpp>
41 #include <com/sun/star/security/CertificateKind.hpp>
42 #include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
43 #include <com/sun/star/system/SystemShellExecute.hpp>
44 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
45 #include <com/sun/star/system/SystemShellExecuteException.hpp>
46 
47 #include <osl/file.hxx>
48 #include <rtl/ustrbuf.hxx>
49 #include <rtl/uri.hxx>
50 #include <sal/log.hxx>
51 
52 #include <tools/date.hxx>
53 #include <tools/time.hxx>
54 #include <unotools/datetime.hxx>
55 
56 #include <bitmaps.hlst>
57 #include <strings.hrc>
58 #include <resourcemanager.hxx>
59 #include <comphelper/xmlsechelper.hxx>
60 #include <comphelper/processfactory.hxx>
61 
62 #include <vcl/weld.hxx>
63 #include <vcl/svapp.hxx>
64 #include <unotools/configitem.hxx>
65 
66 #ifdef _WIN32
67 #include <o3tl/char16_t2wchar_t.hxx>
68 #include <prewin.h>
69 #include <Shlobj.h>
70 #endif
71 
72 using namespace comphelper;
73 using namespace css::security;
74 using namespace css::uno;
75 using namespace css;
76 
77 namespace
78 {
79     class SaveODFItem: public utl::ConfigItem
80     {
81     private:
82         sal_Int16 m_nODF;
83 
84         virtual void ImplCommit() override;
85 
86     public:
87     virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override;
88         SaveODFItem();
89         //See group ODF in Common.xcs
90         bool isLessODF1_2() const
91         {
92             return m_nODF < 3;
93         }
94     };
95 
96     void SaveODFItem::ImplCommit() {}
97     void SaveODFItem::Notify( const css::uno::Sequence< OUString >& ) {}
98 
99     SaveODFItem::SaveODFItem(): utl::ConfigItem("Office.Common/Save"), m_nODF(0)
100     {
101         OUString sDef("ODF/DefaultVersion");
102         Sequence< css::uno::Any > aValues = GetProperties( Sequence<OUString>(&sDef,1) );
103         if ( aValues.getLength() != 1)
104             throw uno::RuntimeException(
105                 "[xmlsecurity] Could not open property Office.Common/Save/ODF/DefaultVersion",
106                 nullptr);
107 
108         sal_Int16 nTmp = 0;
109         if ( !(aValues[0] >>= nTmp) )
110             throw uno::RuntimeException(
111                 "[xmlsecurity]SaveODFItem::SaveODFItem(): Wrong Type!",
112                 nullptr );
113 
114         m_nODF = nTmp;
115     }
116 }
117 
118 DigitalSignaturesDialog::DigitalSignaturesDialog(
119     weld::Window* pParent,
120     const uno::Reference< uno::XComponentContext >& rxCtx, DocumentSignatureMode eMode,
121     bool bReadOnly, const OUString& sODFVersion, bool bHasDocumentSignature)
122     : GenericDialogController(pParent, "xmlsec/ui/digitalsignaturesdialog.ui", "DigitalSignaturesDialog")
123     , maSignatureManager(rxCtx, eMode)
124     , m_sODFVersion (sODFVersion)
125     , m_bHasDocumentSignature(bHasDocumentSignature)
126     , m_bWarningShowSignMacro(false)
127     , m_xHintDocFT(m_xBuilder->weld_label("dochint"))
128     , m_xHintBasicFT(m_xBuilder->weld_label("macrohint"))
129     , m_xHintPackageFT(m_xBuilder->weld_label("packagehint"))
130     , m_xSignaturesLB(m_xBuilder->weld_tree_view("signatures"))
131     , m_xSigsValidImg(m_xBuilder->weld_image("validimg"))
132     , m_xSigsValidFI(m_xBuilder->weld_label("validft"))
133     , m_xSigsInvalidImg(m_xBuilder->weld_image("invalidimg"))
134     , m_xSigsInvalidFI(m_xBuilder->weld_label("invalidft"))
135     , m_xSigsNotvalidatedImg(m_xBuilder->weld_image("notvalidatedimg"))
136     , m_xSigsNotvalidatedFI(m_xBuilder->weld_label("notvalidatedft"))
137     , m_xSigsOldSignatureImg(m_xBuilder->weld_image("oldsignatureimg"))
138     , m_xSigsOldSignatureFI(m_xBuilder->weld_label("oldsignatureft"))
139     , m_xAdESCompliantCB(m_xBuilder->weld_check_button("adescompliant"))
140     , m_xViewBtn(m_xBuilder->weld_button("view"))
141     , m_xAddBtn(m_xBuilder->weld_button("sign"))
142     , m_xRemoveBtn(m_xBuilder->weld_button("remove"))
143     , m_xStartCertMgrBtn(m_xBuilder->weld_button("start_certmanager"))
144     , m_xCloseBtn(m_xBuilder->weld_button("close"))
145 {
146     m_bAdESCompliant = !DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
147 
148     auto nControlWidth = m_xSignaturesLB->get_approximate_digit_width() * 105;
149     m_xSignaturesLB->set_size_request(nControlWidth, m_xSignaturesLB->get_height_rows(10));
150 
151     // Give the first column 6 percent, try to distribute the rest equally.
152     std::vector<int> aWidths;
153     aWidths.push_back(6*nControlWidth/100);
154     auto nColWidth = (nControlWidth - aWidths[0]) / 4;
155     aWidths.push_back(nColWidth);
156     aWidths.push_back(nColWidth);
157     aWidths.push_back(nColWidth);
158     m_xSignaturesLB->set_column_fixed_widths(aWidths);
159 
160     mbVerifySignatures = true;
161     mbSignaturesChanged = false;
162 
163     m_xSignaturesLB->connect_changed( LINK( this, DigitalSignaturesDialog, SignatureHighlightHdl ) );
164     m_xSignaturesLB->connect_row_activated( LINK( this, DigitalSignaturesDialog, SignatureSelectHdl ) );
165 
166     m_xAdESCompliantCB->connect_toggled( LINK( this, DigitalSignaturesDialog, AdESCompliantCheckBoxHdl ) );
167     m_xAdESCompliantCB->set_active(m_bAdESCompliant);
168 
169     m_xViewBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, ViewButtonHdl ) );
170     m_xViewBtn->set_sensitive(false);
171 
172     m_xAddBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, AddButtonHdl ) );
173     if ( bReadOnly  )
174         m_xAddBtn->set_sensitive(false);
175 
176     m_xRemoveBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, RemoveButtonHdl ) );
177     m_xRemoveBtn->set_sensitive(false);
178 
179     m_xStartCertMgrBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, CertMgrButtonHdl ) );
180 
181     m_xCloseBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, OKButtonHdl) );
182 
183     switch( maSignatureManager.getSignatureMode() )
184     {
185         case DocumentSignatureMode::Content:
186             m_xHintDocFT->show();
187             break;
188         case DocumentSignatureMode::Macros:
189             m_xHintBasicFT->show();
190             break;
191         case DocumentSignatureMode::Package:
192             m_xHintPackageFT->show();
193             break;
194     }
195 }
196 
197 DigitalSignaturesDialog::~DigitalSignaturesDialog()
198 {
199 }
200 
201 bool DigitalSignaturesDialog::Init()
202 {
203     bool bInit = maSignatureManager.init();
204 
205     SAL_WARN_IF( !bInit, "xmlsecurity.dialogs", "Error initializing security context!" );
206 
207     if ( bInit )
208     {
209         maSignatureManager.getSignatureHelper().SetStartVerifySignatureHdl( LINK( this, DigitalSignaturesDialog, StartVerifySignatureHdl ) );
210     }
211 
212     return bInit;
213 }
214 
215 void DigitalSignaturesDialog::SetStorage( const css::uno::Reference < css::embed::XStorage >& rxStore )
216 {
217     if (!rxStore.is())
218     {
219         // PDF supports AdES.
220         m_bAdESCompliant = true;
221         m_xAdESCompliantCB->set_active(m_bAdESCompliant);
222         return;
223     }
224 
225     maSignatureManager.setStore(rxStore);
226     maSignatureManager.getSignatureHelper().SetStorage( maSignatureManager.getStore(), m_sODFVersion);
227 }
228 
229 void DigitalSignaturesDialog::SetSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream )
230 {
231     maSignatureManager.setSignatureStream(rxStream);
232 }
233 
234 bool DigitalSignaturesDialog::canAddRemove()
235 {
236     //FIXME: this func needs some cleanup, such as real split between
237     //'canAdd' and 'canRemove' case
238     bool ret = true;
239 
240     uno::Reference<container::XNameAccess> xNameAccess = maSignatureManager.getStore();
241     if (xNameAccess.is() && xNameAccess->hasByName("[Content_Types].xml"))
242         // It's always possible to append an OOXML signature.
243         return ret;
244 
245     if (!maSignatureManager.getStore().is())
246         // It's always possible to append a PDF signature.
247         return ret;
248 
249     OSL_ASSERT(maSignatureManager.getStore().is());
250     bool bDoc1_1 = DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
251     SaveODFItem item;
252     bool bSave1_1 = item.isLessODF1_2();
253 
254     // see specification
255     //cvs: specs/www/appwide/security/Electronic_Signatures_and_Security.sxw
256     //Paragraph 'Behavior with regard to ODF 1.2'
257     //For both, macro and document
258     if ( (!bSave1_1  && bDoc1_1) || (bSave1_1 && bDoc1_1) )
259     {
260         //#4
261         std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
262                                                   VclMessageType::Warning, VclButtonsType::Ok,
263                                                   XsResId(STR_XMLSECDLG_OLD_ODF_FORMAT)));
264         xBox->run();
265         ret = false;
266     }
267 
268     //As of OOo 3.2 the document signature includes in macrosignatures.xml. That is
269     //adding a macro signature will break an existing document signature.
270     //The sfx2 will remove the documentsignature when the user adds a macro signature
271     if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Macros
272         && ret)
273     {
274         if (m_bHasDocumentSignature && !m_bWarningShowSignMacro)
275         {
276             //The warning says that the document signatures will be removed if the user
277             //continues. He can then either press 'OK' or 'NO'
278             //It the user presses 'Add' or 'Remove' several times then, then the warning
279             //is shown every time until the user presses 'OK'. From then on, the warning
280             //is not displayed anymore as long as the signatures dialog is alive.
281             std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
282                                                       VclMessageType::Question, VclButtonsType::YesNo,
283                                                       XsResId(STR_XMLSECDLG_QUERY_REMOVEDOCSIGNBEFORESIGN)));
284             if (xBox->run() == RET_NO)
285                 ret = false;
286             else
287                 m_bWarningShowSignMacro = true;
288 
289         }
290     }
291     return ret;
292 }
293 
294 bool DigitalSignaturesDialog::canAdd()
295 {
296     return canAddRemove();
297 }
298 
299 bool DigitalSignaturesDialog::canRemove()
300 {
301     bool bRet = true;
302 
303     if ( maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content )
304     {
305         std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
306                                                   VclMessageType::Question, VclButtonsType::YesNo,
307                                                   XsResId(STR_XMLSECDLG_QUERY_REALLYREMOVE)));
308         short nDlgRet = xBox->run();
309         bRet = ( nDlgRet == RET_YES );
310     }
311 
312     return (bRet && canAddRemove());
313 }
314 
315 short DigitalSignaturesDialog::run()
316 {
317     // Verify Signatures and add certificates to ListBox...
318     mbVerifySignatures = true;
319     ImplGetSignatureInformations(/*bUseTempStream=*/false, /*bCacheLastSignature=*/true);
320     ImplFillSignaturesBox();
321 
322     // FIXME: Disable the "Use XAdES compliant signatures" checkbox if it is irrelevant. If it is
323     // enabled, set its initial state based on existing signatures, if any.
324 
325     // If it is OOXML, the checkbox is irrelevant.
326 
327     // How to find out here whether it is OOXML? I don't want to create a SignatureStreamHelper and
328     // check its nStorageFormat as that seems overly complicated and seems to have weird indirect
329     // consequences, as I noticed when I tried to use DocumentSignatureManager::IsXAdESRelevant()
330     // (which now is in #if 0).
331 
332     if (!maSignatureManager.getCurrentSignatureInformations().empty())
333     {
334         // If the document has only SHA-1 signatures we probably want it to stay that way?
335     }
336 
337     // Only verify once, content will not change.
338     // But for refreshing signature information, StartVerifySignatureHdl will be called after each add/remove
339     mbVerifySignatures = false;
340 
341     return GenericDialogController::run();
342 }
343 
344 IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureHighlightHdl, weld::TreeView&, void)
345 {
346     bool bSel = m_xSignaturesLB->get_selected_index() != -1;
347     m_xViewBtn->set_sensitive( bSel );
348     if ( m_xAddBtn->get_sensitive() ) // not read only
349         m_xRemoveBtn->set_sensitive( bSel );
350 }
351 
352 IMPL_LINK_NOARG(DigitalSignaturesDialog, OKButtonHdl, weld::Button&, void)
353 {
354     if (mbSignaturesChanged)
355         maSignatureManager.write(m_bAdESCompliant);
356 
357     m_xDialog->response(RET_OK);
358 }
359 
360 IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureSelectHdl, weld::TreeView&, bool)
361 {
362     ImplShowSignaturesDetails();
363     return true;
364 }
365 
366 IMPL_LINK_NOARG(DigitalSignaturesDialog, AdESCompliantCheckBoxHdl, weld::Toggleable&, void)
367 {
368     m_bAdESCompliant = m_xAdESCompliantCB->get_active();
369 }
370 
371 IMPL_LINK_NOARG(DigitalSignaturesDialog, ViewButtonHdl, weld::Button&, void)
372 {
373     ImplShowSignaturesDetails();
374 }
375 
376 IMPL_LINK_NOARG(DigitalSignaturesDialog, AddButtonHdl, weld::Button&, void)
377 {
378     if( ! canAdd())
379         return;
380     try
381     {
382         std::vector<uno::Reference<xml::crypto::XXMLSecurityContext>> xSecContexts;
383         xSecContexts.push_back(maSignatureManager.getSecurityContext());
384         // Gpg signing is only possible with ODF >= 1.2 documents
385         if (DocumentSignatureHelper::CanSignWithGPG(maSignatureManager.getStore(), m_sODFVersion))
386             xSecContexts.push_back(maSignatureManager.getGpgSecurityContext());
387 
388         CertificateChooser aChooser(m_xDialog.get(), xSecContexts, UserAction::Sign);
389         if (aChooser.run() == RET_OK)
390         {
391             sal_Int32 nSecurityId;
392             if (!maSignatureManager.add(aChooser.GetSelectedCertificates()[0], aChooser.GetSelectedSecurityContext(),
393                                         aChooser.GetDescription(), nSecurityId, m_bAdESCompliant))
394                 return;
395             mbSignaturesChanged = true;
396 
397             xml::crypto::SecurityOperationStatus nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
398 
399             if (maSignatureManager.getStore().is())
400                 // In the PDF case the signature information is only available after parsing.
401                 nStatus = maSignatureManager.getSignatureHelper().GetSignatureInformation( nSecurityId ).nStatus;
402 
403             if ( nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED )
404             {
405                 mbSignaturesChanged = true;
406 
407                 // Can't simply remember current information, need parsing for getting full information :(
408                 // We need to verify the signatures again, otherwise the status in the signature information
409                 // will not contain
410                 // SecurityOperationStatus_OPERATION_SUCCEEDED
411                 mbVerifySignatures = true;
412                 ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
413                 ImplFillSignaturesBox();
414             }
415         }
416     }
417     catch ( uno::Exception& )
418     {
419         TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "adding a signature!" );
420         std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
421                                                   VclMessageType::Error, VclButtonsType::Ok,
422                                                   XsResId(STR_XMLSECDLG_SIGNING_FAILED)));
423         xBox->run();
424         // Don't keep invalid entries...
425         ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
426         ImplFillSignaturesBox();
427     }
428 }
429 
430 IMPL_LINK_NOARG(DigitalSignaturesDialog, RemoveButtonHdl, weld::Button&, void)
431 {
432     if (!canRemove())
433         return;
434     int nEntry = m_xSignaturesLB->get_selected_index();
435     if (nEntry == -1)
436         return;
437 
438     try
439     {
440         sal_uInt16 nSelected = m_xSignaturesLB->get_id(nEntry).toUInt32();
441         maSignatureManager.remove(nSelected);
442 
443         mbSignaturesChanged = true;
444 
445         ImplFillSignaturesBox();
446     }
447     catch ( uno::Exception& )
448     {
449         TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "Exception while removing a signature!" );
450         // Don't keep invalid entries...
451         ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/true);
452         ImplFillSignaturesBox();
453     }
454 }
455 
456 IMPL_LINK_NOARG(DigitalSignaturesDialog, CertMgrButtonHdl, weld::Button&, void)
457 {
458 #ifdef _WIN32
459     // FIXME: call GpgME::dirInfo("bindir") somewhere in
460     // SecurityEnvironmentGpg or whatnot
461     // FIXME: perhaps poke GpgME for uiserver, and hope it returns something useful?
462     static const std::u16string_view aGUIServers[] = { u"Gpg4win\\kleopatra.exe",
463                                                    u"Gpg4win\\bin\\kleopatra.exe",
464                                                    u"GNU\\GnuPG\\kleopatra.exe",
465                                                    u"GNU\\GnuPG\\launch-gpa.exe",
466                                                    u"GNU\\GnuPG\\gpa.exe",
467                                                    u"GnuPG\\bin\\gpa.exe",
468                                                    u"GNU\\GnuPG\\bin\\kleopatra.exe",
469                                                    u"GNU\\GnuPG\\bin\\launch-gpa.exe",
470                                                    u"GNU\\GnuPG\\bin\\gpa.exe",
471                                                  };
472     static const OUString aPath = [] {
473         OUString sRet;
474         PWSTR sPath = nullptr;
475         HRESULT hr
476             = SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, KF_FLAG_DEFAULT, nullptr, &sPath);
477         if (SUCCEEDED(hr))
478         {
479             sRet = o3tl::toU(sPath);
480             CoTaskMemFree(sPath);
481         }
482         return sRet;
483     }();
484     if (aPath.isEmpty())
485         return;
486 #else
487     static const std::u16string_view aGUIServers[] = { u"kleopatra", u"seahorse", u"gpa", u"kgpg" };
488     const char* cPath = getenv("PATH");
489     if (!cPath)
490         return;
491     OUString aPath(cPath, strlen(cPath), osl_getThreadTextEncoding());
492 #endif
493 
494     OUString sFoundGUIServer, sExecutable;
495 
496     for ( auto const &rServer : aGUIServers )
497     {
498         osl::FileBase::RC searchError = osl::File::searchFileURL(OUString(rServer), aPath, sFoundGUIServer );
499         if (searchError == osl::FileBase::E_None)
500         {
501             osl::File::getSystemPathFromFileURL( sFoundGUIServer, sExecutable );
502             break;
503         }
504 
505     }
506 
507     if ( !sExecutable.isEmpty() )
508     {
509         uno::Reference< uno::XComponentContext > xContext =
510             ::comphelper::getProcessComponentContext();
511         uno::Reference< css::system::XSystemShellExecute > xSystemShell(
512                  css::system::SystemShellExecute::create(xContext) );
513 
514         xSystemShell->execute( sExecutable, OUString(),
515             css::system::SystemShellExecuteFlags::DEFAULTS );
516     }
517     else
518     {
519         std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
520                                                       VclMessageType::Info, VclButtonsType::Ok,
521                                                       XsResId(STR_XMLSECDLG_NO_CERT_MANAGER)));
522         xInfoBox->run();
523     }
524 }
525 
526 IMPL_LINK_NOARG(DigitalSignaturesDialog, StartVerifySignatureHdl, LinkParamNone*, bool)
527 {
528     return mbVerifySignatures;
529 }
530 
531 void DigitalSignaturesDialog::ImplFillSignaturesBox()
532 {
533     m_xSignaturesLB->clear();
534 
535     size_t nInfos = maSignatureManager.getCurrentSignatureInformations().size();
536     size_t nValidSigs = 0, nValidCerts = 0;
537     bool bAllNewSignatures = true;
538     bool bSomePartial = false;
539 
540     if( nInfos )
541     {
542         for( size_t n = 0; n < nInfos; ++n )
543         {
544             DocumentSignatureAlgorithm mode = DocumentSignatureHelper::getDocumentAlgorithm(
545                 m_sODFVersion, maSignatureManager.getCurrentSignatureInformations()[n]);
546             std::vector< OUString > aElementsToBeVerified;
547             if (maSignatureManager.getStore().is())
548                 aElementsToBeVerified = DocumentSignatureHelper::CreateElementList(maSignatureManager.getStore(), maSignatureManager.getSignatureMode(), mode);
549 
550             const SignatureInformation& rInfo = maSignatureManager.getCurrentSignatureInformations()[n];
551             uno::Reference< css::security::XCertificate > xCert = getCertificate(rInfo);
552 
553             OUString aSubject;
554             OUString aIssuer;
555             OUString aDateTimeStr;
556             OUString aDescription;
557             OUString aType;
558 
559             bool bCertValid = false;
560             if( xCert.is() )
561             {
562                 //check the validity of the cert
563                 try {
564                     sal_Int32 certResult = getSecurityEnvironmentForCertificate(xCert)->verifyCertificate(xCert,
565                                                                                                           Sequence<uno::Reference<security::XCertificate> >());
566 
567                     bCertValid = certResult == css::security::CertificateValidity::VALID;
568                     if ( bCertValid )
569                         nValidCerts++;
570 
571                 } catch (css::uno::SecurityException& ) {
572                     OSL_FAIL("Verification of certificate failed");
573                     bCertValid = false;
574                 }
575 
576                 aSubject = xmlsec::GetContentPart( xCert->getSubjectName(), xCert->getCertificateKind() );
577                 aIssuer = xmlsec::GetContentPart( xCert->getIssuerName(), xCert->getCertificateKind() );
578             }
579             else if (!rInfo.ouGpgCertificate.isEmpty())
580             {
581                 // In case we don't have the gpg key locally, get some data from the document
582                 aIssuer = rInfo.ouGpgOwner;
583             }
584 
585             aDateTimeStr = utl::GetDateTimeString( rInfo.stDateTime );
586             aDescription = rInfo.ouDescription;
587 
588             // Decide type string.
589             if (maSignatureManager.getStore().is())
590             {
591                 // OpenPGP
592                 if (!rInfo.ouGpgCertificate.isEmpty())
593                     aType = "OpenPGP";
594                 // XML based: XAdES or not.
595                 else if (rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->CertDigest.isEmpty())
596                     aType = "XAdES";
597                 else
598                     aType = "XML-DSig";
599             }
600             else
601             {
602                 // Assume PDF: PAdES or not.
603                 if (rInfo.bHasSigningCertificate)
604                     aType = "PAdES";
605                 else
606                     aType = "PDF";
607             }
608 
609             bool bSigValid = rInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
610 
611             if ( bSigValid )
612             {
613                 if (maSignatureManager.getStore().is())
614                 {
615                     // ZIP based.
616                     bSigValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
617                           aElementsToBeVerified, rInfo, mode);
618                 }
619                 else
620                 {
621                     // Assume PDF.
622                     bSigValid = !rInfo.bPartialDocumentSignature;
623                 }
624 
625                 if( bSigValid )
626                     nValidSigs++;
627                 else
628                 {
629                     bSomePartial = true;
630                 }
631             }
632 
633             OUString sImage;
634             if (!bSigValid)
635             {
636                 sImage = BMP_SIG_INVALID;
637             }
638             else if (!bCertValid)
639             {
640                 sImage = BMP_SIG_NOT_VALIDATED;
641             }
642             //Check if the signature is a "old" document signature, that is, which was created
643             //by a version of OOo previous to 3.2
644             // If there is no storage, then it's pointless to check storage
645             // stream references.
646             else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content
647                 && (maSignatureManager.getStore().is() && !DocumentSignatureHelper::isOOo3_2_Signature(
648                 maSignatureManager.getCurrentSignatureInformations()[n])))
649             {
650                 sImage = BMP_SIG_NOT_VALIDATED;
651                 bAllNewSignatures = false;
652             }
653             else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content
654                 && DocumentSignatureHelper::isOOo3_2_Signature(
655                 maSignatureManager.getCurrentSignatureInformations()[n]))
656             {
657                 sImage = BMP_SIG_VALID;
658             }
659             else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Macros)
660             {
661                 sImage = BMP_SIG_VALID;
662             }
663 
664             m_xSignaturesLB->insert(nullptr, n, nullptr, nullptr,
665                                     &sImage, nullptr, false, nullptr);
666             m_xSignaturesLB->set_text(n, aSubject, 1);
667             m_xSignaturesLB->set_text(n, aIssuer, 2);
668             m_xSignaturesLB->set_text(n, aDateTimeStr, 3);
669             m_xSignaturesLB->set_text(n, aDescription, 4);
670             m_xSignaturesLB->set_text(n, aType, 5);
671             m_xSignaturesLB->set_id(n, OUString::number(n)); // misuse user data as index
672         }
673     }
674 
675     bool bAllSigsValid = (nValidSigs == nInfos);
676     bool bAllCertsValid = (nValidCerts == nInfos);
677     bool bShowValidState = nInfos && (bAllSigsValid && bAllCertsValid && bAllNewSignatures);
678 
679     m_xSigsValidImg->set_visible( bShowValidState);
680     m_xSigsValidFI->set_visible( bShowValidState );
681 
682     bool bShowInvalidState = nInfos && !bAllSigsValid;
683 
684     m_xSigsInvalidImg->set_visible( bShowInvalidState && !bSomePartial);
685     m_xSigsInvalidFI->set_visible( bShowInvalidState && !bSomePartial);
686 
687     bool bShowNotValidatedState = nInfos && bAllSigsValid && !bAllCertsValid;
688 
689     m_xSigsNotvalidatedImg->set_visible(bShowNotValidatedState);
690     m_xSigsNotvalidatedFI->set_visible(bShowNotValidatedState);
691 
692     //bAllNewSignatures is always true if we are not in document mode
693     bool bShowOldSignature = nInfos && bAllSigsValid && bAllCertsValid && !bAllNewSignatures;
694     m_xSigsOldSignatureImg->set_visible(bShowOldSignature || bSomePartial);
695     m_xSigsOldSignatureFI->set_visible(bShowOldSignature || bSomePartial);
696 
697     SignatureHighlightHdl(*m_xSignaturesLB);
698 }
699 
700 uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(const SignatureInformation& rInfo)
701 {
702     uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = maSignatureManager.getSecurityEnvironment();
703     uno::Reference<xml::crypto::XSecurityEnvironment> xGpgSecEnv = maSignatureManager.getGpgSecurityEnvironment();
704     uno::Reference<security::XCertificate> xCert;
705 
706     //First we try to get the certificate which is embedded in the XML Signature
707     if (xSecEnv.is() && rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->X509Certificate.isEmpty())
708         xCert = xSecEnv->createCertificateFromAscii(rInfo.GetSigningCertificate()->X509Certificate);
709     else {
710         //There must be an embedded certificate because we use it to get the
711         //issuer name. We cannot use /Signature/KeyInfo/X509Data/X509IssuerName
712         //because it could be modified by an attacker. The issuer is displayed
713         //in the digital signature dialog.
714         //Comparing the X509IssuerName with the one from the X509Certificate in order
715         //to find out if the X509IssuerName was modified does not work. See #i62684
716         SAL_WARN( "xmlsecurity.dialogs", "Could not find embedded certificate!");
717     }
718 
719     //In case there is no embedded certificate we try to get it from a local store
720     if (!xCert.is() && xSecEnv.is() && rInfo.GetSigningCertificate())
721     {
722         xCert = xSecEnv->getCertificate(rInfo.GetSigningCertificate()->X509IssuerName,
723             xmlsecurity::numericStringToBigInteger(rInfo.GetSigningCertificate()->X509SerialNumber));
724     }
725     if (!xCert.is() && xGpgSecEnv.is() && !rInfo.ouGpgKeyID.isEmpty())
726         xCert = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger(u"") );
727 
728     SAL_WARN_IF( !xCert.is(), "xmlsecurity.dialogs", "Certificate not found and can't be created!" );
729 
730     return xCert;
731 }
732 
733 uno::Reference<xml::crypto::XSecurityEnvironment> DigitalSignaturesDialog::getSecurityEnvironmentForCertificate(const uno::Reference<security::XCertificate>& xCert)
734 {
735     if (xCert->getCertificateKind() == CertificateKind_OPENPGP)
736         return maSignatureManager.getGpgSecurityEnvironment();
737     else if (xCert->getCertificateKind() == CertificateKind_X509)
738         return maSignatureManager.getSecurityEnvironment();
739 
740     throw RuntimeException("Unknown certificate kind");
741 }
742 
743 //If bUseTempStream is true then the temporary signature stream is used.
744 //Otherwise the real signature stream is used.
745 void DigitalSignaturesDialog::ImplGetSignatureInformations(bool bUseTempStream, bool bCacheLastSignature)
746 {
747     maSignatureManager.read(bUseTempStream, bCacheLastSignature);
748     mbVerifySignatures = false;
749 }
750 
751 void DigitalSignaturesDialog::ImplShowSignaturesDetails()
752 {
753     int nEntry = m_xSignaturesLB->get_selected_index();
754     if (nEntry == -1)
755         return;
756 
757     sal_uInt16 nSelected = m_xSignaturesLB->get_id(nEntry).toUInt32();
758     const SignatureInformation& rInfo = maSignatureManager.getCurrentSignatureInformations()[ nSelected ];
759     uno::Reference<security::XCertificate> xCert = getCertificate(rInfo);
760 
761     if ( xCert.is() )
762     {
763         uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = getSecurityEnvironmentForCertificate(xCert);
764         CertificateViewer aViewer(m_xDialog.get(), xSecEnv, xCert, false, nullptr);
765         aViewer.run();
766     }
767     else
768     {
769         std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
770                                                       VclMessageType::Info, VclButtonsType::Ok,
771                                                       XsResId(STR_XMLSECDLG_NO_CERT_FOUND)));
772         xInfoBox->run();
773     }
774 }
775 
776 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
777