QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
18#include "ui_qgsauthcertificateinfo.h"
20
21#include "qgsapplication.h"
22#include "qgsauthcertutils.h"
23#include "qgsauthguiutils.h"
24#include "qgsauthmanager.h"
25#include "qgslogger.h"
26
27#include <QDialogButtonBox>
28#include <QLineEdit>
29#include <QPlainTextEdit>
30#include <QPushButton>
31#include <QTextEdit>
32#include <QtCrypto>
33
34#include "moc_qgsauthcertificateinfo.cpp"
35
36QgsAuthCertInfo::QgsAuthCertInfo( const QSslCertificate &cert, bool manageCertTrust, QWidget *parent, const QList<QSslCertificate> &connectionCAs )
37 : QWidget( parent )
38 , mConnectionCAs( connectionCAs )
39 , mDefaultItemForeground( QBrush() )
40 , mManageTrust( manageCertTrust )
41{
42 if ( QgsApplication::authManager()->isDisabled() )
43 {
44 mAuthNotifyLayout = new QVBoxLayout;
45 this->setLayout( mAuthNotifyLayout );
46 mAuthNotify = new QLabel( QgsApplication::authManager()->disabledMessage(), this );
47 mAuthNotifyLayout->addWidget( mAuthNotify );
48 }
49 else
50 {
51 setupUi( this );
52 connect( btnSaveTrust, &QToolButton::clicked, this, &QgsAuthCertInfo::btnSaveTrust_clicked );
53
54 lblError->setHidden( true );
55
56 treeHierarchy->setRootIsDecorated( false );
57
58 connect( treeHierarchy, &QTreeWidget::currentItemChanged, this, &QgsAuthCertInfo::currentCertItemChanged );
59
60 mCaCertsCache = QgsApplication::authManager()->caCertsCache();
61
62 setUpCertDetailsTree();
63
64 grpbxTrust->setVisible( mManageTrust );
65
66 // trust policy is still queried, even if not managing the policy, so public getter will work
67 mDefaultTrustPolicy = QgsApplication::authManager()->defaultCertTrustPolicy();
68 mCurrentTrustPolicy = QgsAuthCertUtils::DefaultTrust;
69
70 bool res;
71 res = populateQcaCertCollection();
72 if ( res )
73 res = setQcaCertificate( cert );
74 if ( res )
75 res = populateCertChain();
76 if ( res )
77 setCertHierarchy();
78
79 connect( cmbbxTrust, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAuthCertInfo::currentPolicyIndexChanged );
80 }
81}
82
83void QgsAuthCertInfo::setupError( const QString &msg )
84{
85 lblError->setVisible( true );
86 QString out = tr( "<b>Setup ERROR:</b>\n\n" );
87 out += msg;
88 lblError->setText( out );
89 lblError->setStyleSheet( QgsAuthGuiUtils::redTextStyleSheet() );
90}
91
92void QgsAuthCertInfo::currentCertItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
93{
94 Q_UNUSED( previous )
95 updateCurrentCert( current );
96}
97
98void QgsAuthCertInfo::updateCurrentCert( QTreeWidgetItem *item )
99{
100 if ( !item )
101 item = treeHierarchy->currentItem();
102 if ( !item )
103 return;
104
105 const int indx( item->data( 0, Qt::UserRole ).toInt() );
106 updateCurrentCertInfo( indx );
107}
108
109bool QgsAuthCertInfo::populateQcaCertCollection()
110{
111 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate>> &certpairs( mCaCertsCache.values() );
112 for ( int i = 0; i < certpairs.size(); ++i )
113 {
114 QCA::ConvertResult res;
115 const QCA::Certificate acert = QCA::Certificate::fromPEM( certpairs.at( i ).second.toPem(), &res, QStringLiteral( "qca-ossl" ) );
116 if ( res == QCA::ConvertGood && !acert.isNull() )
117 {
118 mCaCerts.addCertificate( acert );
119 }
120 }
121 if ( !mConnectionCAs.isEmpty() )
122 {
123 const auto constMConnectionCAs = mConnectionCAs;
124 for ( const QSslCertificate &cert : constMConnectionCAs )
125 {
126 QCA::ConvertResult res;
127 const QCA::Certificate acert = QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral( "qca-ossl" ) );
128 if ( res == QCA::ConvertGood && !acert.isNull() )
129 {
130 mCaCerts.addCertificate( acert );
131 }
132 }
133 }
134
135 if ( mCaCerts.certificates().empty() )
136 {
137 setupError( tr( "Could not populate QCA certificate collection" ) );
138 return false;
139 }
140 return true;
141}
142
143bool QgsAuthCertInfo::setQcaCertificate( const QSslCertificate &cert )
144{
145 QCA::ConvertResult res;
146 mCert = QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral( "qca-ossl" ) );
147 if ( res != QCA::ConvertGood || mCert.isNull() )
148 {
149 setupError( tr( "Could not set QCA certificate" ) );
150 return false;
151 }
152 return true;
153}
154
155bool QgsAuthCertInfo::populateCertChain()
156{
157 const QCA::CertificateChain certchain( mCert );
158 QCA::Validity valid;
159 mACertChain = certchain.complete( mCaCerts.certificates(), &valid );
160 if ( valid != QCA::ValidityGood && valid != QCA::ErrorInvalidCA )
161 {
162 // invalid CAs are skipped to allow an incomplete chain
163 setupError( tr( "Invalid population of QCA certificate chain.<br><br>"
164 "Validity message: %1" )
165 .arg( QgsAuthCertUtils::qcaValidityMessage( valid ) ) );
166 return false;
167 }
168
169 if ( mACertChain.isEmpty() )
170 {
171 QgsDebugError( QStringLiteral( "Could not populate QCA certificate chain" ) );
172 mACertChain = certchain;
173 }
174
175 if ( !mACertChain.last().isSelfSigned() )
176 {
177 // chain is incomplete, add null certificate to signify local missing parent CA
178 mACertChain.append( QCA::Certificate() );
179 }
180
181 // mirror chain to QSslCertificate
182 const auto constMACertChain = mACertChain;
183 for ( const QCA::Certificate &cert : constMACertChain )
184 {
185 QSslCertificate qcert;
186 if ( !cert.isNull() )
187 {
188 qcert = QSslCertificate( cert.toPEM().toLatin1() );
189 }
190 mQCertChain.append( qcert );
191 }
192 return true;
193}
194
195void QgsAuthCertInfo::setCertHierarchy()
196{
197 QListIterator<QSslCertificate> it( mQCertChain );
198 it.toBack();
199 int i = mQCertChain.size();
200 QTreeWidgetItem *item = nullptr;
201 QTreeWidgetItem *previtem = nullptr;
202 while ( it.hasPrevious() )
203 {
204 const QSslCertificate cert( it.previous() );
205 const bool missingCA = cert.isNull();
206 QString cert_source;
207 if ( missingCA && it.hasPrevious() )
208 {
209 cert_source = QgsAuthCertUtils::resolvedCertName( it.peekPrevious(), true );
210 cert_source += QStringLiteral( " (%1)" ).arg( tr( "Missing CA" ) );
211 }
212 else
213 {
214 cert_source = QgsAuthCertUtils::resolvedCertName( cert );
215 const QString sha = QgsAuthCertUtils::shaHexForCert( cert );
216 if ( mCaCertsCache.contains( sha ) )
217 {
218 const QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> &certpair( mCaCertsCache.value( sha ) );
219 cert_source += QStringLiteral( " (%1)" ).arg( QgsAuthCertUtils::getCaSourceName( certpair.first, true ) );
220 }
221 else if ( mConnectionCAs.contains( cert ) )
222 {
223 cert_source += QStringLiteral( " (%1)" )
225 }
226 }
227
228 if ( !previtem )
229 {
230 item = new QTreeWidgetItem( treeHierarchy, QStringList() << cert_source );
231 }
232 else
233 {
234 item = new QTreeWidgetItem( previtem, QStringList() << cert_source );
235 }
236 if ( missingCA && it.hasPrevious() )
237 {
238 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
239 }
240
241 item->setData( 0, Qt::UserRole, --i );
242
243 if ( mDefaultItemForeground.style() == Qt::NoBrush )
244 {
245 mDefaultItemForeground = item->foreground( 0 );
246 }
247
248 decorateCertTreeItem( cert, QgsApplication::authManager()->certificateTrustPolicy( cert ), item );
249
250 item->setFirstColumnSpanned( true );
251 if ( !previtem )
252 treeHierarchy->addTopLevelItem( item );
253 previtem = item;
254 }
255 treeHierarchy->setCurrentItem( item, 0, QItemSelectionModel::ClearAndSelect );
256 treeHierarchy->expandAll();
257}
258
259void QgsAuthCertInfo::updateCurrentCertInfo( int chainindx )
260{
261 btnSaveTrust->setEnabled( false );
262
263 mCurrentQCert = mQCertChain.at( chainindx );
264 mCurrentACert = mACertChain.at( chainindx );
265
266 if ( mManageTrust )
267 {
268 grpbxTrust->setHidden( mCurrentQCert.isNull() );
269 }
270
271 if ( !mCurrentQCert.isNull() )
272 {
273 const QgsAuthCertUtils::CertTrustPolicy trustpolicy( QgsApplication::authManager()->certificateTrustPolicy( mCurrentQCert ) );
274 mCurrentTrustPolicy = trustpolicy;
275
276 cmbbxTrust->setTrustPolicy( trustpolicy );
277 if ( !QgsAuthCertUtils::certIsViable( mCurrentQCert ) )
278 {
279 cmbbxTrust->setDefaultTrustPolicy( QgsAuthCertUtils::Untrusted );
280 }
281 }
282
283 populateCertInfo();
284}
285
286void QgsAuthCertInfo::setUpCertDetailsTree()
287{
288 treeDetails->setColumnCount( 2 );
289 treeDetails->setHeaderLabels( QStringList() << tr( "Field" ) << tr( "Value" ) );
290 treeDetails->setColumnWidth( 0, 200 );
291
292 QTreeWidgetItem *headeritem = treeDetails->headerItem();
293 headeritem->setTextAlignment( 0, Qt::AlignRight );
294 headeritem->setTextAlignment( 1, Qt::AlignLeft );
295
296 treeDetails->setRootIsDecorated( true );
297 treeDetails->setWordWrap( true );
298
299 // add root items
300 mSecGeneral = new QTreeWidgetItem(
301 treeDetails,
302 QStringList( tr( "General" ) ),
303 static_cast<int>( DetailsSection )
304 );
305 QgsAuthGuiUtils::setItemBold( mSecGeneral );
306 mSecGeneral->setFirstColumnSpanned( true );
307 mSecGeneral->setFlags( Qt::ItemIsEnabled );
308 mSecGeneral->setExpanded( true );
309 treeDetails->insertTopLevelItem( 0, mSecGeneral );
310
311 mSecDetails = new QTreeWidgetItem(
312 treeDetails,
313 QStringList( tr( "Details" ) ),
314 static_cast<int>( DetailsSection )
315 );
316 QgsAuthGuiUtils::setItemBold( mSecDetails );
317 mSecDetails->setFirstColumnSpanned( true );
318 mSecDetails->setFlags( Qt::ItemIsEnabled );
319 mSecDetails->setExpanded( false );
320 treeDetails->insertTopLevelItem( 0, mSecDetails );
321
322 // add details groups
323 mGrpSubj = addGroupItem( mSecDetails, tr( "Subject Info" ) );
324 mGrpIssu = addGroupItem( mSecDetails, tr( "Issuer Info" ) );
325 mGrpCert = addGroupItem( mSecDetails, tr( "Certificate Info" ) );
326 mGrpPkey = addGroupItem( mSecDetails, tr( "Public Key Info" ) );
327 mGrpExts = addGroupItem( mSecDetails, tr( "Extensions" ) );
328
329 mSecPemText = new QTreeWidgetItem(
330 treeDetails,
331 QStringList( tr( "PEM Text" ) ),
332 static_cast<int>( DetailsSection )
333 );
334 QgsAuthGuiUtils::setItemBold( mSecPemText );
335 mSecPemText->setFirstColumnSpanned( true );
336 mSecPemText->setFlags( Qt::ItemIsEnabled );
337 mSecPemText->setExpanded( false );
338 treeDetails->insertTopLevelItem( 0, mSecPemText );
339}
340
341void QgsAuthCertInfo::populateCertInfo()
342{
343 mSecDetails->setHidden( false );
344 mSecPemText->setHidden( false );
345
346 populateInfoGeneralSection();
347 populateInfoDetailsSection();
348 populateInfoPemTextSection();
349}
350
351QTreeWidgetItem *QgsAuthCertInfo::addGroupItem( QTreeWidgetItem *parent, const QString &group )
352{
353 QTreeWidgetItem *grpitem = new QTreeWidgetItem(
354 parent,
355 QStringList( group ),
356 static_cast<int>( DetailsGroup )
357 );
358
359 grpitem->setFirstColumnSpanned( true );
360 grpitem->setFlags( Qt::ItemIsEnabled );
361 grpitem->setExpanded( true );
362
363 QBrush orgb( grpitem->foreground( 0 ) );
364 orgb.setColor( QColor::fromRgb( 90, 90, 90 ) );
365 grpitem->setForeground( 0, orgb );
366 QFont grpf( grpitem->font( 0 ) );
367 grpf.setItalic( true );
368 grpitem->setFont( 0, grpf );
369
370 return grpitem;
371}
372
373void QgsAuthCertInfo::addFieldItem( QTreeWidgetItem *parent, const QString &field, const QString &value, QgsAuthCertInfo::FieldWidget wdgt, const QColor &color )
374{
375 if ( value.isEmpty() )
376 return;
377
378 QTreeWidgetItem *item = new QTreeWidgetItem(
379 parent,
380 QStringList() << field << ( wdgt == NoWidget ? value : QString() ),
381 static_cast<int>( DetailsField )
382 );
383
384 item->setTextAlignment( 0, Qt::AlignRight );
385 item->setTextAlignment( 1, Qt::AlignLeft );
386
387 QBrush fieldb( item->foreground( 0 ) );
388 fieldb.setColor( QColor::fromRgb( 90, 90, 90 ) );
389 item->setForeground( 0, fieldb );
390
391 if ( wdgt == NoWidget )
392 {
393 if ( color.isValid() )
394 {
395 QBrush valueb( item->foreground( 1 ) );
396 valueb.setColor( color );
397 item->setForeground( 1, valueb );
398 }
399 }
400 else if ( wdgt == LineEdit )
401 {
402 QLineEdit *le = new QLineEdit( value, treeDetails );
403 le->setReadOnly( true );
404 le->setAlignment( Qt::AlignLeft );
405 le->setCursorPosition( 0 );
406 if ( color.isValid() )
407 {
408 le->setStyleSheet( QStringLiteral( "QLineEdit { color: %1; }" ).arg( color.name() ) );
409 }
410 item->treeWidget()->setItemWidget( item, 1, le );
411 }
412 else if ( wdgt == TextEdit )
413 {
414 QPlainTextEdit *pte = new QPlainTextEdit( value, treeDetails );
415 pte->setReadOnly( true );
416 pte->setMinimumHeight( 75 );
417 pte->setMaximumHeight( 75 );
418 pte->moveCursor( QTextCursor::Start );
419 if ( color.isValid() )
420 {
421 pte->setStyleSheet( QStringLiteral( "QPlainTextEdit { color: %1; }" ).arg( color.name() ) );
422 }
423 item->treeWidget()->setItemWidget( item, 1, pte );
424 }
425}
426
427void QgsAuthCertInfo::populateInfoGeneralSection()
428{
429 QgsAuthGuiUtils::removeChildren( mSecGeneral );
430
431 if ( mCurrentQCert.isNull() )
432 {
433 addFieldItem( mSecGeneral, tr( "Type" ), tr( "Missing CA (incomplete local CA chain)" ), LineEdit );
434 mSecGeneral->setExpanded( true );
435 mSecDetails->setHidden( true );
436 mSecPemText->setHidden( true );
437 return;
438 }
439
440 QString certype;
441 const bool isselfsigned = mCurrentACert.isSelfSigned();
442 const QString selfsigned( tr( "self-signed" ) );
443
444 const QList<QgsAuthCertUtils::CertUsageType> usagetypes(
446 );
447 const bool isca = usagetypes.contains( QgsAuthCertUtils::CertAuthorityUsage );
448 const bool isissuer = usagetypes.contains( QgsAuthCertUtils::CertIssuerUsage );
449 const bool issslserver = usagetypes.contains( QgsAuthCertUtils::TlsServerUsage );
450 const bool issslclient = usagetypes.contains( QgsAuthCertUtils::TlsClientUsage );
451
452 if ( issslclient )
453 {
455 }
456 if ( issslserver )
457 {
459 }
460 if ( isissuer || ( isca && !isselfsigned ) )
461 {
463 }
464 if ( ( isissuer || isca ) && isselfsigned )
465 {
466 certype = QStringLiteral( "%1 %2" )
468 }
469 if ( isselfsigned )
470 {
471 certype.append( certype.isEmpty() ? selfsigned : QStringLiteral( " (%1)" ).arg( selfsigned ) );
472 }
473
474 addFieldItem( mSecGeneral, tr( "Usage type" ), certype, LineEdit );
475 addFieldItem( mSecGeneral, tr( "Subject" ), QgsAuthCertUtils::resolvedCertName( mCurrentQCert ), LineEdit );
476 addFieldItem( mSecGeneral, tr( "Issuer" ), QgsAuthCertUtils::resolvedCertName( mCurrentQCert, true ), LineEdit );
477 addFieldItem( mSecGeneral, tr( "Not valid after" ), mCurrentQCert.expiryDate().toString(), LineEdit, mCurrentQCert.expiryDate() < QDateTime::currentDateTime() ? QgsAuthGuiUtils::redColor() : QColor() );
478
479 const QSslKey pubkey( mCurrentQCert.publicKey() );
480 const QString alg( pubkey.algorithm() == QSsl::Rsa ? "RSA" : "DSA" );
481 const int bitsize( pubkey.length() );
482 addFieldItem( mSecGeneral, tr( "Public key" ), QStringLiteral( "%1, %2 bits" ).arg( alg, bitsize == -1 ? QStringLiteral( "?" ) : QString::number( bitsize ) ), LineEdit );
483 addFieldItem( mSecGeneral, tr( "Signature algorithm" ), QgsAuthCertUtils::qcaSignatureAlgorithm( mCurrentACert.signatureAlgorithm() ), LineEdit );
484}
485
486void QgsAuthCertInfo::populateInfoDetailsSection()
487{
493
494 if ( mCurrentQCert.isNull() )
495 return;
496
497 // Subject Info
498 addFieldItem( mGrpSubj, tr( "Country (C)" ), SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::CountryName ), LineEdit );
499 addFieldItem( mGrpSubj, tr( "State/Province (ST)" ), SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::StateOrProvinceName ), LineEdit );
500 addFieldItem( mGrpSubj, tr( "Locality (L)" ), SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::LocalityName ), LineEdit );
501 addFieldItem( mGrpSubj, tr( "Organization (O)" ), SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::Organization ), LineEdit );
502 addFieldItem( mGrpSubj, tr( "Organizational unit (OU)" ), SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::OrganizationalUnitName ), LineEdit );
503 addFieldItem( mGrpSubj, tr( "Common name (CN)" ), SSL_SUBJECT_INFO( mCurrentQCert, QSslCertificate::CommonName ), LineEdit );
504 addFieldItem( mGrpSubj, tr( "Email address (E)" ), mCurrentACert.subjectInfo().value( QCA::Email ), LineEdit );
505 addFieldItem( mGrpSubj, tr( "Distinguished name" ), QgsAuthCertUtils::getCertDistinguishedName( mCurrentQCert, mCurrentACert, false ), LineEdit );
506 addFieldItem( mGrpSubj, tr( "Email Legacy" ), mCurrentACert.subjectInfo().value( QCA::EmailLegacy ), LineEdit );
507 addFieldItem( mGrpSubj, tr( "Incorporation Country" ), mCurrentACert.subjectInfo().value( QCA::IncorporationCountry ), LineEdit );
508 addFieldItem( mGrpSubj, tr( "Incorporation State/Province" ), mCurrentACert.subjectInfo().value( QCA::IncorporationState ), LineEdit );
509 addFieldItem( mGrpSubj, tr( "Incorporation Locality" ), mCurrentACert.subjectInfo().value( QCA::IncorporationLocality ), LineEdit );
510 addFieldItem( mGrpSubj, tr( "URI" ), mCurrentACert.subjectInfo().value( QCA::URI ), LineEdit );
511 addFieldItem( mGrpSubj, tr( "DNS" ), mCurrentACert.subjectInfo().value( QCA::DNS ), LineEdit );
512 addFieldItem( mGrpSubj, tr( "IP Address" ), mCurrentACert.subjectInfo().value( QCA::IPAddress ), LineEdit );
513 addFieldItem( mGrpSubj, tr( "XMPP" ), mCurrentACert.subjectInfo().value( QCA::XMPP ), LineEdit );
514
515 const QMultiMap<QSsl::AlternativeNameEntryType, QString> alts( mCurrentQCert.subjectAlternativeNames() );
516 QStringList altslist;
517 const QString email( tr( "Email: " ) );
518 const QStringList emails( alts.values( QSsl::EmailEntry ) );
519 if ( !emails.isEmpty() )
520 {
521 altslist << email + emails.join( '\n' + email );
522 }
523 const QString dns( tr( "DNS: " ) );
524 const QStringList dnss( alts.values( QSsl::DnsEntry ) );
525 if ( !dnss.isEmpty() )
526 {
527 altslist << dns + dnss.join( '\n' + dns );
528 }
529 addFieldItem( mGrpSubj, tr( "Alternate names" ), altslist.join( QLatin1Char( '\n' ) ), TextEdit );
530
531 // Issuer Info
532 addFieldItem( mGrpIssu, tr( "Country (C)" ), SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::CountryName ), LineEdit );
533 addFieldItem( mGrpIssu, tr( "State/Province (ST)" ), SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::StateOrProvinceName ), LineEdit );
534 addFieldItem( mGrpIssu, tr( "Locality (L)" ), SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::LocalityName ), LineEdit );
535 addFieldItem( mGrpIssu, tr( "Organization (O)" ), SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::Organization ), LineEdit );
536 addFieldItem( mGrpIssu, tr( "Organizational unit (OU)" ), SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::OrganizationalUnitName ), LineEdit );
537 addFieldItem( mGrpIssu, tr( "Common name (CN)" ), SSL_ISSUER_INFO( mCurrentQCert, QSslCertificate::CommonName ), LineEdit );
538 addFieldItem( mGrpIssu, tr( "Email address (E)" ), mCurrentACert.issuerInfo().value( QCA::Email ), LineEdit );
539 addFieldItem( mGrpIssu, tr( "Distinguished name" ), QgsAuthCertUtils::getCertDistinguishedName( mCurrentQCert, mCurrentACert, true ), LineEdit );
540 addFieldItem( mGrpIssu, tr( "Email Legacy" ), mCurrentACert.issuerInfo().value( QCA::EmailLegacy ), LineEdit );
541 addFieldItem( mGrpIssu, tr( "Incorporation Country" ), mCurrentACert.issuerInfo().value( QCA::IncorporationCountry ), LineEdit );
542 addFieldItem( mGrpIssu, tr( "Incorporation State/Province" ), mCurrentACert.issuerInfo().value( QCA::IncorporationState ), LineEdit );
543 addFieldItem( mGrpIssu, tr( "Incorporation Locality" ), mCurrentACert.issuerInfo().value( QCA::IncorporationLocality ), LineEdit );
544 addFieldItem( mGrpIssu, tr( "URI" ), mCurrentACert.issuerInfo().value( QCA::URI ), LineEdit );
545 addFieldItem( mGrpIssu, tr( "DNS" ), mCurrentACert.issuerInfo().value( QCA::DNS ), LineEdit );
546 addFieldItem( mGrpIssu, tr( "IP Address" ), mCurrentACert.issuerInfo().value( QCA::IPAddress ), LineEdit );
547 addFieldItem( mGrpIssu, tr( "XMPP" ), mCurrentACert.issuerInfo().value( QCA::XMPP ), LineEdit );
548
549 // Certificate Info
550 addFieldItem( mGrpCert, tr( "Version" ), mCurrentQCert.version(), LineEdit );
551 addFieldItem( mGrpCert, tr( "Serial #" ), mCurrentQCert.serialNumber(), LineEdit );
552 addFieldItem( mGrpCert, tr( "Not valid before" ), mCurrentQCert.effectiveDate().toString(), LineEdit, mCurrentQCert.effectiveDate() > QDateTime::currentDateTime() ? QgsAuthGuiUtils::redColor() : QColor() );
553 addFieldItem( mGrpCert, tr( "Not valid after" ), mCurrentQCert.expiryDate().toString(), LineEdit, mCurrentQCert.expiryDate() < QDateTime::currentDateTime() ? QgsAuthGuiUtils::redColor() : QColor() );
554 addFieldItem( mGrpCert, tr( "Signature algorithm" ), QgsAuthCertUtils::qcaSignatureAlgorithm( mCurrentACert.signatureAlgorithm() ), LineEdit );
555 addFieldItem( mGrpCert, tr( "MD5 fingerprint" ), QgsAuthCertUtils::getColonDelimited( mCurrentQCert.digest().toHex().toUpper() ), LineEdit );
556 addFieldItem( mGrpCert, tr( "SHA1 fingerprint" ), QgsAuthCertUtils::shaHexForCert( mCurrentQCert, true ).toUpper(), LineEdit );
557
558 const QStringList crllocs( mCurrentACert.crlLocations() );
559 if ( !crllocs.isEmpty() )
560 {
561 addFieldItem( mGrpCert, tr( "CRL locations" ), crllocs.join( QLatin1Char( '\n' ) ), TextEdit );
562 }
563 const QStringList issulocs( mCurrentACert.issuerLocations() );
564 if ( !issulocs.isEmpty() )
565 {
566 addFieldItem( mGrpCert, tr( "Issuer locations" ), issulocs.join( QLatin1Char( '\n' ) ), TextEdit );
567 }
568 const QStringList ocsplocs( mCurrentACert.ocspLocations() );
569 if ( !ocsplocs.isEmpty() )
570 {
571 addFieldItem( mGrpCert, tr( "OCSP locations" ), ocsplocs.join( QLatin1Char( '\n' ) ), TextEdit );
572 }
573
574 // Public Key Info
575 // TODO: handle ECC (Elliptic Curve) keys when Qt supports them
576 const QSslKey pubqkey( mCurrentQCert.publicKey() );
577 const QString alg( pubqkey.algorithm() == QSsl::Rsa ? "RSA" : "DSA" );
578 const int bitsize( pubqkey.length() );
579 addFieldItem( mGrpPkey, tr( "Algorithm" ), bitsize == -1 ? QStringLiteral( "Unknown (possibly Elliptic Curve)" ) : alg, LineEdit );
580 addFieldItem( mGrpPkey, tr( "Key size" ), bitsize == -1 ? QStringLiteral( "?" ) : QString::number( bitsize ), LineEdit );
581 if ( bitsize > 0 ) // ECC keys unsupported by Qt/QCA, so returned key size is 0
582 {
583 const QCA::PublicKey pubakey( mCurrentACert.subjectPublicKey() );
584
585 if ( pubqkey.algorithm() == QSsl::Rsa )
586 {
587 const QCA::RSAPublicKey rsakey( pubakey.toRSA() );
588 const QCA::BigInteger modulus = rsakey.n();
589 QByteArray modarray( modulus.toArray().toByteArray().toHex() );
590 if ( modarray.size() > 2 && modarray.mid( 0, 2 ) == QByteArray( "00" ) )
591 {
592 modarray = modarray.mid( 2 );
593 }
594 const QCA::BigInteger exponent = rsakey.e();
595 addFieldItem( mGrpPkey, tr( "Public key" ), QgsAuthCertUtils::getColonDelimited( modarray ).toUpper(), TextEdit );
596 addFieldItem( mGrpPkey, tr( "Exponent" ), exponent.toString(), LineEdit );
597 }
598 // TODO: how is DSA textually represented using QCA?
599 // QCA::DSAPublicKey dsakey( pubakey.toDSA() );
600
601 // TODO: how to get the signature and show it as hex?
602 // addFieldItem( mGrpPkey, tr( "Signature" ),
603 // mCurrentACert.???.toHex(),
604 // TextEdit );
605
606 // key usage
607 QStringList usage;
608 if ( pubakey.canVerify() )
609 {
610 usage.append( tr( "Verify" ) );
611 }
612
613 // TODO: these two seem to always show up, why?
614 if ( pubakey.canEncrypt() )
615 {
616 usage.append( tr( "Encrypt" ) );
617 }
618#if QCA_VERSION >= 0x020100
619 if ( pubakey.canDecrypt() )
620 {
621 usage.append( tr( "Decrypt" ) );
622 }
623#endif
624 if ( pubakey.canKeyAgree() )
625 {
626 usage.append( tr( "Key agreement" ) );
627 }
628 if ( pubakey.canExport() )
629 {
630 usage.append( tr( "Export" ) );
631 }
632 if ( !usage.isEmpty() )
633 {
634 addFieldItem( mGrpPkey, tr( "Key usage" ), usage.join( QLatin1String( ", " ) ), LineEdit );
635 }
636 }
637
638 // Extensions
639 QStringList basicconst;
640 basicconst << tr( "Certificate Authority: %1" ).arg( mCurrentACert.isCA() ? tr( "Yes" ) : tr( "No" ) )
641 << tr( "Chain Path Limit: %1" ).arg( mCurrentACert.pathLimit() );
642 addFieldItem( mGrpExts, tr( "Basic constraints" ), basicconst.join( QLatin1Char( '\n' ) ), TextEdit );
643
644 QStringList keyusage;
645 QStringList extkeyusage;
646 const QList<QCA::ConstraintType> certconsts = mCurrentACert.constraints();
647 const auto constCertconsts = certconsts;
648 for ( const QCA::ConstraintType &certconst : constCertconsts )
649 {
650 if ( certconst.section() == QCA::ConstraintType::KeyUsage )
651 {
652 keyusage.append( QgsAuthCertUtils::qcaKnownConstraint( certconst.known() ) );
653 }
654 else if ( certconst.section() == QCA::ConstraintType::ExtendedKeyUsage )
655 {
656 extkeyusage.append( QgsAuthCertUtils::qcaKnownConstraint( certconst.known() ) );
657 }
658 }
659 if ( !keyusage.isEmpty() )
660 {
661 addFieldItem( mGrpExts, tr( "Key usage" ), keyusage.join( QLatin1Char( '\n' ) ), TextEdit );
662 }
663 if ( !extkeyusage.isEmpty() )
664 {
665 addFieldItem( mGrpExts, tr( "Extended key usage" ), extkeyusage.join( QLatin1Char( '\n' ) ), TextEdit );
666 }
667
668 addFieldItem( mGrpExts, tr( "Subject key ID" ), QgsAuthCertUtils::getColonDelimited( mCurrentACert.subjectKeyId().toHex() ).toUpper(), LineEdit );
669 addFieldItem( mGrpExts, tr( "Authority key ID" ), QgsAuthCertUtils::getColonDelimited( mCurrentACert.issuerKeyId().toHex() ).toUpper(), LineEdit );
670}
671
672void QgsAuthCertInfo::populateInfoPemTextSection()
673{
674 QgsAuthGuiUtils::removeChildren( mSecPemText );
675
676 if ( mCurrentQCert.isNull() )
677 return;
678
679 QTreeWidgetItem *item = new QTreeWidgetItem(
680 mSecPemText,
681 QStringList( QString() ),
682 static_cast<int>( DetailsField )
683 );
684
685 item->setFirstColumnSpanned( true );
686
687 QPlainTextEdit *pte = new QPlainTextEdit( mCurrentQCert.toPem(), treeDetails );
688 pte->setReadOnly( true );
689 pte->setMinimumHeight( 150 );
690 pte->setMaximumHeight( 150 );
691 pte->moveCursor( QTextCursor::Start );
692 item->treeWidget()->setItemWidget( item, 0, pte );
693}
694
695void QgsAuthCertInfo::btnSaveTrust_clicked()
696{
697 const QgsAuthCertUtils::CertTrustPolicy newpolicy( cmbbxTrust->trustPolicy() );
698 if ( !QgsApplication::authManager()->storeCertTrustPolicy( mCurrentQCert, newpolicy ) )
699 {
700 QgsDebugError( QStringLiteral( "Could not set trust policy for certificate" ) );
701 }
702 mCurrentTrustPolicy = newpolicy;
703 decorateCertTreeItem( mCurrentQCert, newpolicy, nullptr );
704 btnSaveTrust->setEnabled( false );
705
706 // rebuild trust cache
708 mTrustCacheRebuilt = true;
710}
711
712void QgsAuthCertInfo::currentPolicyIndexChanged( int indx )
713{
714 const QgsAuthCertUtils::CertTrustPolicy newpolicy( cmbbxTrust->trustPolicyForIndex( indx ) );
715 btnSaveTrust->setEnabled( newpolicy != mCurrentTrustPolicy );
716}
717
718void QgsAuthCertInfo::decorateCertTreeItem( const QSslCertificate &cert, QgsAuthCertUtils::CertTrustPolicy trustpolicy, QTreeWidgetItem *item )
719{
720 if ( !item )
721 {
722 item = treeHierarchy->currentItem();
723 }
724 if ( !item )
725 {
726 return;
727 }
728
729 if ( cert.isNull() || trustpolicy == QgsAuthCertUtils::NoPolicy )
730 {
731 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateMissing.svg" ) ) );
732 // missing CA, color gray and italicize
733 QBrush b( item->foreground( 0 ) );
734 b.setColor( QColor::fromRgb( 90, 90, 90 ) );
735 item->setForeground( 0, b );
736 QFont f( item->font( 0 ) );
737 f.setItalic( true );
738 item->setFont( 0, f );
739 return;
740 }
741
742 if ( !QgsAuthCertUtils::certIsViable( cert ) )
743 {
744 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateUntrusted.svg" ) ) );
745 return;
746 }
747
748 if ( trustpolicy == QgsAuthCertUtils::Trusted )
749 {
750 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateTrusted.svg" ) ) );
751 }
752 else if ( trustpolicy == QgsAuthCertUtils::Untrusted
753 || ( trustpolicy == QgsAuthCertUtils::DefaultTrust && mDefaultTrustPolicy == QgsAuthCertUtils::Untrusted ) )
754 {
755 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateUntrusted.svg" ) ) );
756 }
757 else
758 {
759 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificate.svg" ) ) );
760 }
761}
762
764
765QgsAuthCertInfoDialog::QgsAuthCertInfoDialog( const QSslCertificate &cert, bool manageCertTrust, QWidget *parent, const QList<QSslCertificate> &connectionCAs )
766 : QDialog( parent )
767
768{
769 setWindowTitle( tr( "Certificate Information" ) );
770 QVBoxLayout *layout = new QVBoxLayout( this );
771 layout->setContentsMargins( 6, 6, 6, 6 );
772
773 mCertInfoWdgt = new QgsAuthCertInfo( cert, manageCertTrust, this, connectionCAs );
774 layout->addWidget( mCertInfoWdgt );
775
776 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this );
777 buttonBox->button( QDialogButtonBox::Close )->setDefault( true );
778 connect( buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close );
779 layout->addWidget( buttonBox );
780
781 setLayout( layout );
782}
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.
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 void setItemBold(QTreeWidgetItem *item)
Call setFirstColumnSpanned(true) on the item and make its font bold.
static void removeChildren(QTreeWidgetItem *item)
Remove the children of the passed item.
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:57