LCOV - code coverage report
Current view: top level - core/auth - qgsauthcertutils.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 645 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsauthcertutils.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : May 1, 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                 :            : #include "qgsauthcertutils.h"
      18                 :            : 
      19                 :            : #include <QColor>
      20                 :            : #include <QDir>
      21                 :            : #include <QFile>
      22                 :            : #include <QObject>
      23                 :            : #include <QSslCertificate>
      24                 :            : #include <QUuid>
      25                 :            : 
      26                 :            : #include "qgsapplication.h"
      27                 :            : #include "qgsauthmanager.h"
      28                 :            : #include "qgslogger.h"
      29                 :            : 
      30                 :            : #ifdef Q_OS_MAC
      31                 :            : #include <string.h>
      32                 :            : #include "libtasn1.h"
      33                 :            : #endif
      34                 :            : 
      35                 :            : 
      36                 :          0 : QString QgsAuthCertUtils::getSslProtocolName( QSsl::SslProtocol protocol )
      37                 :            : {
      38                 :          0 :   switch ( protocol )
      39                 :            :   {
      40                 :            :     case QSsl::SecureProtocols:
      41                 :          0 :       return QObject::tr( "SecureProtocols" );
      42                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
      43                 :            :     case QSsl::TlsV1SslV3:
      44                 :            :       return QObject::tr( "TlsV1SslV3" );
      45                 :            : #endif
      46                 :            :     case QSsl::TlsV1_0:
      47                 :          0 :       return QObject::tr( "TlsV1" );
      48                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
      49                 :            :     // not supported by Qt 5.15+
      50                 :            :     case QSsl::SslV3:
      51                 :            :       return QObject::tr( "SslV3" );
      52                 :            :     case QSsl::SslV2:
      53                 :            :       return QObject::tr( "SslV2" );
      54                 :            : #endif
      55                 :            :     default:
      56                 :          0 :       return QString();
      57                 :            :   }
      58                 :          0 : }
      59                 :            : 
      60                 :          0 : QMap<QString, QSslCertificate> QgsAuthCertUtils::mapDigestToCerts( const QList<QSslCertificate> &certs )
      61                 :            : {
      62                 :          0 :   QMap<QString, QSslCertificate> digestmap;
      63                 :          0 :   for ( const auto &cert : certs )
      64                 :            :   {
      65                 :          0 :     digestmap.insert( shaHexForCert( cert ), cert );
      66                 :            :   }
      67                 :          0 :   return digestmap;
      68                 :          0 : }
      69                 :            : 
      70                 :          0 : QMap<QString, QList<QSslCertificate> > QgsAuthCertUtils::certsGroupedByOrg( const QList<QSslCertificate> &certs )
      71                 :            : {
      72                 :          0 :   QMap< QString, QList<QSslCertificate> > orgcerts;
      73                 :          0 :   for ( const auto &cert : certs )
      74                 :            :   {
      75                 :          0 :     QString org( SSL_SUBJECT_INFO( cert, QSslCertificate::Organization ) );
      76                 :          0 :     if ( org.isEmpty() )
      77                 :          0 :       org = QStringLiteral( "(Organization not defined)" );
      78                 :          0 :     QList<QSslCertificate> valist = orgcerts.contains( org ) ? orgcerts.value( org ) : QList<QSslCertificate>();
      79                 :          0 :     orgcerts.insert( org, valist << cert );
      80                 :          0 :   }
      81                 :          0 :   return orgcerts;
      82                 :          0 : }
      83                 :            : 
      84                 :          0 : QMap<QString, QgsAuthConfigSslServer> QgsAuthCertUtils::mapDigestToSslConfigs( const QList<QgsAuthConfigSslServer> &configs )
      85                 :            : {
      86                 :          0 :   QMap<QString, QgsAuthConfigSslServer> digestmap;
      87                 :          0 :   for ( const auto &config : configs )
      88                 :            :   {
      89                 :          0 :     digestmap.insert( shaHexForCert( config.sslCertificate() ), config );
      90                 :            :   }
      91                 :          0 :   return digestmap;
      92                 :          0 : }
      93                 :            : 
      94                 :          0 : QMap<QString, QList<QgsAuthConfigSslServer> > QgsAuthCertUtils::sslConfigsGroupedByOrg( const QList<QgsAuthConfigSslServer> &configs )
      95                 :            : {
      96                 :          0 :   QMap< QString, QList<QgsAuthConfigSslServer> > orgconfigs;
      97                 :          0 :   for ( const auto &config : configs )
      98                 :            :   {
      99                 :          0 :     QString org( SSL_SUBJECT_INFO( config.sslCertificate(), QSslCertificate::Organization ) );
     100                 :            : 
     101                 :          0 :     if ( org.isEmpty() )
     102                 :          0 :       org = QObject::tr( "(Organization not defined)" );
     103                 :          0 :     QList<QgsAuthConfigSslServer> valist = orgconfigs.contains( org ) ? orgconfigs.value( org ) : QList<QgsAuthConfigSslServer>();
     104                 :          0 :     orgconfigs.insert( org, valist << config );
     105                 :          0 :   }
     106                 :          0 :   return orgconfigs;
     107                 :          0 : }
     108                 :            : 
     109                 :          0 : QByteArray QgsAuthCertUtils::fileData( const QString &path )
     110                 :            : {
     111                 :          0 :   QByteArray data;
     112                 :          0 :   QFile file( path );
     113                 :          0 :   if ( !file.exists() )
     114                 :            :   {
     115                 :          0 :     QgsDebugMsg( QStringLiteral( "Read file error, file not found: %1" ).arg( path ) );
     116                 :          0 :     return data;
     117                 :            :   }
     118                 :            :   // TODO: add checks for locked file, etc., to ensure it can be read
     119                 :          0 :   QFile::OpenMode openflags( QIODevice::ReadOnly );
     120                 :          0 :   bool ret = file.open( openflags );
     121                 :          0 :   if ( ret )
     122                 :            :   {
     123                 :          0 :     data = file.readAll();
     124                 :          0 :   }
     125                 :          0 :   file.close();
     126                 :            : 
     127                 :          0 :   return data;
     128                 :          0 : }
     129                 :            : 
     130                 :          0 : QList<QSslCertificate> QgsAuthCertUtils::certsFromFile( const QString &certspath )
     131                 :            : {
     132                 :          0 :   QList<QSslCertificate> certs;
     133                 :          0 :   const QByteArray payload( QgsAuthCertUtils::fileData( certspath ) );
     134                 :          0 :   certs = QSslCertificate::fromData( payload, sniffEncoding( payload ) );
     135                 :          0 :   if ( certs.isEmpty() )
     136                 :            :   {
     137                 :          0 :     QgsDebugMsg( QStringLiteral( "Parsed cert(s) EMPTY for path: %1" ).arg( certspath ) );
     138                 :          0 :   }
     139                 :          0 :   return certs;
     140                 :          0 : }
     141                 :            : 
     142                 :          0 : QList<QSslCertificate> QgsAuthCertUtils::casFromFile( const QString &certspath )
     143                 :            : {
     144                 :          0 :   QList<QSslCertificate> cas;
     145                 :          0 :   const QList<QSslCertificate> certs( certsFromFile( certspath ) );
     146                 :          0 :   for ( const auto &cert : certs )
     147                 :            :   {
     148                 :          0 :     if ( certificateIsAuthority( cert ) )
     149                 :            :     {
     150                 :          0 :       cas.append( cert );
     151                 :          0 :     }
     152                 :            :   }
     153                 :          0 :   return cas;
     154                 :          0 : }
     155                 :            : 
     156                 :          0 : QList<QSslCertificate> QgsAuthCertUtils::casMerge( const QList<QSslCertificate> &bundle1, const QList<QSslCertificate> &bundle2 )
     157                 :            : {
     158                 :          0 :   QStringList shas;
     159                 :          0 :   QList<QSslCertificate> result( bundle1 );
     160                 :          0 :   const QList<QSslCertificate> c_bundle1( bundle1 );
     161                 :          0 :   for ( const auto &cert : c_bundle1 )
     162                 :            :   {
     163                 :          0 :     shas.append( shaHexForCert( cert ) );
     164                 :            :   }
     165                 :          0 :   const QList<QSslCertificate> c_bundle2( bundle2 );
     166                 :          0 :   for ( const auto &cert : c_bundle2 )
     167                 :            :   {
     168                 :          0 :     if ( ! shas.contains( shaHexForCert( cert ) ) )
     169                 :            :     {
     170                 :          0 :       result.append( cert );
     171                 :          0 :     }
     172                 :            :   }
     173                 :          0 :   return result;
     174                 :          0 : }
     175                 :            : 
     176                 :            : 
     177                 :            : 
     178                 :          0 : QSslCertificate QgsAuthCertUtils::certFromFile( const QString &certpath )
     179                 :            : {
     180                 :          0 :   QSslCertificate cert;
     181                 :          0 :   QList<QSslCertificate> certs( QgsAuthCertUtils::certsFromFile( certpath ) );
     182                 :          0 :   if ( !certs.isEmpty() )
     183                 :            :   {
     184                 :          0 :     cert = certs.first();
     185                 :          0 :   }
     186                 :          0 :   if ( cert.isNull() )
     187                 :            :   {
     188                 :          0 :     QgsDebugMsg( QStringLiteral( "Parsed cert is NULL for path: %1" ).arg( certpath ) );
     189                 :          0 :   }
     190                 :          0 :   return cert;
     191                 :          0 : }
     192                 :            : 
     193                 :          0 : QSslKey QgsAuthCertUtils::keyFromFile( const QString &keypath,
     194                 :            :                                        const QString &keypass,
     195                 :            :                                        QString *algtype )
     196                 :            : {
     197                 :            :   // The approach here is to try all possible encodings and algorithms
     198                 :          0 :   QByteArray keydata( QgsAuthCertUtils::fileData( keypath ) );
     199                 :          0 :   QSslKey clientkey;
     200                 :            : 
     201                 :          0 :   QSsl::EncodingFormat keyEncoding( sniffEncoding( keydata ) );
     202                 :            : 
     203                 :          0 :   const std::vector<QSsl::KeyAlgorithm> algs
     204                 :          0 :   {
     205                 :            :     QSsl::KeyAlgorithm::Rsa,
     206                 :            :     QSsl::KeyAlgorithm::Dsa,
     207                 :            :     QSsl::KeyAlgorithm::Ec,
     208                 :            :     QSsl::KeyAlgorithm::Opaque
     209                 :            :   };
     210                 :            : 
     211                 :          0 :   for ( const auto &alg : algs )
     212                 :            :   {
     213                 :          0 :     clientkey = QSslKey( keydata,
     214                 :          0 :                          alg,
     215                 :          0 :                          keyEncoding,
     216                 :            :                          QSsl::PrivateKey,
     217                 :          0 :                          !keypass.isEmpty() ? keypass.toUtf8() : QByteArray() );
     218                 :          0 :     if ( ! clientkey.isNull() )
     219                 :            :     {
     220                 :          0 :       if ( algtype )
     221                 :            :       {
     222                 :          0 :         switch ( alg )
     223                 :            :         {
     224                 :            :           case QSsl::KeyAlgorithm::Rsa:
     225                 :          0 :             *algtype = QStringLiteral( "rsa" );
     226                 :          0 :             break;
     227                 :            :           case QSsl::KeyAlgorithm::Dsa:
     228                 :          0 :             *algtype = QStringLiteral( "dsa" );
     229                 :          0 :             break;
     230                 :            :           case QSsl::KeyAlgorithm::Ec:
     231                 :          0 :             *algtype = QStringLiteral( "ec" );
     232                 :          0 :             break;
     233                 :            :           case QSsl::KeyAlgorithm::Opaque:
     234                 :          0 :             *algtype = QStringLiteral( "opaque" );
     235                 :          0 :             break;
     236                 :            : #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
     237                 :            :           case QSsl::KeyAlgorithm::Dh:
     238                 :          0 :             *algtype = QStringLiteral( "dh" );
     239                 :          0 :             break;
     240                 :            : #endif
     241                 :            :         }
     242                 :          0 :       }
     243                 :          0 :       return clientkey;
     244                 :            :     }
     245                 :            :   }
     246                 :          0 :   return QSslKey();
     247                 :          0 : }
     248                 :            : 
     249                 :          0 : QList<QSslCertificate> QgsAuthCertUtils::certsFromString( const QString &pemtext )
     250                 :            : {
     251                 :          0 :   QList<QSslCertificate> certs;
     252                 :          0 :   certs = QSslCertificate::fromData( pemtext.toLatin1(), QSsl::Pem );
     253                 :          0 :   if ( certs.isEmpty() )
     254                 :            :   {
     255                 :          0 :     QgsDebugMsg( QStringLiteral( "Parsed cert(s) EMPTY" ) );
     256                 :          0 :   }
     257                 :          0 :   return certs;
     258                 :          0 : }
     259                 :            : 
     260                 :          0 : QList<QSslCertificate> QgsAuthCertUtils::casRemoveSelfSigned( const QList<QSslCertificate> &caList )
     261                 :            : {
     262                 :          0 :   QList<QSslCertificate> certs;
     263                 :          0 :   for ( const auto &cert : caList )
     264                 :            :   {
     265                 :          0 :     if ( ! cert.isSelfSigned( ) )
     266                 :            :     {
     267                 :          0 :       certs.append( cert );
     268                 :          0 :     }
     269                 :            :   }
     270                 :          0 :   return certs;
     271                 :          0 : }
     272                 :            : 
     273                 :          0 : QStringList QgsAuthCertUtils::certKeyBundleToPem( const QString &certpath,
     274                 :            :     const QString &keypath,
     275                 :            :     const QString &keypass,
     276                 :            :     bool reencrypt )
     277                 :            : {
     278                 :          0 :   QString certpem;
     279                 :          0 :   QSslCertificate clientcert = QgsAuthCertUtils::certFromFile( certpath );
     280                 :          0 :   if ( !clientcert.isNull() )
     281                 :            :   {
     282                 :          0 :     certpem = QString( clientcert.toPem() );
     283                 :          0 :   }
     284                 :            : 
     285                 :          0 :   QString keypem;
     286                 :          0 :   QString algtype;
     287                 :          0 :   QSslKey clientkey = QgsAuthCertUtils::keyFromFile( keypath, keypass, &algtype );
     288                 :            : 
     289                 :            :   // reapply passphrase if protection is requested and passphrase exists
     290                 :          0 :   if ( !clientkey.isNull() )
     291                 :            :   {
     292                 :          0 :     keypem = QString( clientkey.toPem( ( reencrypt && !keypass.isEmpty() ) ? keypass.toUtf8() : QByteArray() ) );
     293                 :          0 :   }
     294                 :            : 
     295                 :          0 :   return QStringList() << certpem << keypem << algtype;
     296                 :          0 : }
     297                 :            : 
     298                 :          0 : bool QgsAuthCertUtils::pemIsPkcs8( const QString &keyPemTxt )
     299                 :            : {
     300                 :          0 :   QString pkcs8Header = QStringLiteral( "-----BEGIN PRIVATE KEY-----" );
     301                 :          0 :   QString pkcs8Footer = QStringLiteral( "-----END PRIVATE KEY-----" );
     302                 :          0 :   return keyPemTxt.contains( pkcs8Header ) && keyPemTxt.contains( pkcs8Footer );
     303                 :          0 : }
     304                 :            : 
     305                 :            : #ifdef Q_OS_MAC
     306                 :            : QByteArray QgsAuthCertUtils::pkcs8PrivateKey( QByteArray &pkcs8Der )
     307                 :            : {
     308                 :            :   QByteArray pkcs1;
     309                 :            : 
     310                 :            :   if ( pkcs8Der.isEmpty() )
     311                 :            :   {
     312                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, passed DER is empty" ) );
     313                 :            :     return pkcs1;
     314                 :            :   }
     315                 :            :   // Dump as unarmored PEM format, e.g. missing '-----BEGIN|END...' wrapper
     316                 :            :   //QgsDebugMsg ( QStringLiteral( "pkcs8Der: %1" ).arg( QString( pkcs8Der.toBase64() ) ) );
     317                 :            : 
     318                 :            :   QFileInfo asnDefsRsrc( QgsApplication::pkgDataPath() + QStringLiteral( "/resources/pkcs8.asn" ) );
     319                 :            :   if ( ! asnDefsRsrc.exists() )
     320                 :            :   {
     321                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, pkcs.asn resource file not found: %1" ).arg( asnDefsRsrc.filePath() ) );
     322                 :            :     return pkcs1;
     323                 :            :   }
     324                 :            :   const char *asnDefsFile = asnDefsRsrc.absoluteFilePath().toLocal8Bit().constData();
     325                 :            : 
     326                 :            :   int asn1_result = ASN1_SUCCESS, der_len = 0, oct_len = 0;
     327                 :            :   asn1_node definitions = NULL, structure = NULL;
     328                 :            :   char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE], oct_data[1024];
     329                 :            :   unsigned char *der = NULL;
     330                 :            :   unsigned int flags = 0; //TODO: see if any or all ASN1_DECODE_FLAG_* flags can be set
     331                 :            :   unsigned oct_etype;
     332                 :            : 
     333                 :            :   // Base PKCS#8 element to decode
     334                 :            :   QString typeName( QStringLiteral( "PKCS-8.PrivateKeyInfo" ) );
     335                 :            : 
     336                 :            :   asn1_result = asn1_parser2tree( asnDefsFile, &definitions, errorDescription );
     337                 :            : 
     338                 :            :   switch ( asn1_result )
     339                 :            :   {
     340                 :            :     case ASN1_SUCCESS:
     341                 :            :       QgsDebugMsgLevel( QStringLiteral( "Parse: done.\n" ), 4 );
     342                 :            :       break;
     343                 :            :     case ASN1_FILE_NOT_FOUND:
     344                 :            :       QgsDebugMsg( QStringLiteral( "ERROR, file not found: %1" ).arg( asnDefsFile ) );
     345                 :            :       return pkcs1;
     346                 :            :     case ASN1_SYNTAX_ERROR:
     347                 :            :     case ASN1_IDENTIFIER_NOT_FOUND:
     348                 :            :     case ASN1_NAME_TOO_LONG:
     349                 :            :       QgsDebugMsg( QStringLiteral( "ERROR, asn1 parsing: %1" ).arg( errorDescription ) );
     350                 :            :       return pkcs1;
     351                 :            :     default:
     352                 :            :       QgsDebugMsg( QStringLiteral( "ERROR, libtasn1: %1" ).arg( asn1_strerror( asn1_result ) ) );
     353                 :            :       return pkcs1;
     354                 :            :   }
     355                 :            : 
     356                 :            :   // Generate the ASN.1 structure
     357                 :            :   asn1_result = asn1_create_element( definitions, typeName.toLatin1().constData(), &structure );
     358                 :            : 
     359                 :            :   //asn1_print_structure( stdout, structure, "", ASN1_PRINT_ALL);
     360                 :            : 
     361                 :            :   if ( asn1_result != ASN1_SUCCESS )
     362                 :            :   {
     363                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, structure creation: %1" ).arg( asn1_strerror( asn1_result ) ) );
     364                 :            :     goto PKCS1DONE;
     365                 :            :   }
     366                 :            : 
     367                 :            :   // Populate the ASN.1 structure with decoded DER data
     368                 :            :   der = reinterpret_cast<unsigned char *>( pkcs8Der.data() );
     369                 :            :   der_len = pkcs8Der.size();
     370                 :            : 
     371                 :            :   if ( flags != 0 )
     372                 :            :   {
     373                 :            :     asn1_result = asn1_der_decoding2( &structure, der, &der_len, flags, errorDescription );
     374                 :            :   }
     375                 :            :   else
     376                 :            :   {
     377                 :            :     asn1_result = asn1_der_decoding( &structure, der, der_len, errorDescription );
     378                 :            :   }
     379                 :            : 
     380                 :            :   if ( asn1_result != ASN1_SUCCESS )
     381                 :            :   {
     382                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, decoding: %1" ).arg( errorDescription ) );
     383                 :            :     goto PKCS1DONE;
     384                 :            :   }
     385                 :            :   else
     386                 :            :   {
     387                 :            :     QgsDebugMsgLevel( QStringLiteral( "Decoding: %1" ).arg( asn1_strerror( asn1_result ) ), 4 );
     388                 :            :   }
     389                 :            : 
     390                 :            :   if ( QgsLogger::debugLevel() >= 4 )
     391                 :            :   {
     392                 :            :     QgsDebugMsg( QStringLiteral( "DECODING RESULT:" ) );
     393                 :            :     asn1_print_structure( stdout, structure, "", ASN1_PRINT_NAME_TYPE_VALUE );
     394                 :            :   }
     395                 :            : 
     396                 :            :   // Validate and extract privateKey value
     397                 :            :   QgsDebugMsgLevel( QStringLiteral( "Validating privateKey type..." ), 4 );
     398                 :            :   typeName.append( QStringLiteral( ".privateKey" ) );
     399                 :            :   QgsDebugMsgLevel( QStringLiteral( "privateKey element name: %1" ).arg( typeName ), 4 );
     400                 :            : 
     401                 :            :   asn1_result = asn1_read_value_type( structure, "privateKey", NULL, &oct_len, &oct_etype );
     402                 :            : 
     403                 :            :   if ( asn1_result != ASN1_MEM_ERROR ) // not sure why ASN1_MEM_ERROR = success, but it does
     404                 :            :   {
     405                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, asn1 read privateKey value type: %1" ).arg( asn1_strerror( asn1_result ) ) );
     406                 :            :     goto PKCS1DONE;
     407                 :            :   }
     408                 :            : 
     409                 :            :   if ( oct_etype != ASN1_ETYPE_OCTET_STRING )
     410                 :            :   {
     411                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, asn1 privateKey value not octet string, but type: %1" ).arg( static_cast<int>( oct_etype ) ) );
     412                 :            :     goto PKCS1DONE;
     413                 :            :   }
     414                 :            : 
     415                 :            :   if ( oct_len == 0 )
     416                 :            :   {
     417                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, asn1 privateKey octet string empty" ) );
     418                 :            :     goto PKCS1DONE;
     419                 :            :   }
     420                 :            : 
     421                 :            :   QgsDebugMsgLevel( QStringLiteral( "Reading privateKey value..." ), 4 );
     422                 :            :   asn1_result = asn1_read_value( structure, "privateKey", oct_data, &oct_len );
     423                 :            : 
     424                 :            :   if ( asn1_result != ASN1_SUCCESS )
     425                 :            :   {
     426                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, asn1 read privateKey value: %1" ).arg( asn1_strerror( asn1_result ) ) );
     427                 :            :     goto PKCS1DONE;
     428                 :            :   }
     429                 :            : 
     430                 :            :   if ( oct_len == 0 )
     431                 :            :   {
     432                 :            :     QgsDebugMsg( QStringLiteral( "ERROR, asn1 privateKey value octet string empty" ) );
     433                 :            :     goto PKCS1DONE;
     434                 :            :   }
     435                 :            : 
     436                 :            :   pkcs1 = QByteArray( oct_data, oct_len );
     437                 :            : 
     438                 :            :   // !!! SENSITIVE DATA - DO NOT LEAVE UNCOMMENTED !!!
     439                 :            :   //QgsDebugMsgLevel( QStringLiteral( "privateKey octet data as PEM: %1" ).arg( QString( pkcs1.toBase64() ) ), 4 );
     440                 :            : 
     441                 :            : PKCS1DONE:
     442                 :            : 
     443                 :            :   asn1_delete_structure( &structure );
     444                 :            :   return pkcs1;
     445                 :            : }
     446                 :            : #endif
     447                 :            : 
     448                 :          0 : QStringList QgsAuthCertUtils::pkcs12BundleToPem( const QString &bundlepath,
     449                 :            :     const QString &bundlepass,
     450                 :            :     bool reencrypt )
     451                 :            : {
     452                 :          0 :   QStringList empty;
     453                 :          0 :   if ( !QCA::isSupported( "pkcs12" ) )
     454                 :            :   {
     455                 :          0 :     QgsDebugMsg( QStringLiteral( "QCA does not support PKCS#12" ) );
     456                 :          0 :     return empty;
     457                 :            :   }
     458                 :            : 
     459                 :          0 :   QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
     460                 :          0 :   if ( bundle.isNull() )
     461                 :            :   {
     462                 :          0 :     QgsDebugMsg( QStringLiteral( "FAILED to convert PKCS#12 file to QCA key bundle: %1" ).arg( bundlepath ) );
     463                 :          0 :     return empty;
     464                 :            :   }
     465                 :            : 
     466                 :          0 :   QCA::SecureArray passarray;
     467                 :          0 :   if ( reencrypt && !bundlepass.isEmpty() )
     468                 :            :   {
     469                 :          0 :     passarray = QCA::SecureArray( bundlepass.toUtf8() );
     470                 :          0 :   }
     471                 :            : 
     472                 :          0 :   QString algtype;
     473                 :          0 :   QSsl::KeyAlgorithm keyalg = QSsl::Opaque;
     474                 :          0 :   if ( bundle.privateKey().isRSA() )
     475                 :            :   {
     476                 :          0 :     algtype = QStringLiteral( "rsa" );
     477                 :          0 :     keyalg = QSsl::Rsa;
     478                 :          0 :   }
     479                 :          0 :   else if ( bundle.privateKey().isDSA() )
     480                 :            :   {
     481                 :          0 :     algtype = QStringLiteral( "dsa" );
     482                 :          0 :     keyalg = QSsl::Dsa;
     483                 :          0 :   }
     484                 :          0 :   else if ( bundle.privateKey().isDH() )
     485                 :            :   {
     486                 :          0 :     algtype = QStringLiteral( "dh" );
     487                 :          0 :   }
     488                 :            :   // TODO: add support for EC keys, once QCA supports them
     489                 :            : 
     490                 :            :   // can currently only support RSA and DSA between QCA and Qt
     491                 :          0 :   if ( keyalg == QSsl::Opaque )
     492                 :            :   {
     493                 :          0 :     QgsDebugMsg( QStringLiteral( "FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1" ).arg( bundlepath ) );
     494                 :          0 :     return empty;
     495                 :            :   }
     496                 :            : 
     497                 :          0 :   QString keyPem;
     498                 :            : #ifdef Q_OS_MAC
     499                 :            :   if ( keyalg == QSsl::Rsa && QgsAuthCertUtils::pemIsPkcs8( bundle.privateKey().toPEM() ) )
     500                 :            :   {
     501                 :            :     QgsDebugMsgLevel( QStringLiteral( "Private key is PKCS#8: attempting conversion to PKCS#1..." ), 4 );
     502                 :            :     // if RSA, convert from PKCS#8 key to 'traditional' OpenSSL RSA format, which Qt prefers
     503                 :            :     // note: QCA uses OpenSSL, regardless of the Qt SSL backend, and 1.0.2+ OpenSSL versions return
     504                 :            :     //       RSA private keys as PKCS#8, which choke Qt upon QSslKey creation
     505                 :            : 
     506                 :            :     QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
     507                 :            :     if ( pkcs8Der.isEmpty() )
     508                 :            :     {
     509                 :            :       QgsDebugMsg( QStringLiteral( "FAILED to convert PKCS#12 key to DER-encoded format: %1" ).arg( bundlepath ) );
     510                 :            :       return empty;
     511                 :            :     }
     512                 :            : 
     513                 :            :     QByteArray pkcs1Der = QgsAuthCertUtils::pkcs8PrivateKey( pkcs8Der );
     514                 :            :     if ( pkcs1Der.isEmpty() )
     515                 :            :     {
     516                 :            :       QgsDebugMsg( QStringLiteral( "FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1: %1" ).arg( bundlepath ) );
     517                 :            :       return empty;
     518                 :            :     }
     519                 :            : 
     520                 :            :     QSslKey pkcs1Key( pkcs1Der, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey );
     521                 :            :     if ( pkcs1Key.isNull() )
     522                 :            :     {
     523                 :            :       QgsDebugMsg( QStringLiteral( "FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1 QSslKey: %1" ).arg( bundlepath ) );
     524                 :            :       return empty;
     525                 :            :     }
     526                 :            :     keyPem = QString( pkcs1Key.toPem( passarray.toByteArray() ) );
     527                 :            :   }
     528                 :            :   else
     529                 :            :   {
     530                 :            :     keyPem = bundle.privateKey().toPEM( passarray );
     531                 :            :   }
     532                 :            : #else
     533                 :          0 :   keyPem = bundle.privateKey().toPEM( passarray );
     534                 :            : #endif
     535                 :            : 
     536                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "PKCS#12 cert as PEM:\n%1" ).arg( QString( bundle.certificateChain().primary().toPEM() ) ), 4 );
     537                 :            :   // !!! SENSITIVE DATA - DO NOT LEAVE UNCOMMENTED !!!
     538                 :            :   //QgsDebugMsgLevel( QStringLiteral( "PKCS#12 key as PEM:\n%1" ).arg( QString( keyPem ) ), 4 );
     539                 :            : 
     540                 :          0 :   return QStringList() << bundle.certificateChain().primary().toPEM() << keyPem << algtype;
     541                 :          0 : }
     542                 :            : 
     543                 :          0 : QList<QSslCertificate> QgsAuthCertUtils::pkcs12BundleCas( const QString &bundlepath, const QString &bundlepass )
     544                 :            : {
     545                 :          0 :   QList<QSslCertificate> result;
     546                 :          0 :   if ( !QCA::isSupported( "pkcs12" ) )
     547                 :          0 :     return result;
     548                 :            : 
     549                 :          0 :   QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
     550                 :          0 :   if ( bundle.isNull() )
     551                 :          0 :     return result;
     552                 :            : 
     553                 :          0 :   const QCA::CertificateChain chain( bundle.certificateChain() );
     554                 :          0 :   for ( const auto &cert : chain )
     555                 :            :   {
     556                 :          0 :     if ( cert.isCA( ) )
     557                 :            :     {
     558                 :          0 :       result.append( QSslCertificate::fromData( cert.toPEM().toLatin1() ) );
     559                 :          0 :     }
     560                 :            :   }
     561                 :          0 :   return result;
     562                 :          0 : }
     563                 :            : 
     564                 :          0 : QByteArray QgsAuthCertUtils::certsToPemText( const QList<QSslCertificate> &certs )
     565                 :            : {
     566                 :          0 :   QByteArray capem;
     567                 :          0 :   if ( !certs.isEmpty() )
     568                 :            :   {
     569                 :          0 :     QStringList certslist;
     570                 :          0 :     for ( const auto &cert : certs )
     571                 :            :     {
     572                 :          0 :       certslist << cert.toPem();
     573                 :            :     }
     574                 :          0 :     capem = certslist.join( QLatin1Char( '\n' ) ).toLatin1(); //+ "\n";
     575                 :          0 :   }
     576                 :          0 :   return capem;
     577                 :          0 : }
     578                 :            : 
     579                 :          0 : QString QgsAuthCertUtils::pemTextToTempFile( const QString &name, const QByteArray &pemtext )
     580                 :            : {
     581                 :          0 :   QFile pemFile( QDir::tempPath() + QDir::separator() + name );
     582                 :          0 :   QString pemFilePath( pemFile.fileName() );
     583                 :            : 
     584                 :          0 :   if ( pemFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
     585                 :            :   {
     586                 :          0 :     qint64 bytesWritten = pemFile.write( pemtext );
     587                 :          0 :     if ( bytesWritten == -1 )
     588                 :            :     {
     589                 :          0 :       QgsDebugMsg( QStringLiteral( "FAILED to write to temp PEM file: %1" ).arg( pemFilePath ) );
     590                 :          0 :       pemFilePath.clear();
     591                 :          0 :     }
     592                 :          0 :   }
     593                 :            :   else
     594                 :            :   {
     595                 :          0 :     QgsDebugMsg( QStringLiteral( "FAILED to open writing for temp PEM file: %1" ).arg( pemFilePath ) );
     596                 :          0 :     pemFilePath.clear();
     597                 :            :   }
     598                 :            : 
     599                 :          0 :   if ( !pemFile.setPermissions( QFile::ReadUser ) )
     600                 :            :   {
     601                 :          0 :     QgsDebugMsg( QStringLiteral( "FAILED to set permissions on temp PEM file: %1" ).arg( pemFilePath ) );
     602                 :          0 :     pemFilePath.clear();
     603                 :          0 :   }
     604                 :            : 
     605                 :          0 :   return pemFilePath;
     606                 :          0 : }
     607                 :            : 
     608                 :          0 : QString QgsAuthCertUtils::getCaSourceName( QgsAuthCertUtils::CaCertSource source, bool single )
     609                 :            : {
     610                 :          0 :   switch ( source )
     611                 :            :   {
     612                 :            :     case SystemRoot:
     613                 :          0 :       return single ? QObject::tr( "System Root CA" ) : QObject::tr( "System Root Authorities" );
     614                 :            :     case FromFile:
     615                 :          0 :       return single ? QObject::tr( "File CA" ) : QObject::tr( "Authorities from File" );
     616                 :            :     case InDatabase:
     617                 :          0 :       return single ? QObject::tr( "Database CA" ) : QObject::tr( "Authorities in Database" );
     618                 :            :     case Connection:
     619                 :          0 :       return single ? QObject::tr( "Connection CA" ) : QObject::tr( "Authorities from connection" );
     620                 :            :     default:
     621                 :          0 :       return QString();
     622                 :            :   }
     623                 :          0 : }
     624                 :            : 
     625                 :          0 : QString QgsAuthCertUtils::resolvedCertName( const QSslCertificate &cert, bool issuer )
     626                 :            : {
     627                 :          0 :   QString name( issuer ? SSL_ISSUER_INFO( cert, QSslCertificate::CommonName )
     628                 :          0 :                 : SSL_SUBJECT_INFO( cert, QSslCertificate::CommonName ) );
     629                 :            : 
     630                 :          0 :   if ( name.isEmpty() )
     631                 :          0 :     name = issuer ? SSL_ISSUER_INFO( cert, QSslCertificate::OrganizationalUnitName )
     632                 :          0 :            : SSL_SUBJECT_INFO( cert, QSslCertificate::OrganizationalUnitName );
     633                 :            : 
     634                 :          0 :   if ( name.isEmpty() )
     635                 :          0 :     name = issuer ? SSL_ISSUER_INFO( cert, QSslCertificate::Organization )
     636                 :          0 :            : SSL_SUBJECT_INFO( cert, QSslCertificate::Organization );
     637                 :            : 
     638                 :          0 :   if ( name.isEmpty() )
     639                 :          0 :     name = issuer ? SSL_ISSUER_INFO( cert, QSslCertificate::LocalityName )
     640                 :          0 :            : SSL_SUBJECT_INFO( cert, QSslCertificate::LocalityName );
     641                 :            : 
     642                 :          0 :   if ( name.isEmpty() )
     643                 :          0 :     name = issuer ? SSL_ISSUER_INFO( cert, QSslCertificate::StateOrProvinceName )
     644                 :          0 :            : SSL_SUBJECT_INFO( cert, QSslCertificate::StateOrProvinceName );
     645                 :            : 
     646                 :          0 :   if ( name.isEmpty() )
     647                 :          0 :     name = issuer ? SSL_ISSUER_INFO( cert, QSslCertificate::CountryName )
     648                 :          0 :            : SSL_SUBJECT_INFO( cert, QSslCertificate::CountryName );
     649                 :            : 
     650                 :          0 :   return name;
     651                 :          0 : }
     652                 :            : 
     653                 :            : // private
     654                 :          0 : void QgsAuthCertUtils::appendDirSegment_( QStringList &dirname,
     655                 :            :     const QString &segment, QString value )
     656                 :            : {
     657                 :          0 :   if ( !value.isEmpty() )
     658                 :            :   {
     659                 :          0 :     dirname.append( segment + '=' + value.replace( ',', QLatin1String( "\\," ) ) );
     660                 :          0 :   }
     661                 :          0 : }
     662                 :            : 
     663                 :          0 : QSsl::EncodingFormat QgsAuthCertUtils::sniffEncoding( const QByteArray &payload )
     664                 :            : {
     665                 :          0 :   return payload.contains( QByteArrayLiteral( "-----BEGIN " ) ) ?
     666                 :            :          QSsl::Pem :
     667                 :            :          QSsl::Der;
     668                 :          0 : }
     669                 :            : 
     670                 :          0 : QString QgsAuthCertUtils::getCertDistinguishedName( const QSslCertificate &qcert,
     671                 :            :     const QCA::Certificate &acert,
     672                 :            :     bool issuer )
     673                 :            : {
     674                 :          0 :   if ( QgsApplication::authManager()->isDisabled() )
     675                 :          0 :     return QString();
     676                 :            : 
     677                 :          0 :   if ( acert.isNull() )
     678                 :            :   {
     679                 :            :     QCA::ConvertResult res;
     680                 :          0 :     QCA::Certificate acert( QCA::Certificate::fromPEM( qcert.toPem(), &res, QStringLiteral( "qca-ossl" ) ) );
     681                 :          0 :     if ( res != QCA::ConvertGood || acert.isNull() )
     682                 :            :     {
     683                 :          0 :       QgsDebugMsg( QStringLiteral( "Certificate could not be converted to QCA cert" ) );
     684                 :          0 :       return QString();
     685                 :            :     }
     686                 :          0 :   }
     687                 :            :   //  E=testcert@boundlessgeo.com,
     688                 :            :   //  CN=Boundless Test Root CA,
     689                 :            :   //  OU=Certificate Authority,
     690                 :            :   //  O=Boundless Test CA,
     691                 :            :   //  L=District of Columbia,
     692                 :            :   //  ST=Washington\, DC,
     693                 :            :   //  C=US
     694                 :          0 :   QStringList dirname;
     695                 :          0 :   QgsAuthCertUtils::appendDirSegment_(
     696                 :          0 :     dirname, QStringLiteral( "E" ), issuer ? acert.issuerInfo().value( QCA::Email )
     697                 :          0 :     : acert.subjectInfo().value( QCA::Email ) );
     698                 :          0 :   QgsAuthCertUtils::appendDirSegment_(
     699                 :          0 :     dirname, QStringLiteral( "CN" ), issuer ? SSL_ISSUER_INFO( qcert, QSslCertificate::CommonName )
     700                 :          0 :     : SSL_SUBJECT_INFO( qcert, QSslCertificate::CommonName ) );
     701                 :          0 :   QgsAuthCertUtils::appendDirSegment_(
     702                 :          0 :     dirname, QStringLiteral( "OU" ), issuer ? SSL_ISSUER_INFO( qcert, QSslCertificate::OrganizationalUnitName )
     703                 :          0 :     : SSL_SUBJECT_INFO( qcert, QSslCertificate::OrganizationalUnitName ) );
     704                 :          0 :   QgsAuthCertUtils::appendDirSegment_(
     705                 :          0 :     dirname, QStringLiteral( "O" ), issuer ? SSL_ISSUER_INFO( qcert, QSslCertificate::Organization )
     706                 :          0 :     : SSL_SUBJECT_INFO( qcert, QSslCertificate::Organization ) );
     707                 :          0 :   QgsAuthCertUtils::appendDirSegment_(
     708                 :          0 :     dirname, QStringLiteral( "L" ), issuer ? SSL_ISSUER_INFO( qcert, QSslCertificate::LocalityName )
     709                 :          0 :     : SSL_SUBJECT_INFO( qcert, QSslCertificate::LocalityName ) );
     710                 :          0 :   QgsAuthCertUtils::appendDirSegment_(
     711                 :          0 :     dirname, QStringLiteral( "ST" ), issuer ? SSL_ISSUER_INFO( qcert, QSslCertificate::StateOrProvinceName )
     712                 :          0 :     : SSL_SUBJECT_INFO( qcert, QSslCertificate::StateOrProvinceName ) );
     713                 :          0 :   QgsAuthCertUtils::appendDirSegment_(
     714                 :          0 :     dirname, QStringLiteral( "C" ), issuer ? SSL_ISSUER_INFO( qcert, QSslCertificate::CountryName )
     715                 :          0 :     : SSL_SUBJECT_INFO( qcert, QSslCertificate::CountryName ) );
     716                 :            : 
     717                 :          0 :   return dirname.join( QLatin1Char( ',' ) );
     718                 :          0 : }
     719                 :            : 
     720                 :          0 : QString QgsAuthCertUtils::getCertTrustName( QgsAuthCertUtils::CertTrustPolicy trust )
     721                 :            : {
     722                 :          0 :   switch ( trust )
     723                 :            :   {
     724                 :            :     case DefaultTrust:
     725                 :          0 :       return QObject::tr( "Default" );
     726                 :            :     case Trusted:
     727                 :          0 :       return QObject::tr( "Trusted" );
     728                 :            :     case Untrusted:
     729                 :          0 :       return QObject::tr( "Untrusted" );
     730                 :            :     default:
     731                 :          0 :       return QString();
     732                 :            :   }
     733                 :          0 : }
     734                 :            : 
     735                 :          0 : QString QgsAuthCertUtils::getColonDelimited( const QString &txt )
     736                 :            : {
     737                 :            :   // 64321c05b0ebab8e2b67ec0d7d9e2b6d4bc3c303
     738                 :            :   //   -> 64:32:1c:05:b0:eb:ab:8e:2b:67:ec:0d:7d:9e:2b:6d:4b:c3:c3:03
     739                 :          0 :   QStringList sl;
     740                 :          0 :   sl.reserve( txt.size() );
     741                 :          0 :   for ( int i = 0; i < txt.size(); i += 2 )
     742                 :            :   {
     743                 :          0 :     sl << txt.mid( i, ( i + 2 > txt.size() ) ? -1 : 2 );
     744                 :          0 :   }
     745                 :          0 :   return sl.join( QLatin1Char( ':' ) );
     746                 :          0 : }
     747                 :            : 
     748                 :          0 : QString QgsAuthCertUtils::shaHexForCert( const QSslCertificate &cert, bool formatted )
     749                 :            : {
     750                 :          0 :   QString sha( cert.digest( QCryptographicHash::Sha1 ).toHex() );
     751                 :          0 :   if ( formatted )
     752                 :            :   {
     753                 :          0 :     return QgsAuthCertUtils::getColonDelimited( sha );
     754                 :            :   }
     755                 :          0 :   return sha;
     756                 :          0 : }
     757                 :            : 
     758                 :          0 : QCA::Certificate QgsAuthCertUtils::qtCertToQcaCert( const QSslCertificate &cert )
     759                 :            : {
     760                 :          0 :   if ( QgsApplication::authManager()->isDisabled() )
     761                 :          0 :     return QCA::Certificate();
     762                 :            : 
     763                 :            :   QCA::ConvertResult res;
     764                 :          0 :   QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral( "qca-ossl" ) ) );
     765                 :          0 :   if ( res != QCA::ConvertGood || qcacert.isNull() )
     766                 :            :   {
     767                 :          0 :     QgsDebugMsg( QStringLiteral( "Certificate could not be converted to QCA cert" ) );
     768                 :          0 :     qcacert = QCA::Certificate();
     769                 :          0 :   }
     770                 :          0 :   return qcacert;
     771                 :          0 : }
     772                 :            : 
     773                 :          0 : QCA::CertificateCollection QgsAuthCertUtils::qtCertsToQcaCollection( const QList<QSslCertificate> &certs )
     774                 :            : {
     775                 :          0 :   QCA::CertificateCollection qcacoll;
     776                 :          0 :   if ( QgsApplication::authManager()->isDisabled() )
     777                 :          0 :     return qcacoll;
     778                 :            : 
     779                 :          0 :   for ( const auto &cert : certs )
     780                 :            :   {
     781                 :          0 :     QCA::Certificate qcacert( qtCertToQcaCert( cert ) );
     782                 :          0 :     if ( !qcacert.isNull() )
     783                 :            :     {
     784                 :          0 :       qcacoll.addCertificate( qcacert );
     785                 :          0 :     }
     786                 :          0 :   }
     787                 :          0 :   return qcacoll;
     788                 :          0 : }
     789                 :            : 
     790                 :          0 : QCA::KeyBundle QgsAuthCertUtils::qcaKeyBundle( const QString &path, const QString &pass )
     791                 :            : {
     792                 :          0 :   QCA::SecureArray passarray;
     793                 :          0 :   if ( !pass.isEmpty() )
     794                 :          0 :     passarray = QCA::SecureArray( pass.toUtf8() );
     795                 :            : 
     796                 :            :   QCA::ConvertResult res;
     797                 :          0 :   QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( path, passarray, &res, QStringLiteral( "qca-ossl" ) ) );
     798                 :            : 
     799                 :          0 :   return ( res == QCA::ConvertGood ? bundle : QCA::KeyBundle() );
     800                 :          0 : }
     801                 :            : 
     802                 :          0 : QString QgsAuthCertUtils::qcaValidityMessage( QCA::Validity validity )
     803                 :            : {
     804                 :          0 :   switch ( validity )
     805                 :            :   {
     806                 :            :     case QCA::ValidityGood:
     807                 :          0 :       return QObject::tr( "Certificate is valid." );
     808                 :            :     case QCA::ErrorRejected:
     809                 :          0 :       return QObject::tr( "Root CA rejected the certificate purpose." );
     810                 :            :     case QCA::ErrorUntrusted:
     811                 :          0 :       return QObject::tr( "Certificate is not trusted." );
     812                 :            :     case QCA::ErrorSignatureFailed:
     813                 :          0 :       return QObject::tr( "Signature does not match." );
     814                 :            :     case QCA::ErrorInvalidCA:
     815                 :          0 :       return QObject::tr( "Certificate Authority is invalid or not found." );
     816                 :            :     case QCA::ErrorInvalidPurpose:
     817                 :          0 :       return QObject::tr( "Purpose does not match the intended usage." );
     818                 :            :     case QCA::ErrorSelfSigned:
     819                 :          0 :       return QObject::tr( "Certificate is self-signed, and is not found in the list of trusted certificates." );
     820                 :            :     case QCA::ErrorRevoked:
     821                 :          0 :       return QObject::tr( "Certificate has been revoked." );
     822                 :            :     case QCA::ErrorPathLengthExceeded:
     823                 :          0 :       return QObject::tr( "Path length from the root CA to this certificate is too long." );
     824                 :            :     case QCA::ErrorExpired:
     825                 :          0 :       return QObject::tr( "Certificate has expired or is not yet valid." );
     826                 :            :     case QCA::ErrorExpiredCA:
     827                 :          0 :       return QObject::tr( "Certificate Authority has expired." );
     828                 :            :     case QCA::ErrorValidityUnknown:
     829                 :          0 :       return QObject::tr( "Validity is unknown." );
     830                 :            :     default:
     831                 :          0 :       return QString();
     832                 :            :   }
     833                 :          0 : }
     834                 :            : 
     835                 :          0 : QString QgsAuthCertUtils::qcaSignatureAlgorithm( QCA::SignatureAlgorithm algorithm )
     836                 :            : {
     837                 :          0 :   switch ( algorithm )
     838                 :            :   {
     839                 :            :     case QCA::EMSA1_SHA1:
     840                 :          0 :       return QObject::tr( "SHA1, with EMSA1" );
     841                 :            :     case QCA::EMSA3_SHA1:
     842                 :          0 :       return QObject::tr( "SHA1, with EMSA3" );
     843                 :            :     case QCA::EMSA3_MD5:
     844                 :          0 :       return QObject::tr( "MD5, with EMSA3" );
     845                 :            :     case QCA::EMSA3_MD2:
     846                 :          0 :       return QObject::tr( "MD2, with EMSA3" );
     847                 :            :     case QCA::EMSA3_RIPEMD160:
     848                 :          0 :       return QObject::tr( "RIPEMD160, with EMSA3" );
     849                 :            :     case QCA::EMSA3_Raw:
     850                 :          0 :       return QObject::tr( "EMSA3, without digest" );
     851                 :            : #if QCA_VERSION >= 0x020100
     852                 :            :     case QCA::EMSA3_SHA224:
     853                 :          0 :       return QObject::tr( "SHA224, with EMSA3" );
     854                 :            :     case QCA::EMSA3_SHA256:
     855                 :          0 :       return QObject::tr( "SHA256, with EMSA3" );
     856                 :            :     case QCA::EMSA3_SHA384:
     857                 :          0 :       return QObject::tr( "SHA384, with EMSA3" );
     858                 :            :     case QCA::EMSA3_SHA512:
     859                 :          0 :       return QObject::tr( "SHA512, with EMSA3" );
     860                 :            : #endif
     861                 :            :     default:
     862                 :          0 :       return QObject::tr( "Unknown (possibly Elliptic Curve)" );
     863                 :            :   }
     864                 :          0 : }
     865                 :            : 
     866                 :          0 : QString QgsAuthCertUtils::qcaKnownConstraint( QCA::ConstraintTypeKnown constraint )
     867                 :            : {
     868                 :          0 :   switch ( constraint )
     869                 :            :   {
     870                 :            :     case QCA::DigitalSignature:
     871                 :          0 :       return QObject::tr( "Digital Signature" );
     872                 :            :     case QCA::NonRepudiation:
     873                 :          0 :       return QObject::tr( "Non-repudiation" );
     874                 :            :     case QCA::KeyEncipherment:
     875                 :          0 :       return QObject::tr( "Key Encipherment" );
     876                 :            :     case QCA::DataEncipherment:
     877                 :          0 :       return QObject::tr( "Data Encipherment" );
     878                 :            :     case QCA::KeyAgreement:
     879                 :          0 :       return QObject::tr( "Key Agreement" );
     880                 :            :     case QCA::KeyCertificateSign:
     881                 :          0 :       return QObject::tr( "Key Certificate Sign" );
     882                 :            :     case QCA::CRLSign:
     883                 :          0 :       return QObject::tr( "CRL Sign" );
     884                 :            :     case QCA::EncipherOnly:
     885                 :          0 :       return QObject::tr( "Encipher Only" );
     886                 :            :     case QCA::DecipherOnly:
     887                 :          0 :       return QObject::tr( "Decipher Only" );
     888                 :            :     case QCA::ServerAuth:
     889                 :          0 :       return QObject::tr( "Server Authentication" );
     890                 :            :     case QCA::ClientAuth:
     891                 :          0 :       return QObject::tr( "Client Authentication" );
     892                 :            :     case QCA::CodeSigning:
     893                 :          0 :       return QObject::tr( "Code Signing" );
     894                 :            :     case QCA::EmailProtection:
     895                 :          0 :       return QObject::tr( "Email Protection" );
     896                 :            :     case QCA::IPSecEndSystem:
     897                 :          0 :       return QObject::tr( "IPSec Endpoint" );
     898                 :            :     case QCA::IPSecTunnel:
     899                 :          0 :       return QObject::tr( "IPSec Tunnel" );
     900                 :            :     case QCA::IPSecUser:
     901                 :          0 :       return QObject::tr( "IPSec User" );
     902                 :            :     case QCA::TimeStamping:
     903                 :          0 :       return QObject::tr( "Time Stamping" );
     904                 :            :     case QCA::OCSPSigning:
     905                 :          0 :       return QObject::tr( "OCSP Signing" );
     906                 :            :     default:
     907                 :          0 :       return QString();
     908                 :            :   }
     909                 :          0 : }
     910                 :            : 
     911                 :          0 : QString QgsAuthCertUtils::certificateUsageTypeString( QgsAuthCertUtils::CertUsageType usagetype )
     912                 :            : {
     913                 :          0 :   switch ( usagetype )
     914                 :            :   {
     915                 :            :     case QgsAuthCertUtils::AnyOrUnspecifiedUsage:
     916                 :          0 :       return QObject::tr( "Any or unspecified" );
     917                 :            :     case QgsAuthCertUtils::CertAuthorityUsage:
     918                 :          0 :       return QObject::tr( "Certificate Authority" );
     919                 :            :     case QgsAuthCertUtils::CertIssuerUsage:
     920                 :          0 :       return QObject::tr( "Certificate Issuer" );
     921                 :            :     case QgsAuthCertUtils::TlsServerUsage:
     922                 :          0 :       return QObject::tr( "TLS/SSL Server" );
     923                 :            :     case QgsAuthCertUtils::TlsServerEvUsage:
     924                 :          0 :       return QObject::tr( "TLS/SSL Server EV" );
     925                 :            :     case QgsAuthCertUtils::TlsClientUsage:
     926                 :          0 :       return QObject::tr( "TLS/SSL Client" );
     927                 :            :     case QgsAuthCertUtils::CodeSigningUsage:
     928                 :          0 :       return QObject::tr( "Code Signing" );
     929                 :            :     case QgsAuthCertUtils::EmailProtectionUsage:
     930                 :          0 :       return QObject::tr( "Email Protection" );
     931                 :            :     case QgsAuthCertUtils::TimeStampingUsage:
     932                 :          0 :       return QObject::tr( "Time Stamping" );
     933                 :            :     case QgsAuthCertUtils::CRLSigningUsage:
     934                 :          0 :       return QObject::tr( "CRL Signing" );
     935                 :            :     case QgsAuthCertUtils::UndeterminedUsage:
     936                 :            :     default:
     937                 :          0 :       return QObject::tr( "Undetermined usage" );
     938                 :            :   }
     939                 :          0 : }
     940                 :            : 
     941                 :          0 : QList<QgsAuthCertUtils::CertUsageType> QgsAuthCertUtils::certificateUsageTypes( const QSslCertificate &cert )
     942                 :            : {
     943                 :          0 :   QList<QgsAuthCertUtils::CertUsageType> usages;
     944                 :            : 
     945                 :          0 :   if ( QgsApplication::authManager()->isDisabled() )
     946                 :          0 :     return usages;
     947                 :            : 
     948                 :            :   QCA::ConvertResult res;
     949                 :          0 :   QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral( "qca-ossl" ) ) );
     950                 :          0 :   if ( res != QCA::ConvertGood || qcacert.isNull() )
     951                 :            :   {
     952                 :          0 :     QgsDebugMsg( QStringLiteral( "Certificate could not be converted to QCA cert" ) );
     953                 :          0 :     return usages;
     954                 :            :   }
     955                 :            : 
     956                 :          0 :   if ( qcacert.isCA() )
     957                 :            :   {
     958                 :          0 :     QgsDebugMsg( QStringLiteral( "Certificate has 'CA:TRUE' basic constraint" ) );
     959                 :          0 :     usages << QgsAuthCertUtils::CertAuthorityUsage;
     960                 :          0 :   }
     961                 :            : 
     962                 :          0 :   const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
     963                 :          0 :   for ( const auto &certconst : certconsts )
     964                 :            :   {
     965                 :          0 :     if ( certconst.known() == QCA::KeyCertificateSign )
     966                 :            :     {
     967                 :          0 :       QgsDebugMsg( QStringLiteral( "Certificate has 'Certificate Sign' key usage" ) );
     968                 :          0 :       usages << QgsAuthCertUtils::CertIssuerUsage;
     969                 :          0 :     }
     970                 :          0 :     else if ( certconst.known() == QCA::ServerAuth )
     971                 :            :     {
     972                 :          0 :       QgsDebugMsg( QStringLiteral( "Certificate has 'server authentication' extended key usage" ) );
     973                 :          0 :       usages << QgsAuthCertUtils::TlsServerUsage;
     974                 :          0 :     }
     975                 :            :   }
     976                 :            : 
     977                 :            :   // ask QCA what it thinks about potential usages
     978                 :            :   QCA::CertificateCollection trustedCAs(
     979                 :          0 :     qtCertsToQcaCollection( QgsApplication::authManager()->trustedCaCertsCache() ) );
     980                 :            :   QCA::CertificateCollection untrustedCAs(
     981                 :          0 :     qtCertsToQcaCollection( QgsApplication::authManager()->untrustedCaCerts() ) );
     982                 :            : 
     983                 :            :   QCA::Validity v_any;
     984                 :          0 :   v_any = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageAny, QCA::ValidateAll );
     985                 :          0 :   if ( v_any == QCA::ValidityGood )
     986                 :            :   {
     987                 :          0 :     usages << QgsAuthCertUtils::AnyOrUnspecifiedUsage;
     988                 :          0 :   }
     989                 :            : 
     990                 :            :   QCA::Validity v_tlsserver;
     991                 :          0 :   v_tlsserver = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSServer, QCA::ValidateAll );
     992                 :          0 :   if ( v_tlsserver == QCA::ValidityGood )
     993                 :            :   {
     994                 :          0 :     if ( !usages.contains( QgsAuthCertUtils::TlsServerUsage ) )
     995                 :            :     {
     996                 :          0 :       usages << QgsAuthCertUtils::TlsServerUsage;
     997                 :          0 :     }
     998                 :          0 :   }
     999                 :            : 
    1000                 :            :   // TODO: why doesn't this tag client certs?
    1001                 :            :   //       always seems to return QCA::ErrorInvalidPurpose (enum #5)
    1002                 :            :   QCA::Validity v_tlsclient;
    1003                 :          0 :   v_tlsclient = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSClient, QCA::ValidateAll );
    1004                 :            :   //QgsDebugMsg( QStringLiteral( "QCA::UsageTLSClient validity: %1" ).arg( static_cast<int>(v_tlsclient) ) );
    1005                 :          0 :   if ( v_tlsclient == QCA::ValidityGood )
    1006                 :            :   {
    1007                 :          0 :     usages << QgsAuthCertUtils::TlsClientUsage;
    1008                 :          0 :   }
    1009                 :            : 
    1010                 :            :   // TODO: add TlsServerEvUsage, CodeSigningUsage, EmailProtectionUsage, TimeStampingUsage, CRLSigningUsage
    1011                 :            :   //       as they become necessary, since we do not want the overhead of checking just yet.
    1012                 :            : 
    1013                 :          0 :   return usages;
    1014                 :          0 : }
    1015                 :            : 
    1016                 :          0 : bool QgsAuthCertUtils::certificateIsAuthority( const QSslCertificate &cert )
    1017                 :            : {
    1018                 :          0 :   return QgsAuthCertUtils::certificateUsageTypes( cert ).contains( QgsAuthCertUtils::CertAuthorityUsage );
    1019                 :          0 : }
    1020                 :            : 
    1021                 :          0 : bool QgsAuthCertUtils::certificateIsIssuer( const QSslCertificate &cert )
    1022                 :            : {
    1023                 :          0 :   return QgsAuthCertUtils::certificateUsageTypes( cert ).contains( QgsAuthCertUtils::CertIssuerUsage );
    1024                 :          0 : }
    1025                 :            : 
    1026                 :          0 : bool QgsAuthCertUtils::certificateIsAuthorityOrIssuer( const QSslCertificate &cert )
    1027                 :            : {
    1028                 :          0 :   return ( QgsAuthCertUtils::certificateIsAuthority( cert )
    1029                 :          0 :            || QgsAuthCertUtils::certificateIsIssuer( cert ) );
    1030                 :            : }
    1031                 :            : 
    1032                 :          0 : bool QgsAuthCertUtils::certificateIsSslServer( const QSslCertificate &cert )
    1033                 :            : {
    1034                 :          0 :   return ( QgsAuthCertUtils::certificateUsageTypes( cert ).contains( QgsAuthCertUtils::TlsServerUsage )
    1035                 :          0 :            || QgsAuthCertUtils::certificateUsageTypes( cert ).contains( QgsAuthCertUtils::TlsServerEvUsage ) );
    1036                 :          0 : }
    1037                 :            : 
    1038                 :            : #if 0
    1039                 :            : bool QgsAuthCertUtils::certificateIsSslServer( const QSslCertificate &cert )
    1040                 :            : {
    1041                 :            :   // TODO: There is no difinitive method for strictly enforcing what determines an SSL server cert;
    1042                 :            :   //       only what it should not be able to do (cert sign, etc.). The logic here may need refined
    1043                 :            :   // see: http://security.stackexchange.com/a/26650
    1044                 :            : 
    1045                 :            :   if ( QgsApplication::authManager()->isDisabled() )
    1046                 :            :     return false;
    1047                 :            : 
    1048                 :            :   QCA::ConvertResult res;
    1049                 :            :   QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QString( "qca-ossl" ) ) );
    1050                 :            :   if ( res != QCA::ConvertGood || qcacert.isNull() )
    1051                 :            :   {
    1052                 :            :     QgsDebugMsg( QStringLiteral( "Certificate could not be converted to QCA cert" ) );
    1053                 :            :     return false;
    1054                 :            :   }
    1055                 :            : 
    1056                 :            :   if ( qcacert.isCA() )
    1057                 :            :   {
    1058                 :            :     QgsDebugMsg( QStringLiteral( "SSL server certificate has 'CA:TRUE' basic constraint (and should not)" ) );
    1059                 :            :     return false;
    1060                 :            :   }
    1061                 :            : 
    1062                 :            :   const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
    1063                 :            :   for ( const auto & certconst, certconsts )
    1064                 :            :   {
    1065                 :            :     if ( certconst.known() == QCA::KeyCertificateSign )
    1066                 :            :     {
    1067                 :            :       QgsDebugMsg( QStringLiteral( "SSL server certificate has 'Certificate Sign' key usage (and should not)" ) );
    1068                 :            :       return false;
    1069                 :            :     }
    1070                 :            :   }
    1071                 :            : 
    1072                 :            :   // check for common key usage and extended key usage constraints
    1073                 :            :   // see: https://www.ietf.org/rfc/rfc3280.txt  4.2.1.3(Key Usage) and  4.2.1.13(Extended Key Usage)
    1074                 :            :   bool serverauth = false;
    1075                 :            :   bool dsignature = false;
    1076                 :            :   bool keyencrypt = false;
    1077                 :            :   for ( const auto &certconst : certconsts )
    1078                 :            :   {
    1079                 :            :     if ( certconst.known() == QCA::DigitalSignature )
    1080                 :            :     {
    1081                 :            :       QgsDebugMsg( QStringLiteral( "SSL server certificate has 'digital signature' key usage" ) );
    1082                 :            :       dsignature = true;
    1083                 :            :     }
    1084                 :            :     else if ( certconst.known() == QCA::KeyEncipherment )
    1085                 :            :     {
    1086                 :            :       QgsDebugMsg( QStringLiteral( "SSL server certificate has 'key encipherment' key usage" ) );
    1087                 :            :       keyencrypt = true;
    1088                 :            :     }
    1089                 :            :     else if ( certconst.known() == QCA::KeyAgreement )
    1090                 :            :     {
    1091                 :            :       QgsDebugMsg( QStringLiteral( "SSL server certificate has 'key agreement' key usage" ) );
    1092                 :            :       keyencrypt = true;
    1093                 :            :     }
    1094                 :            :     else if ( certconst.known() == QCA::ServerAuth )
    1095                 :            :     {
    1096                 :            :       QgsDebugMsg( QStringLiteral( "SSL server certificate has 'server authentication' extended key usage" ) );
    1097                 :            :       serverauth = true;
    1098                 :            :     }
    1099                 :            :   }
    1100                 :            :   // From 4.2.1.13(Extended Key Usage):
    1101                 :            :   //   "If a certificate contains both a key usage extension and an extended
    1102                 :            :   //   key usage extension, then both extensions MUST be processed
    1103                 :            :   //   independently and the certificate MUST only be used for a purpose
    1104                 :            :   //   consistent with both extensions.  If there is no purpose consistent
    1105                 :            :   //   with both extensions, then the certificate MUST NOT be used for any
    1106                 :            :   //   purpose."
    1107                 :            : 
    1108                 :            :   if ( serverauth && dsignature && keyencrypt )
    1109                 :            :   {
    1110                 :            :     return true;
    1111                 :            :   }
    1112                 :            :   if ( dsignature && keyencrypt )
    1113                 :            :   {
    1114                 :            :     return true;
    1115                 :            :   }
    1116                 :            : 
    1117                 :            :   // lastly, check for DH key and key agreement
    1118                 :            :   bool keyagree = false;
    1119                 :            :   bool encipheronly = false;
    1120                 :            :   bool decipheronly = false;
    1121                 :            : 
    1122                 :            :   QCA::PublicKey pubkey( qcacert.subjectPublicKey() );
    1123                 :            :   // key size may be 0 for eliptical curve-based keys, in which case isDH() crashes QCA
    1124                 :            :   if ( pubkey.bitSize() > 0 && pubkey.isDH() )
    1125                 :            :   {
    1126                 :            :     keyagree = pubkey.canKeyAgree();
    1127                 :            :     if ( !keyagree )
    1128                 :            :     {
    1129                 :            :       return false;
    1130                 :            :     }
    1131                 :            :     for ( const auto &certconst : certconsts )
    1132                 :            :     {
    1133                 :            :       if ( certconst.known() == QCA::EncipherOnly )
    1134                 :            :       {
    1135                 :            :         QgsDebugMsg( QStringLiteral( "SSL server public key has 'encipher only' key usage" ) );
    1136                 :            :         encipheronly = true;
    1137                 :            :       }
    1138                 :            :       else if ( certconst.known() == QCA::DecipherOnly )
    1139                 :            :       {
    1140                 :            :         QgsDebugMsg( QStringLiteral( "SSL server public key has 'decipher only' key usage" ) );
    1141                 :            :         decipheronly = true;
    1142                 :            :       }
    1143                 :            :     }
    1144                 :            :     if ( !encipheronly && !decipheronly )
    1145                 :            :     {
    1146                 :            :       return true;
    1147                 :            :     }
    1148                 :            :   }
    1149                 :            :   return false;
    1150                 :            : }
    1151                 :            : #endif
    1152                 :            : 
    1153                 :          0 : bool QgsAuthCertUtils::certificateIsSslClient( const QSslCertificate &cert )
    1154                 :            : {
    1155                 :          0 :   return QgsAuthCertUtils::certificateUsageTypes( cert ).contains( QgsAuthCertUtils::TlsClientUsage );
    1156                 :          0 : }
    1157                 :            : 
    1158                 :          0 : QString QgsAuthCertUtils::sslErrorEnumString( QSslError::SslError errenum )
    1159                 :            : {
    1160                 :          0 :   switch ( errenum )
    1161                 :            :   {
    1162                 :            :     case QSslError::UnableToGetIssuerCertificate:
    1163                 :          0 :       return QObject::tr( "Unable to Get Issuer Certificate" );
    1164                 :            :     case QSslError::UnableToDecryptCertificateSignature:
    1165                 :          0 :       return QObject::tr( "Unable to Decrypt Certificate Signature" );
    1166                 :            :     case QSslError::UnableToDecodeIssuerPublicKey:
    1167                 :          0 :       return QObject::tr( "Unable to Decode Issuer Public Key" );
    1168                 :            :     case QSslError::CertificateSignatureFailed:
    1169                 :          0 :       return QObject::tr( "Certificate Signature Failed" );
    1170                 :            :     case QSslError::CertificateNotYetValid:
    1171                 :          0 :       return QObject::tr( "Certificate Not Yet Valid" );
    1172                 :            :     case QSslError::CertificateExpired:
    1173                 :          0 :       return QObject::tr( "Certificate Expired" );
    1174                 :            :     case QSslError::InvalidNotBeforeField:
    1175                 :          0 :       return QObject::tr( "Invalid Not Before Field" );
    1176                 :            :     case QSslError::InvalidNotAfterField:
    1177                 :          0 :       return QObject::tr( "Invalid Not After Field" );
    1178                 :            :     case QSslError::SelfSignedCertificate:
    1179                 :          0 :       return QObject::tr( "Self-signed Certificate" );
    1180                 :            :     case QSslError::SelfSignedCertificateInChain:
    1181                 :          0 :       return QObject::tr( "Self-signed Certificate In Chain" );
    1182                 :            :     case QSslError::UnableToGetLocalIssuerCertificate:
    1183                 :          0 :       return QObject::tr( "Unable to Get Local Issuer Certificate" );
    1184                 :            :     case QSslError::UnableToVerifyFirstCertificate:
    1185                 :          0 :       return QObject::tr( "Unable to Verify First Certificate" );
    1186                 :            :     case QSslError::CertificateRevoked:
    1187                 :          0 :       return QObject::tr( "Certificate Revoked" );
    1188                 :            :     case QSslError::InvalidCaCertificate:
    1189                 :          0 :       return QObject::tr( "Invalid CA Certificate" );
    1190                 :            :     case QSslError::PathLengthExceeded:
    1191                 :          0 :       return QObject::tr( "Path Length Exceeded" );
    1192                 :            :     case QSslError::InvalidPurpose:
    1193                 :          0 :       return QObject::tr( "Invalid Purpose" );
    1194                 :            :     case QSslError::CertificateUntrusted:
    1195                 :          0 :       return QObject::tr( "Certificate Untrusted" );
    1196                 :            :     case QSslError::CertificateRejected:
    1197                 :          0 :       return QObject::tr( "Certificate Rejected" );
    1198                 :            :     case QSslError::SubjectIssuerMismatch:
    1199                 :          0 :       return QObject::tr( "Subject Issuer Mismatch" );
    1200                 :            :     case QSslError::AuthorityIssuerSerialNumberMismatch:
    1201                 :          0 :       return QObject::tr( "Authority Issuer Serial Number Mismatch" );
    1202                 :            :     case QSslError::NoPeerCertificate:
    1203                 :          0 :       return QObject::tr( "No Peer Certificate" );
    1204                 :            :     case QSslError::HostNameMismatch:
    1205                 :          0 :       return QObject::tr( "Host Name Mismatch" );
    1206                 :            :     case QSslError::UnspecifiedError:
    1207                 :          0 :       return QObject::tr( "Unspecified Error" );
    1208                 :            :     case QSslError::CertificateBlacklisted:
    1209                 :          0 :       return QObject::tr( "Certificate Blacklisted" );
    1210                 :            :     case QSslError::NoError:
    1211                 :          0 :       return QObject::tr( "No Error" );
    1212                 :            :     case QSslError::NoSslSupport:
    1213                 :          0 :       return QObject::tr( "No SSL Support" );
    1214                 :            :     default:
    1215                 :          0 :       return QString();
    1216                 :            :   }
    1217                 :          0 : }
    1218                 :            : 
    1219                 :          0 : QList<QPair<QSslError::SslError, QString> > QgsAuthCertUtils::sslErrorEnumStrings()
    1220                 :            : {
    1221                 :          0 :   QList<QPair<QSslError::SslError, QString> > errenums;
    1222                 :          0 :   errenums << qMakePair( QSslError::UnableToGetIssuerCertificate,
    1223                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetIssuerCertificate ) );
    1224                 :          0 :   errenums << qMakePair( QSslError::UnableToDecryptCertificateSignature,
    1225                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecryptCertificateSignature ) );
    1226                 :          0 :   errenums << qMakePair( QSslError::UnableToDecodeIssuerPublicKey,
    1227                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecodeIssuerPublicKey ) );
    1228                 :          0 :   errenums << qMakePair( QSslError::CertificateSignatureFailed,
    1229                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateSignatureFailed ) );
    1230                 :          0 :   errenums << qMakePair( QSslError::CertificateNotYetValid,
    1231                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateNotYetValid ) );
    1232                 :          0 :   errenums << qMakePair( QSslError::CertificateExpired,
    1233                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateExpired ) );
    1234                 :          0 :   errenums << qMakePair( QSslError::InvalidNotBeforeField,
    1235                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotBeforeField ) );
    1236                 :          0 :   errenums << qMakePair( QSslError::InvalidNotAfterField,
    1237                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotAfterField ) );
    1238                 :          0 :   errenums << qMakePair( QSslError::SelfSignedCertificate,
    1239                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificate ) );
    1240                 :          0 :   errenums << qMakePair( QSslError::SelfSignedCertificateInChain,
    1241                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificateInChain ) );
    1242                 :          0 :   errenums << qMakePair( QSslError::UnableToGetLocalIssuerCertificate,
    1243                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetLocalIssuerCertificate ) );
    1244                 :          0 :   errenums << qMakePair( QSslError::UnableToVerifyFirstCertificate,
    1245                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToVerifyFirstCertificate ) );
    1246                 :          0 :   errenums << qMakePair( QSslError::CertificateRevoked,
    1247                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRevoked ) );
    1248                 :          0 :   errenums << qMakePair( QSslError::InvalidCaCertificate,
    1249                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidCaCertificate ) );
    1250                 :          0 :   errenums << qMakePair( QSslError::PathLengthExceeded,
    1251                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::PathLengthExceeded ) );
    1252                 :          0 :   errenums << qMakePair( QSslError::InvalidPurpose,
    1253                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidPurpose ) );
    1254                 :          0 :   errenums << qMakePair( QSslError::CertificateUntrusted,
    1255                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateUntrusted ) );
    1256                 :          0 :   errenums << qMakePair( QSslError::CertificateRejected,
    1257                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRejected ) );
    1258                 :          0 :   errenums << qMakePair( QSslError::SubjectIssuerMismatch,
    1259                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::SubjectIssuerMismatch ) );
    1260                 :          0 :   errenums << qMakePair( QSslError::AuthorityIssuerSerialNumberMismatch,
    1261                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::AuthorityIssuerSerialNumberMismatch ) );
    1262                 :          0 :   errenums << qMakePair( QSslError::NoPeerCertificate,
    1263                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::NoPeerCertificate ) );
    1264                 :          0 :   errenums << qMakePair( QSslError::HostNameMismatch,
    1265                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::HostNameMismatch ) );
    1266                 :          0 :   errenums << qMakePair( QSslError::UnspecifiedError,
    1267                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::UnspecifiedError ) );
    1268                 :          0 :   errenums << qMakePair( QSslError::CertificateBlacklisted,
    1269                 :          0 :                          QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateBlacklisted ) );
    1270                 :          0 :   return errenums;
    1271                 :          0 : }
    1272                 :            : 
    1273                 :          0 : bool QgsAuthCertUtils::certIsCurrent( const QSslCertificate &cert )
    1274                 :            : {
    1275                 :          0 :   if ( cert.isNull() )
    1276                 :          0 :     return false;
    1277                 :          0 :   const QDateTime currentTime = QDateTime::currentDateTime();
    1278                 :          0 :   return cert.effectiveDate() <= currentTime && cert.expiryDate() >= currentTime;
    1279                 :          0 : }
    1280                 :            : 
    1281                 :          0 : QList<QSslError> QgsAuthCertUtils::certViabilityErrors( const QSslCertificate &cert )
    1282                 :            : {
    1283                 :          0 :   QList<QSslError> sslErrors;
    1284                 :            : 
    1285                 :          0 :   if ( cert.isNull() )
    1286                 :          0 :     return sslErrors;
    1287                 :            : 
    1288                 :          0 :   const QDateTime currentTime = QDateTime::currentDateTime();
    1289                 :          0 :   if ( cert.expiryDate() <= currentTime )
    1290                 :            :   {
    1291                 :          0 :     sslErrors << QSslError( QSslError::SslError::CertificateExpired, cert );
    1292                 :          0 :   }
    1293                 :          0 :   if ( cert.effectiveDate() >= QDateTime::currentDateTime() )
    1294                 :            :   {
    1295                 :          0 :     sslErrors << QSslError( QSslError::SslError::CertificateNotYetValid, cert );
    1296                 :          0 :   }
    1297                 :          0 :   if ( cert.isBlacklisted() )
    1298                 :            :   {
    1299                 :          0 :     sslErrors << QSslError( QSslError::SslError::CertificateBlacklisted, cert );
    1300                 :          0 :   }
    1301                 :            : 
    1302                 :          0 :   return sslErrors;
    1303                 :          0 : }
    1304                 :            : 
    1305                 :          0 : bool QgsAuthCertUtils::certIsViable( const QSslCertificate &cert )
    1306                 :            : {
    1307                 :          0 :   return !cert.isNull() && QgsAuthCertUtils::certViabilityErrors( cert ).isEmpty();
    1308                 :            : }
    1309                 :            : 
    1310                 :          0 : QList<QSslError> QgsAuthCertUtils::validateCertChain( const QList<QSslCertificate> &certificateChain,
    1311                 :            :     const QString &hostName,
    1312                 :            :     bool trustRootCa )
    1313                 :            : {
    1314                 :          0 :   QList<QSslError> sslErrors;
    1315                 :          0 :   QList<QSslCertificate> trustedChain;
    1316                 :            :   // Filter out all CAs that are not trusted from QgsAuthManager
    1317                 :          0 :   for ( const auto &cert : certificateChain )
    1318                 :            :   {
    1319                 :          0 :     bool untrusted = false;
    1320                 :          0 :     for ( const auto &untrustedCert : QgsApplication::authManager()->untrustedCaCerts() )
    1321                 :            :     {
    1322                 :          0 :       if ( cert.digest( ) == untrustedCert.digest( ) )
    1323                 :            :       {
    1324                 :          0 :         untrusted = true;
    1325                 :          0 :         break;
    1326                 :            :       }
    1327                 :            :     }
    1328                 :          0 :     if ( ! untrusted )
    1329                 :            :     {
    1330                 :          0 :       trustedChain << cert;
    1331                 :          0 :     }
    1332                 :            :   }
    1333                 :            : 
    1334                 :            :   // Check that no certs in the chain are expired or not yet valid or blocklisted
    1335                 :          0 :   const QList<QSslCertificate> constTrustedChain( trustedChain );
    1336                 :          0 :   for ( const auto &cert : constTrustedChain )
    1337                 :            :   {
    1338                 :          0 :     sslErrors << QgsAuthCertUtils::certViabilityErrors( cert );
    1339                 :            :   }
    1340                 :            : 
    1341                 :            :   // Merge in the root CA if present and asked for
    1342                 :          0 :   if ( trustRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
    1343                 :            :   {
    1344                 :          0 :     static QMutex sMutex;
    1345                 :          0 :     QMutexLocker lock( &sMutex );
    1346                 :          0 :     QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
    1347                 :          0 :     QSslConfiguration sslConfig( oldSslConfig );
    1348                 :          0 :     sslConfig.setCaCertificates( casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
    1349                 :          0 :     QSslConfiguration::setDefaultConfiguration( sslConfig );
    1350                 :          0 :     sslErrors = QSslCertificate::verify( trustedChain, hostName );
    1351                 :          0 :     QSslConfiguration::setDefaultConfiguration( oldSslConfig );
    1352                 :          0 :   }
    1353                 :            :   else
    1354                 :            :   {
    1355                 :          0 :     sslErrors = QSslCertificate::verify( trustedChain, hostName );
    1356                 :            :   }
    1357                 :          0 :   return sslErrors;
    1358                 :          0 : }
    1359                 :            : 
    1360                 :          0 : QStringList QgsAuthCertUtils::validatePKIBundle( QgsPkiBundle &bundle, bool useIntermediates, bool trustRootCa )
    1361                 :            : {
    1362                 :          0 :   QStringList errors;
    1363                 :          0 :   if ( bundle.clientCert().isNull() )
    1364                 :          0 :     errors << QObject::tr( "Client certificate is NULL." );
    1365                 :            : 
    1366                 :          0 :   if ( bundle.clientKey().isNull() )
    1367                 :          0 :     errors << QObject::tr( "Client certificate key is NULL." );
    1368                 :            : 
    1369                 :            :   // immediately bail out if cert or key is NULL
    1370                 :          0 :   if ( !errors.isEmpty() )
    1371                 :          0 :     return errors;
    1372                 :            : 
    1373                 :          0 :   QList<QSslError> sslErrors;
    1374                 :          0 :   if ( useIntermediates )
    1375                 :            :   {
    1376                 :          0 :     QList<QSslCertificate> certsList( bundle.caChain() );
    1377                 :          0 :     certsList.insert( 0, bundle.clientCert( ) );
    1378                 :          0 :     sslErrors = QgsAuthCertUtils::validateCertChain( certsList, QString(), trustRootCa );
    1379                 :          0 :   }
    1380                 :            :   else
    1381                 :            :   {
    1382                 :          0 :     sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.clientCert() );
    1383                 :            :   }
    1384                 :          0 :   const QList<QSslError> constSslErrors( sslErrors );
    1385                 :          0 :   for ( const auto &sslError : constSslErrors )
    1386                 :            :   {
    1387                 :          0 :     if ( sslError.error() != QSslError::NoError )
    1388                 :            :     {
    1389                 :          0 :       errors << sslError.errorString();
    1390                 :          0 :     }
    1391                 :            :   }
    1392                 :            :   // Now check the key with QCA!
    1393                 :          0 :   QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.clientKey().toPem() ) );
    1394                 :          0 :   QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.clientCert().publicKey().toPem( ) ) );
    1395                 :          0 :   bool keyValid( ! pvtKey.isNull() );
    1396                 :          0 :   if ( keyValid && !( pubKey.toRSA().isNull( ) || pvtKey.toRSA().isNull( ) ) )
    1397                 :            :   {
    1398                 :          0 :     keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
    1399                 :          0 :   }
    1400                 :          0 :   else if ( keyValid && !( pubKey.toDSA().isNull( ) || pvtKey.toDSA().isNull( ) ) )
    1401                 :            :   {
    1402                 :          0 :     keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
    1403                 :          0 :   }
    1404                 :            :   else
    1405                 :            :   {
    1406                 :          0 :     QgsDebugMsg( QStringLiteral( "Key is not DSA, RSA: validation is not supported by QCA" ) );
    1407                 :            :   }
    1408                 :          0 :   if ( ! keyValid )
    1409                 :            :   {
    1410                 :          0 :     errors << QObject::tr( "Private key does not match client certificate public key." );
    1411                 :          0 :   }
    1412                 :          0 :   return errors;
    1413                 :          0 : }

Generated by: LCOV version 1.14