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