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 );
171QSslCertificate QgsAuthCertUtils::certFromFile(
const QString &certpath )
173 QSslCertificate cert;
174 QList<QSslCertificate> certs( QgsAuthCertUtils::certsFromFile( certpath ) );
175 if ( !certs.isEmpty() )
177 cert = certs.first();
181 QgsDebugError( u
"Parsed cert is NULL for path: %1"_s.arg( certpath ) );
186QSslKey QgsAuthCertUtils::keyFromFile(
const QString &keypath,
187 const QString &keypass,
191 const QByteArray keydata( QgsAuthCertUtils::fileData( keypath ) );
194 const QSsl::EncodingFormat keyEncoding( sniffEncoding( keydata ) );
196 const std::vector<QSsl::KeyAlgorithm> algs
198 QSsl::KeyAlgorithm::Rsa,
199 QSsl::KeyAlgorithm::Dsa,
200 QSsl::KeyAlgorithm::Ec,
201 QSsl::KeyAlgorithm::Opaque
204 for (
const auto &alg : algs )
206 clientkey = QSslKey( keydata,
210 !keypass.isEmpty() ? keypass.toUtf8() : QByteArray() );
211 if ( ! clientkey.isNull() )
217 case QSsl::KeyAlgorithm::Rsa:
220 case QSsl::KeyAlgorithm::Dsa:
223 case QSsl::KeyAlgorithm::Ec:
226 case QSsl::KeyAlgorithm::Opaque:
227 *algtype = u
"opaque"_s;
229 case QSsl::KeyAlgorithm::Dh:
240QList<QSslCertificate> QgsAuthCertUtils::certsFromString(
const QString &pemtext )
242 QList<QSslCertificate> certs;
243 certs = QSslCertificate::fromData( pemtext.toLatin1(), QSsl::Pem );
244 if ( certs.isEmpty() )
251QList<QSslCertificate> QgsAuthCertUtils::casRemoveSelfSigned(
const QList<QSslCertificate> &caList )
253 QList<QSslCertificate> certs;
254 for (
const auto &cert : caList )
256 if ( ! cert.isSelfSigned( ) )
258 certs.append( cert );
264QStringList QgsAuthCertUtils::certKeyBundleToPem(
const QString &certpath,
265 const QString &keypath,
266 const QString &keypass,
270 const QSslCertificate clientcert = QgsAuthCertUtils::certFromFile( certpath );
271 if ( !clientcert.isNull() )
273 certpem = QString( clientcert.toPem() );
278 const QSslKey clientkey = QgsAuthCertUtils::keyFromFile( keypath, keypass, &algtype );
281 if ( !clientkey.isNull() )
283 keypem = QString( clientkey.toPem( ( reencrypt && !keypass.isEmpty() ) ? keypass.toUtf8() : QByteArray() ) );
286 return QStringList() << certpem << keypem << algtype;
289bool QgsAuthCertUtils::pemIsPkcs8(
const QString &keyPemTxt )
291 const QString pkcs8Header = u
"-----BEGIN PRIVATE KEY-----"_s;
292 const QString pkcs8Footer = u
"-----END PRIVATE KEY-----"_s;
293 return keyPemTxt.contains( pkcs8Header ) && keyPemTxt.contains( pkcs8Footer );
297QByteArray QgsAuthCertUtils::pkcs8PrivateKey( QByteArray &pkcs8Der )
301 if ( pkcs8Der.isEmpty() )
310 if ( ! asnDefsRsrc.exists() )
312 QgsDebugError( u
"ERROR, pkcs.asn resource file not found: %1"_s.arg( asnDefsRsrc.filePath() ) );
315 const char *asnDefsFile = asnDefsRsrc.absoluteFilePath().toLocal8Bit().constData();
317 int asn1_result = ASN1_SUCCESS, der_len = 0, oct_len = 0;
318 asn1_node definitions = NULL, structure = NULL;
319 char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE], oct_data[1024];
320 unsigned char *der = NULL;
321 unsigned int flags = 0;
325 QString typeName( u
"PKCS-8.PrivateKeyInfo"_s );
327 asn1_result = asn1_parser2tree( asnDefsFile, &definitions, errorDescription );
329 switch ( asn1_result )
334 case ASN1_FILE_NOT_FOUND:
335 QgsDebugError( u
"ERROR, file not found: %1"_s.arg( asnDefsFile ) );
337 case ASN1_SYNTAX_ERROR:
338 case ASN1_IDENTIFIER_NOT_FOUND:
339 case ASN1_NAME_TOO_LONG:
340 QgsDebugError( u
"ERROR, asn1 parsing: %1"_s.arg( errorDescription ) );
343 QgsDebugError( u
"ERROR, libtasn1: %1"_s.arg( asn1_strerror( asn1_result ) ) );
348 asn1_result = asn1_create_element( definitions, typeName.toLatin1().constData(), &structure );
352 if ( asn1_result != ASN1_SUCCESS )
354 QgsDebugError( u
"ERROR, structure creation: %1"_s.arg( asn1_strerror( asn1_result ) ) );
359 der =
reinterpret_cast<unsigned char *
>( pkcs8Der.data() );
360 der_len = pkcs8Der.size();
364 asn1_result = asn1_der_decoding2( &structure, der, &der_len, flags, errorDescription );
368 asn1_result = asn1_der_decoding( &structure, der, der_len, errorDescription );
371 if ( asn1_result != ASN1_SUCCESS )
373 QgsDebugError( u
"ERROR, decoding: %1"_s.arg( errorDescription ) );
378 QgsDebugMsgLevel( u
"Decoding: %1"_s.arg( asn1_strerror( asn1_result ) ), 4 );
384 asn1_print_structure( stdout, structure,
"", ASN1_PRINT_NAME_TYPE_VALUE );
389 typeName.append( u
".privateKey"_s );
392 asn1_result = asn1_read_value_type( structure,
"privateKey", NULL, &oct_len, &oct_etype );
394 if ( asn1_result != ASN1_MEM_ERROR )
396 QgsDebugError( u
"ERROR, asn1 read privateKey value type: %1"_s.arg( asn1_strerror( asn1_result ) ) );
400 if ( oct_etype != ASN1_ETYPE_OCTET_STRING )
402 QgsDebugError( u
"ERROR, asn1 privateKey value not octet string, but type: %1"_s.arg(
static_cast<int>( oct_etype ) ) );
408 QgsDebugError( u
"ERROR, asn1 privateKey octet string empty"_s );
413 asn1_result = asn1_read_value( structure,
"privateKey", oct_data, &oct_len );
415 if ( asn1_result != ASN1_SUCCESS )
417 QgsDebugError( u
"ERROR, asn1 read privateKey value: %1"_s.arg( asn1_strerror( asn1_result ) ) );
423 QgsDebugError( u
"ERROR, asn1 privateKey value octet string empty"_s );
427 pkcs1 = QByteArray( oct_data, oct_len );
434 asn1_delete_structure( &structure );
439QStringList QgsAuthCertUtils::pkcs12BundleToPem(
const QString &bundlepath,
440 const QString &bundlepass,
444 if ( !QCA::isSupported(
"pkcs12" ) )
450 const QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
451 if ( bundle.isNull() )
453 QgsDebugError( u
"FAILED to convert PKCS#12 file to QCA key bundle: %1"_s.arg( bundlepath ) );
457 QCA::SecureArray passarray;
458 if ( reencrypt && !bundlepass.isEmpty() )
460 passarray = QCA::SecureArray( bundlepass.toUtf8() );
464 QSsl::KeyAlgorithm keyalg = QSsl::Opaque;
465 if ( bundle.privateKey().isRSA() )
470 else if ( bundle.privateKey().isDSA() )
475 else if ( bundle.privateKey().isDH() )
482 if ( keyalg == QSsl::Opaque )
484 QgsDebugError( u
"FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1"_s.arg( bundlepath ) );
490 if ( keyalg == QSsl::Rsa && QgsAuthCertUtils::pemIsPkcs8( bundle.privateKey().toPEM() ) )
492 QgsDebugMsgLevel( u
"Private key is PKCS#8: attempting conversion to PKCS#1..."_s, 4 );
497 QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
498 if ( pkcs8Der.isEmpty() )
500 QgsDebugError( u
"FAILED to convert PKCS#12 key to DER-encoded format: %1"_s.arg( bundlepath ) );
504 QByteArray pkcs1Der = QgsAuthCertUtils::pkcs8PrivateKey( pkcs8Der );
505 if ( pkcs1Der.isEmpty() )
507 QgsDebugError( u
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1: %1"_s.arg( bundlepath ) );
511 QSslKey pkcs1Key( pkcs1Der, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey );
512 if ( pkcs1Key.isNull() )
514 QgsDebugError( u
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1 QSslKey: %1"_s.arg( bundlepath ) );
517 keyPem = QString( pkcs1Key.toPem( passarray.toByteArray() ) );
521 keyPem = bundle.privateKey().toPEM( passarray );
524 keyPem = bundle.privateKey().toPEM( passarray );
527 QgsDebugMsgLevel( u
"PKCS#12 cert as PEM:\n%1"_s.arg( QString( bundle.certificateChain().primary().toPEM() ) ), 4 );
531 return QStringList() << bundle.certificateChain().primary().toPEM() << keyPem << algtype;
534QList<QSslCertificate> QgsAuthCertUtils::pkcs12BundleCas(
const QString &bundlepath,
const QString &bundlepass )
536 QList<QSslCertificate> result;
537 if ( !QCA::isSupported(
"pkcs12" ) )
540 const QCA::KeyBundle bundle( QgsAuthCertUtils::qcaKeyBundle( bundlepath, bundlepass ) );
541 if ( bundle.isNull() )
544 const QCA::CertificateChain chain( bundle.certificateChain() );
545 for (
const auto &cert : chain )
549 result.append( QSslCertificate::fromData( cert.toPEM().toLatin1() ) );
555QByteArray QgsAuthCertUtils::certsToPemText(
const QList<QSslCertificate> &certs )
558 if ( !certs.isEmpty() )
560 QStringList certslist;
561 for (
const auto &cert : certs )
563 certslist << cert.toPem();
565 capem = certslist.join( QLatin1Char(
'\n' ) ).toLatin1();
570QString QgsAuthCertUtils::pemTextToTempFile(
const QString &name,
const QByteArray &pemtext )
572 QFile pemFile( QDir::tempPath() + QDir::separator() + name );
573 QString pemFilePath( pemFile.fileName() );
575 if ( pemFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
577 const qint64 bytesWritten = pemFile.write( pemtext );
578 if ( bytesWritten == -1 )
580 QgsDebugError( u
"FAILED to write to temp PEM file: %1"_s.arg( pemFilePath ) );
586 QgsDebugError( u
"FAILED to open writing for temp PEM file: %1"_s.arg( pemFilePath ) );
590 if ( !pemFile.setPermissions( QFile::ReadUser ) )
592 QgsDebugError( u
"FAILED to set permissions on temp PEM file: %1"_s.arg( pemFilePath ) );
604 return single ? QObject::tr(
"System Root CA" ) : QObject::tr(
"System Root Authorities" );
606 return single ? QObject::tr(
"File CA" ) : QObject::tr(
"Authorities from File" );
608 return single ? QObject::tr(
"Database CA" ) : QObject::tr(
"Authorities in Database" );
610 return single ? QObject::tr(
"Connection CA" ) : QObject::tr(
"Authorities from connection" );
616QString QgsAuthCertUtils::resolvedCertName(
const QSslCertificate &cert,
bool issuer )
618 QString name( issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::CommonName )
621 if ( name.isEmpty() )
622 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::OrganizationalUnitName )
625 if ( name.isEmpty() )
629 if ( name.isEmpty() )
633 if ( name.isEmpty() )
634 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::StateOrProvinceName )
637 if ( name.isEmpty() )
645void QgsAuthCertUtils::appendDirSegment_( QStringList &dirname,
646 const QString &
segment, QString value )
648 if ( !value.isEmpty() )
650 dirname.append(
segment +
'=' + value.replace(
',',
"\\,"_L1 ) );
654QSsl::EncodingFormat QgsAuthCertUtils::sniffEncoding(
const QByteArray &payload )
656 return payload.contains( QByteArrayLiteral(
"-----BEGIN " ) ) ?
661QString QgsAuthCertUtils::getCertDistinguishedName(
const QSslCertificate &qcert,
662 const QCA::Certificate &acert,
668 if ( acert.isNull() )
670 QCA::ConvertResult res;
671 const QCA::Certificate acert( QCA::Certificate::fromPEM( qcert.toPem(), &res, u
"qca-ossl"_s ) );
672 if ( res != QCA::ConvertGood || acert.isNull() )
674 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
686 QgsAuthCertUtils::appendDirSegment_(
687 dirname, u
"E"_s, issuer ? acert.issuerInfo().value( QCA::Email )
688 : acert.subjectInfo().value( QCA::Email ) );
689 QgsAuthCertUtils::appendDirSegment_(
690 dirname, u
"CN"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CommonName )
692 QgsAuthCertUtils::appendDirSegment_(
693 dirname, u
"OU"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::OrganizationalUnitName )
695 QgsAuthCertUtils::appendDirSegment_(
696 dirname, u
"O"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::Organization )
698 QgsAuthCertUtils::appendDirSegment_(
699 dirname, u
"L"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::LocalityName )
701 QgsAuthCertUtils::appendDirSegment_(
702 dirname, u
"ST"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::StateOrProvinceName )
704 QgsAuthCertUtils::appendDirSegment_(
705 dirname, u
"C"_s, issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CountryName )
708 return dirname.join(
','_L1 );
716 return QObject::tr(
"Default" );
718 return QObject::tr(
"Trusted" );
720 return QObject::tr(
"Untrusted" );
726QString QgsAuthCertUtils::getColonDelimited(
const QString &txt )
731 sl.reserve( txt.size() );
732 for (
int i = 0; i < txt.size(); i += 2 )
734 sl << txt.mid( i, ( i + 2 > txt.size() ) ? -1 : 2 );
736 return sl.join(
':'_L1 );
739QString QgsAuthCertUtils::shaHexForCert(
const QSslCertificate &cert,
bool formatted )
741 QString sha( cert.digest( QCryptographicHash::Sha1 ).toHex() );
744 return QgsAuthCertUtils::getColonDelimited( sha );
749QCA::Certificate QgsAuthCertUtils::qtCertToQcaCert(
const QSslCertificate &cert )
752 return QCA::Certificate();
754 QCA::ConvertResult res;
755 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, u
"qca-ossl"_s ) );
756 if ( res != QCA::ConvertGood || qcacert.isNull() )
758 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
759 qcacert = QCA::Certificate();
764QCA::CertificateCollection QgsAuthCertUtils::qtCertsToQcaCollection(
const QList<QSslCertificate> &certs )
766 QCA::CertificateCollection qcacoll;
770 for (
const auto &cert : certs )
772 const QCA::Certificate qcacert( qtCertToQcaCert( cert ) );
773 if ( !qcacert.isNull() )
775 qcacoll.addCertificate( qcacert );
781QCA::KeyBundle QgsAuthCertUtils::qcaKeyBundle(
const QString &path,
const QString &pass )
783 QCA::SecureArray passarray;
784 if ( !pass.isEmpty() )
785 passarray = QCA::SecureArray( pass.toUtf8() );
787 QCA::ConvertResult res;
788 const QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( path, passarray, &res, u
"qca-ossl"_s ) );
790 return ( res == QCA::ConvertGood ? bundle : QCA::KeyBundle() );
793QString QgsAuthCertUtils::qcaValidityMessage( QCA::Validity validity )
797 case QCA::ValidityGood:
798 return QObject::tr(
"Certificate is valid." );
799 case QCA::ErrorRejected:
800 return QObject::tr(
"Root CA rejected the certificate purpose." );
801 case QCA::ErrorUntrusted:
802 return QObject::tr(
"Certificate is not trusted." );
803 case QCA::ErrorSignatureFailed:
804 return QObject::tr(
"Signature does not match." );
805 case QCA::ErrorInvalidCA:
806 return QObject::tr(
"Certificate Authority is invalid or not found." );
807 case QCA::ErrorInvalidPurpose:
808 return QObject::tr(
"Purpose does not match the intended usage." );
809 case QCA::ErrorSelfSigned:
810 return QObject::tr(
"Certificate is self-signed, and is not found in the list of trusted certificates." );
811 case QCA::ErrorRevoked:
812 return QObject::tr(
"Certificate has been revoked." );
813 case QCA::ErrorPathLengthExceeded:
814 return QObject::tr(
"Path length from the root CA to this certificate is too long." );
815 case QCA::ErrorExpired:
816 return QObject::tr(
"Certificate has expired or is not yet valid." );
817 case QCA::ErrorExpiredCA:
818 return QObject::tr(
"Certificate Authority has expired." );
819 case QCA::ErrorValidityUnknown:
820 return QObject::tr(
"Validity is unknown." );
826QString QgsAuthCertUtils::qcaSignatureAlgorithm( QCA::SignatureAlgorithm
algorithm )
830 case QCA::EMSA1_SHA1:
831 return QObject::tr(
"SHA1, with EMSA1" );
832 case QCA::EMSA3_SHA1:
833 return QObject::tr(
"SHA1, with EMSA3" );
835 return QObject::tr(
"MD5, with EMSA3" );
837 return QObject::tr(
"MD2, with EMSA3" );
838 case QCA::EMSA3_RIPEMD160:
839 return QObject::tr(
"RIPEMD160, with EMSA3" );
841 return QObject::tr(
"EMSA3, without digest" );
842#if QCA_VERSION >= 0x020100
843 case QCA::EMSA3_SHA224:
844 return QObject::tr(
"SHA224, with EMSA3" );
845 case QCA::EMSA3_SHA256:
846 return QObject::tr(
"SHA256, with EMSA3" );
847 case QCA::EMSA3_SHA384:
848 return QObject::tr(
"SHA384, with EMSA3" );
849 case QCA::EMSA3_SHA512:
850 return QObject::tr(
"SHA512, with EMSA3" );
853 return QObject::tr(
"Unknown (possibly Elliptic Curve)" );
857QString QgsAuthCertUtils::qcaKnownConstraint( QCA::ConstraintTypeKnown constraint )
859 switch ( constraint )
861 case QCA::DigitalSignature:
862 return QObject::tr(
"Digital Signature" );
863 case QCA::NonRepudiation:
864 return QObject::tr(
"Non-repudiation" );
865 case QCA::KeyEncipherment:
866 return QObject::tr(
"Key Encipherment" );
867 case QCA::DataEncipherment:
868 return QObject::tr(
"Data Encipherment" );
869 case QCA::KeyAgreement:
870 return QObject::tr(
"Key Agreement" );
871 case QCA::KeyCertificateSign:
872 return QObject::tr(
"Key Certificate Sign" );
874 return QObject::tr(
"CRL Sign" );
875 case QCA::EncipherOnly:
876 return QObject::tr(
"Encipher Only" );
877 case QCA::DecipherOnly:
878 return QObject::tr(
"Decipher Only" );
879 case QCA::ServerAuth:
880 return QObject::tr(
"Server Authentication" );
881 case QCA::ClientAuth:
882 return QObject::tr(
"Client Authentication" );
883 case QCA::CodeSigning:
884 return QObject::tr(
"Code Signing" );
885 case QCA::EmailProtection:
886 return QObject::tr(
"Email Protection" );
887 case QCA::IPSecEndSystem:
888 return QObject::tr(
"IPSec Endpoint" );
889 case QCA::IPSecTunnel:
890 return QObject::tr(
"IPSec Tunnel" );
892 return QObject::tr(
"IPSec User" );
893 case QCA::TimeStamping:
894 return QObject::tr(
"Time Stamping" );
895 case QCA::OCSPSigning:
896 return QObject::tr(
"OCSP Signing" );
907 return QObject::tr(
"Any or unspecified" );
909 return QObject::tr(
"Certificate Authority" );
911 return QObject::tr(
"Certificate Issuer" );
913 return QObject::tr(
"TLS/SSL Server" );
915 return QObject::tr(
"TLS/SSL Server EV" );
917 return QObject::tr(
"TLS/SSL Client" );
919 return QObject::tr(
"Code Signing" );
921 return QObject::tr(
"Email Protection" );
923 return QObject::tr(
"Time Stamping" );
925 return QObject::tr(
"CRL Signing" );
928 return QObject::tr(
"Undetermined usage" );
932QList<QgsAuthCertUtils::CertUsageType> QgsAuthCertUtils::certificateUsageTypes(
const QSslCertificate &cert )
934 QList<QgsAuthCertUtils::CertUsageType> usages;
939 QCA::ConvertResult res;
940 const QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, u
"qca-ossl"_s ) );
941 if ( res != QCA::ConvertGood || qcacert.isNull() )
943 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
947 if ( qcacert.isCA() )
953 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
954 for (
const auto &certconst : certconsts )
956 if ( certconst.known() == QCA::KeyCertificateSign )
961 else if ( certconst.known() == QCA::ServerAuth )
963 QgsDebugMsgLevel( u
"Certificate has 'server authentication' extended key usage"_s, 2 );
969 const QCA::CertificateCollection trustedCAs(
971 const QCA::CertificateCollection untrustedCAs(
975 v_any = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageAny, QCA::ValidateAll );
976 if ( v_any == QCA::ValidityGood )
981 QCA::Validity v_tlsserver;
982 v_tlsserver = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSServer, QCA::ValidateAll );
983 if ( v_tlsserver == QCA::ValidityGood )
993 QCA::Validity v_tlsclient;
994 v_tlsclient = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSClient, QCA::ValidateAll );
996 if ( v_tlsclient == QCA::ValidityGood )
1007bool QgsAuthCertUtils::certificateIsAuthority(
const QSslCertificate &cert )
1012bool QgsAuthCertUtils::certificateIsIssuer(
const QSslCertificate &cert )
1017bool QgsAuthCertUtils::certificateIsAuthorityOrIssuer(
const QSslCertificate &cert )
1019 return ( QgsAuthCertUtils::certificateIsAuthority( cert )
1020 || QgsAuthCertUtils::certificateIsIssuer( cert ) );
1023bool QgsAuthCertUtils::certificateIsSslServer(
const QSslCertificate &cert )
1030bool QgsAuthCertUtils::certificateIsSslServer(
const QSslCertificate &cert )
1039 QCA::ConvertResult res;
1040 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QString(
"qca-ossl" ) ) );
1041 if ( res != QCA::ConvertGood || qcacert.isNull() )
1043 QgsDebugError( u
"Certificate could not be converted to QCA cert"_s );
1047 if ( qcacert.isCA() )
1049 QgsDebugMsgLevel( u
"SSL server certificate has 'CA:TRUE' basic constraint (and should not)"_s, 2 );
1053 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
1054 for (
const auto & certconst, certconsts )
1056 if ( certconst.known() == QCA::KeyCertificateSign )
1058 QgsDebugError( u
"SSL server certificate has 'Certificate Sign' key usage (and should not)"_s );
1065 bool serverauth =
false;
1066 bool dsignature =
false;
1067 bool keyencrypt =
false;
1068 for (
const auto &certconst : certconsts )
1070 if ( certconst.known() == QCA::DigitalSignature )
1072 QgsDebugMsgLevel( u
"SSL server certificate has 'digital signature' key usage"_s, 2 );
1075 else if ( certconst.known() == QCA::KeyEncipherment )
1077 QgsDebugMsgLevel( u
"SSL server certificate has 'key encipherment' key usage"_s, 2 );
1080 else if ( certconst.known() == QCA::KeyAgreement )
1082 QgsDebugMsgLevel( u
"SSL server certificate has 'key agreement' key usage"_s, 2 );
1085 else if ( certconst.known() == QCA::ServerAuth )
1087 QgsDebugMsgLevel( u
"SSL server certificate has 'server authentication' extended key usage"_s, 2 );
1099 if ( serverauth && dsignature && keyencrypt )
1103 if ( dsignature && keyencrypt )
1109 bool keyagree =
false;
1110 bool encipheronly =
false;
1111 bool decipheronly =
false;
1113 QCA::PublicKey pubkey( qcacert.subjectPublicKey() );
1115 if ( pubkey.bitSize() > 0 && pubkey.isDH() )
1117 keyagree = pubkey.canKeyAgree();
1122 for (
const auto &certconst : certconsts )
1124 if ( certconst.known() == QCA::EncipherOnly )
1126 QgsDebugMsgLevel( u
"SSL server public key has 'encipher only' key usage"_s, 2 );
1127 encipheronly =
true;
1129 else if ( certconst.known() == QCA::DecipherOnly )
1131 QgsDebugMsgLevel( u
"SSL server public key has 'decipher only' key usage"_s, 2 );
1132 decipheronly =
true;
1135 if ( !encipheronly && !decipheronly )
1144bool QgsAuthCertUtils::certificateIsSslClient(
const QSslCertificate &cert )
1149QString QgsAuthCertUtils::sslErrorEnumString( QSslError::SslError errenum )
1153 case QSslError::UnableToGetIssuerCertificate:
1154 return QObject::tr(
"Unable to Get Issuer Certificate" );
1155 case QSslError::UnableToDecryptCertificateSignature:
1156 return QObject::tr(
"Unable to Decrypt Certificate Signature" );
1157 case QSslError::UnableToDecodeIssuerPublicKey:
1158 return QObject::tr(
"Unable to Decode Issuer Public Key" );
1159 case QSslError::CertificateSignatureFailed:
1160 return QObject::tr(
"Certificate Signature Failed" );
1161 case QSslError::CertificateNotYetValid:
1162 return QObject::tr(
"Certificate Not Yet Valid" );
1163 case QSslError::CertificateExpired:
1164 return QObject::tr(
"Certificate Expired" );
1165 case QSslError::InvalidNotBeforeField:
1166 return QObject::tr(
"Invalid Not Before Field" );
1167 case QSslError::InvalidNotAfterField:
1168 return QObject::tr(
"Invalid Not After Field" );
1169 case QSslError::SelfSignedCertificate:
1170 return QObject::tr(
"Self-signed Certificate" );
1171 case QSslError::SelfSignedCertificateInChain:
1172 return QObject::tr(
"Self-signed Certificate In Chain" );
1173 case QSslError::UnableToGetLocalIssuerCertificate:
1174 return QObject::tr(
"Unable to Get Local Issuer Certificate" );
1175 case QSslError::UnableToVerifyFirstCertificate:
1176 return QObject::tr(
"Unable to Verify First Certificate" );
1177 case QSslError::CertificateRevoked:
1178 return QObject::tr(
"Certificate Revoked" );
1179 case QSslError::InvalidCaCertificate:
1180 return QObject::tr(
"Invalid CA Certificate" );
1181 case QSslError::PathLengthExceeded:
1182 return QObject::tr(
"Path Length Exceeded" );
1183 case QSslError::InvalidPurpose:
1184 return QObject::tr(
"Invalid Purpose" );
1185 case QSslError::CertificateUntrusted:
1186 return QObject::tr(
"Certificate Untrusted" );
1187 case QSslError::CertificateRejected:
1188 return QObject::tr(
"Certificate Rejected" );
1189 case QSslError::SubjectIssuerMismatch:
1190 return QObject::tr(
"Subject Issuer Mismatch" );
1191 case QSslError::AuthorityIssuerSerialNumberMismatch:
1192 return QObject::tr(
"Authority Issuer Serial Number Mismatch" );
1193 case QSslError::NoPeerCertificate:
1194 return QObject::tr(
"No Peer Certificate" );
1195 case QSslError::HostNameMismatch:
1196 return QObject::tr(
"Host Name Mismatch" );
1197 case QSslError::UnspecifiedError:
1198 return QObject::tr(
"Unspecified Error" );
1199 case QSslError::CertificateBlacklisted:
1200 return QObject::tr(
"Certificate Blacklisted" );
1201 case QSslError::NoError:
1202 return QObject::tr(
"No Error" );
1203 case QSslError::NoSslSupport:
1204 return QObject::tr(
"No SSL Support" );
1210QList<QPair<QSslError::SslError, QString> > QgsAuthCertUtils::sslErrorEnumStrings()
1212 QList<QPair<QSslError::SslError, QString> > errenums;
1213 errenums << qMakePair( QSslError::UnableToGetIssuerCertificate,
1214 QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetIssuerCertificate ) );
1215 errenums << qMakePair( QSslError::UnableToDecryptCertificateSignature,
1216 QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecryptCertificateSignature ) );
1217 errenums << qMakePair( QSslError::UnableToDecodeIssuerPublicKey,
1218 QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToDecodeIssuerPublicKey ) );
1219 errenums << qMakePair( QSslError::CertificateSignatureFailed,
1220 QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateSignatureFailed ) );
1221 errenums << qMakePair( QSslError::CertificateNotYetValid,
1222 QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateNotYetValid ) );
1223 errenums << qMakePair( QSslError::CertificateExpired,
1224 QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateExpired ) );
1225 errenums << qMakePair( QSslError::InvalidNotBeforeField,
1226 QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotBeforeField ) );
1227 errenums << qMakePair( QSslError::InvalidNotAfterField,
1228 QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidNotAfterField ) );
1229 errenums << qMakePair( QSslError::SelfSignedCertificate,
1230 QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificate ) );
1231 errenums << qMakePair( QSslError::SelfSignedCertificateInChain,
1232 QgsAuthCertUtils::sslErrorEnumString( QSslError::SelfSignedCertificateInChain ) );
1233 errenums << qMakePair( QSslError::UnableToGetLocalIssuerCertificate,
1234 QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToGetLocalIssuerCertificate ) );
1235 errenums << qMakePair( QSslError::UnableToVerifyFirstCertificate,
1236 QgsAuthCertUtils::sslErrorEnumString( QSslError::UnableToVerifyFirstCertificate ) );
1237 errenums << qMakePair( QSslError::CertificateRevoked,
1238 QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRevoked ) );
1239 errenums << qMakePair( QSslError::InvalidCaCertificate,
1240 QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidCaCertificate ) );
1241 errenums << qMakePair( QSslError::PathLengthExceeded,
1242 QgsAuthCertUtils::sslErrorEnumString( QSslError::PathLengthExceeded ) );
1243 errenums << qMakePair( QSslError::InvalidPurpose,
1244 QgsAuthCertUtils::sslErrorEnumString( QSslError::InvalidPurpose ) );
1245 errenums << qMakePair( QSslError::CertificateUntrusted,
1246 QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateUntrusted ) );
1247 errenums << qMakePair( QSslError::CertificateRejected,
1248 QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateRejected ) );
1249 errenums << qMakePair( QSslError::SubjectIssuerMismatch,
1250 QgsAuthCertUtils::sslErrorEnumString( QSslError::SubjectIssuerMismatch ) );
1251 errenums << qMakePair( QSslError::AuthorityIssuerSerialNumberMismatch,
1252 QgsAuthCertUtils::sslErrorEnumString( QSslError::AuthorityIssuerSerialNumberMismatch ) );
1253 errenums << qMakePair( QSslError::NoPeerCertificate,
1254 QgsAuthCertUtils::sslErrorEnumString( QSslError::NoPeerCertificate ) );
1255 errenums << qMakePair( QSslError::HostNameMismatch,
1256 QgsAuthCertUtils::sslErrorEnumString( QSslError::HostNameMismatch ) );
1257 errenums << qMakePair( QSslError::UnspecifiedError,
1258 QgsAuthCertUtils::sslErrorEnumString( QSslError::UnspecifiedError ) );
1259 errenums << qMakePair( QSslError::CertificateBlacklisted,
1260 QgsAuthCertUtils::sslErrorEnumString( QSslError::CertificateBlacklisted ) );
1264bool QgsAuthCertUtils::certIsCurrent(
const QSslCertificate &cert )
1266 if ( cert.isNull() )
1268 const QDateTime currentTime = QDateTime::currentDateTime();
1269 return cert.effectiveDate() <= currentTime && cert.expiryDate() >= currentTime;
1272QList<QSslError> QgsAuthCertUtils::certViabilityErrors(
const QSslCertificate &cert )
1274 QList<QSslError> sslErrors;
1276 if ( cert.isNull() )
1279 const QDateTime currentTime = QDateTime::currentDateTime();
1280 if ( cert.expiryDate() <= currentTime )
1282 sslErrors << QSslError( QSslError::SslError::CertificateExpired, cert );
1284 if ( cert.effectiveDate() >= QDateTime::currentDateTime() )
1286 sslErrors << QSslError( QSslError::SslError::CertificateNotYetValid, cert );
1288 if ( cert.isBlacklisted() )
1290 sslErrors << QSslError( QSslError::SslError::CertificateBlacklisted, cert );
1296bool QgsAuthCertUtils::certIsViable(
const QSslCertificate &cert )
1298 return !cert.isNull() && QgsAuthCertUtils::certViabilityErrors( cert ).isEmpty();
1301QList<QSslError> QgsAuthCertUtils::validateCertChain(
const QList<QSslCertificate> &certificateChain,
1302 const QString &hostName,
1305 QList<QSslError> sslErrors;
1306 QList<QSslCertificate> trustedChain;
1308 for (
const auto &cert : certificateChain )
1310 bool untrusted =
false;
1313 if ( cert.digest( ) == untrustedCert.digest( ) )
1321 trustedChain << cert;
1326 const QList<QSslCertificate> constTrustedChain( trustedChain );
1327 for (
const auto &cert : constTrustedChain )
1329 sslErrors << QgsAuthCertUtils::certViabilityErrors( cert );
1333 if ( trustRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
1335 static QMutex sMutex;
1336 const QMutexLocker lock( &sMutex );
1337 const QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
1338 QSslConfiguration sslConfig( oldSslConfig );
1339 sslConfig.setCaCertificates( casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
1340 QSslConfiguration::setDefaultConfiguration( sslConfig );
1341 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1342 QSslConfiguration::setDefaultConfiguration( oldSslConfig );
1346 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1351QStringList QgsAuthCertUtils::validatePKIBundle(
QgsPkiBundle &bundle,
bool useIntermediates,
bool trustRootCa )
1355 errors << QObject::tr(
"Client certificate is NULL." );
1358 errors << QObject::tr(
"Client certificate key is NULL." );
1361 if ( !errors.isEmpty() )
1364 QList<QSslError> sslErrors;
1365 if ( useIntermediates )
1367 QList<QSslCertificate> certsList( bundle.
caChain() );
1369 sslErrors = QgsAuthCertUtils::validateCertChain( certsList, QString(), trustRootCa );
1373 sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.
clientCert() );
1375 const QList<QSslError> constSslErrors( sslErrors );
1376 for (
const auto &sslError : constSslErrors )
1378 if ( sslError.error() != QSslError::NoError )
1380 errors << sslError.errorString();
1384 const QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.
clientKey().toPem() ) );
1385 const QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.
clientCert().publicKey().toPem( ) ) );
1386 bool keyValid( ! pvtKey.isNull() );
1387 if ( keyValid && !( pubKey.toRSA().isNull( ) || pvtKey.toRSA().isNull( ) ) )
1389 keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
1391 else if ( keyValid && !( pubKey.toDSA().isNull( ) || pvtKey.toDSA().isNull( ) ) )
1393 keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
1397 QgsDebugError( u
"Key is not DSA, RSA: validation is not supported by QCA"_s );
1401 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)