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( QString(
"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( QString(
"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() )
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( QString(
"QCA does not support PKCS#12" ) );
450 if ( bundle.isNull() )
452 QgsDebugMsg( QString(
"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( QString(
"FAILED to read PKCS#12 key (only RSA and DSA algorithms supported): %1" ).arg( bundlepath ) );
491 QgsDebugMsgLevel( QString(
"Private key is PKCS#8: attempting conversion to PKCS#1..." ), 4 );
496 QByteArray pkcs8Der = bundle.privateKey().toDER().toByteArray();
497 if ( pkcs8Der.isEmpty() )
499 QgsDebugMsg( QString(
"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( QString(
"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( QString(
"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( QString(
"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( QString(
"FAILED to write to temp PEM file: %1" ).arg( pemFilePath ) );
585 QgsDebugMsg( QString(
"FAILED to open writing for temp PEM file: %1" ).arg( pemFilePath ) );
589 if ( !pemFile.setPermissions( QFile::ReadUser ) )
591 QgsDebugMsg( QString(
"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(
"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(
"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(
"Certificate could not be converted to QCA cert" );
946 if ( qcacert.isCA() )
948 QgsDebugMsg(
"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(
"Certificate has 'Certificate Sign' key usage" );
960 else if ( certconst.known() == QCA::ServerAuth )
962 QgsDebugMsg(
"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(
"Certificate could not be converted to QCA cert" );
1046 if ( qcacert.isCA() )
1048 QgsDebugMsg(
"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(
"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(
"SSL server certificate has 'digital signature' key usage" );
1074 else if ( certconst.known() == QCA::KeyEncipherment )
1076 QgsDebugMsg(
"SSL server certificate has 'key encipherment' key usage" );
1079 else if ( certconst.known() == QCA::KeyAgreement )
1081 QgsDebugMsg(
"SSL server certificate has 'key agreement' key usage" );
1084 else if ( certconst.known() == QCA::ServerAuth )
1086 QgsDebugMsg(
"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(
"SSL server public key has 'encipher only' key usage" );
1126 encipheronly =
true;
1128 else if ( certconst.known() == QCA::DecipherOnly )
1130 QgsDebugMsg(
"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(
"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)
Get 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)
Return non-encrypted key from a PEM or DER formatted file.
static QList< QSslCertificate > casFromFile(const QString &certspath)
Return list of concatenated CAs from a PEM or DER formatted file.
static QString sslErrorEnumString(QSslError::SslError errenum)
Get short strings describing an SSL error.
static QList< QSslCertificate > certsFromFile(const QString &certspath)
Return 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)
Get 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)
Return list of certificate, private key and algorithm (as PEM text) for a PKCS#12 bundle...
static bool certificateIsAuthority(const QSslCertificate &cert)
Get whether a certificate is an Authority.
static QByteArray fileData(const QString &path)
Return 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)
Return 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)
Get 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)
Get 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)
Return 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)
Get whether a certificate is probably used for a client identity.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Get 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())
Return 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)
Get 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()
Get short strings describing SSL errors.
static QString getCertTrustName(QgsAuthCertUtils::CertTrustPolicy trust)
Get the general name for certificate trust.
static QSslCertificate certFromFile(const QString &certpath)
Return first cert from a PEM or DER formatted file.
#define SSL_ISSUER_INFO(var, prop)
static QString getCaSourceName(QgsAuthCertUtils::CaCertSource source, bool single=false)
Get the general name for CA source enum type.
static QString resolvedCertName(const QSslCertificate &cert, bool issuer=false)
Get 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