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