23 #include <QSslCertificate>
40 case QSsl::SecureProtocols:
41 return QObject::tr(
"SecureProtocols" );
42 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
43 case QSsl::TlsV1SslV3:
44 return QObject::tr(
"TlsV1SslV3" );
47 return QObject::tr(
"TlsV1" );
48 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
51 return QObject::tr(
"SslV3" );
53 return QObject::tr(
"SslV2" );
62 QMap<QString, QSslCertificate> digestmap;
63 for (
const auto &cert : certs )
72 QMap< QString, QList<QSslCertificate> > orgcerts;
73 for (
const auto &cert : certs )
77 org = QStringLiteral(
"(Organization not defined)" );
78 QList<QSslCertificate> valist = orgcerts.contains( org ) ? orgcerts.value( org ) : QList<QSslCertificate>();
79 orgcerts.insert( org, valist << cert );
86 QMap<QString, QgsAuthConfigSslServer> digestmap;
87 for (
const auto &config : configs )
89 digestmap.insert(
shaHexForCert( config.sslCertificate() ), config );
96 QMap< QString, QList<QgsAuthConfigSslServer> > orgconfigs;
97 for (
const auto &config : configs )
99 QString org(
SSL_SUBJECT_INFO( config.sslCertificate(), QSslCertificate::Organization ) );
102 org = QObject::tr(
"(Organization not defined)" );
103 QList<QgsAuthConfigSslServer> valist = orgconfigs.contains( org ) ? orgconfigs.value( org ) : QList<QgsAuthConfigSslServer>();
104 orgconfigs.insert( org, valist << config );
113 if ( !file.exists() )
115 QgsDebugMsg( QStringLiteral(
"Read file error, file not found: %1" ).arg( path ) );
119 QFile::OpenMode openflags( QIODevice::ReadOnly );
120 bool ret = file.open( openflags );
123 data = file.readAll();
132 QList<QSslCertificate> certs;
134 certs = QSslCertificate::fromData( payload, sniffEncoding( payload ) );
135 if ( certs.isEmpty() )
137 QgsDebugMsg( QStringLiteral(
"Parsed cert(s) EMPTY for path: %1" ).arg( certspath ) );
144 QList<QSslCertificate> cas;
145 const QList<QSslCertificate> certs(
certsFromFile( certspath ) );
146 for (
const auto &cert : certs )
159 QList<QSslCertificate> result( bundle1 );
160 const QList<QSslCertificate> c_bundle1( bundle1 );
161 for (
const auto &cert : c_bundle1 )
165 const QList<QSslCertificate> c_bundle2( bundle2 );
166 for (
const auto &cert : c_bundle2 )
170 result.append( cert );
180 QSslCertificate cert;
182 if ( !certs.isEmpty() )
184 cert = certs.first();
188 QgsDebugMsg( QStringLiteral(
"Parsed cert is NULL for path: %1" ).arg( certpath ) );
194 const QString &keypass,
201 QSsl::EncodingFormat keyEncoding( sniffEncoding( keydata ) );
203 const std::vector<QSsl::KeyAlgorithm> algs
205 QSsl::KeyAlgorithm::Rsa,
206 QSsl::KeyAlgorithm::Dsa,
207 QSsl::KeyAlgorithm::Ec,
208 QSsl::KeyAlgorithm::Opaque
211 for (
const auto &alg : algs )
213 clientkey = QSslKey( keydata,
217 !keypass.isEmpty() ? keypass.toUtf8() : QByteArray() );
218 if ( ! clientkey.isNull() )
224 case QSsl::KeyAlgorithm::Rsa:
225 *algtype = QStringLiteral(
"rsa" );
227 case QSsl::KeyAlgorithm::Dsa:
228 *algtype = QStringLiteral(
"dsa" );
230 case QSsl::KeyAlgorithm::Ec:
231 *algtype = QStringLiteral(
"ec" );
233 case QSsl::KeyAlgorithm::Opaque:
234 *algtype = QStringLiteral(
"opaque" );
236 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
237 case QSsl::KeyAlgorithm::Dh:
238 *algtype = QStringLiteral(
"dh" );
251 QList<QSslCertificate> certs;
252 certs = QSslCertificate::fromData( pemtext.toLatin1(), QSsl::Pem );
253 if ( certs.isEmpty() )
255 QgsDebugMsg( QStringLiteral(
"Parsed cert(s) EMPTY" ) );
262 QList<QSslCertificate> certs;
263 for (
const auto &cert : caList )
265 if ( ! cert.isSelfSigned( ) )
267 certs.append( cert );
274 const QString &keypath,
275 const QString &keypass,
280 if ( !clientcert.isNull() )
282 certpem = QString( clientcert.toPem() );
290 if ( !clientkey.isNull() )
292 keypem = QString( clientkey.toPem( ( reencrypt && !keypass.isEmpty() ) ? keypass.toUtf8() : QByteArray() ) );
295 return QStringList() << certpem << keypem << algtype;
300 QString pkcs8Header = QStringLiteral(
"-----BEGIN PRIVATE KEY-----" );
301 QString pkcs8Footer = QStringLiteral(
"-----END PRIVATE KEY-----" );
302 return keyPemTxt.contains( pkcs8Header ) && keyPemTxt.contains( pkcs8Footer );
306 QByteArray QgsAuthCertUtils::pkcs8PrivateKey( QByteArray &pkcs8Der )
310 if ( pkcs8Der.isEmpty() )
312 QgsDebugMsg( QStringLiteral(
"ERROR, passed DER is empty" ) );
319 if ( ! asnDefsRsrc.exists() )
321 QgsDebugMsg( QStringLiteral(
"ERROR, pkcs.asn resource file not found: %1" ).arg( asnDefsRsrc.filePath() ) );
324 const char *asnDefsFile = asnDefsRsrc.absoluteFilePath().toLocal8Bit().constData();
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;
334 QString
typeName( QStringLiteral(
"PKCS-8.PrivateKeyInfo" ) );
336 asn1_result = asn1_parser2tree( asnDefsFile, &definitions, errorDescription );
338 switch ( asn1_result )
343 case ASN1_FILE_NOT_FOUND:
344 QgsDebugMsg( QStringLiteral(
"ERROR, file not found: %1" ).arg( asnDefsFile ) );
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 ) );
352 QgsDebugMsg( QStringLiteral(
"ERROR, libtasn1: %1" ).arg( asn1_strerror( asn1_result ) ) );
357 asn1_result = asn1_create_element( definitions,
typeName.toLatin1().constData(), &structure );
361 if ( asn1_result != ASN1_SUCCESS )
363 QgsDebugMsg( QStringLiteral(
"ERROR, structure creation: %1" ).arg( asn1_strerror( asn1_result ) ) );
368 der =
reinterpret_cast<unsigned char *
>( pkcs8Der.data() );
369 der_len = pkcs8Der.size();
373 asn1_result = asn1_der_decoding2( &structure, der, &der_len, flags, errorDescription );
377 asn1_result = asn1_der_decoding( &structure, der, der_len, errorDescription );
380 if ( asn1_result != ASN1_SUCCESS )
382 QgsDebugMsg( QStringLiteral(
"ERROR, decoding: %1" ).arg( errorDescription ) );
387 QgsDebugMsgLevel( QStringLiteral(
"Decoding: %1" ).arg( asn1_strerror( asn1_result ) ), 4 );
392 QgsDebugMsg( QStringLiteral(
"DECODING RESULT:" ) );
393 asn1_print_structure( stdout, structure,
"", ASN1_PRINT_NAME_TYPE_VALUE );
398 typeName.append( QStringLiteral(
".privateKey" ) );
401 asn1_result = asn1_read_value_type( structure,
"privateKey", NULL, &oct_len, &oct_etype );
403 if ( asn1_result != ASN1_MEM_ERROR )
405 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 read privateKey value type: %1" ).arg( asn1_strerror( asn1_result ) ) );
409 if ( oct_etype != ASN1_ETYPE_OCTET_STRING )
411 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey value not octet string, but type: %1" ).arg(
static_cast<int>( oct_etype ) ) );
417 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey octet string empty" ) );
422 asn1_result = asn1_read_value( structure,
"privateKey", oct_data, &oct_len );
424 if ( asn1_result != ASN1_SUCCESS )
426 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 read privateKey value: %1" ).arg( asn1_strerror( asn1_result ) ) );
432 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey value octet string empty" ) );
436 pkcs1 = QByteArray( oct_data, oct_len );
443 asn1_delete_structure( &structure );
449 const QString &bundlepass,
453 if ( !QCA::isSupported(
"pkcs12" ) )
455 QgsDebugMsg( QStringLiteral(
"QCA does not support PKCS#12" ) );
460 if ( bundle.isNull() )
462 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 file to QCA key bundle: %1" ).arg( bundlepath ) );
466 QCA::SecureArray passarray;
467 if ( reencrypt && !bundlepass.isEmpty() )
469 passarray = QCA::SecureArray( bundlepass.toUtf8() );
473 QSsl::KeyAlgorithm keyalg = QSsl::Opaque;
474 if ( bundle.privateKey().isRSA() )
476 algtype = QStringLiteral(
"rsa" );
479 else if ( bundle.privateKey().isDSA() )
481 algtype = QStringLiteral(
"dsa" );
484 else if ( bundle.privateKey().isDH() )
486 algtype = QStringLiteral(
"dh" );
491 if ( keyalg == QSsl::Opaque )
493 QgsDebugMsg( QStringLiteral(
"FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1" ).arg( bundlepath ) );
501 QgsDebugMsgLevel( QStringLiteral(
"Private key is PKCS#8: attempting conversion to PKCS#1..." ), 4 );
506 QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
507 if ( pkcs8Der.isEmpty() )
509 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key to DER-encoded format: %1" ).arg( bundlepath ) );
513 QByteArray pkcs1Der = QgsAuthCertUtils::pkcs8PrivateKey( pkcs8Der );
514 if ( pkcs1Der.isEmpty() )
516 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1: %1" ).arg( bundlepath ) );
520 QSslKey pkcs1Key( pkcs1Der, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey );
521 if ( pkcs1Key.isNull() )
523 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1 QSslKey: %1" ).arg( bundlepath ) );
526 keyPem = QString( pkcs1Key.toPem( passarray.toByteArray() ) );
530 keyPem = bundle.privateKey().toPEM( passarray );
533 keyPem = bundle.privateKey().toPEM( passarray );
536 QgsDebugMsgLevel( QStringLiteral(
"PKCS#12 cert as PEM:\n%1" ).arg( QString( bundle.certificateChain().primary().toPEM() ) ), 4 );
540 return QStringList() << bundle.certificateChain().primary().toPEM() << keyPem << algtype;
545 QList<QSslCertificate> result;
546 if ( !QCA::isSupported(
"pkcs12" ) )
550 if ( bundle.isNull() )
553 const QCA::CertificateChain chain( bundle.certificateChain() );
554 for (
const auto &cert : chain )
558 result.append( QSslCertificate::fromData( cert.toPEM().toLatin1() ) );
567 if ( !certs.isEmpty() )
569 QStringList certslist;
570 for (
const auto &cert : certs )
572 certslist << cert.toPem();
574 capem = certslist.join( QLatin1Char(
'\n' ) ).toLatin1();
581 QFile pemFile( QDir::tempPath() + QDir::separator() + name );
582 QString pemFilePath( pemFile.fileName() );
584 if ( pemFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
586 qint64 bytesWritten = pemFile.write( pemtext );
587 if ( bytesWritten == -1 )
589 QgsDebugMsg( QStringLiteral(
"FAILED to write to temp PEM file: %1" ).arg( pemFilePath ) );
595 QgsDebugMsg( QStringLiteral(
"FAILED to open writing for temp PEM file: %1" ).arg( pemFilePath ) );
599 if ( !pemFile.setPermissions( QFile::ReadUser ) )
601 QgsDebugMsg( QStringLiteral(
"FAILED to set permissions on temp PEM file: %1" ).arg( pemFilePath ) );
613 return single ? QObject::tr(
"System Root CA" ) : QObject::tr(
"System Root Authorities" );
615 return single ? QObject::tr(
"File CA" ) : QObject::tr(
"Authorities from File" );
617 return single ? QObject::tr(
"Database CA" ) : QObject::tr(
"Authorities in Database" );
619 return single ? QObject::tr(
"Connection CA" ) : QObject::tr(
"Authorities from connection" );
627 QString name( issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::CommonName )
630 if ( name.isEmpty() )
631 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::OrganizationalUnitName )
634 if ( name.isEmpty() )
638 if ( name.isEmpty() )
642 if ( name.isEmpty() )
643 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::StateOrProvinceName )
646 if ( name.isEmpty() )
654 void QgsAuthCertUtils::appendDirSegment_( QStringList &dirname,
655 const QString &segment, QString value )
657 if ( !value.isEmpty() )
659 dirname.append( segment +
'=' + value.replace(
',', QLatin1String(
"\\," ) ) );
663 QSsl::EncodingFormat QgsAuthCertUtils::sniffEncoding(
const QByteArray &payload )
665 return payload.contains( QByteArrayLiteral(
"-----BEGIN " ) ) ?
671 const QCA::Certificate &acert,
677 if ( acert.isNull() )
679 QCA::ConvertResult res;
680 QCA::Certificate acert( QCA::Certificate::fromPEM( qcert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
681 if ( res != QCA::ConvertGood || acert.isNull() )
683 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
695 QgsAuthCertUtils::appendDirSegment_(
696 dirname, QStringLiteral(
"E" ), issuer ? acert.issuerInfo().value( QCA::Email )
697 : acert.subjectInfo().value( QCA::Email ) );
698 QgsAuthCertUtils::appendDirSegment_(
699 dirname, QStringLiteral(
"CN" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CommonName )
701 QgsAuthCertUtils::appendDirSegment_(
702 dirname, QStringLiteral(
"OU" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::OrganizationalUnitName )
704 QgsAuthCertUtils::appendDirSegment_(
705 dirname, QStringLiteral(
"O" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::Organization )
707 QgsAuthCertUtils::appendDirSegment_(
708 dirname, QStringLiteral(
"L" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::LocalityName )
710 QgsAuthCertUtils::appendDirSegment_(
711 dirname, QStringLiteral(
"ST" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::StateOrProvinceName )
713 QgsAuthCertUtils::appendDirSegment_(
714 dirname, QStringLiteral(
"C" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CountryName )
717 return dirname.join( QLatin1Char(
',' ) );
725 return QObject::tr(
"Default" );
727 return QObject::tr(
"Trusted" );
729 return QObject::tr(
"Untrusted" );
740 sl.reserve( txt.size() );
741 for (
int i = 0; i < txt.size(); i += 2 )
743 sl << txt.mid( i, ( i + 2 > txt.size() ) ? -1 : 2 );
745 return sl.join( QLatin1Char(
':' ) );
750 QString sha( cert.digest( QCryptographicHash::Sha1 ).toHex() );
761 return QCA::Certificate();
763 QCA::ConvertResult res;
764 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
765 if ( res != QCA::ConvertGood || qcacert.isNull() )
767 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
768 qcacert = QCA::Certificate();
775 QCA::CertificateCollection qcacoll;
779 for (
const auto &cert : certs )
782 if ( !qcacert.isNull() )
784 qcacoll.addCertificate( qcacert );
792 QCA::SecureArray passarray;
793 if ( !pass.isEmpty() )
794 passarray = QCA::SecureArray( pass.toUtf8() );
796 QCA::ConvertResult res;
797 QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( path, passarray, &res, QStringLiteral(
"qca-ossl" ) ) );
799 return ( res == QCA::ConvertGood ? bundle : QCA::KeyBundle() );
806 case QCA::ValidityGood:
807 return QObject::tr(
"Certificate is valid." );
808 case QCA::ErrorRejected:
809 return QObject::tr(
"Root CA rejected the certificate purpose." );
810 case QCA::ErrorUntrusted:
811 return QObject::tr(
"Certificate is not trusted." );
812 case QCA::ErrorSignatureFailed:
813 return QObject::tr(
"Signature does not match." );
814 case QCA::ErrorInvalidCA:
815 return QObject::tr(
"Certificate Authority is invalid or not found." );
816 case QCA::ErrorInvalidPurpose:
817 return QObject::tr(
"Purpose does not match the intended usage." );
818 case QCA::ErrorSelfSigned:
819 return QObject::tr(
"Certificate is self-signed, and is not found in the list of trusted certificates." );
820 case QCA::ErrorRevoked:
821 return QObject::tr(
"Certificate has been revoked." );
822 case QCA::ErrorPathLengthExceeded:
823 return QObject::tr(
"Path length from the root CA to this certificate is too long." );
824 case QCA::ErrorExpired:
825 return QObject::tr(
"Certificate has expired or is not yet valid." );
826 case QCA::ErrorExpiredCA:
827 return QObject::tr(
"Certificate Authority has expired." );
828 case QCA::ErrorValidityUnknown:
829 return QObject::tr(
"Validity is unknown." );
839 case QCA::EMSA1_SHA1:
840 return QObject::tr(
"SHA1, with EMSA1" );
841 case QCA::EMSA3_SHA1:
842 return QObject::tr(
"SHA1, with EMSA3" );
844 return QObject::tr(
"MD5, with EMSA3" );
846 return QObject::tr(
"MD2, with EMSA3" );
847 case QCA::EMSA3_RIPEMD160:
848 return QObject::tr(
"RIPEMD160, with EMSA3" );
850 return QObject::tr(
"EMSA3, without digest" );
851 #if QCA_VERSION >= 0x020100
852 case QCA::EMSA3_SHA224:
853 return QObject::tr(
"SHA224, with EMSA3" );
854 case QCA::EMSA3_SHA256:
855 return QObject::tr(
"SHA256, with EMSA3" );
856 case QCA::EMSA3_SHA384:
857 return QObject::tr(
"SHA384, with EMSA3" );
858 case QCA::EMSA3_SHA512:
859 return QObject::tr(
"SHA512, with EMSA3" );
862 return QObject::tr(
"Unknown (possibly Elliptic Curve)" );
868 switch ( constraint )
870 case QCA::DigitalSignature:
871 return QObject::tr(
"Digital Signature" );
872 case QCA::NonRepudiation:
873 return QObject::tr(
"Non-repudiation" );
874 case QCA::KeyEncipherment:
875 return QObject::tr(
"Key Encipherment" );
876 case QCA::DataEncipherment:
877 return QObject::tr(
"Data Encipherment" );
878 case QCA::KeyAgreement:
879 return QObject::tr(
"Key Agreement" );
880 case QCA::KeyCertificateSign:
881 return QObject::tr(
"Key Certificate Sign" );
883 return QObject::tr(
"CRL Sign" );
884 case QCA::EncipherOnly:
885 return QObject::tr(
"Encipher Only" );
886 case QCA::DecipherOnly:
887 return QObject::tr(
"Decipher Only" );
888 case QCA::ServerAuth:
889 return QObject::tr(
"Server Authentication" );
890 case QCA::ClientAuth:
891 return QObject::tr(
"Client Authentication" );
892 case QCA::CodeSigning:
893 return QObject::tr(
"Code Signing" );
894 case QCA::EmailProtection:
895 return QObject::tr(
"Email Protection" );
896 case QCA::IPSecEndSystem:
897 return QObject::tr(
"IPSec Endpoint" );
898 case QCA::IPSecTunnel:
899 return QObject::tr(
"IPSec Tunnel" );
901 return QObject::tr(
"IPSec User" );
902 case QCA::TimeStamping:
903 return QObject::tr(
"Time Stamping" );
904 case QCA::OCSPSigning:
905 return QObject::tr(
"OCSP Signing" );
916 return QObject::tr(
"Any or unspecified" );
918 return QObject::tr(
"Certificate Authority" );
920 return QObject::tr(
"Certificate Issuer" );
922 return QObject::tr(
"TLS/SSL Server" );
924 return QObject::tr(
"TLS/SSL Server EV" );
926 return QObject::tr(
"TLS/SSL Client" );
928 return QObject::tr(
"Code Signing" );
930 return QObject::tr(
"Email Protection" );
932 return QObject::tr(
"Time Stamping" );
934 return QObject::tr(
"CRL Signing" );
937 return QObject::tr(
"Undetermined usage" );
943 QList<QgsAuthCertUtils::CertUsageType> usages;
948 QCA::ConvertResult res;
949 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
950 if ( res != QCA::ConvertGood || qcacert.isNull() )
952 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
956 if ( qcacert.isCA() )
958 QgsDebugMsg( QStringLiteral(
"Certificate has 'CA:TRUE' basic constraint" ) );
962 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
963 for (
const auto &certconst : certconsts )
965 if ( certconst.known() == QCA::KeyCertificateSign )
967 QgsDebugMsg( QStringLiteral(
"Certificate has 'Certificate Sign' key usage" ) );
970 else if ( certconst.known() == QCA::ServerAuth )
972 QgsDebugMsg( QStringLiteral(
"Certificate has 'server authentication' extended key usage" ) );
978 QCA::CertificateCollection trustedCAs(
980 QCA::CertificateCollection untrustedCAs(
984 v_any = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageAny, QCA::ValidateAll );
985 if ( v_any == QCA::ValidityGood )
990 QCA::Validity v_tlsserver;
991 v_tlsserver = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSServer, QCA::ValidateAll );
992 if ( v_tlsserver == QCA::ValidityGood )
1002 QCA::Validity v_tlsclient;
1003 v_tlsclient = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSClient, QCA::ValidateAll );
1005 if ( v_tlsclient == QCA::ValidityGood )
1048 QCA::ConvertResult res;
1049 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QString(
"qca-ossl" ) ) );
1050 if ( res != QCA::ConvertGood || qcacert.isNull() )
1052 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
1056 if ( qcacert.isCA() )
1058 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'CA:TRUE' basic constraint (and should not)" ) );
1062 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
1063 for (
const auto & certconst, certconsts )
1065 if ( certconst.known() == QCA::KeyCertificateSign )
1067 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'Certificate Sign' key usage (and should not)" ) );
1074 bool serverauth =
false;
1075 bool dsignature =
false;
1076 bool keyencrypt =
false;
1077 for (
const auto &certconst : certconsts )
1079 if ( certconst.known() == QCA::DigitalSignature )
1081 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'digital signature' key usage" ) );
1084 else if ( certconst.known() == QCA::KeyEncipherment )
1086 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'key encipherment' key usage" ) );
1089 else if ( certconst.known() == QCA::KeyAgreement )
1091 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'key agreement' key usage" ) );
1094 else if ( certconst.known() == QCA::ServerAuth )
1096 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'server authentication' extended key usage" ) );
1108 if ( serverauth && dsignature && keyencrypt )
1112 if ( dsignature && keyencrypt )
1118 bool keyagree =
false;
1119 bool encipheronly =
false;
1120 bool decipheronly =
false;
1122 QCA::PublicKey pubkey( qcacert.subjectPublicKey() );
1124 if ( pubkey.bitSize() > 0 && pubkey.isDH() )
1126 keyagree = pubkey.canKeyAgree();
1131 for (
const auto &certconst : certconsts )
1133 if ( certconst.known() == QCA::EncipherOnly )
1135 QgsDebugMsg( QStringLiteral(
"SSL server public key has 'encipher only' key usage" ) );
1136 encipheronly =
true;
1138 else if ( certconst.known() == QCA::DecipherOnly )
1140 QgsDebugMsg( QStringLiteral(
"SSL server public key has 'decipher only' key usage" ) );
1141 decipheronly =
true;
1144 if ( !encipheronly && !decipheronly )
1162 case QSslError::UnableToGetIssuerCertificate:
1163 return QObject::tr(
"Unable to Get Issuer Certificate" );
1164 case QSslError::UnableToDecryptCertificateSignature:
1165 return QObject::tr(
"Unable to Decrypt Certificate Signature" );
1166 case QSslError::UnableToDecodeIssuerPublicKey:
1167 return QObject::tr(
"Unable to Decode Issuer Public Key" );
1168 case QSslError::CertificateSignatureFailed:
1169 return QObject::tr(
"Certificate Signature Failed" );
1170 case QSslError::CertificateNotYetValid:
1171 return QObject::tr(
"Certificate Not Yet Valid" );
1172 case QSslError::CertificateExpired:
1173 return QObject::tr(
"Certificate Expired" );
1174 case QSslError::InvalidNotBeforeField:
1175 return QObject::tr(
"Invalid Not Before Field" );
1176 case QSslError::InvalidNotAfterField:
1177 return QObject::tr(
"Invalid Not After Field" );
1178 case QSslError::SelfSignedCertificate:
1179 return QObject::tr(
"Self-signed Certificate" );
1180 case QSslError::SelfSignedCertificateInChain:
1181 return QObject::tr(
"Self-signed Certificate In Chain" );
1182 case QSslError::UnableToGetLocalIssuerCertificate:
1183 return QObject::tr(
"Unable to Get Local Issuer Certificate" );
1184 case QSslError::UnableToVerifyFirstCertificate:
1185 return QObject::tr(
"Unable to Verify First Certificate" );
1186 case QSslError::CertificateRevoked:
1187 return QObject::tr(
"Certificate Revoked" );
1188 case QSslError::InvalidCaCertificate:
1189 return QObject::tr(
"Invalid CA Certificate" );
1190 case QSslError::PathLengthExceeded:
1191 return QObject::tr(
"Path Length Exceeded" );
1192 case QSslError::InvalidPurpose:
1193 return QObject::tr(
"Invalid Purpose" );
1194 case QSslError::CertificateUntrusted:
1195 return QObject::tr(
"Certificate Untrusted" );
1196 case QSslError::CertificateRejected:
1197 return QObject::tr(
"Certificate Rejected" );
1198 case QSslError::SubjectIssuerMismatch:
1199 return QObject::tr(
"Subject Issuer Mismatch" );
1200 case QSslError::AuthorityIssuerSerialNumberMismatch:
1201 return QObject::tr(
"Authority Issuer Serial Number Mismatch" );
1202 case QSslError::NoPeerCertificate:
1203 return QObject::tr(
"No Peer Certificate" );
1204 case QSslError::HostNameMismatch:
1205 return QObject::tr(
"Host Name Mismatch" );
1206 case QSslError::UnspecifiedError:
1207 return QObject::tr(
"Unspecified Error" );
1208 case QSslError::CertificateBlacklisted:
1209 return QObject::tr(
"Certificate Blacklisted" );
1210 case QSslError::NoError:
1211 return QObject::tr(
"No Error" );
1212 case QSslError::NoSslSupport:
1213 return QObject::tr(
"No SSL Support" );
1221 QList<QPair<QSslError::SslError, QString> > errenums;
1222 errenums << qMakePair( QSslError::UnableToGetIssuerCertificate,
1224 errenums << qMakePair( QSslError::UnableToDecryptCertificateSignature,
1226 errenums << qMakePair( QSslError::UnableToDecodeIssuerPublicKey,
1228 errenums << qMakePair( QSslError::CertificateSignatureFailed,
1230 errenums << qMakePair( QSslError::CertificateNotYetValid,
1232 errenums << qMakePair( QSslError::CertificateExpired,
1234 errenums << qMakePair( QSslError::InvalidNotBeforeField,
1236 errenums << qMakePair( QSslError::InvalidNotAfterField,
1238 errenums << qMakePair( QSslError::SelfSignedCertificate,
1240 errenums << qMakePair( QSslError::SelfSignedCertificateInChain,
1242 errenums << qMakePair( QSslError::UnableToGetLocalIssuerCertificate,
1244 errenums << qMakePair( QSslError::UnableToVerifyFirstCertificate,
1246 errenums << qMakePair( QSslError::CertificateRevoked,
1248 errenums << qMakePair( QSslError::InvalidCaCertificate,
1250 errenums << qMakePair( QSslError::PathLengthExceeded,
1252 errenums << qMakePair( QSslError::InvalidPurpose,
1254 errenums << qMakePair( QSslError::CertificateUntrusted,
1256 errenums << qMakePair( QSslError::CertificateRejected,
1258 errenums << qMakePair( QSslError::SubjectIssuerMismatch,
1260 errenums << qMakePair( QSslError::AuthorityIssuerSerialNumberMismatch,
1262 errenums << qMakePair( QSslError::NoPeerCertificate,
1264 errenums << qMakePair( QSslError::HostNameMismatch,
1266 errenums << qMakePair( QSslError::UnspecifiedError,
1268 errenums << qMakePair( QSslError::CertificateBlacklisted,
1275 if ( cert.isNull() )
1277 const QDateTime currentTime = QDateTime::currentDateTime();
1278 return cert.effectiveDate() <= currentTime && cert.expiryDate() >= currentTime;
1283 QList<QSslError> sslErrors;
1285 if ( cert.isNull() )
1288 const QDateTime currentTime = QDateTime::currentDateTime();
1289 if ( cert.expiryDate() <= currentTime )
1291 sslErrors << QSslError( QSslError::SslError::CertificateExpired, cert );
1293 if ( cert.effectiveDate() >= QDateTime::currentDateTime() )
1295 sslErrors << QSslError( QSslError::SslError::CertificateNotYetValid, cert );
1297 if ( cert.isBlacklisted() )
1299 sslErrors << QSslError( QSslError::SslError::CertificateBlacklisted, cert );
1311 const QString &hostName,
1314 QList<QSslError> sslErrors;
1315 QList<QSslCertificate> trustedChain;
1317 for (
const auto &cert : certificateChain )
1319 bool untrusted =
false;
1322 if ( cert.digest( ) == untrustedCert.digest( ) )
1330 trustedChain << cert;
1335 const QList<QSslCertificate> constTrustedChain( trustedChain );
1336 for (
const auto &cert : constTrustedChain )
1342 if ( trustRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
1344 static QMutex sMutex;
1345 QMutexLocker lock( &sMutex );
1346 QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
1347 QSslConfiguration sslConfig( oldSslConfig );
1348 sslConfig.setCaCertificates(
casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
1349 QSslConfiguration::setDefaultConfiguration( sslConfig );
1350 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1351 QSslConfiguration::setDefaultConfiguration( oldSslConfig );
1355 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1364 errors << QObject::tr(
"Client certificate is NULL." );
1367 errors << QObject::tr(
"Client certificate key is NULL." );
1370 if ( !errors.isEmpty() )
1373 QList<QSslError> sslErrors;
1374 if ( useIntermediates )
1376 QList<QSslCertificate> certsList( bundle.
caChain() );
1382 sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.
clientCert() );
1384 const QList<QSslError> constSslErrors( sslErrors );
1385 for (
const auto &sslError : constSslErrors )
1387 if ( sslError.error() != QSslError::NoError )
1389 errors << sslError.errorString();
1393 QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.
clientKey().toPem() ) );
1394 QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.
clientCert().publicKey().toPem( ) ) );
1395 bool keyValid( ! pvtKey.isNull() );
1396 if ( keyValid && !( pubKey.toRSA().isNull( ) || pvtKey.toRSA().isNull( ) ) )
1398 keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
1400 else if ( keyValid && !( pubKey.toDSA().isNull( ) || pvtKey.toDSA().isNull( ) ) )
1402 keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
1406 QgsDebugMsg( QStringLiteral(
"Key is not DSA, RSA: validation is not supported by QCA" ) );
1410 errors << QObject::tr(
"Private key does not match client certificate public key." );