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 { QSsl::KeyAlgorithm::Rsa, QSsl::KeyAlgorithm::Dsa, QSsl::KeyAlgorithm::Ec, QSsl::KeyAlgorithm::Opaque };
195 for (
const auto &alg : algs )
197 clientkey = QSslKey( keydata, alg, keyEncoding, QSsl::PrivateKey, !keypass.isEmpty() ? keypass.toUtf8() : QByteArray() );
198 if ( !clientkey.isNull() )
204 case QSsl::KeyAlgorithm::Rsa:
207 case QSsl::KeyAlgorithm::Dsa:
210 case QSsl::KeyAlgorithm::Ec:
213 case QSsl::KeyAlgorithm::Opaque:
214 *algtype = u
"opaque"_s;
216 case QSsl::KeyAlgorithm::Dh:
227QList<QSslCertificate> QgsAuthCertUtils::certsFromString(
const QString &pemtext )
229 QList<QSslCertificate> certs;
230 certs = QSslCertificate::fromData( pemtext.toLatin1(), QSsl::Pem );
231 if ( certs.isEmpty() )
238QList<QSslCertificate> QgsAuthCertUtils::casRemoveSelfSigned(
const QList<QSslCertificate> &caList )
240 QList<QSslCertificate> certs;
241 for (
const auto &cert : caList )
243 if ( !cert.isSelfSigned() )
245 certs.append( cert );
251QStringList QgsAuthCertUtils::certKeyBundleToPem(
const QString &certpath,
const QString &keypath,
const QString &keypass,
bool reencrypt )
254 const QSslCertificate clientcert = QgsAuthCertUtils::certFromFile( certpath );
255 if ( !clientcert.isNull() )
257 certpem = QString( clientcert.toPem() );
262 const QSslKey clientkey = QgsAuthCertUtils::keyFromFile( keypath, keypass, &algtype );
265 if ( !clientkey.isNull() )
267 keypem = QString( clientkey.toPem( ( reencrypt && !keypass.isEmpty() ) ? keypass.toUtf8() : QByteArray() ) );
270 return QStringList() << certpem << keypem << algtype;
273bool QgsAuthCertUtils::pemIsPkcs8(
const QString &keyPemTxt )
275 const QString pkcs8Header = u
"-----BEGIN PRIVATE KEY-----"_s;
276 const QString pkcs8Footer = u
"-----END PRIVATE KEY-----"_s;
277 return keyPemTxt.contains( pkcs8Header ) && keyPemTxt.contains( pkcs8Footer );
281QByteArray QgsAuthCertUtils::pkcs8PrivateKey( QByteArray &pkcs8Der )
285 if ( pkcs8Der.isEmpty() )
294 if ( !asnDefsRsrc.exists() )
296 QgsDebugError( u
"ERROR, pkcs.asn resource file not found: %1"_s.arg( asnDefsRsrc.filePath() ) );
299 const char *asnDefsFile = asnDefsRsrc.absoluteFilePath().toLocal8Bit().constData();
301 int asn1_result = ASN1_SUCCESS, der_len = 0, oct_len = 0;
302 asn1_node definitions = NULL, structure = NULL;
303 char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE], oct_data[1024];
304 unsigned char *der = NULL;
305 unsigned int flags = 0;
309 QString typeName( u
"PKCS-8.PrivateKeyInfo"_s );
311 asn1_result = asn1_parser2tree( asnDefsFile, &definitions, errorDescription );
313 switch ( asn1_result )
318 case ASN1_FILE_NOT_FOUND:
319 QgsDebugError( u
"ERROR, file not found: %1"_s.arg( asnDefsFile ) );
321 case ASN1_SYNTAX_ERROR:
322 case ASN1_IDENTIFIER_NOT_FOUND:
323 case ASN1_NAME_TOO_LONG:
324 QgsDebugError( u
"ERROR, asn1 parsing: %1"_s.arg( errorDescription ) );
327 QgsDebugError( u
"ERROR, libtasn1: %1"_s.arg( asn1_strerror( asn1_result ) ) );
332 asn1_result = asn1_create_element( definitions, typeName.toLatin1().constData(), &structure );
336 if ( asn1_result != ASN1_SUCCESS )
338 QgsDebugError( u
"ERROR, structure creation: %1"_s.arg( asn1_strerror( asn1_result ) ) );
343 der =
reinterpret_cast<unsigned char *
>( pkcs8Der.data() );
344 der_len = pkcs8Der.size();
348 asn1_result = asn1_der_decoding2( &structure, der, &der_len, flags, errorDescription );
352 asn1_result = asn1_der_decoding( &structure, der, der_len, errorDescription );
355 if ( asn1_result != ASN1_SUCCESS )
357 QgsDebugError( u
"ERROR, decoding: %1"_s.arg( errorDescription ) );
362 QgsDebugMsgLevel( u
"Decoding: %1"_s.arg( asn1_strerror( asn1_result ) ), 4 );
368 asn1_print_structure( stdout, structure,
"", ASN1_PRINT_NAME_TYPE_VALUE );
373 typeName.append( u
".privateKey"_s );
376 asn1_result = asn1_read_value_type( structure,
"privateKey", NULL, &oct_len, &oct_etype );
378 if ( asn1_result != ASN1_MEM_ERROR )
380 QgsDebugError( u
"ERROR, asn1 read privateKey value type: %1"_s.arg( asn1_strerror( asn1_result ) ) );
384 if ( oct_etype != ASN1_ETYPE_OCTET_STRING )
386 QgsDebugError( u
"ERROR, asn1 privateKey value not octet string, but type: %1"_s.arg(
static_cast<int>( oct_etype ) ) );
392 QgsDebugError( u
"ERROR, asn1 privateKey octet string empty"_s );
397 asn1_result = asn1_read_value( structure,
"privateKey", oct_data, &oct_len );
399 if ( asn1_result != ASN1_SUCCESS )
401 QgsDebugError( u
"ERROR, asn1 read privateKey value: %1"_s.arg( asn1_strerror( asn1_result ) ) );
407 QgsDebugError( u
"ERROR, asn1 privateKey value octet string empty"_s );
411 pkcs1 = QByteArray( oct_data, oct_len );
418 asn1_delete_structure( &structure );
423QStringList QgsAuthCertUtils::pkcs12BundleToPem(
const QString &bundlepath,
const QString &bundlepass,
bool reencrypt )
426 if ( !QCA::isSupported(
"pkcs12" ) )
432 const QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
433 if ( bundle.isNull() )
435 QgsDebugError( u
"FAILED to convert PKCS#12 file to QCA key bundle: %1"_s.arg( bundlepath ) );
439 QCA::SecureArray passarray;
440 if ( reencrypt && !bundlepass.isEmpty() )
442 passarray = QCA::SecureArray( bundlepass.toUtf8() );
446 QSsl::KeyAlgorithm keyalg = QSsl::Opaque;
447 if ( bundle.privateKey().isRSA() )
452 else if ( bundle.privateKey().isDSA() )
457 else if ( bundle.privateKey().isDH() )
464 if ( keyalg == QSsl::Opaque )
466 QgsDebugError( u
"FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1"_s.arg( bundlepath ) );
472 if ( keyalg == QSsl::Rsa && QgsAuthCertUtils::pemIsPkcs8( bundle.privateKey().toPEM() ) )
474 QgsDebugMsgLevel( u
"Private key is PKCS#8: attempting conversion to PKCS#1..."_s, 4 );
479 QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
480 if ( pkcs8Der.isEmpty() )
482 QgsDebugError( u
"FAILED to convert PKCS#12 key to DER-encoded format: %1"_s.arg( bundlepath ) );
486 QByteArray pkcs1Der = QgsAuthCertUtils::pkcs8PrivateKey( pkcs8Der );
487 if ( pkcs1Der.isEmpty() )
489 QgsDebugError( u
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1: %1"_s.arg( bundlepath ) );
493 QSslKey pkcs1Key( pkcs1Der, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey );
494 if ( pkcs1Key.isNull() )
496 QgsDebugError( u
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1 QSslKey: %1"_s.arg( bundlepath ) );
499 keyPem = QString( pkcs1Key.toPem( passarray.toByteArray() ) );
503 keyPem = bundle.privateKey().toPEM( passarray );
506 keyPem = bundle.privateKey().toPEM( passarray );
509 QgsDebugMsgLevel( u
"PKCS#12 cert as PEM:\n%1"_s.arg( QString( bundle.certificateChain().primary().toPEM() ) ), 4 );
513 return QStringList() << bundle.certificateChain().primary().toPEM() << keyPem << algtype;
516QList<QSslCertificate> QgsAuthCertUtils::pkcs12BundleCas(
const QString &bundlepath,
const QString &bundlepass )
518 QList<QSslCertificate> result;
519 if ( !QCA::isSupported(
"pkcs12" ) )
522 const QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
523 if ( bundle.isNull() )
526 const QCA::CertificateChain chain( bundle.certificateChain() );
527 for (
const auto &cert : chain )
531 result.append( QSslCertificate::fromData( cert.toPEM().toLatin1() ) );
537QByteArray QgsAuthCertUtils::certsToPemText(
const QList<QSslCertificate> &certs )
540 if ( !certs.isEmpty() )
542 QStringList certslist;
543 for (
const auto &cert : certs )
545 certslist << cert.toPem();
547 capem = certslist.join( QLatin1Char(
'\n' ) ).toLatin1();
552QString QgsAuthCertUtils::pemTextToTempFile(
const QString &name,
const QByteArray &pemtext )
554 QFile pemFile( QDir::tempPath() + QDir::separator() + name );
555 QString pemFilePath( pemFile.fileName() );
557 if ( pemFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
559 const qint64 bytesWritten = pemFile.write( pemtext );
560 if ( bytesWritten == -1 )
562 QgsDebugError( u
"FAILED to write to temp PEM file: %1"_s.arg( pemFilePath ) );
568 QgsDebugError( u
"FAILED to open writing for temp PEM file: %1"_s.arg( pemFilePath ) );
572 if ( !pemFile.setPermissions( QFile::ReadUser ) )
574 QgsDebugError( u
"FAILED to set permissions on temp PEM file: %1"_s.arg( pemFilePath ) );
586 return single ? QObject::tr(
"System Root CA" ) : QObject::tr(
"System Root Authorities" );
588 return single ? QObject::tr(
"File CA" ) : QObject::tr(
"Authorities from File" );
590 return single ? QObject::tr(
"Database CA" ) : QObject::tr(
"Authorities in Database" );
592 return single ? QObject::tr(
"Connection CA" ) : QObject::tr(
"Authorities from connection" );
598QString QgsAuthCertUtils::resolvedCertName(
const QSslCertificate &cert,
bool issuer )
602 if ( name.isEmpty() )
605 if ( name.isEmpty() )
608 if ( name.isEmpty() )
611 if ( name.isEmpty() )
614 if ( name.isEmpty() )
621void QgsAuthCertUtils::appendDirSegment_( QStringList &dirname,
const QString &
segment, QString value )
623 if ( !value.isEmpty() )
625 dirname.append(
segment +
'=' + value.replace(
',',
"\\,"_L1 ) );
629QSsl::EncodingFormat QgsAuthCertUtils::sniffEncoding(
const QByteArray &payload )
631 return payload.contains( QByteArrayLiteral(
"-----BEGIN " ) ) ? QSsl::Pem : QSsl::Der;
634QString QgsAuthCertUtils::getCertDistinguishedName(
const QSslCertificate &qcert,
const QCA::Certificate &acert,
bool issuer )
639 if ( acert.isNull() )
641 QCA::ConvertResult res;
642 const QCA::Certificate acert( QCA::Certificate::fromPEM( qcert.toPem(), &res, u
"qca-ossl"_s ) );
643 if ( res != QCA::ConvertGood || acert.isNull() )
645 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
657 QgsAuthCertUtils::appendDirSegment_( dirname, u
"E"_s, issuer ? acert.issuerInfo().value( QCA::Email ) : acert.subjectInfo().value( QCA::Email ) );
658 QgsAuthCertUtils::appendDirSegment_( dirname, u
"CN"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CommonName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::CommonName ) );
659 QgsAuthCertUtils::appendDirSegment_( dirname, u
"OU"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::OrganizationalUnitName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::OrganizationalUnitName ) );
660 QgsAuthCertUtils::appendDirSegment_( dirname, u
"O"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::Organization ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::Organization ) );
661 QgsAuthCertUtils::appendDirSegment_( dirname, u
"L"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::LocalityName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::LocalityName ) );
662 QgsAuthCertUtils::appendDirSegment_( dirname, u
"ST"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::StateOrProvinceName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::StateOrProvinceName ) );
663 QgsAuthCertUtils::appendDirSegment_( dirname, u
"C"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CountryName ) :
SSL_SUBJECT_INFO( qcert, QSslCertificate::CountryName ) );
665 return dirname.join(
','_L1 );
673 return QObject::tr(
"Default" );
675 return QObject::tr(
"Trusted" );
677 return QObject::tr(
"Untrusted" );
683QString QgsAuthCertUtils::getColonDelimited(
const QString &txt )
688 sl.reserve( txt.size() );
689 for (
int i = 0; i < txt.size(); i += 2 )
691 sl << txt.mid( i, ( i + 2 > txt.size() ) ? -1 : 2 );
693 return sl.join(
':'_L1 );
696QString QgsAuthCertUtils::shaHexForCert(
const QSslCertificate &cert,
bool formatted )
698 QString sha( cert.digest( QCryptographicHash::Sha1 ).toHex() );
701 return QgsAuthCertUtils::getColonDelimited( sha );
706QCA::Certificate QgsAuthCertUtils::qtCertToQcaCert(
const QSslCertificate &cert )
709 return QCA::Certificate();
711 QCA::ConvertResult res;
712 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, u
"qca-ossl"_s ) );
713 if ( res != QCA::ConvertGood || qcacert.isNull() )
715 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
716 qcacert = QCA::Certificate();
721QCA::CertificateCollection QgsAuthCertUtils::qtCertsToQcaCollection(
const QList<QSslCertificate> &certs )
723 QCA::CertificateCollection qcacoll;
727 for (
const auto &cert : certs )
729 const QCA::Certificate qcacert( qtCertToQcaCert( cert ) );
730 if ( !qcacert.isNull() )
732 qcacoll.addCertificate( qcacert );
738QCA::KeyBundle QgsAuthCertUtils::qcaKeyBundle(
const QString &path,
const QString &pass )
740 QCA::SecureArray passarray;
741 if ( !pass.isEmpty() )
742 passarray = QCA::SecureArray( pass.toUtf8() );
744 QCA::ConvertResult res;
745 const QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( path, passarray, &res, u
"qca-ossl"_s ) );
747 return ( res == QCA::ConvertGood ? bundle : QCA::KeyBundle() );
750QString QgsAuthCertUtils::qcaValidityMessage( QCA::Validity validity )
754 case QCA::ValidityGood:
755 return QObject::tr(
"Certificate is valid." );
756 case QCA::ErrorRejected:
757 return QObject::tr(
"Root CA rejected the certificate purpose." );
758 case QCA::ErrorUntrusted:
759 return QObject::tr(
"Certificate is not trusted." );
760 case QCA::ErrorSignatureFailed:
761 return QObject::tr(
"Signature does not match." );
762 case QCA::ErrorInvalidCA:
763 return QObject::tr(
"Certificate Authority is invalid or not found." );
764 case QCA::ErrorInvalidPurpose:
765 return QObject::tr(
"Purpose does not match the intended usage." );
766 case QCA::ErrorSelfSigned:
767 return QObject::tr(
"Certificate is self-signed, and is not found in the list of trusted certificates." );
768 case QCA::ErrorRevoked:
769 return QObject::tr(
"Certificate has been revoked." );
770 case QCA::ErrorPathLengthExceeded:
771 return QObject::tr(
"Path length from the root CA to this certificate is too long." );
772 case QCA::ErrorExpired:
773 return QObject::tr(
"Certificate has expired or is not yet valid." );
774 case QCA::ErrorExpiredCA:
775 return QObject::tr(
"Certificate Authority has expired." );
776 case QCA::ErrorValidityUnknown:
777 return QObject::tr(
"Validity is unknown." );
783QString QgsAuthCertUtils::qcaSignatureAlgorithm( QCA::SignatureAlgorithm
algorithm )
787 case QCA::EMSA1_SHA1:
788 return QObject::tr(
"SHA1, with EMSA1" );
789 case QCA::EMSA3_SHA1:
790 return QObject::tr(
"SHA1, with EMSA3" );
792 return QObject::tr(
"MD5, with EMSA3" );
794 return QObject::tr(
"MD2, with EMSA3" );
795 case QCA::EMSA3_RIPEMD160:
796 return QObject::tr(
"RIPEMD160, with EMSA3" );
798 return QObject::tr(
"EMSA3, without digest" );
799#if QCA_VERSION >= 0x020100
800 case QCA::EMSA3_SHA224:
801 return QObject::tr(
"SHA224, with EMSA3" );
802 case QCA::EMSA3_SHA256:
803 return QObject::tr(
"SHA256, with EMSA3" );
804 case QCA::EMSA3_SHA384:
805 return QObject::tr(
"SHA384, with EMSA3" );
806 case QCA::EMSA3_SHA512:
807 return QObject::tr(
"SHA512, with EMSA3" );
810 return QObject::tr(
"Unknown (possibly Elliptic Curve)" );
814QString QgsAuthCertUtils::qcaKnownConstraint( QCA::ConstraintTypeKnown constraint )
816 switch ( constraint )
818 case QCA::DigitalSignature:
819 return QObject::tr(
"Digital Signature" );
820 case QCA::NonRepudiation:
821 return QObject::tr(
"Non-repudiation" );
822 case QCA::KeyEncipherment:
823 return QObject::tr(
"Key Encipherment" );
824 case QCA::DataEncipherment:
825 return QObject::tr(
"Data Encipherment" );
826 case QCA::KeyAgreement:
827 return QObject::tr(
"Key Agreement" );
828 case QCA::KeyCertificateSign:
829 return QObject::tr(
"Key Certificate Sign" );
831 return QObject::tr(
"CRL Sign" );
832 case QCA::EncipherOnly:
833 return QObject::tr(
"Encipher Only" );
834 case QCA::DecipherOnly:
835 return QObject::tr(
"Decipher Only" );
836 case QCA::ServerAuth:
837 return QObject::tr(
"Server Authentication" );
838 case QCA::ClientAuth:
839 return QObject::tr(
"Client Authentication" );
840 case QCA::CodeSigning:
841 return QObject::tr(
"Code Signing" );
842 case QCA::EmailProtection:
843 return QObject::tr(
"Email Protection" );
844 case QCA::IPSecEndSystem:
845 return QObject::tr(
"IPSec Endpoint" );
846 case QCA::IPSecTunnel:
847 return QObject::tr(
"IPSec Tunnel" );
849 return QObject::tr(
"IPSec User" );
850 case QCA::TimeStamping:
851 return QObject::tr(
"Time Stamping" );
852 case QCA::OCSPSigning:
853 return QObject::tr(
"OCSP Signing" );
864 return QObject::tr(
"Any or unspecified" );
866 return QObject::tr(
"Certificate Authority" );
868 return QObject::tr(
"Certificate Issuer" );
870 return QObject::tr(
"TLS/SSL Server" );
872 return QObject::tr(
"TLS/SSL Server EV" );
874 return QObject::tr(
"TLS/SSL Client" );
876 return QObject::tr(
"Code Signing" );
878 return QObject::tr(
"Email Protection" );
880 return QObject::tr(
"Time Stamping" );
882 return QObject::tr(
"CRL Signing" );
885 return QObject::tr(
"Undetermined usage" );
889QList<QgsAuthCertUtils::CertUsageType> QgsAuthCertUtils::certificateUsageTypes(
const QSslCertificate &cert )
891 QList<QgsAuthCertUtils::CertUsageType> usages;
896 QCA::ConvertResult res;
897 const QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, u
"qca-ossl"_s ) );
898 if ( res != QCA::ConvertGood || qcacert.isNull() )
900 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
904 if ( qcacert.isCA() )
910 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
911 for (
const auto &certconst : certconsts )
913 if ( certconst.known() == QCA::KeyCertificateSign )
918 else if ( certconst.known() == QCA::ServerAuth )
920 QgsDebugMsgLevel( u
"Certificate has 'server authentication' extended key usage"_s, 2 );
930 v_any = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageAny, QCA::ValidateAll );
931 if ( v_any == QCA::ValidityGood )
936 QCA::Validity v_tlsserver;
937 v_tlsserver = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSServer, QCA::ValidateAll );
938 if ( v_tlsserver == QCA::ValidityGood )
948 QCA::Validity v_tlsclient;
949 v_tlsclient = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSClient, QCA::ValidateAll );
951 if ( v_tlsclient == QCA::ValidityGood )
962bool QgsAuthCertUtils::certificateIsAuthority(
const QSslCertificate &cert )
967bool QgsAuthCertUtils::certificateIsIssuer(
const QSslCertificate &cert )
972bool QgsAuthCertUtils::certificateIsAuthorityOrIssuer(
const QSslCertificate &cert )
974 return ( QgsAuthCertUtils::certificateIsAuthority( cert ) || QgsAuthCertUtils::certificateIsIssuer( cert ) );
977bool QgsAuthCertUtils::certificateIsSslServer(
const QSslCertificate &cert )
983bool QgsAuthCertUtils::certificateIsSslServer(
const QSslCertificate &cert )
992 QCA::ConvertResult res;
993 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QString(
"qca-ossl" ) ) );
994 if ( res != QCA::ConvertGood || qcacert.isNull() )
996 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
1000 if ( qcacert.isCA() )
1002 QgsDebugMsgLevel( u
"SSL server certificate has 'CA:TRUE' basic constraint (and should not)"_s, 2 );
1006 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
1007 for (
const auto & certconst, certconsts )
1009 if ( certconst.known() == QCA::KeyCertificateSign )
1011 QgsDebugError( u
"SSL server certificate has 'Certificate Sign' key usage (and should not)"_s );
1018 bool serverauth =
false;
1019 bool dsignature =
false;
1020 bool keyencrypt =
false;
1021 for (
const auto &certconst : certconsts )
1023 if ( certconst.known() == QCA::DigitalSignature )
1025 QgsDebugMsgLevel( u
"SSL server certificate has 'digital signature' key usage"_s, 2 );
1028 else if ( certconst.known() == QCA::KeyEncipherment )
1030 QgsDebugMsgLevel( u
"SSL server certificate has 'key encipherment' key usage"_s, 2 );
1033 else if ( certconst.known() == QCA::KeyAgreement )
1035 QgsDebugMsgLevel( u
"SSL server certificate has 'key agreement' key usage"_s, 2 );
1038 else if ( certconst.known() == QCA::ServerAuth )
1040 QgsDebugMsgLevel( u
"SSL server certificate has 'server authentication' extended key usage"_s, 2 );
1052 if ( serverauth && dsignature && keyencrypt )
1056 if ( dsignature && keyencrypt )
1062 bool keyagree =
false;
1063 bool encipheronly =
false;
1064 bool decipheronly =
false;
1066 QCA::PublicKey pubkey( qcacert.subjectPublicKey() );
1068 if ( pubkey.bitSize() > 0 && pubkey.isDH() )
1070 keyagree = pubkey.canKeyAgree();
1075 for (
const auto &certconst : certconsts )
1077 if ( certconst.known() == QCA::EncipherOnly )
1079 QgsDebugMsgLevel( u
"SSL server public key has 'encipher only' key usage"_s, 2 );
1080 encipheronly =
true;
1082 else if ( certconst.known() == QCA::DecipherOnly )
1084 QgsDebugMsgLevel( u
"SSL server public key has 'decipher only' key usage"_s, 2 );
1085 decipheronly =
true;
1088 if ( !encipheronly && !decipheronly )
1097bool QgsAuthCertUtils::certificateIsSslClient(
const QSslCertificate &cert )
1102QString QgsAuthCertUtils::sslErrorEnumString( QSslError::SslError errenum )
1106 case QSslError::UnableToGetIssuerCertificate:
1107 return QObject::tr(
"Unable to Get Issuer Certificate" );
1108 case QSslError::UnableToDecryptCertificateSignature:
1109 return QObject::tr(
"Unable to Decrypt Certificate Signature" );
1110 case QSslError::UnableToDecodeIssuerPublicKey:
1111 return QObject::tr(
"Unable to Decode Issuer Public Key" );
1112 case QSslError::CertificateSignatureFailed:
1113 return QObject::tr(
"Certificate Signature Failed" );
1114 case QSslError::CertificateNotYetValid:
1115 return QObject::tr(
"Certificate Not Yet Valid" );
1116 case QSslError::CertificateExpired:
1117 return QObject::tr(
"Certificate Expired" );
1118 case QSslError::InvalidNotBeforeField:
1119 return QObject::tr(
"Invalid Not Before Field" );
1120 case QSslError::InvalidNotAfterField:
1121 return QObject::tr(
"Invalid Not After Field" );
1122 case QSslError::SelfSignedCertificate:
1123 return QObject::tr(
"Self-signed Certificate" );
1124 case QSslError::SelfSignedCertificateInChain:
1125 return QObject::tr(
"Self-signed Certificate In Chain" );
1126 case QSslError::UnableToGetLocalIssuerCertificate:
1127 return QObject::tr(
"Unable to Get Local Issuer Certificate" );
1128 case QSslError::UnableToVerifyFirstCertificate:
1129 return QObject::tr(
"Unable to Verify First Certificate" );
1130 case QSslError::CertificateRevoked:
1131 return QObject::tr(
"Certificate Revoked" );
1132 case QSslError::InvalidCaCertificate:
1133 return QObject::tr(
"Invalid CA Certificate" );
1134 case QSslError::PathLengthExceeded:
1135 return QObject::tr(
"Path Length Exceeded" );
1136 case QSslError::InvalidPurpose:
1137 return QObject::tr(
"Invalid Purpose" );
1138 case QSslError::CertificateUntrusted:
1139 return QObject::tr(
"Certificate Untrusted" );
1140 case QSslError::CertificateRejected:
1141 return QObject::tr(
"Certificate Rejected" );
1142 case QSslError::SubjectIssuerMismatch:
1143 return QObject::tr(
"Subject Issuer Mismatch" );
1144 case QSslError::AuthorityIssuerSerialNumberMismatch:
1145 return QObject::tr(
"Authority Issuer Serial Number Mismatch" );
1146 case QSslError::NoPeerCertificate:
1147 return QObject::tr(
"No Peer Certificate" );
1148 case QSslError::HostNameMismatch:
1149 return QObject::tr(
"Host Name Mismatch" );
1150 case QSslError::UnspecifiedError:
1151 return QObject::tr(
"Unspecified Error" );
1152 case QSslError::CertificateBlacklisted:
1153 return QObject::tr(
"Certificate Blacklisted" );
1154 case QSslError::NoError:
1155 return QObject::tr(
"No Error" );
1156 case QSslError::NoSslSupport:
1157 return QObject::tr(
"No SSL Support" );
1163QList<QPair<QSslError::SslError, QString> > QgsAuthCertUtils::sslErrorEnumStrings()
1165 QList<QPair<QSslError::SslError, QString> > errenums;
1166 errenums << qMakePair( QSslError::UnableToGetIssuerCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetIssuerCertificate ) );
1167 errenums << qMakePair( QSslError::UnableToDecryptCertificateSignature, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecryptCertificateSignature ) );
1168 errenums << qMakePair( QSslError::UnableToDecodeIssuerPublicKey, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecodeIssuerPublicKey ) );
1169 errenums << qMakePair( QSslError::CertificateSignatureFailed, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateSignatureFailed ) );
1170 errenums << qMakePair( QSslError::CertificateNotYetValid, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateNotYetValid ) );
1171 errenums << qMakePair( QSslError::CertificateExpired, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateExpired ) );
1172 errenums << qMakePair( QSslError::InvalidNotBeforeField, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotBeforeField ) );
1173 errenums << qMakePair( QSslError::InvalidNotAfterField, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotAfterField ) );
1174 errenums << qMakePair( QSslError::SelfSignedCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificate ) );
1175 errenums << qMakePair( QSslError::SelfSignedCertificateInChain, QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificateInChain ) );
1176 errenums << qMakePair( QSslError::UnableToGetLocalIssuerCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetLocalIssuerCertificate ) );
1177 errenums << qMakePair( QSslError::UnableToVerifyFirstCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToVerifyFirstCertificate ) );
1178 errenums << qMakePair( QSslError::CertificateRevoked, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRevoked ) );
1179 errenums << qMakePair( QSslError::InvalidCaCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidCaCertificate ) );
1180 errenums << qMakePair( QSslError::PathLengthExceeded, QgsAuthCertUtils::sslErrorEnumString( QSslError::PathLengthExceeded ) );
1181 errenums << qMakePair( QSslError::InvalidPurpose, QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidPurpose ) );
1182 errenums << qMakePair( QSslError::CertificateUntrusted, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateUntrusted ) );
1183 errenums << qMakePair( QSslError::CertificateRejected, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRejected ) );
1184 errenums << qMakePair( QSslError::SubjectIssuerMismatch, QgsAuthCertUtils::sslErrorEnumString( QSslError::SubjectIssuerMismatch ) );
1185 errenums << qMakePair( QSslError::AuthorityIssuerSerialNumberMismatch, QgsAuthCertUtils::sslErrorEnumString( QSslError::AuthorityIssuerSerialNumberMismatch ) );
1186 errenums << qMakePair( QSslError::NoPeerCertificate, QgsAuthCertUtils::sslErrorEnumString( QSslError::NoPeerCertificate ) );
1187 errenums << qMakePair( QSslError::HostNameMismatch, QgsAuthCertUtils::sslErrorEnumString( QSslError::HostNameMismatch ) );
1188 errenums << qMakePair( QSslError::UnspecifiedError, QgsAuthCertUtils::sslErrorEnumString( QSslError::UnspecifiedError ) );
1189 errenums << qMakePair( QSslError::CertificateBlacklisted, QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateBlacklisted ) );
1193bool QgsAuthCertUtils::certIsCurrent(
const QSslCertificate &cert )
1195 if ( cert.isNull() )
1197 const QDateTime currentTime = QDateTime::currentDateTime();
1198 return cert.effectiveDate() <= currentTime && cert.expiryDate() >= currentTime;
1201QList<QSslError> QgsAuthCertUtils::certViabilityErrors(
const QSslCertificate &cert )
1203 QList<QSslError> sslErrors;
1205 if ( cert.isNull() )
1208 const QDateTime currentTime = QDateTime::currentDateTime();
1209 if ( cert.expiryDate() <= currentTime )
1211 sslErrors << QSslError( QSslError::SslError::CertificateExpired, cert );
1213 if ( cert.effectiveDate() >= QDateTime::currentDateTime() )
1215 sslErrors << QSslError( QSslError::SslError::CertificateNotYetValid, cert );
1217 if ( cert.isBlacklisted() )
1219 sslErrors << QSslError( QSslError::SslError::CertificateBlacklisted, cert );
1225bool QgsAuthCertUtils::certIsViable(
const QSslCertificate &cert )
1227 return !cert.isNull() && QgsAuthCertUtils::certViabilityErrors( cert ).isEmpty();
1230QList<QSslError> QgsAuthCertUtils::validateCertChain(
const QList<QSslCertificate> &certificateChain,
const QString &hostName,
bool trustRootCa )
1232 QList<QSslError> sslErrors;
1233 QList<QSslCertificate> trustedChain;
1235 for (
const auto &cert : certificateChain )
1237 bool untrusted =
false;
1240 if ( cert.digest() == untrustedCert.digest() )
1248 trustedChain << cert;
1253 const QList<QSslCertificate> constTrustedChain( trustedChain );
1254 for (
const auto &cert : constTrustedChain )
1256 sslErrors << QgsAuthCertUtils::certViabilityErrors( cert );
1260 if ( trustRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
1262 static QMutex sMutex;
1263 const QMutexLocker lock( &sMutex );
1264 const QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
1265 QSslConfiguration sslConfig( oldSslConfig );
1266 sslConfig.setCaCertificates( casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
1267 QSslConfiguration::setDefaultConfiguration( sslConfig );
1268 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1269 QSslConfiguration::setDefaultConfiguration( oldSslConfig );
1273 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1278QStringList QgsAuthCertUtils::validatePKIBundle(
QgsPkiBundle &bundle,
bool useIntermediates,
bool trustRootCa )
1282 errors << QObject::tr(
"Client certificate is NULL." );
1285 errors << QObject::tr(
"Client certificate key is NULL." );
1288 if ( !errors.isEmpty() )
1291 QList<QSslError> sslErrors;
1292 if ( useIntermediates )
1294 QList<QSslCertificate> certsList( bundle.
caChain() );
1296 sslErrors = QgsAuthCertUtils::validateCertChain( certsList, QString(), trustRootCa );
1300 sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.
clientCert() );
1302 const QList<QSslError> constSslErrors( sslErrors );
1303 for (
const auto &sslError : constSslErrors )
1305 if ( sslError.error() != QSslError::NoError )
1307 errors << sslError.errorString();
1311 const QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.
clientKey().toPem() ) );
1312 const QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.
clientCert().publicKey().toPem() ) );
1313 bool keyValid( !pvtKey.isNull() );
1314 if ( keyValid && !( pubKey.toRSA().isNull() || pvtKey.toRSA().isNull() ) )
1316 keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
1318 else if ( keyValid && !( pubKey.toDSA().isNull() || pvtKey.toDSA().isNull() ) )
1320 keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
1324 QgsDebugError( u
"Key is not DSA, RSA: validation is not supported by QCA"_s );
1328 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)