QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsauthcertificateinfo.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsauthcertificateinfo.cpp
3 ---------------------
4 begin : April 26, 2015
5 copyright : (C) 2015 by Boundless Spatial, Inc. USA
6 author : Larry Shaffer
7 email : lshaffer at boundlessgeo dot com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17
19#include "moc_qgsauthcertificateinfo.cpp"
20#include "ui_qgsauthcertificateinfo.h"
21
22#include <QtCrypto>
23#include <QDialogButtonBox>
24#include <QLineEdit>
25#include <QPlainTextEdit>
26#include <QPushButton>
27#include <QTextEdit>
28
29#include "qgsapplication.h"
30#include "qgsauthcertutils.h"
31#include "qgsauthguiutils.h"
32#include "qgsauthmanager.h"
33#include "qgslogger.h"
34
35
36static void setItemBold_( QTreeWidgetItem *item )
37{
38 item->setFirstColumnSpanned( true );
39 QFont secf( item->font( 0 ) );
40 secf.setBold( true );
41 item->setFont( 0, secf );
42}
43
44static void removeChildren_( QTreeWidgetItem *item )
45{
46 const auto constTakeChildren = item->takeChildren();
47 for ( QTreeWidgetItem *child : constTakeChildren )
48 {
49 delete child;
50 }
51}
52
53QgsAuthCertInfo::QgsAuthCertInfo( const QSslCertificate &cert,
54 bool manageCertTrust,
55 QWidget *parent,
56 const QList<QSslCertificate> &connectionCAs )
57 : QWidget( parent )
58 , mConnectionCAs( connectionCAs )
59 , mDefaultItemForeground( QBrush() )
60 , mManageTrust( manageCertTrust )
61 , mTrustCacheRebuilt( false )
62 , mDefaultTrustPolicy( QgsAuthCertUtils::DefaultTrust )
63 , mCurrentTrustPolicy( QgsAuthCertUtils::DefaultTrust )
64
65{
66 if ( QgsApplication::authManager()->isDisabled() )
67 {
68 mAuthNotifyLayout = new QVBoxLayout;
69 this->setLayout( mAuthNotifyLayout );
70 mAuthNotify = new QLabel( QgsApplication::authManager()->disabledMessage(), this );
71 mAuthNotifyLayout->addWidget( mAuthNotify );
72 }
73 else
74 {
75 setupUi( this );
76 connect( btnSaveTrust, &QToolButton::clicked, this, &QgsAuthCertInfo::btnSaveTrust_clicked );
77
78 lblError->setHidden( true );
79
80 treeHierarchy->setRootIsDecorated( false );
81
82 connect( treeHierarchy, &QTreeWidget::currentItemChanged,
83 this, &QgsAuthCertInfo::currentCertItemChanged );
84
85 mCaCertsCache = QgsApplication::authManager()->caCertsCache();
86
87 setUpCertDetailsTree();
88
89 grpbxTrust->setVisible( mManageTrust );
90
91 // trust policy is still queried, even if not managing the policy, so public getter will work
92 mDefaultTrustPolicy = QgsApplication::authManager()->defaultCertTrustPolicy();
93 mCurrentTrustPolicy = QgsAuthCertUtils::DefaultTrust;
94
95 bool res;
96 res = populateQcaCertCollection();
97 if ( res )
98 res = setQcaCertificate( cert );
99 if ( res )
100 res = populateCertChain();
101 if ( res )
102 setCertHierarchy();
103
104 connect( cmbbxTrust, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ),
105 this, &QgsAuthCertInfo::currentPolicyIndexChanged );
106 }
107}
108
109void QgsAuthCertInfo::setupError( const QString &msg )
110{
111 lblError->setVisible( true );
112 QString out = tr( "<b>Setup ERROR:</b>\n\n" );
113 out += msg;
114 lblError->setText( out );
115 lblError->setStyleSheet( QgsAuthGuiUtils::redTextStyleSheet() );
116}
117
118void QgsAuthCertInfo::currentCertItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
119{
120 Q_UNUSED( previous )
121 updateCurrentCert( current );
122}
123
124void QgsAuthCertInfo::updateCurrentCert( QTreeWidgetItem *item )
125{
126 if ( !item )
127 item = treeHierarchy->currentItem();
128 if ( !item )
129 return;
130
131 const int indx( item->data( 0, Qt::UserRole ).toInt() );
132 updateCurrentCertInfo( indx );
133}
134
135bool QgsAuthCertInfo::populateQcaCertCollection()
136{
137 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
138 for ( int i = 0; i < certpairs.size(); ++i )
139 {
140 QCA::ConvertResult res;
141 const QCA::Certificate acert = QCA::Certificate::fromPEM( certpairs.at( i ).second.toPem(), &res, QStringLiteral( "qca-ossl" ) );
142 if ( res == QCA::ConvertGood && !acert.isNull() )
143 {
144 mCaCerts.addCertificate( acert );
145 }
146 }
147 if ( !mConnectionCAs.isEmpty() )
148 {
149 const auto constMConnectionCAs = mConnectionCAs;
150 for ( const QSslCertificate &cert : constMConnectionCAs )
151 {
152 QCA::ConvertResult res;
153 const QCA::Certificate acert = QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral( "qca-ossl" ) );
154 if ( res == QCA::ConvertGood && !acert.isNull() )
155 {
156 mCaCerts.addCertificate( acert );
157 }
158 }
159 }
160
161 if ( mCaCerts.certificates().empty() )
162 {
163 setupError( tr( "Could not populate QCA certificate collection" ) );
164 return false;
165 }
166 return true;
167}
168
169bool QgsAuthCertInfo::setQcaCertificate( const QSslCertificate &cert )
170{
171 QCA::ConvertResult res;
172 mCert = QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral( "qca-ossl" ) );
173 if ( res != QCA::ConvertGood || mCert.isNull() )
174 {
175 setupError( tr( "Could not set QCA certificate" ) );
176 return false;
177 }
178 return true;
179}
180
181bool QgsAuthCertInfo::populateCertChain()
182{
183 const QCA::CertificateChain certchain( mCert );
184 QCA::Validity valid;
185 mACertChain = certchain.complete( mCaCerts.certificates(), &valid );
186 if ( valid != QCA::ValidityGood && valid != QCA::ErrorInvalidCA )
187 {
188 // invalid CAs are skipped to allow an incomplete chain
189 setupError( tr( "Invalid population of QCA certificate chain.<br><br>"
190 "Validity message: %1" ).arg( QgsAuthCertUtils::qcaValidityMessage( valid ) ) );
191 return false;
192 }
193
194 if ( mACertChain.isEmpty() )
195 {
196 QgsDebugError( QStringLiteral( "Could not populate QCA certificate chain" ) );
197 mACertChain = certchain;
198 }
199
200 if ( !mACertChain.last().isSelfSigned() )
201 {
202 // chain is incomplete, add null certificate to signify local missing parent CA
203 mACertChain.append( QCA::Certificate() );
204 }
205
206 // mirror chain to QSslCertificate
207 const auto constMACertChain = mACertChain;
208 for ( const QCA::Certificate &cert : constMACertChain )
209 {
210 QSslCertificate qcert;
211 if ( !cert.isNull() )
212 {
213 qcert = QSslCertificate( cert.toPEM().toLatin1() );
214 }
215 mQCertChain.append( qcert );
216 }
217 return true;
218}
219
220void QgsAuthCertInfo::setCertHierarchy()
221{
222 QListIterator<QSslCertificate> it( mQCertChain );
223 it.toBack();
224 int i = mQCertChain.size();
225 QTreeWidgetItem *item = nullptr;
226 QTreeWidgetItem *previtem = nullptr;
227 while ( it.hasPrevious() )
228 {
229 const QSslCertificate cert( it.previous() );
230 const bool missingCA = cert.isNull();
231 QString cert_source;
232 if ( missingCA && it.hasPrevious() )
233 {
234 cert_source = QgsAuthCertUtils::resolvedCertName( it.peekPrevious(), true );
235 cert_source += QStringLiteral( " (%1)" ).arg( tr( "Missing CA" ) );
236 }
237 else
238 {
239 cert_source = QgsAuthCertUtils::resolvedCertName( cert );
240 const QString sha = QgsAuthCertUtils::shaHexForCert( cert );
241 if ( mCaCertsCache.contains( sha ) )
242 {
243 const QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate > &certpair( mCaCertsCache.value( sha ) );
244 cert_source += QStringLiteral( " (%1)" ).arg( QgsAuthCertUtils::getCaSourceName( certpair.first, true ) );
245 }
246 else if ( mConnectionCAs.contains( cert ) )
247 {
248 cert_source += QStringLiteral( " (%1)" )
250 }
251 }
252
253 if ( !previtem )
254 {
255 item = new QTreeWidgetItem( treeHierarchy, QStringList() << cert_source );
256 }
257 else
258 {
259 item = new QTreeWidgetItem( previtem, QStringList() << cert_source );
260 }
261 if ( missingCA && it.hasPrevious() )
262 {
263 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
264 }
265
266 item->setData( 0, Qt::UserRole, --i );
267
268 if ( mDefaultItemForeground.style() == Qt::NoBrush )
269 {
270 mDefaultItemForeground = item->foreground( 0 );
271 }
272
273 decorateCertTreeItem( cert, QgsApplication::authManager()->certificateTrustPolicy( cert ), item );
274
275 item->setFirstColumnSpanned( true );
276 if ( !previtem )
277 treeHierarchy->addTopLevelItem( item );
278 previtem = item;
279 }
280 treeHierarchy->setCurrentItem( item, 0, QItemSelectionModel::ClearAndSelect );
281 treeHierarchy->expandAll();
282}
283
284void QgsAuthCertInfo::updateCurrentCertInfo( int chainindx )
285{
286 btnSaveTrust->setEnabled( false );
287
288 mCurrentQCert = mQCertChain.at( chainindx );
289 mCurrentACert = mACertChain.at( chainindx );
290
291 if ( mManageTrust )
292 {
293 grpbxTrust->setHidden( mCurrentQCert.isNull() );
294 }
295
296 if ( !mCurrentQCert.isNull() )
297 {
298 const QgsAuthCertUtils::CertTrustPolicy trustpolicy( QgsApplication::authManager()->certificateTrustPolicy( mCurrentQCert ) );
299 mCurrentTrustPolicy = trustpolicy;
300
301 cmbbxTrust->setTrustPolicy( trustpolicy );
302 if ( !QgsAuthCertUtils::certIsViable( mCurrentQCert ) )
303 {
304 cmbbxTrust->setDefaultTrustPolicy( QgsAuthCertUtils::Untrusted );
305 }
306 }
307
308 populateCertInfo();
309}
310
311void QgsAuthCertInfo::setUpCertDetailsTree()
312{
313 treeDetails->setColumnCount( 2 );
314 treeDetails->setHeaderLabels( QStringList() << tr( "Field" ) << tr( "Value" ) );
315 treeDetails->setColumnWidth( 0, 200 );
316
317 QTreeWidgetItem *headeritem = treeDetails->headerItem();
318 headeritem->setTextAlignment( 0, Qt::AlignRight );
319 headeritem->setTextAlignment( 1, Qt::AlignLeft );
320
321 treeDetails->setRootIsDecorated( true );
322 treeDetails->setWordWrap( true );
323
324 // add root items
325 mSecGeneral = new QTreeWidgetItem(
326 treeDetails,
327 QStringList( tr( "General" ) ),
328 static_cast<int>( DetailsSection ) );
329 setItemBold_( mSecGeneral );
330 mSecGeneral->setFirstColumnSpanned( true );
331 mSecGeneral->setFlags( Qt::ItemIsEnabled );
332 mSecGeneral->setExpanded( true );
333 treeDetails->insertTopLevelItem( 0, mSecGeneral );
334
335 mSecDetails = new QTreeWidgetItem(
336 treeDetails,
337 QStringList( tr( "Details" ) ),
338 static_cast<int>( DetailsSection ) );
339 setItemBold_( mSecDetails );
340 mSecDetails->setFirstColumnSpanned( true );
341 mSecDetails->setFlags( Qt::ItemIsEnabled );
342 mSecDetails->setExpanded( false );
343 treeDetails->insertTopLevelItem( 0, mSecDetails );
344
345 // add details groups
346 mGrpSubj = addGroupItem( mSecDetails, tr( "Subject Info" ) );
347 mGrpIssu = addGroupItem( mSecDetails, tr( "Issuer Info" ) );
348 mGrpCert = addGroupItem( mSecDetails, tr( "Certificate Info" ) );
349 mGrpPkey = addGroupItem( mSecDetails, tr( "Public Key Info" ) );
350 mGrpExts = addGroupItem( mSecDetails, tr( "Extensions" ) );
351
352 mSecPemText = new QTreeWidgetItem(
353 treeDetails,
354 QStringList( tr( "PEM Text" ) ),
355 static_cast<int>( DetailsSection ) );
356 setItemBold_( mSecPemText );
357 mSecPemText->setFirstColumnSpanned( true );
358 mSecPemText->setFlags( Qt::ItemIsEnabled );
359 mSecPemText->setExpanded( false );
360 treeDetails->insertTopLevelItem( 0, mSecPemText );
361}
362
363void QgsAuthCertInfo::populateCertInfo()
364{
365 mSecDetails->setHidden( false );
366 mSecPemText->setHidden( false );
367
368 populateInfoGeneralSection();
369 populateInfoDetailsSection();
370 populateInfoPemTextSection();
371}
372
373QTreeWidgetItem *QgsAuthCertInfo::addGroupItem( QTreeWidgetItem *parent, const QString &group )
374{
375 QTreeWidgetItem *grpitem = new QTreeWidgetItem(
376 parent,
377 QStringList( group ),
378 static_cast<int>( DetailsGroup ) );
379
380 grpitem->setFirstColumnSpanned( true );
381 grpitem->setFlags( Qt::ItemIsEnabled );
382 grpitem->setExpanded( true );
383
384 QBrush orgb( grpitem->foreground( 0 ) );
385 orgb.setColor( QColor::fromRgb( 90, 90, 90 ) );
386 grpitem->setForeground( 0, orgb );
387 QFont grpf( grpitem->font( 0 ) );
388 grpf.setItalic( true );
389 grpitem->setFont( 0, grpf );
390
391 return grpitem;
392}
393
394void QgsAuthCertInfo::addFieldItem( QTreeWidgetItem *parent, const QString &field, const QString &value,
395 QgsAuthCertInfo::FieldWidget wdgt, const QColor &color )
396{
397 if ( value.isEmpty() )
398 return;
399
400 QTreeWidgetItem *item = new QTreeWidgetItem(
401 parent,
402 QStringList() << field << ( wdgt == NoWidget ? value : QString() ),
403 static_cast<int>( DetailsField ) );
404
405 item->setTextAlignment( 0, Qt::AlignRight );
406 item->setTextAlignment( 1, Qt::AlignLeft );
407
408 QBrush fieldb( item->foreground( 0 ) );
409 fieldb.setColor( QColor::fromRgb( 90, 90, 90 ) );
410 item->setForeground( 0, fieldb );
411
412 if ( wdgt == NoWidget )
413 {
414 if ( color.isValid() )
415 {
416 QBrush valueb( item->foreground( 1 ) );
417 valueb.setColor( color );
418 item->setForeground( 1, valueb );
419 }
420 }
421 else if ( wdgt == LineEdit )
422 {
423 QLineEdit *le = new QLineEdit( value, treeDetails );
424 le->setReadOnly( true );
425 le->setAlignment( Qt::AlignLeft );
426 le->setCursorPosition( 0 );
427 if ( color.isValid() )
428 {
429 le->setStyleSheet( QStringLiteral( "QLineEdit { color: %1; }" ).arg( color.name() ) );
430 }
431 item->treeWidget()->setItemWidget( item, 1, le );
432 }
433 else if ( wdgt == TextEdit )
434 {
435 QPlainTextEdit *pte = new QPlainTextEdit( value, treeDetails );
436 pte->setReadOnly( true );
437 pte->setMinimumHeight( 75 );
438 pte->setMaximumHeight( 75 );
439 pte->moveCursor( QTextCursor::Start );
440 if ( color.isValid() )
441 {
442 pte->setStyleSheet( QStringLiteral( "QPlainTextEdit { color: %1; }" ).arg( color.name() ) );
443 }
444 item->treeWidget()->setItemWidget( item, 1, pte );
445 }
446
447}
448
449void QgsAuthCertInfo::populateInfoGeneralSection()
450{
451 removeChildren_( mSecGeneral );
452
453 if ( mCurrentQCert.isNull() )
454 {
455 addFieldItem( mSecGeneral, tr( "Type" ),
456 tr( "Missing CA (incomplete local CA chain)" ),
457 LineEdit );
458 mSecGeneral->setExpanded( true );
459 mSecDetails->setHidden( true );
460 mSecPemText->setHidden( true );
461 return;
462 }
463
464 QString certype;
465 const bool isselfsigned = mCurrentACert.isSelfSigned();
466 const QString selfsigned( tr( "self-signed" ) );
467
468 const QList<QgsAuthCertUtils::CertUsageType> usagetypes(
470 const bool isca = usagetypes.contains( QgsAuthCertUtils::CertAuthorityUsage );
471 const bool isissuer = usagetypes.contains( QgsAuthCertUtils::CertIssuerUsage );
472 const bool issslserver = usagetypes.contains( QgsAuthCertUtils::TlsServerUsage );
473 const bool issslclient = usagetypes.contains( QgsAuthCertUtils::TlsClientUsage );
474
475 if ( issslclient )
476 {
478 }
479 if ( issslserver )
480 {
482 }
483 if ( isissuer || ( isca && !isselfsigned ) )
484 {
486 }
487 if ( ( isissuer || isca ) && isselfsigned )
488 {
489 certype = QStringLiteral( "%1 %2" )
490 .arg( tr( "Root" ),
492 }
493 if ( isselfsigned )
494 {
495 certype.append( certype.isEmpty() ? selfsigned : QStringLiteral( " (%1)" ).arg( selfsigned ) );
496 }
497
498 addFieldItem( mSecGeneral, tr( "Usage type" ),
499 certype,
500 LineEdit );
501 addFieldItem( mSecGeneral, tr( "Subject" ),
502 QgsAuthCertUtils::resolvedCertName( mCurrentQCert ),
503 LineEdit );
504 addFieldItem( mSecGeneral, tr( "Issuer" ),
505 QgsAuthCertUtils::resolvedCertName( mCurrentQCert, true ),
506 LineEdit );
507 addFieldItem( mSecGeneral, tr( "Not valid after" ),
508 mCurrentQCert.expiryDate().toString(),
509 LineEdit,
510 mCurrentQCert.expiryDate() < QDateTime::currentDateTime() ? QgsAuthGuiUtils::redColor() : QColor() );
511
512 const QSslKey pubkey( mCurrentQCert.publicKey() );
513 const QString alg( pubkey.algorithm() == QSsl::Rsa ? "RSA" : "DSA" );
514 const int bitsize( pubkey.length() );
515 addFieldItem( mSecGeneral, tr( "Public key" ),
516 QStringLiteral( "%1, %2 bits" ).arg( alg, bitsize == -1 ? QStringLiteral( "?" ) : QString::number( bitsize ) ),
517 LineEdit );
518 addFieldItem( mSecGeneral, tr( "Signature algorithm" ),
519 QgsAuthCertUtils::qcaSignatureAlgorithm( mCurrentACert.signatureAlgorithm() ),
520 LineEdit );
521}
522
523void QgsAuthCertInfo::populateInfoDetailsSection()
524{
525 removeChildren_( mGrpSubj );
526 removeChildren_( mGrpIssu );
527 removeChildren_( mGrpCert );
528 removeChildren_( mGrpPkey );
529 removeChildren_( mGrpExts );
530
531 if ( mCurrentQCert.isNull() )
532 return;
533
534 // Subject Info
535 addFieldItem( mGrpSubj, tr( "Country (C)" ),
536 SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::CountryName ),
537 LineEdit );
538 addFieldItem( mGrpSubj, tr( "State/Province (ST)" ),
539 SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::StateOrProvinceName ),
540 LineEdit );
541 addFieldItem( mGrpSubj, tr( "Locality (L)" ),
542 SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::LocalityName ),
543 LineEdit );
544 addFieldItem( mGrpSubj, tr( "Organization (O)" ),
545 SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::Organization ),
546 LineEdit );
547 addFieldItem( mGrpSubj, tr( "Organizational unit (OU)" ),
548 SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::OrganizationalUnitName ),
549 LineEdit );
550 addFieldItem( mGrpSubj, tr( "Common name (CN)" ),
551 SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::CommonName ),
552 LineEdit );
553 addFieldItem( mGrpSubj, tr( "Email address (E)" ),
554 mCurrentACert.subjectInfo().value( QCA::Email ),
555 LineEdit );
556 addFieldItem( mGrpSubj, tr( "Distinguished name" ),
557 QgsAuthCertUtils::getCertDistinguishedName( mCurrentQCert, mCurrentACert, false ),
558 LineEdit );
559 addFieldItem( mGrpSubj, tr( "Email Legacy" ),
560 mCurrentACert.subjectInfo().value( QCA::EmailLegacy ),
561 LineEdit );
562 addFieldItem( mGrpSubj, tr( "Incorporation Country" ),
563 mCurrentACert.subjectInfo().value( QCA::IncorporationCountry ),
564 LineEdit );
565 addFieldItem( mGrpSubj, tr( "Incorporation State/Province" ),
566 mCurrentACert.subjectInfo().value( QCA::IncorporationState ),
567 LineEdit );
568 addFieldItem( mGrpSubj, tr( "Incorporation Locality" ),
569 mCurrentACert.subjectInfo().value( QCA::IncorporationLocality ),
570 LineEdit );
571 addFieldItem( mGrpSubj, tr( "URI" ),
572 mCurrentACert.subjectInfo().value( QCA::URI ),
573 LineEdit );
574 addFieldItem( mGrpSubj, tr( "DNS" ),
575 mCurrentACert.subjectInfo().value( QCA::DNS ),
576 LineEdit );
577 addFieldItem( mGrpSubj, tr( "IP Address" ),
578 mCurrentACert.subjectInfo().value( QCA::IPAddress ),
579 LineEdit );
580 addFieldItem( mGrpSubj, tr( "XMPP" ),
581 mCurrentACert.subjectInfo().value( QCA::XMPP ),
582 LineEdit );
583
584 const QMultiMap<QSsl::AlternativeNameEntryType, QString> alts( mCurrentQCert.subjectAlternativeNames() );
585 QStringList altslist;
586 const QString email( tr( "Email: " ) );
587 const QStringList emails( alts.values( QSsl::EmailEntry ) );
588 if ( !emails.isEmpty() )
589 {
590 altslist << email + emails.join( '\n' + email );
591 }
592 const QString dns( tr( "DNS: " ) );
593 const QStringList dnss( alts.values( QSsl::DnsEntry ) );
594 if ( !dnss.isEmpty() )
595 {
596 altslist << dns + dnss.join( '\n' + dns );
597 }
598 addFieldItem( mGrpSubj, tr( "Alternate names" ),
599 altslist.join( QLatin1Char( '\n' ) ),
600 TextEdit );
601
602 // Issuer Info
603 addFieldItem( mGrpIssu, tr( "Country (C)" ),
604 SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::CountryName ),
605 LineEdit );
606 addFieldItem( mGrpIssu, tr( "State/Province (ST)" ),
607 SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::StateOrProvinceName ),
608 LineEdit );
609 addFieldItem( mGrpIssu, tr( "Locality (L)" ),
610 SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::LocalityName ),
611 LineEdit );
612 addFieldItem( mGrpIssu, tr( "Organization (O)" ),
613 SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::Organization ),
614 LineEdit );
615 addFieldItem( mGrpIssu, tr( "Organizational unit (OU)" ),
616 SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::OrganizationalUnitName ),
617 LineEdit );
618 addFieldItem( mGrpIssu, tr( "Common name (CN)" ),
619 SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::CommonName ),
620 LineEdit );
621 addFieldItem( mGrpIssu, tr( "Email address (E)" ),
622 mCurrentACert.issuerInfo().value( QCA::Email ),
623 LineEdit );
624 addFieldItem( mGrpIssu, tr( "Distinguished name" ),
625 QgsAuthCertUtils::getCertDistinguishedName( mCurrentQCert, mCurrentACert, true ),
626 LineEdit );
627 addFieldItem( mGrpIssu, tr( "Email Legacy" ),
628 mCurrentACert.issuerInfo().value( QCA::EmailLegacy ),
629 LineEdit );
630 addFieldItem( mGrpIssu, tr( "Incorporation Country" ),
631 mCurrentACert.issuerInfo().value( QCA::IncorporationCountry ),
632 LineEdit );
633 addFieldItem( mGrpIssu, tr( "Incorporation State/Province" ),
634 mCurrentACert.issuerInfo().value( QCA::IncorporationState ),
635 LineEdit );
636 addFieldItem( mGrpIssu, tr( "Incorporation Locality" ),
637 mCurrentACert.issuerInfo().value( QCA::IncorporationLocality ),
638 LineEdit );
639 addFieldItem( mGrpIssu, tr( "URI" ),
640 mCurrentACert.issuerInfo().value( QCA::URI ),
641 LineEdit );
642 addFieldItem( mGrpIssu, tr( "DNS" ),
643 mCurrentACert.issuerInfo().value( QCA::DNS ),
644 LineEdit );
645 addFieldItem( mGrpIssu, tr( "IP Address" ),
646 mCurrentACert.issuerInfo().value( QCA::IPAddress ),
647 LineEdit );
648 addFieldItem( mGrpIssu, tr( "XMPP" ),
649 mCurrentACert.issuerInfo().value( QCA::XMPP ),
650 LineEdit );
651
652 // Certificate Info
653 addFieldItem( mGrpCert, tr( "Version" ),
654 mCurrentQCert.version(),
655 LineEdit );
656 addFieldItem( mGrpCert, tr( "Serial #" ),
657 mCurrentQCert.serialNumber(),
658 LineEdit );
659 addFieldItem( mGrpCert, tr( "Not valid before" ),
660 mCurrentQCert.effectiveDate().toString(),
661 LineEdit,
662 mCurrentQCert.effectiveDate() > QDateTime::currentDateTime() ? QgsAuthGuiUtils::redColor() : QColor() );
663 addFieldItem( mGrpCert, tr( "Not valid after" ),
664 mCurrentQCert.expiryDate().toString(),
665 LineEdit,
666 mCurrentQCert.expiryDate() < QDateTime::currentDateTime() ? QgsAuthGuiUtils::redColor() : QColor() );
667 addFieldItem( mGrpCert, tr( "Signature algorithm" ),
668 QgsAuthCertUtils::qcaSignatureAlgorithm( mCurrentACert.signatureAlgorithm() ),
669 LineEdit );
670 addFieldItem( mGrpCert, tr( "MD5 fingerprint" ),
671 QgsAuthCertUtils::getColonDelimited( mCurrentQCert.digest().toHex().toUpper() ),
672 LineEdit );
673 addFieldItem( mGrpCert, tr( "SHA1 fingerprint" ),
674 QgsAuthCertUtils::shaHexForCert( mCurrentQCert, true ).toUpper(),
675 LineEdit );
676
677 const QStringList crllocs( mCurrentACert.crlLocations() );
678 if ( !crllocs.isEmpty() )
679 {
680 addFieldItem( mGrpCert, tr( "CRL locations" ),
681 crllocs.join( QLatin1Char( '\n' ) ),
682 TextEdit );
683 }
684 const QStringList issulocs( mCurrentACert.issuerLocations() );
685 if ( !issulocs.isEmpty() )
686 {
687 addFieldItem( mGrpCert, tr( "Issuer locations" ),
688 issulocs.join( QLatin1Char( '\n' ) ),
689 TextEdit );
690 }
691 const QStringList ocsplocs( mCurrentACert.ocspLocations() );
692 if ( !ocsplocs.isEmpty() )
693 {
694 addFieldItem( mGrpCert, tr( "OCSP locations" ),
695 ocsplocs.join( QLatin1Char( '\n' ) ),
696 TextEdit );
697 }
698
699 // Public Key Info
700 // TODO: handle ECC (Elliptic Curve) keys when Qt supports them
701 const QSslKey pubqkey( mCurrentQCert.publicKey() );
702 const QString alg( pubqkey.algorithm() == QSsl::Rsa ? "RSA" : "DSA" );
703 const int bitsize( pubqkey.length() );
704 addFieldItem( mGrpPkey, tr( "Algorithm" ),
705 bitsize == -1 ? QStringLiteral( "Unknown (possibly Elliptic Curve)" ) : alg,
706 LineEdit );
707 addFieldItem( mGrpPkey, tr( "Key size" ),
708 bitsize == -1 ? QStringLiteral( "?" ) : QString::number( bitsize ),
709 LineEdit );
710 if ( bitsize > 0 ) // ECC keys unsupported by Qt/QCA, so returned key size is 0
711 {
712 const QCA::PublicKey pubakey( mCurrentACert.subjectPublicKey() );
713
714 if ( pubqkey.algorithm() == QSsl::Rsa )
715 {
716 const QCA::RSAPublicKey rsakey( pubakey.toRSA() );
717 const QCA::BigInteger modulus = rsakey.n();
718 QByteArray modarray( modulus.toArray().toByteArray().toHex() );
719 if ( modarray.size() > 2 && modarray.mid( 0, 2 ) == QByteArray( "00" ) )
720 {
721 modarray = modarray.mid( 2 );
722 }
723 const QCA::BigInteger exponent = rsakey.e();
724 addFieldItem( mGrpPkey, tr( "Public key" ),
725 QgsAuthCertUtils::getColonDelimited( modarray ).toUpper(),
726 TextEdit );
727 addFieldItem( mGrpPkey, tr( "Exponent" ),
728 exponent.toString(),
729 LineEdit );
730 }
731 // TODO: how is DSA textually represented using QCA?
732 // QCA::DSAPublicKey dsakey( pubakey.toDSA() );
733
734 // TODO: how to get the signature and show it as hex?
735 // addFieldItem( mGrpPkey, tr( "Signature" ),
736 // mCurrentACert.???.toHex(),
737 // TextEdit );
738
739 // key usage
740 QStringList usage;
741 if ( pubakey.canVerify() )
742 {
743 usage.append( tr( "Verify" ) );
744 }
745
746 // TODO: these two seem to always show up, why?
747 if ( pubakey.canEncrypt() )
748 {
749 usage.append( tr( "Encrypt" ) );
750 }
751#if QCA_VERSION >= 0x020100
752 if ( pubakey.canDecrypt() )
753 {
754 usage.append( tr( "Decrypt" ) );
755 }
756#endif
757 if ( pubakey.canKeyAgree() )
758 {
759 usage.append( tr( "Key agreement" ) );
760 }
761 if ( pubakey.canExport() )
762 {
763 usage.append( tr( "Export" ) );
764 }
765 if ( !usage.isEmpty() )
766 {
767 addFieldItem( mGrpPkey, tr( "Key usage" ),
768 usage.join( QLatin1String( ", " ) ),
769 LineEdit );
770 }
771 }
772
773 // Extensions
774 QStringList basicconst;
775 basicconst << tr( "Certificate Authority: %1" ).arg( mCurrentACert.isCA() ? tr( "Yes" ) : tr( "No" ) )
776 << tr( "Chain Path Limit: %1" ).arg( mCurrentACert.pathLimit() );
777 addFieldItem( mGrpExts, tr( "Basic constraints" ),
778 basicconst.join( QLatin1Char( '\n' ) ),
779 TextEdit );
780
781 QStringList keyusage;
782 QStringList extkeyusage;
783 const QList<QCA::ConstraintType> certconsts = mCurrentACert.constraints();
784 const auto constCertconsts = certconsts;
785 for ( const QCA::ConstraintType &certconst : constCertconsts )
786 {
787 if ( certconst.section() == QCA::ConstraintType::KeyUsage )
788 {
789 keyusage.append( QgsAuthCertUtils::qcaKnownConstraint( certconst.known() ) );
790 }
791 else if ( certconst.section() == QCA::ConstraintType::ExtendedKeyUsage )
792 {
793 extkeyusage.append( QgsAuthCertUtils::qcaKnownConstraint( certconst.known() ) );
794 }
795 }
796 if ( !keyusage.isEmpty() )
797 {
798 addFieldItem( mGrpExts, tr( "Key usage" ),
799 keyusage.join( QLatin1Char( '\n' ) ),
800 TextEdit );
801 }
802 if ( !extkeyusage.isEmpty() )
803 {
804 addFieldItem( mGrpExts, tr( "Extended key usage" ),
805 extkeyusage.join( QLatin1Char( '\n' ) ),
806 TextEdit );
807 }
808
809 addFieldItem( mGrpExts, tr( "Subject key ID" ),
810 QgsAuthCertUtils::getColonDelimited( mCurrentACert.subjectKeyId().toHex() ).toUpper(),
811 LineEdit );
812 addFieldItem( mGrpExts, tr( "Authority key ID" ),
813 QgsAuthCertUtils::getColonDelimited( mCurrentACert.issuerKeyId().toHex() ).toUpper(),
814 LineEdit );
815}
816
817void QgsAuthCertInfo::populateInfoPemTextSection()
818{
819 removeChildren_( mSecPemText );
820
821 if ( mCurrentQCert.isNull() )
822 return;
823
824 QTreeWidgetItem *item = new QTreeWidgetItem(
825 mSecPemText,
826 QStringList( QString() ),
827 static_cast<int>( DetailsField ) );
828
829 item->setFirstColumnSpanned( true );
830
831 QPlainTextEdit *pte = new QPlainTextEdit( mCurrentQCert.toPem(), treeDetails );
832 pte->setReadOnly( true );
833 pte->setMinimumHeight( 150 );
834 pte->setMaximumHeight( 150 );
835 pte->moveCursor( QTextCursor::Start );
836 item->treeWidget()->setItemWidget( item, 0, pte );
837}
838
839void QgsAuthCertInfo::btnSaveTrust_clicked()
840{
841 const QgsAuthCertUtils::CertTrustPolicy newpolicy( cmbbxTrust->trustPolicy() );
842 if ( !QgsApplication::authManager()->storeCertTrustPolicy( mCurrentQCert, newpolicy ) )
843 {
844 QgsDebugError( QStringLiteral( "Could not set trust policy for certificate" ) );
845 }
846 mCurrentTrustPolicy = newpolicy;
847 decorateCertTreeItem( mCurrentQCert, newpolicy, nullptr );
848 btnSaveTrust->setEnabled( false );
849
850 // rebuild trust cache
852 mTrustCacheRebuilt = true;
854}
855
856void QgsAuthCertInfo::currentPolicyIndexChanged( int indx )
857{
858 const QgsAuthCertUtils::CertTrustPolicy newpolicy( cmbbxTrust->trustPolicyForIndex( indx ) );
859 btnSaveTrust->setEnabled( newpolicy != mCurrentTrustPolicy );
860}
861
862void QgsAuthCertInfo::decorateCertTreeItem( const QSslCertificate &cert,
864 QTreeWidgetItem *item )
865{
866 if ( !item )
867 {
868 item = treeHierarchy->currentItem();
869 }
870 if ( !item )
871 {
872 return;
873 }
874
875 if ( cert.isNull() || trustpolicy == QgsAuthCertUtils::NoPolicy )
876 {
877 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateMissing.svg" ) ) );
878 // missing CA, color gray and italicize
879 QBrush b( item->foreground( 0 ) );
880 b.setColor( QColor::fromRgb( 90, 90, 90 ) );
881 item->setForeground( 0, b );
882 QFont f( item->font( 0 ) );
883 f.setItalic( true );
884 item->setFont( 0, f );
885 return;
886 }
887
888 if ( !QgsAuthCertUtils::certIsViable( cert ) )
889 {
890 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateUntrusted.svg" ) ) );
891 return;
892 }
893
894 if ( trustpolicy == QgsAuthCertUtils::Trusted )
895 {
896 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateTrusted.svg" ) ) );
897 }
898 else if ( trustpolicy == QgsAuthCertUtils::Untrusted
899 || ( trustpolicy == QgsAuthCertUtils::DefaultTrust
900 && mDefaultTrustPolicy == QgsAuthCertUtils::Untrusted ) )
901 {
902 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateUntrusted.svg" ) ) );
903 }
904 else
905 {
906 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificate.svg" ) ) );
907 }
908}
909
911
913 bool manageCertTrust,
914 QWidget *parent,
915 const QList<QSslCertificate> &connectionCAs )
916 : QDialog( parent )
917
918{
919 setWindowTitle( tr( "Certificate Information" ) );
920 QVBoxLayout *layout = new QVBoxLayout( this );
921 layout->setContentsMargins( 6, 6, 6, 6 );
922
923 mCertInfoWdgt = new QgsAuthCertInfo( cert, manageCertTrust, this, connectionCAs );
924 layout->addWidget( mCertInfoWdgt );
925
926 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Close,
927 Qt::Horizontal, this );
928 buttonBox->button( QDialogButtonBox::Close )->setDefault( true );
929 connect( buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close );
930 layout->addWidget( buttonBox );
931
932 setLayout( layout );
933}
934
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
QgsAuthCertInfoDialog(const QSslCertificate &cert, bool manageCertTrust, QWidget *parent=nullptr, const QList< QSslCertificate > &connectionCAs=QList< QSslCertificate >())
Construct a dialog displaying detailed info on a certificate and its hierarchical trust chain.
Widget for viewing detailed info on a certificate and its hierarchical trust chain.
QgsAuthCertInfo(const QSslCertificate &cert, bool manageCertTrust=false, QWidget *parent=nullptr, const QList< QSslCertificate > &connectionCAs=QList< QSslCertificate >())
Constructor for QgsAuthCertInfo.
Utilities for working with certificates and keys.
static QString qcaValidityMessage(QCA::Validity validity)
Certificate validity check messages per enum.
static QList< QgsAuthCertUtils::CertUsageType > certificateUsageTypes(const QSslCertificate &cert)
Try to determine the certificates usage types.
static QString qcaSignatureAlgorithm(QCA::SignatureAlgorithm algorithm)
Certificate signature algorithm strings per enum.
static QString resolvedCertName(const QSslCertificate &cert, bool issuer=false)
Gets the general name via RFC 5280 resolution.
static QString certificateUsageTypeString(QgsAuthCertUtils::CertUsageType usagetype)
Certificate usage type strings per enum.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Gets the sha1 hash for certificate.
CertTrustPolicy
Type of certificate trust policy.
static QString qcaKnownConstraint(QCA::ConstraintTypeKnown constraint)
Certificate well-known constraint strings per enum.
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
static QString getColonDelimited(const QString &txt)
Gets string with colon delimiters every 2 characters.
static QString getCaSourceName(QgsAuthCertUtils::CaCertSource source, bool single=false)
Gets the general name for CA source enum type.
static QString getCertDistinguishedName(const QSslCertificate &qcert, const QCA::Certificate &acert=QCA::Certificate(), bool issuer=false)
Gets combined distinguished name for certificate.
static QString redTextStyleSheet(const QString &selector="*")
Red text stylesheet representing invalid, untrusted, etc. certificate.
static QColor redColor()
Red color representing invalid, untrusted, etc. certificate.
QgsAuthCertUtils::CertTrustPolicy defaultCertTrustPolicy()
Gets the default certificate trust policy preferred by user.
bool rebuildCertTrustCache()
Rebuild certificate authority cache.
const QMap< QString, QPair< QgsAuthCertUtils::CaCertSource, QSslCertificate > > caCertsCache()
caCertsCache get all CA certs mapped to their sha1 from cache.
bool rebuildTrustedCaCertsCache()
Rebuild trusted certificate authorities cache.
#define SSL_SUBJECT_INFO(var, prop)
#define SSL_ISSUER_INFO(var, prop)
#define QgsDebugError(str)
Definition qgslogger.h:38