Skip to content

Commit

Permalink
[auth] Add validatePKIBundle
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso committed Oct 26, 2017
1 parent 287d1f7 commit f1eba3a
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 6 deletions.
5 changes: 5 additions & 0 deletions python/core/auth/qgsauthcertutils.sip
Expand Up @@ -285,6 +285,11 @@ Get short strings describing an SSL error
:rtype: list of QSslError
%End

static QStringList validatePKIBundle( QgsPkiBundle &bundle, bool useIntermediates = true, bool addRootCa = false );
%Docstring
:rtype: list of str
%End

};

/************************************************************************
Expand Down
6 changes: 6 additions & 0 deletions python/core/auth/qgsauthconfig.sip
Expand Up @@ -221,6 +221,7 @@ class QgsPkiBundle
const QString &keyPass = QString(),
const QList<QSslCertificate> &caChain = QList<QSslCertificate>() );
%Docstring
TORM
Construct a bundle of PKI components from PEM-formatted file paths
\param certPath Certificate file path
\param keyPath Private key path
Expand All @@ -232,6 +233,7 @@ class QgsPkiBundle
static const QgsPkiBundle fromPkcs12Paths( const QString &bundlepath,
const QString &bundlepass = QString() );
%Docstring
TORM
Construct a bundle of PKI components from a PKCS#12 file path
\param bundlepath Bundle file path
\param bundlepass Optional bundle passphrase
Expand Down Expand Up @@ -293,6 +295,7 @@ class QgsPkiConfigBundle
{
%Docstring
Storage set for constructed SSL certificate, key, associated with an authentication config
TODO: inherit from PKIBundle
%End

%TypeHeaderCode
Expand Down Expand Up @@ -323,6 +326,7 @@ Whether the bundle is valid
Authentication method configuration
:rtype: QgsAuthMethodConfig
%End

void setConfig( const QgsAuthMethodConfig &config );
%Docstring
Set authentication method configuration
Expand All @@ -333,6 +337,7 @@ Set authentication method configuration
Client certificate object
:rtype: QSslCertificate
%End

void setClientCert( const QSslCertificate &cert );
%Docstring
Set client certificate object
Expand All @@ -343,6 +348,7 @@ Set client certificate object
Private key object
:rtype: QSslKey
%End

void setClientCertKey( const QSslKey &certkey );
%Docstring
Set private key object
Expand Down
81 changes: 75 additions & 6 deletions src/core/auth/qgsauthcertutils.cpp
Expand Up @@ -1021,22 +1021,91 @@ QList<QPair<QSslError::SslError, QString> > QgsAuthCertUtils::sslErrorEnumString

QList<QSslError> QgsAuthCertUtils::validateCertChain( const QList<QSslCertificate> &certificateChain, const QString &hostName, bool addRootCa )
{
QList<QSslError> results;
QList<QSslError> sslErrors;
QList<QSslCertificate> trustedChain;
// Filter out all CAs that are not trusted from QgsAuthManager
for ( const auto &cert : certificateChain )
{
bool untrusted = false;
for ( const auto &untrustedCert : QgsAuthManager::instance()->getUntrustedCaCerts() )
{
if ( cert.digest( ) == untrustedCert.digest( ) )
{
untrusted = true;
break;
}
}
if ( ! untrusted )
{
trustedChain << cert;
}
}

// Merge in the root CA if present and asked for
if ( addRootCa && certificateChain.count() > 1 && certificateChain.last().isSelfSigned() )
if ( addRootCa && trustedChain.count() > 1 && trustedChain.last().isSelfSigned() )
{
static QMutex sMutex;
QMutexLocker lock( &sMutex );
QSslConfiguration oldSslConfig( QSslConfiguration::defaultConfiguration() );
QSslConfiguration sslConfig( oldSslConfig );
sslConfig.setCaCertificates( casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << certificateChain.last() ) );
sslConfig.setCaCertificates( casMerge( sslConfig.caCertificates(), QList<QSslCertificate>() << trustedChain.last() ) );
QSslConfiguration::setDefaultConfiguration( sslConfig );
results = QSslCertificate::verify( certificateChain, hostName );
sslErrors = QSslCertificate::verify( trustedChain, hostName );
QSslConfiguration::setDefaultConfiguration( oldSslConfig );
}
else
{
results = QSslCertificate::verify( certificateChain, hostName );
sslErrors = QSslCertificate::verify( trustedChain, hostName );
}
return sslErrors;
}

QStringList QgsAuthCertUtils::validatePKIBundle( QgsPkiBundle &bundle, bool useIntermediates, bool addRootCa )
{
QStringList errors;
QList<QSslError> sslErrors;
if ( useIntermediates )
{
QList<QSslCertificate> certsList( bundle.caChain() );
certsList.insert( 0, bundle.clientCert( ) );
sslErrors = QgsAuthCertUtils::validateCertChain( certsList, QString(), addRootCa );
}
else
{
sslErrors = QSslCertificate::verify( QList<QSslCertificate>() << bundle.clientCert() );
}
const QList<QSslError> constSslErrors( sslErrors );
for ( const auto &sslError : constSslErrors )
{
if ( sslError.error() != QSslError::NoError )
{
errors << sslError.errorString();
}
}
// Now check the key with QCA!
QCA::PrivateKey pvtKey( QCA::PrivateKey::fromPEM( bundle.clientKey().toPem() ) );
QCA::PublicKey pubKey( QCA::PublicKey::fromPEM( bundle.clientCert().publicKey().toPem( ) ) );
bool keyValid( ! pvtKey.isNull() );
if ( keyValid && !( pubKey.toRSA().isNull( ) || pvtKey.toRSA().isNull( ) ) )
{
keyValid = pubKey.toRSA().n() == pvtKey.toRSA().n();
}
else if ( keyValid && !( pubKey.toDSA().isNull( ) || pvtKey.toDSA().isNull( ) ) )
{
keyValid = pubKey == QCA::DSAPublicKey( pvtKey.toDSA() );
}
// DH is probably not used anymore but the library supports it
else if ( keyValid && !( pubKey.toDH().isNull( ) || pvtKey.toDH().isNull( ) ) )
{
keyValid = pubKey == QCA::DHPublicKey( pvtKey.toDH() );
}
else
{
// Log? Error? Note that elliptical curve is not supported by QCA
}
if ( ! keyValid )
{
errors << QObject::tr( "Private key does not match client certificate public key." );
}
return results;
return errors;
}
3 changes: 3 additions & 0 deletions src/core/auth/qgsauthcertutils.h
Expand Up @@ -24,6 +24,7 @@
#include <QSslCertificate>
#include <QSslError>

#include "qgsauthconfig.h"
#include "qgis_core.h"

class QgsAuthConfigSslServer;
Expand Down Expand Up @@ -305,6 +306,8 @@ class CORE_EXPORT QgsAuthCertUtils
*/
static QList<QSslError> validateCertChain( const QList<QSslCertificate> &certificateChain, const QString &hostName = QString(), bool addRootCa = false ) ;

