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