27#include <QSslCertificate>
31using namespace Qt::StringLiterals;
40QString QgsAuthCertUtils::getSslProtocolName( QSsl::SslProtocol protocol )
44 case QSsl::SecureProtocols:
45 return QObject::tr(
"SecureProtocols" );
47 return QObject::tr(
"TlsV1" );
53QMap<QString, QSslCertificate> QgsAuthCertUtils::mapDigestToCerts(
const QList<QSslCertificate> &certs )
55 QMap<QString, QSslCertificate> digestmap;
56 for (
const auto &cert : certs )
58 digestmap.insert( shaHexForCert( cert ), cert );
63QMap<QString, QList<QSslCertificate> > QgsAuthCertUtils::certsGroupedByOrg(
const QList<QSslCertificate> &certs )
65 QMap< QString, QList<QSslCertificate> > orgcerts;
66 for (
const auto &cert : certs )
70 org = u
"(Organization not defined)"_s;
71 QList<QSslCertificate> valist = orgcerts.contains( org ) ? orgcerts.value( org ) : QList<QSslCertificate>();
72 orgcerts.insert( org, valist << cert );
77QMap<QString, QgsAuthConfigSslServer> QgsAuthCertUtils::mapDigestToSslConfigs(
const QList<QgsAuthConfigSslServer> &configs )
79 QMap<QString, QgsAuthConfigSslServer> digestmap;
80 for (
const auto &config : configs )
82 digestmap.insert( shaHexForCert( config.sslCertificate() ), config );
87QMap<QString, QList<QgsAuthConfigSslServer> > QgsAuthCertUtils::sslConfigsGroupedByOrg(
const QList<QgsAuthConfigSslServer> &configs )
89 QMap< QString, QList<QgsAuthConfigSslServer> > orgconfigs;
90 for (
const auto &config : configs )
92 QString org(
SSL_SUBJECT_INFO( config.sslCertificate(), QSslCertificate::Organization ) );
95 org = QObject::tr(
"(Organization not defined)" );
96 QList<QgsAuthConfigSslServer> valist = orgconfigs.contains( org ) ? orgconfigs.value( org ) : QList<QgsAuthConfigSslServer>();
97 orgconfigs.insert( org, valist << config );
102QByteArray QgsAuthCertUtils::fileData(
const QString &path )
106 if ( !file.exists() )
108 QgsDebugError( u
"Read file error, file not found: %1"_s.arg( path ) );
112 const QFile::OpenMode openflags( QIODevice::ReadOnly );
113 const bool ret = file.open( openflags );
116 data = file.readAll();
123QList<QSslCertificate> QgsAuthCertUtils::certsFromFile(
const QString &certspath )
125 QList<QSslCertificate> certs;
126 const QByteArray payload( QgsAuthCertUtils::fileData( certspath ) );
127 certs = QSslCertificate::fromData( payload, sniffEncoding( payload ) );
128 if ( certs.isEmpty() )
130 QgsDebugError( u
"Parsed cert(s) EMPTY for path: %1"_s.arg( certspath ) );
135QList<QSslCertificate> QgsAuthCertUtils::casFromFile(
const QString &certspath )
137 QList<QSslCertificate> cas;
138 const QList<QSslCertificate> certs( certsFromFile( certspath ) );
139 for (
const auto &cert : certs )
141 if ( certificateIsAuthority( cert ) )
149QList<QSslCertificate> QgsAuthCertUtils::casMerge(
const QList<QSslCertificate> &bundle1,
const QList<QSslCertificate> &bundle2 )
152 QList<QSslCertificate> result( bundle1 );
153 const QList<QSslCertificate> c_bundle1( bundle1 );
154 for (
const auto &cert : c_bundle1 )
156 shas.append( shaHexForCert( cert ) );
158 const QList<QSslCertificate> c_bundle2( bundle2 );
159 for (
const auto &cert : c_bundle2 )
161 if ( !shas.contains( shaHexForCert( cert ) ) )
163 result.append( cert );
170QSslCertificate QgsAuthCertUtils::certFromFile(
const QString &certpath )
172 QSslCertificate cert;
173 QList<QSslCertificate> certs( QgsAuthCertUtils::certsFromFile( certpath ) );
174 if ( !certs.isEmpty() )
176 cert = certs.first();
180 QgsDebugError( u
"Parsed cert is NULL for path: %1"_s.arg( certpath ) );
185QSslKey QgsAuthCertUtils::keyFromFile(
const QString &keypath,
const QString &keypass, QString *algtype )
188 const QByteArray keydata( QgsAuthCertUtils::fileData( keypath ) );
191 const QSsl::EncodingFormat keyEncoding( sniffEncoding( keydata ) );
193 const std::vector<QSsl::KeyAlgorithm> algs {
194 QSsl::KeyAlgorithm::Rsa,
195 QSsl::KeyAlgorithm::Dsa,
196 QSsl::KeyAlgorithm::Ec,
197 QSsl::KeyAlgorithm::Opaque,
198 QSsl::KeyAlgorithm::Dh,
199#if QT_VERSION >= QT_VERSION_CHECK( 6, 11, 0 )
200 QSsl::KeyAlgorithm::MlDsa,
204 for (
const auto &alg : algs )
206 clientkey = QSslKey( keydata, alg, keyEncoding, QSsl::PrivateKey, !keypass.isEmpty() ? keypass.toUtf8() : QByteArray() );
207 if ( !clientkey.isNull() )
213 case QSsl::KeyAlgorithm::Rsa:
216 case QSsl::KeyAlgorithm::Dsa:
219 case QSsl::KeyAlgorithm::Ec:
222 case QSsl::KeyAlgorithm::Opaque:
223 *algtype = u
"opaque"_s;
225 case QSsl::KeyAlgorithm::Dh:
228#if QT_VERSION >= QT_VERSION_CHECK( 6, 11, 0 )
229 case QSsl::KeyAlgorithm::MlDsa:
230 *algtype = u
"mldsa"_s;
241QList<QSslCertificate> QgsAuthCertUtils::certsFromString(
const QString &pemtext )
243 QList<QSslCertificate> certs;
244 certs = QSslCertificate::fromData( pemtext.toLatin1(), QSsl::Pem );
245 if ( certs.isEmpty() )
252QList<QSslCertificate> QgsAuthCertUtils::casRemoveSelfSigned(
const QList<QSslCertificate> &caList )
254 QList<QSslCertificate> certs;
255 for (
const auto &cert : caList )
257 if ( !cert.isSelfSigned() )
259 certs.append( cert );
265QStringList QgsAuthCertUtils::certKeyBundleToPem(
const QString &certpath,
const QString &keypath,
const QString &keypass,
bool reencrypt )
268 const QSslCertificate clientcert = QgsAuthCertUtils::certFromFile( certpath );
269 if ( !clientcert.isNull() )
271 certpem = QString( clientcert.toPem() );
276 const QSslKey clientkey = QgsAuthCertUtils::keyFromFile( keypath, keypass, &algtype );
279 if ( !clientkey.isNull() )
281 keypem = QString( clientkey.toPem( ( reencrypt && !keypass.isEmpty() ) ? keypass.toUtf8() : QByteArray() ) );
284 return QStringList() << certpem << keypem << algtype;
287bool QgsAuthCertUtils::pemIsPkcs8(
const QString &keyPemTxt )
289 const QString pkcs8Header = u
"-----BEGIN PRIVATE KEY-----"_s;
290 const QString pkcs8Footer = u
"-----END PRIVATE KEY-----"_s;
291 return keyPemTxt.contains( pkcs8Header ) && keyPemTxt.contains( pkcs8Footer );
295QByteArray QgsAuthCertUtils::pkcs8PrivateKey( QByteArray &pkcs8Der )
299 if ( pkcs8Der.isEmpty() )
308 if ( !asnDefsRsrc.exists() )
310 QgsDebugError( u
"ERROR, pkcs.asn resource file not found: %1"_s.arg( asnDefsRsrc.filePath() ) );
313 const char *asnDefsFile = asnDefsRsrc.absoluteFilePath().toLocal8Bit().constData();
315 int asn1_result = ASN1_SUCCESS, der_len = 0, oct_len = 0;
316 asn1_node definitions = NULL, structure = NULL;
317 char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE], oct_data[1024];
318 unsigned char *der = NULL;
319 unsigned int flags = 0;
323 QString typeName( u
"PKCS-8.PrivateKeyInfo"_s );
325 asn1_result = asn1_parser2tree( asnDefsFile, &definitions, errorDescription );
327 switch ( asn1_result )
332 case ASN1_FILE_NOT_FOUND:
333 QgsDebugError( u
"ERROR, file not found: %1"_s.arg( asnDefsFile ) );
335 case ASN1_SYNTAX_ERROR:
336 case ASN1_IDENTIFIER_NOT_FOUND:
337 case ASN1_NAME_TOO_LONG:
338 QgsDebugError( u
"ERROR, asn1 parsing: %1"_s.arg( errorDescription ) );
341 QgsDebugError( u
"ERROR, libtasn1: %1"_s.arg( asn1_strerror( asn1_result ) ) );
346 asn1_result = asn1_create_element( definitions, typeName.toLatin1().constData(), &structure );
350 if ( asn1_result != ASN1_SUCCESS )
352 QgsDebugError( u
"ERROR, structure creation: %1"_s.arg( asn1_strerror( asn1_result ) ) );
357 der =
reinterpret_cast<unsigned char *
>( pkcs8Der.data() );
358 der_len = pkcs8Der.size();
362 asn1_result = asn1_der_decoding2( &structure, der, &der_len, flags, errorDescription );
366 asn1_result = asn1_der_decoding( &structure, der, der_len, errorDescription );
369 if ( asn1_result != ASN1_SUCCESS )
371 QgsDebugError( u
"ERROR, decoding: %1"_s.arg( errorDescription ) );
376 QgsDebugMsgLevel( u
"Decoding: %1"_s.arg( asn1_strerror( asn1_result ) ), 4 );
382 asn1_print_structure( stdout, structure,
"", ASN1_PRINT_NAME_TYPE_VALUE );
387 typeName.append( u
".privateKey"_s );
390 asn1_result = asn1_read_value_type( structure,
"privateKey", NULL, &oct_len, &oct_etype );
392 if ( asn1_result != ASN1_MEM_ERROR )
394 QgsDebugError( u
"ERROR, asn1 read privateKey value type: %1"_s.arg( asn1_strerror( asn1_result ) ) );
398 if ( oct_etype != ASN1_ETYPE_OCTET_STRING )
400 QgsDebugError( u
"ERROR, asn1 privateKey value not octet string, but type: %1"_s.arg(
static_cast<int>( oct_etype ) ) );
406 QgsDebugError( u
"ERROR, asn1 privateKey octet string empty"_s );
411 asn1_result = asn1_read_value( structure,
"privateKey", oct_data, &oct_len );
413 if ( asn1_result != ASN1_SUCCESS )
415 QgsDebugError( u
"ERROR, asn1 read privateKey value: %1"_s.arg( asn1_strerror( asn1_result ) ) );
421 QgsDebugError( u
"ERROR, asn1 privateKey value octet string empty"_s );
425 pkcs1 = QByteArray( oct_data, oct_len );
432 asn1_delete_structure( &structure );
437QStringList QgsAuthCertUtils::pkcs12BundleToPem(
const QString &bundlepath,
const QString &bundlepass,
bool reencrypt )
440 if ( !QCA::isSupported(
"pkcs12" ) )
446 const QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
447 if ( bundle.isNull() )
449 QgsDebugError( u
"FAILED to convert PKCS#12 file to QCA key bundle: %1"_s.arg( bundlepath ) );
453 QCA::SecureArray passarray;
454 if ( reencrypt && !bundlepass.isEmpty() )
456 passarray = QCA::SecureArray( bundlepass.toUtf8() );
460 QSsl::KeyAlgorithm keyalg = QSsl::Opaque;
461 if ( bundle.privateKey().isRSA() )
466 else if ( bundle.privateKey().isDSA() )
471 else if ( bundle.privateKey().isDH() )
478 if ( keyalg == QSsl::Opaque )
480 QgsDebugError( u
"FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1"_s.arg( bundlepath ) );
486 if ( keyalg == QSsl::Rsa && QgsAuthCertUtils::pemIsPkcs8( bundle.privateKey().toPEM() ) )
488 QgsDebugMsgLevel( u
"Private key is PKCS#8: attempting conversion to PKCS#1..."_s, 4 );
493 QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
494 if ( pkcs8Der.isEmpty() )
496 QgsDebugError( u
"FAILED to convert PKCS#12 key to DER-encoded format: %1"_s.arg( bundlepath ) );
500 QByteArray pkcs1Der = QgsAuthCertUtils::pkcs8PrivateKey( pkcs8Der );
501 if ( pkcs1Der.isEmpty() )
503 QgsDebugError( u
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1: %1"_s.arg( bundlepath ) );
507 QSslKey pkcs1Key( pkcs1Der, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey );
508 if ( pkcs1Key.isNull() )
510 QgsDebugError( u
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1 QSslKey: %1"_s.arg( bundlepath ) );
513 keyPem = QString( pkcs1Key.toPem( passarray.toByteArray() ) );
517 keyPem = bundle.privateKey().toPEM( passarray );
520 keyPem = bundle.privateKey().toPEM( passarray );
523 QgsDebugMsgLevel( u
"PKCS#12 cert as PEM:\n%1"_s.arg( QString( bundle.certificateChain().primary().toPEM() ) ), 4 );
527 return QStringList() << bundle.certificateChain().primary().toPEM() << keyPem << algtype;
530QList<QSslCertificate> QgsAuthCertUtils::pkcs12BundleCas(
const QString &bundlepath,
const QString &bundlepass )
532 QList<QSslCertificate> result;
533 if ( !QCA::isSupported(
"pkcs12" ) )
536 const QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
537 if ( bundle.isNull() )
540 const QCA::CertificateChain chain( bundle.certificateChain() );
541 for (
const auto &cert : chain )
545 result.append( QSslCertificate::fromData( cert.toPEM().toLatin1() ) );
551QByteArray QgsAuthCertUtils::certsToPemText(
const QList<QSslCertificate> &certs )
554 if ( !certs.isEmpty() )
556 QStringList certslist;
557 for (
const auto &cert : certs )
559 certslist << cert.toPem();
561 capem = certslist.join( QLatin1Char(
'\n' ) ).toLatin1();
566QString QgsAuthCertUtils::pemTextToTempFile(
const QString &name,
const QByteArray &pemtext )
568 QFile pemFile( QDir::tempPath() + QDir::separator() + name );
569 QString pemFilePath( pemFile.fileName() );
571 if ( pemFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
573 const qint64 bytesWritten = pemFile.write( pemtext );
574 if ( bytesWritten == -1 )
576 QgsDebugError( u
"FAILED to write to temp PEM file: %1"_s.arg( pemFilePath ) );
582 QgsDebugError( u
"FAILED to open writing for temp PEM file: %1"_s.arg( pemFilePath ) );
586 if ( !pemFile.setPermissions( QFile::ReadUser ) )
588 QgsDebugError( u
"FAILED to set permissions on temp PEM file: %1"_s.arg( pemFilePath ) );
600 return single ? QObject::tr(
"System Root CA" ) : QObject::tr(
"System Root Authorities" );
602 return single ? QObject::tr(
"File CA" ) : QObject::tr(
"Authorities from File" );
604 return single ? QObject::tr(
"Database CA" ) : QObject::tr(
"Authorities in Database" );
606 return single ? QObject::tr(
"Connection CA" ) : QObject::tr(
"Authorities from connection" );
612QString QgsAuthCertUtils::resolvedCertName(
const QSslCertificate &cert,
bool issuer )
616 if ( name.isEmpty() )
619 if ( name.isEmpty() )
622 if ( name.isEmpty() )
625 if ( name.isEmpty() )
628 if ( name.isEmpty() )
635void QgsAuthCertUtils::appendDirSegment_( QStringList &dirname,
const QString &
segment, QString value )
637 if ( !value.isEmpty() )
639 dirname.append(
segment +
'=' + value.replace(
',',
"\\,"_L1 ) );
643QSsl::EncodingFormat QgsAuthCertUtils::sniffEncoding(
const QByteArray &payload )
645 return payload.contains( QByteArrayLiteral(
"-----BEGIN " ) ) ? QSsl::Pem : QSsl::Der;
648QString QgsAuthCertUtils::getCertDistinguishedName(
const QSslCertificate &qcert,
const QCA::Certificate &acert,
bool issuer )
653 if ( acert.isNull() )
655 QCA::ConvertResult res;
656 const QCA::Certificate acert( QCA::Certificate::fromPEM( qcert.toPem(), &res, u
"qca-ossl"_s ) );
657 if ( res != QCA::ConvertGood || acert.isNull() )
659 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
671 QgsAuthCertUtils::appendDirSegment_( dirname, u
"E"_s, issuer ? acert.issuerInfo().value( QCA::Email ) : acert.subjectInfo().value( QCA::Email ) );
672 QgsAuthCertUtils::appendDirSegment_( dirname, u
"CN"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CommonName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::CommonName ) );
673 QgsAuthCertUtils::appendDirSegment_( dirname, u
"OU"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::OrganizationalUnitName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::OrganizationalUnitName ) );
674 QgsAuthCertUtils::appendDirSegment_( dirname, u
"O"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::Organization ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::Organization ) );
675 QgsAuthCertUtils::appendDirSegment_( dirname, u
"L"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::LocalityName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::LocalityName ) );
676 QgsAuthCertUtils::appendDirSegment_( dirname, u
"ST"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::StateOrProvinceName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::StateOrProvinceName ) );
677 QgsAuthCertUtils::appendDirSegment_( dirname, u
"C"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CountryName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::CountryName ) );
679 return dirname.join(
','_L1 );
687 return QObject::tr(
"Default" );
689 return QObject::tr(
"Trusted" );
691 return QObject::tr(
"Untrusted" );
697QString QgsAuthCertUtils::getColonDelimited(
const QString &txt )
702 sl.reserve( txt.size() );
703 for (
int i = 0; i < txt.size(); i += 2 )
705 sl << txt.mid( i, ( i + 2 > txt.size() ) ? -1 : 2 );
707 return sl.join(
':'_L1 );
710QString QgsAuthCertUtils::shaHexForCert(
const QSslCertificate &cert,
bool formatted )
712 QString sha( cert.digest( QCryptographicHash::Sha1 ).toHex() );
715 return QgsAuthCertUtils::getColonDelimited( sha );
720QCA::Certificate QgsAuthCertUtils::qtCertToQcaCert(
const QSslCertificate &cert )
723 return QCA::Certificate();
725 QCA::ConvertResult res;
726 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, u
"qca-ossl"_s ) );
727 if ( res != QCA::ConvertGood || qcacert.isNull() )
729 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
730 qcacert = QCA::Certificate();
735QCA::CertificateCollection QgsAuthCertUtils::qtCertsToQcaCollection(
const QList<QSslCertificate> &certs )
737 QCA::CertificateCollection qcacoll;
741 for (
const auto &cert : certs )
743 const QCA::Certificate qcacert( qtCertToQcaCert( cert ) );
744 if ( !qcacert.isNull() )
746 qcacoll.addCertificate( qcacert );
752QCA::KeyBundle QgsAuthCertUtils::qcaKeyBundle(
const QString &path,
const QString &pass )
754 QCA::SecureArray passarray;
755 if ( !pass.isEmpty() )
756 passarray = QCA::SecureArray( pass.toUtf8() );
758 QCA::ConvertResult res;
759 const QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( path, passarray, &res, u
"qca-ossl"_s ) );
761 return ( res == QCA::ConvertGood ? bundle : QCA::KeyBundle() );
764QString QgsAuthCertUtils::qcaValidityMessage( QCA::Validity validity )
768 case QCA::ValidityGood:
769 return QObject::tr(
"Certificate is valid." );
770 case QCA::ErrorRejected:
771 return QObject::tr(
"Root CA rejected the certificate purpose." );
772 case QCA::ErrorUntrusted:
773 return QObject::tr(
"Certificate is not trusted." );
774 case QCA::ErrorSignatureFailed:
775 return QObject::tr(
"Signature does not match." );
776 case QCA::ErrorInvalidCA:
777 return QObject::tr(
"Certificate Authority is invalid or not found." );
778 case QCA::ErrorInvalidPurpose:
779 return QObject::tr(
"Purpose does not match the intended usage." );
780 case QCA::ErrorSelfSigned:
781 return QObject::tr(
"Certificate is self-signed, and is not found in the list of trusted certificates." );
782 case QCA::ErrorRevoked:
783 return QObject::tr(
"Certificate has been revoked." );
784 case QCA::ErrorPathLengthExceeded:
785 return QObject::tr(
"Path length from the root CA to this certificate is too long." );
786 case QCA::ErrorExpired:
787 return QObject::tr(
"Certificate has expired or is not yet valid." );
788 case QCA::ErrorExpiredCA:
789 return QObject::tr(
"Certificate Authority has expired." );
790 case QCA::ErrorValidityUnknown:
791 return QObject::tr(
"Validity is unknown." );
797QString QgsAuthCertUtils::qcaSignatureAlgorithm( QCA::SignatureAlgorithm
algorithm )
801 case QCA::EMSA1_SHA1:
802 return QObject::tr(
"SHA1, with EMSA1" );
803 case QCA::EMSA3_SHA1:
804 return QObject::tr(
"SHA1, with EMSA3" );
806 return QObject::tr(
"MD5, with EMSA3" );
808 return QObject::tr(
"MD2, with EMSA3" );
809 case QCA::EMSA3_RIPEMD160:
810 return QObject::tr(
"RIPEMD160, with EMSA3" );
812 return QObject::tr(
"EMSA3, without digest" );
813#if QCA_VERSION >= 0x020100
814 case QCA::EMSA3_SHA224:
815 return QObject::tr(
"SHA224, with EMSA3" );
816 case QCA::EMSA3_SHA256:
817 return QObject::tr(
"SHA256, with EMSA3" );
818 case QCA::EMSA3_SHA384:
819 return QObject::tr(
"SHA384, with EMSA3" );
820 case QCA::EMSA3_SHA512:
821 return QObject::tr(
"SHA512, with EMSA3" );
824 return QObject::tr(
"Unknown (possibly Elliptic Curve)" );
828QString QgsAuthCertUtils::qcaKnownConstraint( QCA::ConstraintTypeKnown constraint )
830 switch ( constraint )
832 case QCA::DigitalSignature:
833 return QObject::tr(
"Digital Signature" );
834 case QCA::NonRepudiation:
835 return QObject::tr(
"Non-repudiation" );
836 case QCA::KeyEncipherment:
837 return QObject::tr(
"Key Encipherment" );
838 case QCA::DataEncipherment:
839 return QObject::tr(
"Data Encipherment" );
840 case QCA::KeyAgreement:
841 return QObject::tr(
"Key Agreement" );
842 case QCA::KeyCertificateSign:
843 return QObject::tr(
"Key Certificate Sign" );
845 return QObject::tr(
"CRL Sign" );
846 case QCA::EncipherOnly:
847 return QObject::tr(
"Encipher Only" );
848 case QCA::DecipherOnly:
849 return QObject::tr(
"Decipher Only" );
850 case QCA::ServerAuth:
851 return QObject::tr(
"Server Authentication" );
852 case QCA::ClientAuth:
853 return QObject::tr(
"Client Authentication" );
854 case QCA::CodeSigning:
855 return QObject::tr(
"Code Signing" );
856 case QCA::EmailProtection:
857 return QObject::tr(
"Email Protection" );
858 case QCA::IPSecEndSystem:
859 return QObject::tr(
"IPSec Endpoint" );
860 case QCA::IPSecTunnel:
861 return QObject::tr(
"IPSec Tunnel" );
863 return QObject::tr(
"IPSec User" );
864 case QCA::TimeStamping:
865 return QObject::tr(
"Time Stamping" );
866 case QCA::OCSPSigning:
867 return QObject::tr(
"OCSP Signing" );
878 return QObject::tr(
"Any or unspecified" );
880 return QObject::tr(
"Certificate Authority" );
882 return QObject::tr(
"Certificate Issuer" );
884 return QObject::tr(
"TLS/SSL Server" );
886 return QObject::tr(
"TLS/SSL Server EV" );
888 return QObject::tr(
"TLS/SSL Client" );
890 return QObject::tr(
"Code Signing" );
892 return QObject::tr(
"Email Protection" );
894 return QObject::tr(
"Time Stamping" );
896 return QObject::tr(
"CRL Signing" );
899 return QObject::tr(
"Undetermined usage" );
903QList<QgsAuthCertUtils::CertUsageType> QgsAuthCertUtils::certificateUsageTypes(
const QSslCertificate &cert )
905 QList<QgsAuthCertUtils::CertUsageType> usages;
910 QCA::ConvertResult res;
911 const QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, u
"qca-ossl"_s ) );
912 if ( res != QCA::ConvertGood || qcacert.isNull() )
914 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
918 if ( qcacert.isCA() )
924 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
925 for (
const auto &certconst : certconsts )
927 if ( certconst.known() == QCA::KeyCertificateSign )
932 else if ( certconst.known() == QCA::ServerAuth )
934 QgsDebugMsgLevel( u
"Certificate has 'server authentication' extended key usage"_s, 2 );
944 v_any = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageAny, QCA::ValidateAll );
945 if ( v_any == QCA::ValidityGood )
950 QCA::Validity v_tlsserver;
951 v_tlsserver = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSServer, QCA::ValidateAll );
952 if ( v_tlsserver == QCA::ValidityGood )
962 QCA::Validity v_tlsclient;
963 v_tlsclient = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSClient, QCA::ValidateAll );
965 if ( v_tlsclient == QCA::ValidityGood )
976bool QgsAuthCertUtils::certificateIsAuthority(
const QSslCertificate &cert )
981bool QgsAuthCertUtils::certificateIsIssuer(
const QSslCertificate &cert )
986bool QgsAuthCertUtils::certificateIsAuthorityOrIssuer(
const QSslCertificate &cert )
988 return ( QgsAuthCertUtils::certificateIsAuthority( cert ) || QgsAuthCertUtils::certificateIsIssuer( cert ) );
991bool QgsAuthCertUtils::certificateIsSslServer(
const QSslCertificate &cert )
997bool QgsAuthCertUtils::certificateIsSslServer(
const QSslCertificate &cert )
1006 QCA::ConvertResult res;
1007 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QString(
"qca-ossl" ) ) );
1008 if ( res != QCA::ConvertGood || qcacert.isNull() )
1010 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
1014 if ( qcacert.isCA() )
1016 QgsDebugMsgLevel( u
"SSL server certificate has 'CA:TRUE' basic constraint (and should not)"_s, 2 );
1020 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
1021 for (
const auto & certconst, certconsts )
1023 if ( certconst.known() == QCA::KeyCertificateSign )
1025 QgsDebugError( u
"SSL server certificate has 'Certificate Sign' key usage (and should not)"_s );
1032 bool serverauth =
false;
1033 bool dsignature =
false;
1034 bool keyencrypt =
false;
1035 for (
const auto &certconst : certconsts )
1037 if ( certconst.known() == QCA::DigitalSignature )
1039 QgsDebugMsgLevel( u
"SSL server certificate has 'digital signature' key usage"_s, 2 );
1042 else if ( certconst.known() == QCA::KeyEncipherment )
1044 QgsDebugMsgLevel( u
"SSL server certificate has 'key encipherment' key usage"_s, 2 );
1047 else if ( certconst.known() == QCA::KeyAgreement )
1049 QgsDebugMsgLevel( u
"SSL server certificate has 'key agreement' key usage"_s, 2 );
1052 else if ( certconst.known() == QCA::ServerAuth )
1054 QgsDebugMsgLevel( u
"SSL server certificate has 'server authentication' extended key usage"_s, 2 );
1066 if ( serverauth && dsignature && keyencrypt )
1070 if ( dsignature && keyencrypt )
1076 bool keyagree =
false;
1077 bool encipheronly =
false;
1078 bool decipheronly =
false;
1080 QCA::PublicKey pubkey( qcacert.subjectPublicKey() );
1082 if ( pubkey.bitSize() > 0 && pubkey.isDH() )
1084 keyagree = pubkey.canKeyAgree();
1089 for (
const auto &certconst : certconsts )
1091 if ( certconst.known() == QCA::EncipherOnly )
1093 QgsDebugMsgLevel( u
"SSL server public key has 'encipher only' key usage"_s, 2 );
1094 encipheronly =
true;
1096 else if ( certconst.known() == QCA::DecipherOnly )
1098 QgsDebugMsgLevel( u
"SSL server public key has 'decipher only' key usage"_s, 2 );
1099 decipheronly =
true;
1102 if ( !encipheronly && !decipheronly )
1111bool QgsAuthCertUtils::certificateIsSslClient(
const QSslCertificate &cert )
1116QString QgsAuthCertUtils::sslErrorEnumString( QSslError::SslError errenum )
1120 case QSslError::UnableToGetIssuerCertificate:
1121 return QObject::tr(
"Unable to Get Issuer Certificate" );
1122 case QSslError::UnableToDecryptCertificateSignature:
1123 return QObject::tr(
"Unable to Decrypt Certificate Signature" );
1124 case QSslError::UnableToDecodeIssuerPublicKey:
1125 return QObject::tr(
"Unable to Decode Issuer Public Key" );
1126 case QSslError::CertificateSignatureFailed:
1127 return QObject::tr(
"Certificate Signature Failed" );
1128 case QSslError::CertificateNotYetValid:
1129 return QObject::tr(
"Certificate Not Yet Valid" );
1130 case QSslError::CertificateExpired:
1131 return QObject::tr(
"Certificate Expired" );
1132 case QSslError::InvalidNotBeforeField:
1133 return QObject::tr(
"Invalid Not Before Field" );
1134 case QSslError::InvalidNotAfterField:
1135 return QObject::tr(
"Invalid Not After Field" );
1136 case QSslError::SelfSignedCertificate:
1137 return QObject::tr(
"Self-signed Certificate" );
1138 case QSslError::SelfSignedCertificateInChain:
1139 return QObject::tr(
"Self-signed Certificate In Chain" );
1140 case QSslError::UnableToGetLocalIssuerCertificate:
1141 return QObject::tr(
"Unable to Get Local Issuer Certificate" );
1142 case QSslError::UnableToVerifyFirstCertificate:
1143 return QObject::tr(
"Unable to Verify First Certificate" );
1144 case QSslError::CertificateRevoked:
1145 return QObject::tr(
"Certificate Revoked" );
1146 case QSslError::InvalidCaCertificate:
1147 return QObject::tr(
"Invalid CA Certificate" );
1148 case QSslError::PathLengthExceeded:
1149 return QObject::tr(
"Path Length Exceeded" );
1150 case QSslError::InvalidPurpose:
1151 return QObject::tr(
"Invalid Purpose" );
1152 case QSslError::CertificateUntrusted:
1153 return QObject::tr(
"Certificate Untrusted" );
1154 case QSslError::CertificateRejected:
1155 return QObject::tr(
"Certificate Rejected" );
1156 case QSslError::SubjectIssuerMismatch:
1157 return QObject::tr(
"Subject Issuer Mismatch" );
1158 case QSslError::AuthorityIssuerSerialNumberMismatch:
1159 return QObject::tr(
"Authority Issuer Serial Number Mismatch" );
1160 case QSslError::NoPeerCertificate:
1161 return QObject::tr(
"No Peer Certificate" );
1162 case QSslError::HostNameMismatch:
1163 return QObject::tr(
"Host Name Mismatch" );
1164 case QSslError::UnspecifiedError:
1165 return QObject::tr(
"Unspecified Error" );
1166 case QSslError::CertificateBlacklisted:
1167 return QObject::tr(
"Certificate Blacklisted" );
1168 case QSslError::NoError:
1169 return QObject::tr(
"No Error" );
1170 case QSslError::NoSslSupport:
1171 return QObject::tr(
"No SSL Support" );
1177QList<QPair<QSslError::SslError, QString> > QgsAuthCertUtils::sslErrorEnumStrings()
1179 QList<QPair<QSslError::SslError, QString> > errenums;
1180 errenums << qMakePair( QSslError::UnableToGetIssuerCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetIssuerCertificate ) );
1181 errenums << qMakePair( QSslError::UnableToDecryptCertificateSignature, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecryptCertificateSignature ) );
1182 errenums << qMakePair( QSslError::UnableToDecodeIssuerPublicKey, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecodeIssuerPublicKey ) );
1183 errenums << qMakePair( QSslError::CertificateSignatureFailed, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateSignatureFailed ) );
1184 errenums << qMakePair( QSslError::CertificateNotYetValid, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateNotYetValid ) );
1185 errenums << qMakePair( QSslError::CertificateExpired, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateExpired ) );
1186 errenums << qMakePair( QSslError::InvalidNotBeforeField, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotBeforeField ) );
1187 errenums << qMakePair( QSslError::InvalidNotAfterField, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotAfterField ) );
1188 errenums << qMakePair( QSslError::SelfSignedCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificate ) );
1189 errenums << qMakePair( QSslError::SelfSignedCertificateInChain, QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificateInChain ) );
1190 errenums << qMakePair( QSslError::UnableToGetLocalIssuerCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetLocalIssuerCertificate ) );
1191 errenums << qMakePair( QSslError::UnableToVerifyFirstCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToVerifyFirstCertificate ) );
1192 errenums << qMakePair( QSslError::CertificateRevoked, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRevoked ) );
1193 errenums << qMakePair( QSslError::InvalidCaCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidCaCertificate ) );
1194 errenums << qMakePair( QSslError::PathLengthExceeded, QgsAuthCertUtils::sslErrorEnumString( QSslError::PathLengthExceeded ) );
1195 errenums << qMakePair( QSslError::InvalidPurpose, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidPurpose ) );
1196 errenums << qMakePair( QSslError::CertificateUntrusted, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateUntrusted ) );
1197 errenums << qMakePair( QSslError::CertificateRejected, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRejected ) );
1198 errenums << qMakePair( QSslError::SubjectIssuerMismatch, QgsAuthCertUtils::sslErrorEnumString( QSslError::SubjectIssuerMismatch ) );
1199 errenums << qMakePair( QSslError::AuthorityIssuerSerialNumberMismatch, QgsAuthCertUtils::sslErrorEnumString( QSslError::AuthorityIssuerSerialNumberMismatch ) );
1200 errenums << qMakePair( QSslError::NoPeerCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::NoPeerCertificate ) );
1201 errenums << qMakePair( QSslError::HostNameMismatch, QgsAuthCertUtils::sslErrorEnumString( QSslError::HostNameMismatch ) );
1202 errenums << qMakePair( QSslError::UnspecifiedError, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnspecifiedError ) );
1203 errenums << qMakePair( QSslError::CertificateBlacklisted, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateBlacklisted ) );
1207bool QgsAuthCertUtils::certIsCurrent(
const QSslCertificate &cert )
1209 if ( cert.isNull() )
1211 const QDateTime currentTime = QDateTime::currentDateTime();
1212 return cert.effectiveDate() <= currentTime && cert.expiryDate() >= currentTime;
1215QList<QSslError> QgsAuthCertUtils::certViabilityErrors(
const QSslCertificate &cert )
1217 QList<QSslError> sslErrors;
1219 if ( cert.isNull() )
1222 const QDateTime currentTime = QDateTime::currentDateTime();
1223 if ( cert.expiryDate() <= currentTime )
1225 sslErrors << QSslError( QSslError::SslError::CertificateExpired, cert );
1227 if ( cert.effectiveDate() >= QDateTime::currentDateTime() )
1229 sslErrors << QSslError( QSslError::SslError::CertificateNotYetValid, cert );
1231 if ( cert.isBlacklisted() )
1233 sslErrors << QSslError( QSslError::SslError::CertificateBlacklisted, cert );
1239bool QgsAuthCertUtils::certIsViable(
const QSslCertificate &cert )
1241 return !cert.isNull() && QgsAuthCertUtils::certViabilityErrors( cert ).isEmpty();
1244QList<QSslError> QgsAuthCertUtils::validateCertChain(
const QList<QSslCertificate> &certificateChain,
const QString &hostName,
bool trustRootCa )
1246 QList<QSslError> sslErrors;
1247 QList<QSslCertificate> trustedChain;
1249 for (
const auto &cert : certificateChain )
1251 bool untrusted =
false;
1254 if ( cert.digest() == untrustedCert.digest() )
1262 trustedChain << cert;
1267 const QList<QSslCertificate> constTrustedChain( trustedChain );
1268 for (
const auto &cert : constTrustedChain )
1270 sslErrors << QgsAuthCertUtils::certViabilityErrors( cert );
1274 if ( trustRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
1276 static QMutex sMutex;
1277 const QMutexLocker lock( &sMutex );
1278 const QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
1279 QSslConfiguration sslConfig( oldSslConfig );
1280 sslConfig.setCaCertificates( casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
1281 QSslConfiguration::setDefaultConfiguration( sslConfig );
1282 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1283 QSslConfiguration::setDefaultConfiguration( oldSslConfig );
1287 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1292QStringList QgsAuthCertUtils::validatePKIBundle(
QgsPkiBundle &bundle,
bool useIntermediates,
bool trustRootCa )
1296 errors << QObject::tr(
"Client certificate is NULL." );
1299 errors << QObject::tr(
"Client certificate key is NULL." );
1302 if ( !errors.isEmpty() )
1305 QList<QSslError> sslErrors;
1306 if ( useIntermediates )
1308 QList<QSslCertificate> certsList( bundle.
caChain() );
1310 sslErrors = QgsAuthCertUtils::validateCertChain( certsList, QString(), trustRootCa );
1314 sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.
clientCert() );
1316 const QList<QSslError> constSslErrors( sslErrors );
1317 for (
const auto &sslError : constSslErrors )
1319 if ( sslError.error() != QSslError::NoError )
1321 errors << sslError.errorString();
1325 const QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.
clientKey().toPem() ) );
1326 const QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.
clientCert().publicKey().toPem() ) );
1327 bool keyValid( !pvtKey.isNull() );
1328 if ( keyValid && !( pubKey.toRSA().isNull() || pvtKey.toRSA().isNull() ) )
1330 keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
1332 else if ( keyValid && !( pubKey.toDSA().isNull() || pvtKey.toDSA().isNull() ) )
1334 keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
1338 QgsDebugError( u
"Key is not DSA, RSA: validation is not supported by QCA"_s );
1342 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.
CertTrustPolicy
Type of certificate trust policy.
CertUsageType
Type of certificate usage.
CaCertSource
Type of CA certificate source.
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)
#define QgsDebugError(str)
QLineF segment(int index, QRectF rect, double radius)