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." );
static QString pkgDataPath()
Returns the common root path of all application data directories.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static QString sslErrorEnumString(QSslError::SslError errenum)
Gets short strings describing an SSL error.
static QString qcaValidityMessage(QCA::Validity validity)
Certificate validity check messages per enum.
static QByteArray fileData(const QString &path)
Returns data from a local file via a read-only operation.
static QList< QgsAuthCertUtils::CertUsageType > certificateUsageTypes(const QSslCertificate &cert)
Try to determine the certificates usage types.
static QString qcaSignatureAlgorithm(QCA::SignatureAlgorithm algorithm)
Certificate signature algorithm strings per enum.
static QString resolvedCertName(const QSslCertificate &cert, bool issuer=false)
Gets the general name via RFC 5280 resolution.
static QCA::Certificate qtCertToQcaCert(const QSslCertificate &cert)
Convert a QSslCertificate to a QCA::Certificate.
static QMap< QString, QList< QSslCertificate > > certsGroupedByOrg(const QList< QSslCertificate > &certs)
Map certificates to their oraganization.
static QString certificateUsageTypeString(QgsAuthCertUtils::CertUsageType usagetype)
Certificate usage type strings per enum.
static QString getCertTrustName(QgsAuthCertUtils::CertTrustPolicy trust)
Gets the general name for certificate trust.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Gets the sha1 hash for certificate.
static QCA::CertificateCollection qtCertsToQcaCollection(const QList< QSslCertificate > &certs)
Convert a QList of QSslCertificate to a QCA::CertificateCollection.
CertTrustPolicy
Type of certificate trust policy.
static QSslCertificate certFromFile(const QString &certpath)
Returns the first cert from a PEM or DER formatted file.
static QString qcaKnownConstraint(QCA::ConstraintTypeKnown constraint)
Certificate well-known constraint strings per enum.
static QMap< QString, QSslCertificate > mapDigestToCerts(const QList< QSslCertificate > &certs)
Map certificate sha1 to certificate as simple cache.
static QList< QSslCertificate > casMerge(const QList< QSslCertificate > &bundle1, const QList< QSslCertificate > &bundle2)
casMerge merges two certificate bundles in a single one removing duplicates, the certificates from th...
static QByteArray certsToPemText(const QList< QSslCertificate > &certs)
certsToPemText dump a list of QSslCertificates to PEM text
static QString getSslProtocolName(QSsl::SslProtocol protocol)
SSL Protocol name strings per enum.
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
static QStringList certKeyBundleToPem(const QString &certpath, const QString &keypath, const QString &keypass=QString(), bool reencrypt=true)
Returns list of certificate, private key and algorithm (as PEM text) from file path components.
static QString getColonDelimited(const QString &txt)
Gets string with colon delimiters every 2 characters.
static bool certificateIsAuthority(const QSslCertificate &cert)
Gets whether a certificate is an Authority.
static QStringList pkcs12BundleToPem(const QString &bundlepath, const QString &bundlepass=QString(), bool reencrypt=true)
Returns list of certificate, private key and algorithm (as PEM text) for a PKCS#12 bundle.
static QMap< QString, QList< QgsAuthConfigSslServer > > sslConfigsGroupedByOrg(const QList< QgsAuthConfigSslServer > &configs)
Map SSL custom configs' certificates to their oraganization.
static QList< QSslCertificate > certsFromFile(const QString &certspath)
Returns a list of concatenated certs from a PEM or DER formatted file.
static bool certificateIsAuthorityOrIssuer(const QSslCertificate &cert)
Gets whether a certificate is an Authority or can at least sign other certificates.
static QList< QSslCertificate > casRemoveSelfSigned(const QList< QSslCertificate > &caList)
casRemoveSelfSigned remove self-signed CA certificates from caList
static bool certificateIsIssuer(const QSslCertificate &cert)
Gets whether a certificate can sign other certificates.
static bool certificateIsSslClient(const QSslCertificate &cert)
Gets whether a certificate is probably used for a client identity.
static QSslKey keyFromFile(const QString &keypath, const QString &keypass=QString(), QString *algtype=nullptr)
Returns non-encrypted key from a PEM or DER formatted file.
static bool certificateIsSslServer(const QSslCertificate &cert)
Gets whether a certificate is probably used for a SSL server.
static QList< QSslCertificate > casFromFile(const QString &certspath)
Returns a list of concatenated CAs from a PEM or DER formatted file.
static QList< QSslCertificate > certsFromString(const QString &pemtext)
Returns a list of concatenated certs from a PEM Base64 text block.
static bool certIsCurrent(const QSslCertificate &cert)
certIsCurrent checks if cert is viable for its not before and not after dates
CertUsageType
Type of certificate usage.
CaCertSource
Type of CA certificate source.
static QList< QSslCertificate > pkcs12BundleCas(const QString &bundlepath, const QString &bundlepass=QString())
Returns list of CA certificates (as QSslCertificate) for a PKCS#12 bundle.
static QCA::KeyBundle qcaKeyBundle(const QString &path, const QString &pass)
PKI key/cert bundle from file path, e.g.
static QString getCaSourceName(QgsAuthCertUtils::CaCertSource source, bool single=false)
Gets the general name for CA source enum type.
static QString pemTextToTempFile(const QString &name, const QByteArray &pemtext)
Write a temporary file for a PEM text of cert/key/CAs bundle component.
static bool pemIsPkcs8(const QString &keyPemTxt)
Determine if the PEM-encoded text of a key is PKCS#8 format.
static QMap< QString, QgsAuthConfigSslServer > mapDigestToSslConfigs(const QList< QgsAuthConfigSslServer > &configs)
Map SSL custom configs' certificate sha1 to custom config as simple cache.
static QList< QSslError > validateCertChain(const QList< QSslCertificate > &certificateChain, const QString &hostName=QString(), bool trustRootCa=false)
validateCertChain validates the given certificateChain
static QList< QPair< QSslError::SslError, QString > > sslErrorEnumStrings()
Gets short strings describing SSL errors.
static QString getCertDistinguishedName(const QSslCertificate &qcert, const QCA::Certificate &acert=QCA::Certificate(), bool issuer=false)
Gets combined distinguished name for certificate.
static QStringList validatePKIBundle(QgsPkiBundle &bundle, bool useIntermediates=true, bool trustRootCa=false)
validatePKIBundle validate the PKI bundle by checking the certificate chain, the expiration and effec...
static QList< QSslError > certViabilityErrors(const QSslCertificate &cert)
certViabilityErrors checks basic characteristics (validity dates, blocklisting, etc....
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
Storage set for PKI bundle: SSL certificate, key, optional CA cert chain.
const QSslKey clientKey() const
Private key object.
const QList< QSslCertificate > caChain() const
Chain of Certificate Authorities for client certificate.
const QSslCertificate clientCert() const
Client certificate object.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
#define SSL_SUBJECT_INFO(var, prop)
#define SSL_ISSUER_INFO(var, prop)
#define QgsDebugMsgLevel(str, level)