static QStringList validatePKIBundle( QgsPkiBundle &bundle, bool useIntermediates = true, bool addRootCa = false );

private:
static void appendDirSegment_( QStringList &dirname, const QString &segment, QString value );
};
Expand Down
6 changes: 6 additions & 0 deletions src/core/auth/qgsauthconfig.h
Expand Up @@ -201,6 +201,7 @@ class CORE_EXPORT QgsPkiBundle
const QList<QSslCertificate> &caChain = QList<QSslCertificate>() );

/**
* TORM
* Construct a bundle of PKI components from PEM-formatted file paths
* \param certPath Certificate file path
* \param keyPath Private key path
Expand All @@ -213,6 +214,7 @@ class CORE_EXPORT QgsPkiBundle
const QList<QSslCertificate> &caChain = QList<QSslCertificate>() );

/**
* TORM
* Construct a bundle of PKI components from a PKCS#12 file path
* \param bundlepath Bundle file path
* \param bundlepass Optional bundle passphrase
Expand Down Expand Up @@ -254,6 +256,7 @@ class CORE_EXPORT QgsPkiBundle
/**
* \ingroup core
* \brief Storage set for constructed SSL certificate, key, associated with an authentication config
* TODO: inherit from PKIBundle
*/
class CORE_EXPORT QgsPkiConfigBundle
{
Expand All @@ -276,16 +279,19 @@ class CORE_EXPORT QgsPkiConfigBundle

//! Authentication method configuration
const QgsAuthMethodConfig config() const { return mConfig; }

//! Set authentication method configuration
void setConfig( const QgsAuthMethodConfig &config ) { mConfig = config; }

//! Client certificate object
const QSslCertificate clientCert() const { return mCert; }

//! Set client certificate object
void setClientCert( const QSslCertificate &cert ) { mCert = cert; }

//! Private key object
const QSslKey clientCertKey() const { return mCertKey; }

//! Set private key object
void setClientCertKey( const QSslKey &certkey ) { mCertKey = certkey; }

Expand Down

0 comments on commit f1eba3a

Please sign in to comment.