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" );
241 QList<QSslCertificate> certs;
242 certs = QSslCertificate::fromData( pemtext.toLatin1(), QSsl::Pem );
243 if ( certs.isEmpty() )
245 QgsDebugMsg( QStringLiteral(
"Parsed cert(s) EMPTY" ) );
252 QList<QSslCertificate> certs;
253 for (
const auto &cert : caList )
255 if ( ! cert.isSelfSigned( ) )
257 certs.append( cert );
264 const QString &keypath,
265 const QString &keypass,
270 if ( !clientcert.isNull() )
272 certpem = QString( clientcert.toPem() );
280 if ( !clientkey.isNull() )
282 keypem = QString( clientkey.toPem( ( reencrypt && !keypass.isEmpty() ) ? keypass.toUtf8() : QByteArray() ) );
285 return QStringList() << certpem << keypem << algtype;
290 QString pkcs8Header = QStringLiteral(
"-----BEGIN PRIVATE KEY-----" );
291 QString pkcs8Footer = QStringLiteral(
"-----END PRIVATE KEY-----" );
292 return keyPemTxt.contains( pkcs8Header ) && keyPemTxt.contains( pkcs8Footer );
296 QByteArray QgsAuthCertUtils::pkcs8PrivateKey( QByteArray &pkcs8Der )
300 if ( pkcs8Der.isEmpty() )
302 QgsDebugMsg( QStringLiteral(
"ERROR, passed DER is empty" ) );
309 if ( ! asnDefsRsrc.exists() )
311 QgsDebugMsg( QStringLiteral(
"ERROR, pkcs.asn resource file not found: %1" ).arg( asnDefsRsrc.filePath() ) );
314 const char *asnDefsFile = asnDefsRsrc.absoluteFilePath().toLocal8Bit().constData();
316 int asn1_result = ASN1_SUCCESS, der_len = 0, oct_len = 0;
317 asn1_node definitions = NULL, structure = NULL;
318 char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE], oct_data[1024];
319 unsigned char *der = NULL;
320 unsigned int flags = 0;
324 QString
typeName( QStringLiteral(
"PKCS-8.PrivateKeyInfo" ) );
326 asn1_result = asn1_parser2tree( asnDefsFile, &definitions, errorDescription );
328 switch ( asn1_result )
333 case ASN1_FILE_NOT_FOUND:
334 QgsDebugMsg( QStringLiteral(
"ERROR, file not found: %1" ).arg( asnDefsFile ) );
336 case ASN1_SYNTAX_ERROR:
337 case ASN1_IDENTIFIER_NOT_FOUND:
338 case ASN1_NAME_TOO_LONG:
339 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 parsing: %1" ).arg( errorDescription ) );
342 QgsDebugMsg( QStringLiteral(
"ERROR, libtasn1: %1" ).arg( asn1_strerror( asn1_result ) ) );
347 asn1_result = asn1_create_element( definitions, typeName.toLatin1().constData(), &structure );
351 if ( asn1_result != ASN1_SUCCESS )
353 QgsDebugMsg( QStringLiteral(
"ERROR, structure creation: %1" ).arg( asn1_strerror( asn1_result ) ) );
358 der =
reinterpret_cast<unsigned char *
>( pkcs8Der.data() );
359 der_len = pkcs8Der.size();
363 asn1_result = asn1_der_decoding2( &structure, der, &der_len, flags, errorDescription );
367 asn1_result = asn1_der_decoding( &structure, der, der_len, errorDescription );
370 if ( asn1_result != ASN1_SUCCESS )
372 QgsDebugMsg( QStringLiteral(
"ERROR, decoding: %1" ).arg( errorDescription ) );
377 QgsDebugMsgLevel( QStringLiteral(
"Decoding: %1" ).arg( asn1_strerror( asn1_result ) ), 4 );
382 QgsDebugMsg( QStringLiteral(
"DECODING RESULT:" ) );
383 asn1_print_structure( stdout, structure,
"", ASN1_PRINT_NAME_TYPE_VALUE );
388 typeName.append( QStringLiteral(
".privateKey" ) );
389 QgsDebugMsgLevel( QStringLiteral(
"privateKey element name: %1" ).arg( typeName ), 4 );
391 asn1_result = asn1_read_value_type( structure,
"privateKey", NULL, &oct_len, &oct_etype );
393 if ( asn1_result != ASN1_MEM_ERROR )
395 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 read privateKey value type: %1" ).arg( asn1_strerror( asn1_result ) ) );
399 if ( oct_etype != ASN1_ETYPE_OCTET_STRING )
401 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey value not octet string, but type: %1" ).arg( static_cast<int>( oct_etype ) ) );
407 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey octet string empty" ) );
412 asn1_result = asn1_read_value( structure,
"privateKey", oct_data, &oct_len );
414 if ( asn1_result != ASN1_SUCCESS )
416 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 read privateKey value: %1" ).arg( asn1_strerror( asn1_result ) ) );
422 QgsDebugMsg( QStringLiteral(
"ERROR, asn1 privateKey value octet string empty" ) );
426 pkcs1 = QByteArray( oct_data, oct_len );
433 asn1_delete_structure( &structure );
439 const QString &bundlepass,
443 if ( !QCA::isSupported(
"pkcs12" ) )
445 QgsDebugMsg( QStringLiteral(
"QCA does not support PKCS#12" ) );
450 if ( bundle.isNull() )
452 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 file to QCA key bundle: %1" ).arg( bundlepath ) );
456 QCA::SecureArray passarray;
457 if ( reencrypt && !bundlepass.isEmpty() )
459 passarray = QCA::SecureArray( bundlepass.toUtf8() );
463 QSsl::KeyAlgorithm keyalg = QSsl::Opaque;
464 if ( bundle.privateKey().isRSA() )
466 algtype = QStringLiteral(
"rsa" );
469 else if ( bundle.privateKey().isDSA() )
471 algtype = QStringLiteral(
"dsa" );
474 else if ( bundle.privateKey().isDH() )
476 algtype = QStringLiteral(
"dh" );
481 if ( keyalg == QSsl::Opaque )
483 QgsDebugMsg( QStringLiteral(
"FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1" ).arg( bundlepath ) );
491 QgsDebugMsgLevel( QStringLiteral(
"Private key is PKCS#8: attempting conversion to PKCS#1..." ), 4 );
496 QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
497 if ( pkcs8Der.isEmpty() )
499 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key to DER-encoded format: %1" ).arg( bundlepath ) );
503 QByteArray pkcs1Der = QgsAuthCertUtils::pkcs8PrivateKey( pkcs8Der );
504 if ( pkcs1Der.isEmpty() )
506 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1: %1" ).arg( bundlepath ) );
510 QSslKey pkcs1Key( pkcs1Der, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey );
511 if ( pkcs1Key.isNull() )
513 QgsDebugMsg( QStringLiteral(
"FAILED to convert PKCS#12 key from PKCS#8 to PKCS#1 QSslKey: %1" ).arg( bundlepath ) );
516 keyPem = QString( pkcs1Key.toPem( passarray.toByteArray() ) );
520 keyPem = bundle.privateKey().toPEM( passarray );
523 keyPem = bundle.privateKey().toPEM( passarray );
526 QgsDebugMsgLevel( QStringLiteral(
"PKCS#12 cert as PEM:\n%1" ).arg( QString( bundle.certificateChain().primary().toPEM() ) ), 4 );
530 return QStringList() << bundle.certificateChain().primary().toPEM() << keyPem << algtype;
535 QList<QSslCertificate> result;
536 if ( !QCA::isSupported(
"pkcs12" ) )
540 if ( bundle.isNull() )
543 const QCA::CertificateChain chain( bundle.certificateChain() );
544 for (
const auto &cert : chain )
548 result.append( QSslCertificate::fromData( cert.toPEM().toAscii() ) );
557 if ( !certs.isEmpty() )
559 QStringList certslist;
560 for (
const auto &cert : certs )
562 certslist << cert.toPem();
564 capem = certslist.join( QStringLiteral(
"\n" ) ).toLatin1();
571 QFile pemFile( QDir::tempPath() + QDir::separator() + name );
572 QString pemFilePath( pemFile.fileName() );
574 if ( pemFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
576 qint64 bytesWritten = pemFile.write( pemtext );
577 if ( bytesWritten == -1 )
579 QgsDebugMsg( QStringLiteral(
"FAILED to write to temp PEM file: %1" ).arg( pemFilePath ) );
585 QgsDebugMsg( QStringLiteral(
"FAILED to open writing for temp PEM file: %1" ).arg( pemFilePath ) );
589 if ( !pemFile.setPermissions( QFile::ReadUser ) )
591 QgsDebugMsg( QStringLiteral(
"FAILED to set permissions on temp PEM file: %1" ).arg( pemFilePath ) );
603 return single ? QObject::tr(
"System Root CA" ) : QObject::tr(
"System Root Authorities" );
605 return single ? QObject::tr(
"File CA" ) : QObject::tr(
"Authorities from File" );
607 return single ? QObject::tr(
"Database CA" ) : QObject::tr(
"Authorities in Database" );
609 return single ? QObject::tr(
"Connection CA" ) : QObject::tr(
"Authorities from connection" );
617 QString name( issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::CommonName )
620 if ( name.isEmpty() )
621 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::OrganizationalUnitName )
624 if ( name.isEmpty() )
628 if ( name.isEmpty() )
632 if ( name.isEmpty() )
633 name = issuer ?
SSL_ISSUER_INFO( cert, QSslCertificate::StateOrProvinceName )
636 if ( name.isEmpty() )
644 void QgsAuthCertUtils::appendDirSegment_( QStringList &dirname,
645 const QString &segment, QString value )
647 if ( !value.isEmpty() )
649 dirname.append( segment +
'=' + value.replace(
',', QLatin1String(
"\\," ) ) );
653 QSsl::EncodingFormat QgsAuthCertUtils::sniffEncoding(
const QByteArray &payload )
655 return payload.contains( QByteArrayLiteral(
"-----BEGIN " ) ) ?
661 const QCA::Certificate &acert,
667 if ( acert.isNull() )
669 QCA::ConvertResult res;
670 QCA::Certificate acert( QCA::Certificate::fromPEM( qcert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
671 if ( res != QCA::ConvertGood || acert.isNull() )
673 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
685 QgsAuthCertUtils::appendDirSegment_(
686 dirname, QStringLiteral(
"E" ), issuer ? acert.issuerInfo().value( QCA::Email )
687 : acert.subjectInfo().value( QCA::Email ) );
688 QgsAuthCertUtils::appendDirSegment_(
689 dirname, QStringLiteral(
"CN" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CommonName )
691 QgsAuthCertUtils::appendDirSegment_(
692 dirname, QStringLiteral(
"OU" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::OrganizationalUnitName )
694 QgsAuthCertUtils::appendDirSegment_(
695 dirname, QStringLiteral(
"O" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::Organization )
697 QgsAuthCertUtils::appendDirSegment_(
698 dirname, QStringLiteral(
"L" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::LocalityName )
700 QgsAuthCertUtils::appendDirSegment_(
701 dirname, QStringLiteral(
"ST" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::StateOrProvinceName )
703 QgsAuthCertUtils::appendDirSegment_(
704 dirname, QStringLiteral(
"C" ), issuer ?
SSL_ISSUER_INFO( qcert, QSslCertificate::CountryName )
707 return dirname.join( QStringLiteral(
"," ) );
715 return QObject::tr(
"Default" );
717 return QObject::tr(
"Trusted" );
719 return QObject::tr(
"Untrusted" );
730 sl.reserve( txt.size() );
731 for (
int i = 0; i < txt.size(); i += 2 )
733 sl << txt.mid( i, ( i + 2 > txt.size() ) ? -1 : 2 );
735 return sl.join( QStringLiteral(
":" ) );
740 QString sha( cert.digest( QCryptographicHash::Sha1 ).toHex() );
751 return QCA::Certificate();
753 QCA::ConvertResult res;
754 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
755 if ( res != QCA::ConvertGood || qcacert.isNull() )
757 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
758 qcacert = QCA::Certificate();
765 QCA::CertificateCollection qcacoll;
769 for (
const auto &cert : certs )
772 if ( !qcacert.isNull() )
774 qcacoll.addCertificate( qcacert );
782 QCA::SecureArray passarray;
783 if ( !pass.isEmpty() )
784 passarray = QCA::SecureArray( pass.toUtf8() );
786 QCA::ConvertResult res;
787 QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( path, passarray, &res, QStringLiteral(
"qca-ossl" ) ) );
789 return ( res == QCA::ConvertGood ? bundle : QCA::KeyBundle() );
796 case QCA::ValidityGood:
797 return QObject::tr(
"Certificate is valid." );
798 case QCA::ErrorRejected:
799 return QObject::tr(
"Root CA rejected the certificate purpose." );
800 case QCA::ErrorUntrusted:
801 return QObject::tr(
"Certificate is not trusted." );
802 case QCA::ErrorSignatureFailed:
803 return QObject::tr(
"Signature does not match." );
804 case QCA::ErrorInvalidCA:
805 return QObject::tr(
"Certificate Authority is invalid or not found." );
806 case QCA::ErrorInvalidPurpose:
807 return QObject::tr(
"Purpose does not match the intended usage." );
808 case QCA::ErrorSelfSigned:
809 return QObject::tr(
"Certificate is self-signed, and is not found in the list of trusted certificates." );
810 case QCA::ErrorRevoked:
811 return QObject::tr(
"Certificate has been revoked." );
812 case QCA::ErrorPathLengthExceeded:
813 return QObject::tr(
"Path length from the root CA to this certificate is too long." );
814 case QCA::ErrorExpired:
815 return QObject::tr(
"Certificate has expired or is not yet valid." );
816 case QCA::ErrorExpiredCA:
817 return QObject::tr(
"Certificate Authority has expired." );
818 case QCA::ErrorValidityUnknown:
819 return QObject::tr(
"Validity is unknown." );
829 case QCA::EMSA1_SHA1:
830 return QObject::tr(
"SHA1, with EMSA1" );
831 case QCA::EMSA3_SHA1:
832 return QObject::tr(
"SHA1, with EMSA3" );
834 return QObject::tr(
"MD5, with EMSA3" );
836 return QObject::tr(
"MD2, with EMSA3" );
837 case QCA::EMSA3_RIPEMD160:
838 return QObject::tr(
"RIPEMD160, with EMSA3" );
840 return QObject::tr(
"EMSA3, without digest" );
841 #if QCA_VERSION >= 0x020100 842 case QCA::EMSA3_SHA224:
843 return QObject::tr(
"SHA224, with EMSA3" );
844 case QCA::EMSA3_SHA256:
845 return QObject::tr(
"SHA256, with EMSA3" );
846 case QCA::EMSA3_SHA384:
847 return QObject::tr(
"SHA384, with EMSA3" );
848 case QCA::EMSA3_SHA512:
849 return QObject::tr(
"SHA512, with EMSA3" );
852 return QObject::tr(
"Unknown (possibly Elliptic Curve)" );
858 switch ( constraint )
860 case QCA::DigitalSignature:
861 return QObject::tr(
"Digital Signature" );
862 case QCA::NonRepudiation:
863 return QObject::tr(
"Non-repudiation" );
864 case QCA::KeyEncipherment:
865 return QObject::tr(
"Key Encipherment" );
866 case QCA::DataEncipherment:
867 return QObject::tr(
"Data Encipherment" );
868 case QCA::KeyAgreement:
869 return QObject::tr(
"Key Agreement" );
870 case QCA::KeyCertificateSign:
871 return QObject::tr(
"Key Certificate Sign" );
873 return QObject::tr(
"CRL Sign" );
874 case QCA::EncipherOnly:
875 return QObject::tr(
"Encipher Only" );
876 case QCA::DecipherOnly:
877 return QObject::tr(
"Decipher Only" );
878 case QCA::ServerAuth:
879 return QObject::tr(
"Server Authentication" );
880 case QCA::ClientAuth:
881 return QObject::tr(
"Client Authentication" );
882 case QCA::CodeSigning:
883 return QObject::tr(
"Code Signing" );
884 case QCA::EmailProtection:
885 return QObject::tr(
"Email Protection" );
886 case QCA::IPSecEndSystem:
887 return QObject::tr(
"IPSec Endpoint" );
888 case QCA::IPSecTunnel:
889 return QObject::tr(
"IPSec Tunnel" );
891 return QObject::tr(
"IPSec User" );
892 case QCA::TimeStamping:
893 return QObject::tr(
"Time Stamping" );
894 case QCA::OCSPSigning:
895 return QObject::tr(
"OCSP Signing" );
906 return QObject::tr(
"Any or unspecified" );
908 return QObject::tr(
"Certificate Authority" );
910 return QObject::tr(
"Certificate Issuer" );
912 return QObject::tr(
"TLS/SSL Server" );
914 return QObject::tr(
"TLS/SSL Server EV" );
916 return QObject::tr(
"TLS/SSL Client" );
918 return QObject::tr(
"Code Signing" );
920 return QObject::tr(
"Email Protection" );
922 return QObject::tr(
"Time Stamping" );
924 return QObject::tr(
"CRL Signing" );
927 return QObject::tr(
"Undetermined usage" );
933 QList<QgsAuthCertUtils::CertUsageType> usages;
938 QCA::ConvertResult res;
939 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QStringLiteral(
"qca-ossl" ) ) );
940 if ( res != QCA::ConvertGood || qcacert.isNull() )
942 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
946 if ( qcacert.isCA() )
948 QgsDebugMsg( QStringLiteral(
"Certificate has 'CA:TRUE' basic constraint" ) );
952 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
953 for (
const auto &certconst : certconsts )
955 if ( certconst.known() == QCA::KeyCertificateSign )
957 QgsDebugMsg( QStringLiteral(
"Certificate has 'Certificate Sign' key usage" ) );
960 else if ( certconst.known() == QCA::ServerAuth )
962 QgsDebugMsg( QStringLiteral(
"Certificate has 'server authentication' extended key usage" ) );
968 QCA::CertificateCollection trustedCAs(
970 QCA::CertificateCollection untrustedCAs(
974 v_any = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageAny, QCA::ValidateAll );
975 if ( v_any == QCA::ValidityGood )
980 QCA::Validity v_tlsserver;
981 v_tlsserver = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSServer, QCA::ValidateAll );
982 if ( v_tlsserver == QCA::ValidityGood )
992 QCA::Validity v_tlsclient;
993 v_tlsclient = qcacert.validate( trustedCAs, untrustedCAs, QCA::UsageTLSClient, QCA::ValidateAll );
995 if ( v_tlsclient == QCA::ValidityGood )
1038 QCA::ConvertResult res;
1039 QCA::Certificate qcacert( QCA::Certificate::fromPEM( cert.toPem(), &res, QString(
"qca-ossl" ) ) );
1040 if ( res != QCA::ConvertGood || qcacert.isNull() )
1042 QgsDebugMsg( QStringLiteral(
"Certificate could not be converted to QCA cert" ) );
1046 if ( qcacert.isCA() )
1048 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'CA:TRUE' basic constraint (and should not)" ) );
1052 const QList<QCA::ConstraintType> certconsts = qcacert.constraints();
1053 for (
const auto & certconst, certconsts )
1055 if ( certconst.known() == QCA::KeyCertificateSign )
1057 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'Certificate Sign' key usage (and should not)" ) );
1064 bool serverauth =
false;
1065 bool dsignature =
false;
1066 bool keyencrypt =
false;
1067 for (
const auto &certconst : certconsts )
1069 if ( certconst.known() == QCA::DigitalSignature )
1071 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'digital signature' key usage" ) );
1074 else if ( certconst.known() == QCA::KeyEncipherment )
1076 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'key encipherment' key usage" ) );
1079 else if ( certconst.known() == QCA::KeyAgreement )
1081 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'key agreement' key usage" ) );
1084 else if ( certconst.known() == QCA::ServerAuth )
1086 QgsDebugMsg( QStringLiteral(
"SSL server certificate has 'server authentication' extended key usage" ) );
1098 if ( serverauth && dsignature && keyencrypt )
1102 if ( dsignature && keyencrypt )
1108 bool keyagree =
false;
1109 bool encipheronly =
false;
1110 bool decipheronly =
false;
1112 QCA::PublicKey pubkey( qcacert.subjectPublicKey() );
1114 if ( pubkey.bitSize() > 0 && pubkey.isDH() )
1116 keyagree = pubkey.canKeyAgree();
1121 for (
const auto &certconst : certconsts )
1123 if ( certconst.known() == QCA::EncipherOnly )
1125 QgsDebugMsg( QStringLiteral(
"SSL server public key has 'encipher only' key usage" ) );
1126 encipheronly =
true;
1128 else if ( certconst.known() == QCA::DecipherOnly )
1130 QgsDebugMsg( QStringLiteral(
"SSL server public key has 'decipher only' key usage" ) );
1131 decipheronly =
true;
1134 if ( !encipheronly && !decipheronly )
1152 case QSslError::UnableToGetIssuerCertificate:
1153 return QObject::tr(
"Unable to Get Issuer Certificate" );
1154 case QSslError::UnableToDecryptCertificateSignature:
1155 return QObject::tr(
"Unable to Decrypt Certificate Signature" );
1156 case QSslError::UnableToDecodeIssuerPublicKey:
1157 return QObject::tr(
"Unable to Decode Issuer Public Key" );
1158 case QSslError::CertificateSignatureFailed:
1159 return QObject::tr(
"Certificate Signature Failed" );
1160 case QSslError::CertificateNotYetValid:
1161 return QObject::tr(
"Certificate Not Yet Valid" );
1162 case QSslError::CertificateExpired:
1163 return QObject::tr(
"Certificate Expired" );
1164 case QSslError::InvalidNotBeforeField:
1165 return QObject::tr(
"Invalid Not Before Field" );
1166 case QSslError::InvalidNotAfterField:
1167 return QObject::tr(
"Invalid Not After Field" );
1168 case QSslError::SelfSignedCertificate:
1169 return QObject::tr(
"Self-signed Certificate" );
1170 case QSslError::SelfSignedCertificateInChain:
1171 return QObject::tr(
"Self-signed Certificate In Chain" );
1172 case QSslError::UnableToGetLocalIssuerCertificate:
1173 return QObject::tr(
"Unable to Get Local Issuer Certificate" );
1174 case QSslError::UnableToVerifyFirstCertificate:
1175 return QObject::tr(
"Unable to Verify First Certificate" );
1176 case QSslError::CertificateRevoked:
1177 return QObject::tr(
"Certificate Revoked" );
1178 case QSslError::InvalidCaCertificate:
1179 return QObject::tr(
"Invalid CA Certificate" );
1180 case QSslError::PathLengthExceeded:
1181 return QObject::tr(
"Path Length Exceeded" );
1182 case QSslError::InvalidPurpose:
1183 return QObject::tr(
"Invalid Purpose" );
1184 case QSslError::CertificateUntrusted:
1185 return QObject::tr(
"Certificate Untrusted" );
1186 case QSslError::CertificateRejected:
1187 return QObject::tr(
"Certificate Rejected" );
1188 case QSslError::SubjectIssuerMismatch:
1189 return QObject::tr(
"Subject Issuer Mismatch" );
1190 case QSslError::AuthorityIssuerSerialNumberMismatch:
1191 return QObject::tr(
"Authority Issuer Serial Number Mismatch" );
1192 case QSslError::NoPeerCertificate:
1193 return QObject::tr(
"No Peer Certificate" );
1194 case QSslError::HostNameMismatch:
1195 return QObject::tr(
"Host Name Mismatch" );
1196 case QSslError::UnspecifiedError:
1197 return QObject::tr(
"Unspecified Error" );
1198 case QSslError::CertificateBlacklisted:
1199 return QObject::tr(
"Certificate Blacklisted" );
1200 case QSslError::NoError:
1201 return QObject::tr(
"No Error" );
1202 case QSslError::NoSslSupport:
1203 return QObject::tr(
"No SSL Support" );
1211 QList<QPair<QSslError::SslError, QString> > errenums;
1212 errenums << qMakePair( QSslError::UnableToGetIssuerCertificate,
1214 errenums << qMakePair( QSslError::UnableToDecryptCertificateSignature,
1216 errenums << qMakePair( QSslError::UnableToDecodeIssuerPublicKey,
1218 errenums << qMakePair( QSslError::CertificateSignatureFailed,
1220 errenums << qMakePair( QSslError::CertificateNotYetValid,
1222 errenums << qMakePair( QSslError::CertificateExpired,
1224 errenums << qMakePair( QSslError::InvalidNotBeforeField,
1226 errenums << qMakePair( QSslError::InvalidNotAfterField,
1228 errenums << qMakePair( QSslError::SelfSignedCertificate,
1230 errenums << qMakePair( QSslError::SelfSignedCertificateInChain,
1232 errenums << qMakePair( QSslError::UnableToGetLocalIssuerCertificate,
1234 errenums << qMakePair( QSslError::UnableToVerifyFirstCertificate,
1236 errenums << qMakePair( QSslError::CertificateRevoked,
1238 errenums << qMakePair( QSslError::InvalidCaCertificate,
1240 errenums << qMakePair( QSslError::PathLengthExceeded,
1242 errenums << qMakePair( QSslError::InvalidPurpose,
1244 errenums << qMakePair( QSslError::CertificateUntrusted,
1246 errenums << qMakePair( QSslError::CertificateRejected,
1248 errenums << qMakePair( QSslError::SubjectIssuerMismatch,
1250 errenums << qMakePair( QSslError::AuthorityIssuerSerialNumberMismatch,
1252 errenums << qMakePair( QSslError::NoPeerCertificate,
1254 errenums << qMakePair( QSslError::HostNameMismatch,
1256 errenums << qMakePair( QSslError::UnspecifiedError,
1258 errenums << qMakePair( QSslError::CertificateBlacklisted,
1265 if ( cert.isNull() )
1267 const QDateTime currentTime = QDateTime::currentDateTime();
1268 return cert.effectiveDate() <= currentTime && cert.expiryDate() >= currentTime;
1273 QList<QSslError> sslErrors;
1275 if ( cert.isNull() )
1278 const QDateTime currentTime = QDateTime::currentDateTime();
1279 if ( cert.expiryDate() <= currentTime )
1281 sslErrors << QSslError( QSslError::SslError::CertificateExpired, cert );
1283 if ( cert.effectiveDate() >= QDateTime::currentDateTime() )
1285 sslErrors << QSslError( QSslError::SslError::CertificateNotYetValid, cert );
1287 if ( cert.isBlacklisted() )
1289 sslErrors << QSslError( QSslError::SslError::CertificateBlacklisted, cert );
1301 const QString &hostName,
1304 QList<QSslError> sslErrors;
1305 QList<QSslCertificate> trustedChain;
1307 for (
const auto &cert : certificateChain )
1309 bool untrusted =
false;
1312 if ( cert.digest( ) == untrustedCert.digest( ) )
1320 trustedChain << cert;
1325 const QList<QSslCertificate> constTrustedChain( trustedChain );
1326 for (
const auto &cert : constTrustedChain )
1332 if ( trustRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
1334 static QMutex sMutex;
1335 QMutexLocker lock( &sMutex );
1336 QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
1337 QSslConfiguration sslConfig( oldSslConfig );
1338 sslConfig.setCaCertificates(
casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
1339 QSslConfiguration::setDefaultConfiguration( sslConfig );
1340 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1341 QSslConfiguration::setDefaultConfiguration( oldSslConfig );
1345 sslErrors = QSslCertificate::verify( trustedChain, hostName );
1354 errors << QObject::tr(
"Client certificate is NULL." );
1357 errors << QObject::tr(
"Client certificate key is NULL." );
1360 if ( !errors.isEmpty() )
1363 QList<QSslError> sslErrors;
1364 if ( useIntermediates )
1366 QList<QSslCertificate> certsList( bundle.
caChain() );
1372 sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.
clientCert() );
1374 const QList<QSslError> constSslErrors( sslErrors );
1375 for (
const auto &sslError : constSslErrors )
1377 if ( sslError.error() != QSslError::NoError )
1379 errors << sslError.errorString();
1383 QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.
clientKey().toPem() ) );
1384 QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.
clientCert().publicKey().toPem( ) ) );
1385 bool keyValid( ! pvtKey.isNull() );
1386 if ( keyValid && !( pubKey.toRSA().isNull( ) || pvtKey.toRSA().isNull( ) ) )
1388 keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
1390 else if ( keyValid && !( pubKey.toDSA().isNull( ) || pvtKey.toDSA().isNull( ) ) )
1392 keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
1396 QgsDebugMsg( QStringLiteral(
"Key is not DSA, RSA: validation is not supported by QCA" ) );
1400 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.
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)
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.
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.
const QList< QSslCertificate > caChain() const
Chain of Certificate Authorities for client certificate.
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
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
const QSslKey clientKey() const
Private key object.
#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.
const QSslCertificate clientCert() const
Client certificate object.
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