23 #include <QSslCertificate> 40 case QSsl::SecureProtocols:
41 return QObject::tr(
"SecureProtocols" );
42 case QSsl::TlsV1SslV3:
43 return QObject::tr(
"TlsV1SslV3" );
45 return QObject::tr(
"TlsV1" );
47 return QObject::tr(
"SslV3" );
49 return QObject::tr(
"SslV2" );
57 QMap<QString, QSslCertificate> digestmap;
58 for (
const auto &cert : certs )
67 QMap< QString, QList<QSslCertificate> > orgcerts;
68 for (
const auto &cert : certs )
72 org = QStringLiteral(
"(Organization not defined)" );
73 QList<QSslCertificate> valist = orgcerts.contains( org ) ? orgcerts.value( org ) : QList<QSslCertificate>();
74 orgcerts.insert( org, valist << cert );
81 QMap<QString, QgsAuthConfigSslServer> digestmap;
82 for (
const auto &config : configs )
84 digestmap.insert(
shaHexForCert( config.sslCertificate() ), config );
91 QMap< QString, QList<QgsAuthConfigSslServer> > orgconfigs;
92 for (
const auto &config : configs )
94 QString org(
SSL_SUBJECT_INFO( config.sslCertificate(), QSslCertificate::Organization ) );
97 org = QObject::tr(
"(Organization not defined)" );
98 QList<QgsAuthConfigSslServer> valist = orgconfigs.contains( org ) ? orgconfigs.value( org ) : QList<QgsAuthConfigSslServer>();
99 orgconfigs.insert( org, valist << config );
108 if ( !file.exists() )
110 QgsDebugMsg( QStringLiteral(
"Read file error, file not found: %1" ).arg( path ) );
114 QFile::OpenMode openflags( QIODevice::ReadOnly );
115 bool ret = file.open( openflags );
118 data = file.readAll();
127 QList<QSslCertificate> certs;
129 certs = QSslCertificate::fromData( payload, sniffEncoding( payload ) );
130 if ( certs.isEmpty() )
132 QgsDebugMsg( QStringLiteral(
"Parsed cert(s) EMPTY for path: %1" ).arg( certspath ) );
139 QList<QSslCertificate> cas;
140 const QList<QSslCertificate> certs(
certsFromFile( certspath ) );
141 for (
const auto &cert : certs )
154 QList<QSslCertificate> result( bundle1 );
155 const QList<QSslCertificate> c_bundle1( bundle1 );
156 for (
const auto &cert : c_bundle1 )
160 const QList<QSslCertificate> c_bundle2( bundle2 );
161 for (
const auto &cert : c_bundle2 )
165 result.append( cert );
175 QSslCertificate cert;
177 if ( !certs.isEmpty() )
179 cert = certs.first();
183 QgsDebugMsg( QStringLiteral(
"Parsed cert is NULL for path: %1" ).arg( certpath ) );
189 const QString &keypass,
196 QSsl::EncodingFormat keyEncoding( sniffEncoding( keydata ) );
198 const std::vector<QSsl::KeyAlgorithm> algs
200 QSsl::KeyAlgorithm::Rsa,
201 QSsl::KeyAlgorithm::Dsa,
202 QSsl::KeyAlgorithm::Ec,
203 QSsl::KeyAlgorithm::Opaque
206 for (
const auto &alg : algs )
208 clientkey = QSslKey( keydata,
212 !keypass.isEmpty() ? keypass.toUtf8() : QByteArray() );
213 if ( ! clientkey.isNull() )
219 case QSsl::KeyAlgorithm::Rsa:
220 *algtype = QStringLiteral(
"rsa" );
222 case QSsl::KeyAlgorithm::Dsa:
223 *algtype = QStringLiteral(
"dsa" );
225 case QSsl::KeyAlgorithm::Ec:
226 *algtype = QStringLiteral(
"ec" );
228 case QSsl::KeyAlgorithm::Opaque:
229 *algtype = QStringLiteral(
"opaque" );
231 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) 232 case QSsl::KeyAlgorithm::Dh:
233 *algtype = QStringLiteral(
"dh" );
246 QList<QSslCertificate> certs;
247 certs = QSslCertificate::fromData( pemtext.toLatin1(), QSsl::Pem );
248 if ( certs.isEmpty() )
250 QgsDebugMsg( QStringLiteral(
"Parsed cert(s) EMPTY" ) );
257 QList<QSslCertificate> certs;
258 for (
const auto &cert : caList )
260 if ( ! cert.isSelfSigned( ) )
262 certs.append( cert );
269 const QString &keypath,
270 const QString &keypass,
275 if ( !clientcert.isNull() )
277 certpem = QString( clientcert.toPem() );
285 if ( !clientkey.isNull() )
287 keypem = QString( clientkey.toPem( ( reencrypt && !keypass.isEmpty() ) ? keypass.toUtf8() : QByteArray() ) );
290 return QStringList() << certpem << keypem << algtype;
295 QString pkcs8Header = QStringLiteral(
"-----BEGIN PRIVATE KEY-----" );
296 QString pkcs8Footer = QStringLiteral(
"-----END PRIVATE KEY-----" );
297 return keyPemTxt.contains( pkcs8Header ) && keyPemTxt.contains( pkcs8Footer );
301 QByteArray QgsAuthCertUtils::pkcs8PrivateKey( QByteArray &pkcs8Der )
305 if ( pkcs8Der.isEmpty() )
307 QgsDebugMsg( QStringLiteral(
"ERROR, passed DER is empty" ) );
314 if ( ! asnDefsRsrc.exists() )
316 QgsDebugMsg( QStringLiteral(
"ERROR, pkcs.asn resource file not found: %1" ).arg( asnDefsRsrc.filePath() ) );
319 const char *asnDefsFile = asnDefsRsrc.absoluteFilePath().toLocal8Bit().constData();
321 int asn1_result = ASN1_SUCCESS, der_len = 0, oct_len = 0;
322 asn1_node definitions = NULL, structure = NULL;
323 char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE], oct_data[1024];
324 unsigned char *der = NULL;
325 unsigned int flags = 0;
329 QString
typeName( QStringLiteral(
"PKCS-8.PrivateKeyInfo" ) );
331 asn1_result = asn1_parser2tree( asnDefsFile, &definitions, errorDescription );
333 switch ( asn1_result )
338 case ASN1_FILE_NOT_FOUND:
339 QgsDebugMsg( QStringLiteral(
"ERROR, file not found: %1" ).arg( asnDefsFile ) );
341 case ASN1_SYNTAX_ERROR:
342 case ASN1_IDENTIFIER_NOT_FOUND:
343 case ASN1_NAME_TOO_LONG:
344 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 parsing: %1" ).arg( errorDescription ) );
347 QgsDebugMsg( QStringLiteral(
"ERROR, libtasn1: %1" ).arg( asn1_strerror( asn1_result ) ) );
352 asn1_result = asn1_create_element( definitions, typeName.toLatin1().constData(), &structure );
356 if ( asn1_result != ASN1_SUCCESS )
358 QgsDebugMsg( QStringLiteral(
"ERROR, structure creation: %1" ).arg( asn1_strerror( asn1_result ) ) );
363 der =
reinterpret_cast<unsigned char *
>( pkcs8Der.data() );
364 der_len = pkcs8Der.size();
368 asn1_result = asn1_der_decoding2( &structure, der, &der_len, flags, errorDescription );
372 asn1_result = asn1_der_decoding( &structure, der, der_len, errorDescription );
375 if ( asn1_result != ASN1_SUCCESS )
377 QgsDebugMsg( QStringLiteral(
"ERROR, decoding: %1" ).arg( errorDescription ) );
382 QgsDebugMsgLevel( QStringLiteral(
"Decoding: %1" ).arg( asn1_strerror( asn1_result ) ), 4 );
387 QgsDebugMsg( QStringLiteral(
"DECODING RESULT:" ) );
388 asn1_print_structure( stdout, structure,
"", ASN1_PRINT_NAME_TYPE_VALUE );
393 typeName.append( QStringLiteral(
".privateKey" ) );
394 QgsDebugMsgLevel( QStringLiteral(
"privateKey element name: %1" ).arg( typeName ), 4 );
396 asn1_result = asn1_read_value_type( structure,
"privateKey", NULL, &oct_len, &oct_etype );
398 if ( asn1_result != ASN1_MEM_ERROR )
400 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 read privateKey value type: %1" ).arg( asn1_strerror( asn1_result ) ) );
404 if ( oct_etype != ASN1_ETYPE_OCTET_STRING )
406 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey value not octet string, but type: %1" ).arg( static_cast<int>( oct_etype ) ) );
412 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey octet string empty" ) );
417 asn1_result = asn1_read_value( structure,
"privateKey", oct_data, &oct_len );
419 if ( asn1_result != ASN1_SUCCESS )
421 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 read privateKey value: %1" ).arg( asn1_strerror( asn1_result ) ) );
427 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey value octet string empty" ) );
431 pkcs1 = QByteArray( oct_data, oct_len );
438 asn1_delete_structure( &structure );
444 const QString &bundlepass,
448 if ( !QCA::isSupported(
"pkcs12" ) )
450 QgsDebugMsg( QStringLiteral(
"QCA does not support PKCS#12" ) );
455 if ( bundle.isNull() )
457 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 file to QCA key bundle: %1" ).arg( bundlepath ) );
461 QCA::SecureArray passarray;
462 if ( reencrypt && !bundlepass.isEmpty() )
464 passarray = QCA::SecureArray( bundlepass.toUtf8() );
468 QSsl::KeyAlgorithm keyalg = QSsl::Opaque;
469 if ( bundle.privateKey().isRSA() )
471 algtype = QStringLiteral(
"rsa" );
474 else if ( bundle.privateKey().isDSA() )
476 algtype = QStringLiteral(
"dsa" );
479 else if ( bundle.privateKey().isDH() )
481 algtype = QStringLiteral(
"dh" );
486 if ( keyalg == QSsl::Opaque )
488 QgsDebugMsg( QStringLiteral(
"FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1" ).arg( bundlepath ) );
496 QgsDebugMsgLevel( QStringLiteral(
"Private key is PKCS#8: attempting conversion to PKCS#1..." ), 4 );
501 QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
502 if ( pkcs8Der.isEmpty() )
504 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key to DER-encoded format: %1" ).arg( bundlepath ) );
508 QByteArray pkcs1Der = QgsAuthCertUtils::pkcs8PrivateKey( pkcs8Der );
509 if ( pkcs1Der.isEmpty() )
511 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1: %1" ).arg( bundlepath ) );
515 QSslKey pkcs1Key( pkcs1Der, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey );
516 if ( pkcs1Key.isNull() )
518 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1 QSslKey: %1" ).arg( bundlepath ) );
521 keyPem = QString( pkcs1Key.toPem( passarray.toByteArray() ) );
525 keyPem = bundle.privateKey().toPEM( passarray );
528 keyPem = bundle.privateKey().toPEM( passarray );
531 QgsDebugMsgLevel( QStringLiteral(
"PKCS#12 cert as PEM:\n%1" ).arg( QString( bundle.certificateChain().primary().toPEM() ) ), 4 );
535 return QStringList() << bundle.certificateChain().primary().toPEM() << keyPem << algtype;
540 QList<QSslCertificate> result;
541 if ( !QCA::isSupported(
"pkcs12" ) )
545 if ( bundle.isNull() )
548 const QCA::CertificateChain chain( bundle.certificateChain() );
549 for (
const auto &cert : chain )
553 result.append( QSslCertificate::fromData( cert.toPEM().toLatin1() ) );
562 if ( !certs.isEmpty() )
564 QStringList certslist;
565 for (
const auto &cert : certs )
567 certslist << cert.toPem();
569 capem = certslist.join( QStringLiteral(
"\n" ) ).toLatin1();
576 QFile pemFile( QDir::tempPath() + QDir::separator() + name );
577 QString pemFilePath( pemFile.fileName() );
579 if ( pemFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
581 qint64 bytesWritten = pemFile.write( pemtext );
582 if ( bytesWritten == -1 )
584 QgsDebugMsg( QStringLiteral(
"FAILED to write to temp PEM file: %1" ).arg( pemFilePath ) );
590 QgsDebugMsg( QStringLiteral(
"FAILED to open writing for temp PEM file: %1" ).arg( pemFilePath ) );
594 if ( !pemFile.setPermissions( QFile::ReadUser ) )
596 QgsDebugMsg( QStringLiteral(
"FAILED to set permissions on temp PEM file: %1" ).arg( pemFilePath ) );
608 return single ? QObject::tr(
"System Root CA" ) : QObject::tr(
"System Root Authorities" );
610 return single ? QObject::tr(
"File CA" ) : QObject::tr(
"Authorities from File" );
612 return single ? QObject::tr(
"Database CA" ) : QObject::tr(
"Authorities in Database" );
614 return single ? QObject::tr(
"Connection CA" ) : QObject::tr(
"Authorities from connection" );
622 QString name( issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::CommonName )
625 if ( name.isEmpty() )
626 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::OrganizationalUnitName )
629 if ( name.isEmpty() )
633 if ( name.isEmpty() )
637 if ( name.isEmpty() )
638 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::StateOrProvinceName )
641 if ( name.isEmpty() )
649 void QgsAuthCertUtils::appendDirSegment_( QStringList &dirname,
650 const QString &segment, QString value )
652 if ( !value.isEmpty() )
654 dirname.append( segment +
'=' + value.replace(
',', QLatin1String(
"\\," ) ) );
658 QSsl::EncodingFormat QgsAuthCertUtils::sniffEncoding(
const QByteArray &payload )
660 return payload.contains( QByteArrayLiteral(
"-----BEGIN " ) ) ?
666 const QCA::Certificate &acert,
672 if ( acert.isNull() )
674 QCA::ConvertResult res;
675 QCA::Certificate acert( QCA::Certificate::fromPEM( qcert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
676 if ( res != QCA::ConvertGood || acert.isNull() )
678 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
690 QgsAuthCertUtils::appendDirSegment_(
691 dirname, QStringLiteral(
"E" ), issuer ? acert.issuerInfo().value( QCA::Email )
692 : acert.subjectInfo().value( QCA::Email ) );
693 QgsAuthCertUtils::appendDirSegment_(
694 dirname, QStringLiteral(
"CN" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CommonName )
696 QgsAuthCertUtils::appendDirSegment_(
697 dirname, QStringLiteral(
"OU" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::OrganizationalUnitName )
699 QgsAuthCertUtils::appendDirSegment_(
700 dirname, QStringLiteral(
"O" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::Organization )
702 QgsAuthCertUtils::appendDirSegment_(
703 dirname, QStringLiteral(
"L" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::LocalityName )
705 QgsAuthCertUtils::appendDirSegment_(
706 dirname, QStringLiteral(
"ST" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::StateOrProvinceName )
708 QgsAuthCertUtils::appendDirSegment_(
709 dirname, QStringLiteral(
"C" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CountryName )
712 return dirname.join( QStringLiteral(
"," ) );
720 return QObject::tr(
"Default" );
722 return QObject::tr(
"Trusted" );
724 return QObject::tr(
"Untrusted" );
735 sl.reserve( txt.size() );
736 for (
int i = 0; i < txt.size(); i += 2 )
738 sl << txt.mid( i, ( i + 2 > txt.size() ) ? -1 : 2 );
740 return sl.join( QStringLiteral(
":" ) );
745 QString sha( cert.digest( QCryptographicHash::Sha1 ).toHex() );
756 return QCA::Certificate();
758 QCA::ConvertResult res;
759 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
760 if ( res != QCA::ConvertGood || qcacert.isNull() )
762 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
763 qcacert = QCA::Certificate();
770 QCA::CertificateCollection qcacoll;
774 for (
const auto &cert : certs )
777 if ( !qcacert.isNull() )
779 qcacoll.addCertificate( qcacert );
787 QCA::SecureArray passarray;
788 if ( !pass.isEmpty() )
789 passarray = QCA::SecureArray( pass.toUtf8() );
791 QCA::ConvertResult res;
792 QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( path, passarray, &res, QStringLiteral(
"qca-ossl" ) ) );
794 return ( res == QCA::ConvertGood ? bundle : QCA::KeyBundle() );
801 case QCA::ValidityGood:
802 return QObject::tr(
"Certificate is valid." );
803 case QCA::ErrorRejected:
804 return QObject::tr(
"Root CA rejected the certificate purpose." );
805 case QCA::ErrorUntrusted:
806 return QObject::tr(
"Certificate is not trusted." );
807 case QCA::ErrorSignatureFailed:
808 return QObject::tr(
"Signature does not match." );
809 case QCA::ErrorInvalidCA:
810 return QObject::tr(
"Certificate Authority is invalid or not found." );
811 case QCA::ErrorInvalidPurpose:
812 return QObject::tr(
"Purpose does not match the intended usage." );
813 case QCA::ErrorSelfSigned:
814 return QObject::tr(
"Certificate is self-signed, and is not found in the list of trusted certificates." );
815 case QCA::ErrorRevoked:
816 return QObject::tr(
"Certificate has been revoked." );
817 case QCA::ErrorPathLengthExceeded:
818 return QObject::tr(
"Path length from the root CA to this certificate is too long." );
819 case QCA::ErrorExpired:
820 return QObject::tr(
"Certificate has expired or is not yet valid." );
821 case QCA::ErrorExpiredCA:
822 return QObject::tr(
"Certificate Authority has expired." );
823 case QCA::ErrorValidityUnknown:
824 return QObject::tr(
"Validity is unknown." );
834 case QCA::EMSA1_SHA1:
835 return QObject::tr(
"SHA1, with EMSA1" );
836 case QCA::EMSA3_SHA1:
837 return QObject::tr(
"SHA1, with EMSA3" );
839 return QObject::tr(
"MD5, with EMSA3" );
841 return QObject::tr(
"MD2, with EMSA3" );
842 case QCA::EMSA3_RIPEMD160:
843 return QObject::tr(
"RIPEMD160, with EMSA3" );
845 return QObject::tr(
"EMSA3, without digest" );
846 #if QCA_VERSION >= 0x020100 847 case QCA::EMSA3_SHA224:
848 return QObject::tr(
"SHA224, with EMSA3" );
849 case QCA::EMSA3_SHA256:
850 return QObject::tr(
"SHA256, with EMSA3" );
851 case QCA::EMSA3_SHA384:
852 return QObject::tr(
"SHA384, with EMSA3" );
853 case QCA::EMSA3_SHA512:
854 return QObject::tr(
"SHA512, with EMSA3" );
857 return QObject::tr(
"Unknown (possibly Elliptic Curve)" );
863 switch ( constraint )
865 case QCA::DigitalSignature:
866 return QObject::tr(
"Digital Signature" );
867 case QCA::NonRepudiation:
868 return QObject::tr(
"Non-repudiation" );
869 case QCA::KeyEncipherment:
870 return QObject::tr(
"Key Encipherment" );
871 case QCA::DataEncipherment:
872 return QObject::tr(
"Data Encipherment" );
873 case QCA::KeyAgreement:
874 return QObject::tr(
"Key Agreement" );
875 case QCA::KeyCertificateSign:
876 return QObject::tr(
"Key Certificate Sign" );
878 return QObject::tr(
"CRL Sign" );
879 case QCA::EncipherOnly:
880 return QObject::tr(
"Encipher Only" );
881 case QCA::DecipherOnly:
882 return QObject::tr(
"Decipher Only" );
883 case QCA::ServerAuth:
884 return QObject::tr(
"Server Authentication" );
885 case QCA::ClientAuth:
886 return QObject::tr(
"Client Authentication" );
887 case QCA::CodeSigning:
888 return QObject::tr(
"Code Signing" );
889 case QCA::EmailProtection:
890 return QObject::tr(
"Email Protection" );
891 case QCA::IPSecEndSystem:
892 return QObject::tr(
"IPSec Endpoint" );
893 case QCA::IPSecTunnel:
894 return QObject::tr(
"IPSec Tunnel" );
896 return QObject::tr(
"IPSec User" );
897 case QCA::TimeStamping:
898 return QObject::tr(
"Time Stamping" );
899 case QCA::OCSPSigning:
900 return QObject::tr(
"OCSP Signing" );
911 return QObject::tr(
"Any or unspecified" );
913 return QObject::tr(
"Certificate Authority" );
915 return QObject::tr(
"Certificate Issuer" );
917 return QObject::tr(
"TLS/SSL Server" );
919 return QObject::tr(
"TLS/SSL Server EV" );
921 return QObject::tr(
"TLS/SSL Client" );
923 return QObject::tr(
"Code Signing" );
925 return QObject::tr(
"Email Protection" );
927 return QObject::tr(
"Time Stamping" );
929 return QObject::tr(
"CRL Signing" );
932 return QObject::tr(
"Undetermined usage" );
938 QList<QgsAuthCertUtils::CertUsageType> usages;
943 QCA::ConvertResult res;
944 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
945 if ( res != QCA::ConvertGood || qcacert.isNull() )
947 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
951 if ( qcacert.isCA() )
953 QgsDebugMsg( QStringLiteral(
"Certificate has 'CA:TRUE' basic constraint" ) );
957 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
958 for (
const auto &certconst : certconsts )
960 if ( certconst.known() == QCA::KeyCertificateSign )
962 QgsDebugMsg( QStringLiteral(
"Certificate has 'Certificate Sign' key usage" ) );
965 else if ( certconst.known() == QCA::ServerAuth )
967 QgsDebugMsg( QStringLiteral(
"Certificate has 'server authentication' extended key usage" ) );
973 QCA::CertificateCollection trustedCAs(
975 QCA::CertificateCollection untrustedCAs(
979 v_any = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageAny, QCA::ValidateAll );
980 if ( v_any == QCA::ValidityGood )
985 QCA::Validity v_tlsserver;
986 v_tlsserver = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSServer, QCA::ValidateAll );
987 if ( v_tlsserver == QCA::ValidityGood )
997 QCA::Validity v_tlsclient;
998 v_tlsclient = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSClient, QCA::ValidateAll );
1000 if ( v_tlsclient == QCA::ValidityGood )
1043 QCA::ConvertResult res;
1044 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QString(
"qca-ossl" ) ) );
1045 if ( res != QCA::ConvertGood || qcacert.isNull() )
1047 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
1051 if ( qcacert.isCA() )
1053 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'CA:TRUE' basic constraint (and should not)" ) );
1057 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
1058 for (
const auto & certconst, certconsts )
1060 if ( certconst.known() == QCA::KeyCertificateSign )
1062 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'Certificate Sign' key usage (and should not)" ) );
1069 bool serverauth =
false;
1070 bool dsignature =
false;
1071 bool keyencrypt =
false;
1072 for (
const auto &certconst : certconsts )
1074 if ( certconst.known() == QCA::DigitalSignature )
1076 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'digital signature' key usage" ) );
1079 else if ( certconst.known() == QCA::KeyEncipherment )
1081 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'key encipherment' key usage" ) );
1084 else if ( certconst.known() == QCA::KeyAgreement )
1086 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'key agreement' key usage" ) );
1089 else if ( certconst.known() == QCA::ServerAuth )
1091 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'server authentication' extended key usage" ) );
1103 if ( serverauth && dsignature && keyencrypt )
1107 if ( dsignature && keyencrypt )
1113 bool keyagree =
false;
1114 bool encipheronly =
false;
1115 bool decipheronly =
false;
1117 QCA::PublicKey pubkey( qcacert.subjectPublicKey() );
1119 if ( pubkey.bitSize() > 0 && pubkey.isDH() )
1121 keyagree = pubkey.canKeyAgree();
1126 for (
const auto &certconst : certconsts )
1128 if ( certconst.known() == QCA::EncipherOnly )
1130 QgsDebugMsg( QStringLiteral(
"SSL server public key has 'encipher only' key usage" ) );
1131 encipheronly =
true;
1133 else if ( certconst.known() == QCA::DecipherOnly )
1135 QgsDebugMsg( QStringLiteral(
"SSL server public key has 'decipher only' key usage" ) );
1136 decipheronly =
true;
1139 if ( !encipheronly && !decipheronly )
1157 case QSslError::UnableToGetIssuerCertificate:
1158 return QObject::tr(
"Unable to Get Issuer Certificate" );
1159 case QSslError::UnableToDecryptCertificateSignature:
1160 return QObject::tr(
"Unable to Decrypt Certificate Signature" );
1161 case QSslError::UnableToDecodeIssuerPublicKey:
1162 return QObject::tr(
"Unable to Decode Issuer Public Key" );
1163 case QSslError::CertificateSignatureFailed:
1164 return QObject::tr(
"Certificate Signature Failed" );
1165 case QSslError::CertificateNotYetValid:
1166 return QObject::tr(
"Certificate Not Yet Valid" );
1167 case QSslError::CertificateExpired:
1168 return QObject::tr(
"Certificate Expired" );
1169 case QSslError::InvalidNotBeforeField:
1170 return QObject::tr(
"Invalid Not Before Field" );
1171 case QSslError::InvalidNotAfterField:
1172 return QObject::tr(
"Invalid Not After Field" );
1173 case QSslError::SelfSignedCertificate:
1174 return QObject::tr(
"Self-signed Certificate" );
1175 case QSslError::SelfSignedCertificateInChain:
1176 return QObject::tr(
"Self-signed Certificate In Chain" );
1177 case QSslError::UnableToGetLocalIssuerCertificate:
1178 return QObject::tr(
"Unable to Get Local Issuer Certificate" );
1179 case QSslError::UnableToVerifyFirstCertificate:
1180 return QObject::tr(
"Unable to Verify First Certificate" );
1181 case QSslError::CertificateRevoked:
1182 return QObject::tr(
"Certificate Revoked" );
1183 case QSslError::InvalidCaCertificate:
1184 return QObject::tr(
"Invalid CA Certificate" );
1185 case QSslError::PathLengthExceeded:
1186 return QObject::tr(
"Path Length Exceeded" );
1187 case QSslError::InvalidPurpose:
1188 return QObject::tr(
"Invalid Purpose" );
1189 case QSslError::CertificateUntrusted:
1190 return QObject::tr(
"Certificate Untrusted" );
1191 case QSslError::CertificateRejected:
1192 return QObject::tr(
"Certificate Rejected" );
1193 case QSslError::SubjectIssuerMismatch:
1194 return QObject::tr(
"Subject Issuer Mismatch" );
1195 case QSslError::AuthorityIssuerSerialNumberMismatch:
1196 return QObject::tr(
"Authority Issuer Serial Number Mismatch" );
1197 case QSslError::NoPeerCertificate:
1198 return QObject::tr(
"No Peer Certificate" );
1199 case QSslError::HostNameMismatch:
1200 return QObject::tr(
"Host Name Mismatch" );
1201 case QSslError::UnspecifiedError:
1202 return QObject::tr(
"Unspecified Error" );
1203 case QSslError::CertificateBlacklisted:
1204 return QObject::tr(
"Certificate Blacklisted" );
1205 case QSslError::NoError:
1206 return QObject::tr(
"No Error" );
1207 case QSslError::NoSslSupport:
1208 return QObject::tr(
"No SSL Support" );
1216 QList<QPair<QSslError::SslError, QString> > errenums;
1217 errenums << qMakePair( QSslError::UnableToGetIssuerCertificate,
1219 errenums << qMakePair( QSslError::UnableToDecryptCertificateSignature,
1221 errenums << qMakePair( QSslError::UnableToDecodeIssuerPublicKey,
1223 errenums << qMakePair( QSslError::CertificateSignatureFailed,
1225 errenums << qMakePair( QSslError::CertificateNotYetValid,
1227 errenums << qMakePair( QSslError::CertificateExpired,
1229 errenums << qMakePair( QSslError::InvalidNotBeforeField,
1231 errenums << qMakePair( QSslError::InvalidNotAfterField,
1233 errenums << qMakePair( QSslError::SelfSignedCertificate,
1235 errenums << qMakePair( QSslError::SelfSignedCertificateInChain,
1237 errenums << qMakePair( QSslError::UnableToGetLocalIssuerCertificate,
1239 errenums << qMakePair( QSslError::UnableToVerifyFirstCertificate,
1241 errenums << qMakePair( QSslError::CertificateRevoked,
1243 errenums << qMakePair( QSslError::InvalidCaCertificate,
1245 errenums << qMakePair( QSslError::PathLengthExceeded,
1247 errenums << qMakePair( QSslError::InvalidPurpose,
1249 errenums << qMakePair( QSslError::CertificateUntrusted,
1251 errenums << qMakePair( QSslError::CertificateRejected,
1253 errenums << qMakePair( QSslError::SubjectIssuerMismatch,
1255 errenums << qMakePair( QSslError::AuthorityIssuerSerialNumberMismatch,
1257 errenums << qMakePair( QSslError::NoPeerCertificate,
1259 errenums << qMakePair( QSslError::HostNameMismatch,
1261 errenums << qMakePair( QSslError::UnspecifiedError,
1263 errenums << qMakePair( QSslError::CertificateBlacklisted,
1270 if ( cert.isNull() )
1272 const QDateTime currentTime = QDateTime::currentDateTime();
1273 return cert.effectiveDate() <= currentTime && cert.expiryDate() >= currentTime;
1278 QList<QSslError> sslErrors;
1280 if ( cert.isNull() )
1283 const QDateTime currentTime = QDateTime::currentDateTime();
1284 if ( cert.expiryDate() <= currentTime )
1286 sslErrors << QSslError( QSslError::SslError::CertificateExpired, cert );
1288 if ( cert.effectiveDate() >= QDateTime::currentDateTime() )
1290 sslErrors << QSslError( QSslError::SslError::CertificateNotYetValid, cert );
1292 if ( cert.isBlacklisted() )
1294 sslErrors << QSslError( QSslError::SslError::CertificateBlacklisted, cert );
1306 const QString &hostName,
1309 QList<QSslError> sslErrors;
1310 QList<QSslCertificate> trustedChain;
1312 for (
const auto &cert : certificateChain )
1314 bool untrusted =
false;
1317 if ( cert.digest( ) == untrustedCert.digest( ) )
1325 trustedChain << cert;
1330 const QList<QSslCertificate> constTrustedChain( trustedChain );
1331 for (
const auto &cert : constTrustedChain )
1337 if ( trustRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
1339 static QMutex sMutex;
1340 QMutexLocker lock( &sMutex );
1341 QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
1342 QSslConfiguration sslConfig( oldSslConfig );
1343 sslConfig.setCaCertificates(
casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
1344 QSslConfiguration::setDefaultConfiguration( sslConfig );
1345 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1346 QSslConfiguration::setDefaultConfiguration( oldSslConfig );
1350 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1359 errors << QObject::tr(
"Client certificate is NULL." );
1362 errors << QObject::tr(
"Client certificate key is NULL." );
1365 if ( !errors.isEmpty() )
1368 QList<QSslError> sslErrors;
1369 if ( useIntermediates )
1371 QList<QSslCertificate> certsList( bundle.
caChain() );
1377 sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.
clientCert() );
1379 const QList<QSslError> constSslErrors( sslErrors );
1380 for (
const auto &sslError : constSslErrors )
1382 if ( sslError.error() != QSslError::NoError )
1384 errors << sslError.errorString();
1388 QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.
clientKey().toPem() ) );
1389 QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.
clientCert().publicKey().toPem( ) ) );
1390 bool keyValid( ! pvtKey.isNull() );
1391 if ( keyValid && !( pubKey.toRSA().isNull( ) || pvtKey.toRSA().isNull( ) ) )
1393 keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
1395 else if ( keyValid && !( pubKey.toDSA().isNull( ) || pvtKey.toDSA().isNull( ) ) )
1397 keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
1401 QgsDebugMsg( QStringLiteral(
"Key is not DSA, RSA: validation is not supported by QCA" ) );
1405 errors << QObject::tr(
"Private key does not match client certificate public key." );
static bool pemIsPkcs8(const QString &keyPemTxt)
Determine if the PEM-encoded text of a key is PKCS#8 format.
static QString certificateUsageTypeString(QgsAuthCertUtils::CertUsageType usagetype)
Certificate usage type strings per enum.
static QList< QSslCertificate > casMerge(const QList< QSslCertificate > &bundle1, const QList< QSslCertificate > &bundle2)
casMerge merges two certificate bundles in a single one removing duplicates, the certificates from th...
static QString pemTextToTempFile(const QString &name, const QByteArray &pemtext)
Write a temporary file for a PEM text of cert/key/CAs bundle component.
static QMap< QString, QgsAuthConfigSslServer > mapDigestToSslConfigs(const QList< QgsAuthConfigSslServer > &configs)
Map SSL custom configs' certificate sha1 to custom config as simple cache.
static bool certificateIsIssuer(const QSslCertificate &cert)
Gets whether a certificate can sign other certificates.
static QString qcaKnownConstraint(QCA::ConstraintTypeKnown constraint)
Certificate well-known constraint strings per enum.
static QSslKey keyFromFile(const QString &keypath, const QString &keypass=QString(), QString *algtype=nullptr)
Returns non-encrypted key from a PEM or DER formatted file.
static QList< QSslCertificate > casFromFile(const QString &certspath)
Returns a list of concatenated CAs from a PEM or DER formatted file.
static QString sslErrorEnumString(QSslError::SslError errenum)
Gets short strings describing an SSL error.
static QList< QSslCertificate > certsFromFile(const QString &certspath)
Returns a list of concatenated certs from a PEM or DER formatted file.
static QStringList validatePKIBundle(QgsPkiBundle &bundle, bool useIntermediates=true, bool trustRootCa=false)
validatePKIBundle validate the PKI bundle by checking the certificate chain, the expiration and effec...
static QMap< QString, QSslCertificate > mapDigestToCerts(const QList< QSslCertificate > &certs)
Map certificate sha1 to certificate as simple cache.
static bool certificateIsAuthorityOrIssuer(const QSslCertificate &cert)
Gets whether a certificate is an Authority or can at least sign other certificates.
static QCA::CertificateCollection qtCertsToQcaCollection(const QList< QSslCertificate > &certs)
Convert a QList of QSslCertificate to a QCA::CertificateCollection.
const QSslCertificate clientCert() const
Client certificate object.
CertUsageType
Type of certificate usage.
static QCA::Certificate qtCertToQcaCert(const QSslCertificate &cert)
Convert a QSslCertificate to a QCA::Certificate.
static QList< QgsAuthCertUtils::CertUsageType > certificateUsageTypes(const QSslCertificate &cert)
Try to determine the certificates usage types.
Storage set for PKI bundle: SSL certificate, key, optional CA cert chain.
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
static QStringList pkcs12BundleToPem(const QString &bundlepath, const QString &bundlepass=QString(), bool reencrypt=true)
Returns list of certificate, private key and algorithm (as PEM text) for a PKCS#12 bundle...
static bool certificateIsAuthority(const QSslCertificate &cert)
Gets whether a certificate is an Authority.
static QByteArray fileData(const QString &path)
Returns data from a local file via a read-only operation.
static QList< QSslCertificate > casRemoveSelfSigned(const QList< QSslCertificate > &caList)
casRemoveSelfSigned remove self-signed CA certificates from caList
#define QgsDebugMsgLevel(str, level)
const QSslKey clientKey() const
Private key object.
static QStringList certKeyBundleToPem(const QString &certpath, const QString &keypath, const QString &keypass=QString(), bool reencrypt=true)
Returns list of certificate, private key and algorithm (as PEM text) from file path components...
static QString getCertDistinguishedName(const QSslCertificate &qcert, const QCA::Certificate &acert=QCA::Certificate(), bool issuer=false)
Gets combined distinguished name for certificate.
static QList< QSslError > certViabilityErrors(const QSslCertificate &cert)
certViabilityErrors checks basic characteristics (validity dates, blacklisting, etc.) of given cert
static QMap< QString, QList< QgsAuthConfigSslServer > > sslConfigsGroupedByOrg(const QList< QgsAuthConfigSslServer > &configs)
Map SSL custom configs' certificates to their oraganization.
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
static bool certificateIsSslServer(const QSslCertificate &cert)
Gets whether a certificate is probably used for a SSL server.
static QString pkgDataPath()
Returns the common root path of all application data directories.
static QList< QSslCertificate > certsFromString(const QString &pemtext)
Returns a list of concatenated certs from a PEM Base64 text block.
static QList< QSslError > validateCertChain(const QList< QSslCertificate > &certificateChain, const QString &hostName=QString(), bool trustRootCa=false)
validateCertChain validates the given certificateChain
static QString getSslProtocolName(QSsl::SslProtocol protocol)
SSL Protocol name strings per enum.
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
static bool certificateIsSslClient(const QSslCertificate &cert)
Gets whether a certificate is probably used for a client identity.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Gets the sha1 hash for certificate.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static QList< QSslCertificate > pkcs12BundleCas(const QString &bundlepath, const QString &bundlepass=QString())
Returns list of CA certificates (as QSslCertificate) for a PKCS#12 bundle.
CaCertSource
Type of CA certificate source.
static bool certIsCurrent(const QSslCertificate &cert)
certIsCurrent checks if cert is viable for its not before and not after dates
const QList< QSslCertificate > caChain() const
Chain of Certificate Authorities for client certificate.
#define SSL_SUBJECT_INFO(var, prop)
static QString qcaSignatureAlgorithm(QCA::SignatureAlgorithm algorithm)
Certificate signature algorithm strings per enum.
static QMap< QString, QList< QSslCertificate > > certsGroupedByOrg(const QList< QSslCertificate > &certs)
Map certificates to their oraganization.
CertTrustPolicy
Type of certificate trust policy.
static QString getColonDelimited(const QString &txt)
Gets string with colon delimiters every 2 characters.
static QCA::KeyBundle qcaKeyBundle(const QString &path, const QString &pass)
PKI key/cert bundle from file path, e.g.
static QList< QPair< QSslError::SslError, QString > > sslErrorEnumStrings()
Gets short strings describing SSL errors.
static QString getCertTrustName(QgsAuthCertUtils::CertTrustPolicy trust)
Gets the general name for certificate trust.
static QSslCertificate certFromFile(const QString &certpath)
Returns the first cert from a PEM or DER formatted file.
#define SSL_ISSUER_INFO(var, prop)
static QString getCaSourceName(QgsAuthCertUtils::CaCertSource source, bool single=false)
Gets the general name for CA source enum type.
static QString resolvedCertName(const QSslCertificate &cert, bool issuer=false)
Gets the general name via RFC 5280 resolution.
static QString qcaValidityMessage(QCA::Validity validity)
Certificate validity check messages per enum.
static QByteArray certsToPemText(const QList< QSslCertificate > &certs)
certsToPemText dump a list of QSslCertificates to PEM text