-
-
Notifications
You must be signed in to change notification settings - Fork 3k
/
qgsauthcertutils.h
378 lines (315 loc) · 14.8 KB
/
qgsauthcertutils.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/***************************************************************************
qgsauthcertutils.h
---------------------
begin : May 1, 2015
copyright : (C) 2015 by Boundless Spatial, Inc. USA
author : Larry Shaffer
email : lshaffer at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSAUTHCERTUTILS_H
#define QGSAUTHCERTUTILS_H
#include <QFile>
#include "qgis_sip.h"
#include <QtCrypto>
#include <QSslCertificate>
#include <QSslError>
#include "qgsauthconfig.h"
#include "qgis_core.h"
class QgsAuthConfigSslServer;
#define SSL_ISSUER_INFO( var, prop ) var.issuerInfo( prop ).value(0)
#define SSL_SUBJECT_INFO( var, prop ) var.subjectInfo( prop ).value(0)
/**
* \ingroup core
* \brief Utilities for working with certificates and keys
*/
class CORE_EXPORT QgsAuthCertUtils
{
public:
//! Type of CA certificate source
enum CaCertSource
{
SystemRoot = 0,
FromFile = 1,
InDatabase = 2,
Connection = 3
};
//! Type of certificate trust policy
enum CertTrustPolicy
{
DefaultTrust = 0,
Trusted = 1,
Untrusted = 2,
NoPolicy = 3
};
//! Type of certificate usage
enum CertUsageType
{
UndeterminedUsage = 0,
AnyOrUnspecifiedUsage,
CertAuthorityUsage,
CertIssuerUsage,
TlsServerUsage,
TlsServerEvUsage,
TlsClientUsage,
CodeSigningUsage,
EmailProtectionUsage,
TimeStampingUsage,
CRLSigningUsage
};
//! Type of certificate key group
enum ConstraintGroup
{
KeyUsage = 0,
ExtendedKeyUsage = 1
};
//! SSL Protocol name strings per enum
static QString getSslProtocolName( QSsl::SslProtocol protocol );
//! Map certificate sha1 to certificate as simple cache
static QMap<QString, QSslCertificate> mapDigestToCerts( const QList<QSslCertificate> &certs );
/**
* Map certificates to their oraganization.
* \note not available in Python bindings
*/
static QMap< QString, QList<QSslCertificate> > certsGroupedByOrg( const QList<QSslCertificate> &certs ) SIP_SKIP;
/**
* Map SSL custom configs' certificate sha1 to custom config as simple cache
*/
static QMap<QString, QgsAuthConfigSslServer> mapDigestToSslConfigs( const QList<QgsAuthConfigSslServer> &configs );
/**
* Map SSL custom configs' certificates to their oraganization.
* \note not available in Python bindings
*/
static QMap< QString, QList<QgsAuthConfigSslServer> > sslConfigsGroupedByOrg( const QList<QgsAuthConfigSslServer> &configs ) SIP_SKIP;
/**
* Return data from a local file via a read-only operation
* \param path Path to file to read
* \returns All data contained in file or empty contents if file does not exist
*/
static QByteArray fileData( const QString &path );
//! Return list of concatenated certs from a PEM or DER formatted file
static QList<QSslCertificate> certsFromFile( const QString &certspath );
//! Return list of concatenated CAs from a PEM or DER formatted file
static QList<QSslCertificate> casFromFile( const QString &certspath );
//! Return first cert from a PEM or DER formatted file
static QSslCertificate certFromFile( const QString &certpath );
/**
* \brief casMerge merges two certificate bundles in a single one removing duplicates, the certificates
* from the \a bundle2 are appended to \a bundle1 if not already there
* \param bundle1 first bundle
* \param bundle2 second bundle
* \return a list of unique certificates
*/
static QList<QSslCertificate> casMerge( const QList<QSslCertificate> &bundle1,
const QList<QSslCertificate> &bundle2 );
/**
* Return non-encrypted key from a PEM or DER formatted file
* \param keypath File path to private key
* \param keypass Passphrase for private key
* \param algtype QString to set with resolved algorithm type
*/
static QSslKey keyFromFile( const QString &keypath,
const QString &keypass = QString(),
QString *algtype = nullptr );
//! Return list of concatenated certs from a PEM Base64 text block
static QList<QSslCertificate> certsFromString( const QString &pemtext );
/**
* \brief casRemoveSelfSigned remove self-signed CA certificates from \a caList
* \param caList list of CA certificates
* \return a list of non self-signed certificates
*/
static QList<QSslCertificate> casRemoveSelfSigned( const QList<QSslCertificate> &caList );
/**
* Return list of certificate, private key and algorithm (as PEM text) from file path components
* \param certpath File path to certificate
* \param keypath File path to private key
* \param keypass Passphrase for private key
* \param reencrypt Whether to re-encrypt the private key with the passphrase
* \returns certificate, private key, key's algorithm type
*/
static QStringList certKeyBundleToPem( const QString &certpath,
const QString &keypath,
const QString &keypass = QString(),
bool reencrypt = true );
/**
* Determine if the PEM-encoded text of a key is PKCS#8 format
* \param keyPemTxt PEM-encoded text
* \returns True if PKCS#8, otherwise false
*/
static bool pemIsPkcs8( const QString &keyPemTxt );
#ifdef Q_OS_MAC
/**
* Extract the PrivateKey ASN.1 element of a DER-encoded PKCS#8 private key
* \param pkcs8Der PKCS#8 DER-encoded private key data
* \returns DER-encoded private key on success or an empty QByteArray upon failure
* \note On some platforms, e.g. macOS, where the default SSL backend is not OpenSSL, a QSslKey
* can not be created using PKCS#8-formatted data. However, PKCS#8 private key ASN.1 structures
* contain the key data inside a wrapper describing the algorithm used, e.g. RSA, DSA, ECC etc.
* Extracted PrivateKey ASN.1 data can be used to create a compatible QSslKey,
* e.g. 'traditional' SSLeay RSA-specific PKCS#1.
* By default OpenSSL 1.0.0+ returns private keys as PKCS#8, previously it was PKCS#1.
* \note This function requires 'libtasn1' development files and library, which is used
* to parse and extract the PrivateKey element from an ASN.1 PKCS#8 structure.
*/
static QByteArray pkcs8PrivateKey( QByteArray &pkcs8Der ) SIP_SKIP;
#endif
/**
* Return list of certificate, private key and algorithm (as PEM text) for a PKCS#12 bundle
* \param bundlepath File path to the PKCS bundle
* \param bundlepass Passphrase for bundle
* \param reencrypt Whether to re-encrypt the private key with the passphrase
* \returns certificate, private key, key's algorithm type
*/
static QStringList pkcs12BundleToPem( const QString &bundlepath,
const QString &bundlepass = QString(),
bool reencrypt = true );
/**
* Return list of CA certificates (as QSslCertificate) for a PKCS#12 bundle
* \param bundlepath File path to the PKCS bundle
* \param bundlepass Passphrase for bundle
* \returns list of certificate
*/
static QList<QSslCertificate> pkcs12BundleCas( const QString &bundlepath,
const QString &bundlepass = QString() );
/**
* \brief certsToPemText dump a list of QSslCertificates to PEM text
* \param certs list of certs
* \return a byte array of concatenated certificates as PEM text
*/
static QByteArray certsToPemText( const QList<QSslCertificate> &certs );
/**
* Write a temporary file for a PEM text of cert/key/CAs bundle component
* \param pemtext Component content as PEM text
* \param name Name of file
* \returns File path to temporary file
*/
static QString pemTextToTempFile( const QString &name, const QByteArray &pemtext );
/**
* Get the general name for CA source enum type
* \param source The enum source type for the CA
* \param single Whether to return singular or plural description
*/
static QString getCaSourceName( QgsAuthCertUtils::CaCertSource source, bool single = false );
//! Get the general name via RFC 5280 resolution
static QString resolvedCertName( const QSslCertificate &cert, bool issuer = false );
/**
* Get combined distinguished name for certificate
* \param qcert Qt SSL cert object
* \param acert QCA SSL cert object to add more info to the output
* \param issuer Whether to return cert's subject or issuer combined name
* \note not available in Python bindings
*/
static QString getCertDistinguishedName( const QSslCertificate &qcert,
const QCA::Certificate &acert = QCA::Certificate(),
bool issuer = false ) SIP_SKIP;
//! Get the general name for certificate trust
static QString getCertTrustName( QgsAuthCertUtils::CertTrustPolicy trust );
//! Get string with colon delimiters every 2 characters
static QString getColonDelimited( const QString &txt );
/**
* Get the sha1 hash for certificate
* \param cert Qt SSL certificate to generate hash from
* \param formatted Whether to colon-delimit the hash
*/
static QString shaHexForCert( const QSslCertificate &cert, bool formatted = false );
/**
* Convert a QSslCertificate to a QCA::Certificate.
* \note not available in Python bindings
*/
static QCA::Certificate qtCertToQcaCert( const QSslCertificate &cert ) SIP_SKIP;
/**
* Convert a QList of QSslCertificate to a QCA::CertificateCollection.
* \note not available in Python bindings
*/
static QCA::CertificateCollection qtCertsToQcaCollection( const QList<QSslCertificate> &certs ) SIP_SKIP;
/**
* PKI key/cert bundle from file path, e.g. from .p12 or pfx files.
* \note not available in Python bindings
*/
static QCA::KeyBundle qcaKeyBundle( const QString &path, const QString &pass ) SIP_SKIP;
/**
* Certificate validity check messages per enum.
* \note not available in Python bindings
*/
static QString qcaValidityMessage( QCA::Validity validity ) SIP_SKIP;
/**
* Certificate signature algorithm strings per enum.
* \note not available in Python bindings
*/
static QString qcaSignatureAlgorithm( QCA::SignatureAlgorithm algorithm ) SIP_SKIP;
/**
* Certificate well-known constraint strings per enum.
* \note not available in Python bindings
*/
static QString qcaKnownConstraint( QCA::ConstraintTypeKnown constraint ) SIP_SKIP;
/**
* Certificate usage type strings per enum
* \note not available in Python bindings
*/
static QString certificateUsageTypeString( QgsAuthCertUtils::CertUsageType usagetype ) SIP_SKIP;
//! Try to determine the certificates usage types
static QList<QgsAuthCertUtils::CertUsageType> certificateUsageTypes( const QSslCertificate &cert );
//! Get whether a certificate is an Authority
static bool certificateIsAuthority( const QSslCertificate &cert );
//! Get whether a certificate can sign other certificates
static bool certificateIsIssuer( const QSslCertificate &cert );
//! Get whether a certificate is an Authority or can at least sign other certificates
static bool certificateIsAuthorityOrIssuer( const QSslCertificate &cert );
//! Get whether a certificate is probably used for a SSL server
static bool certificateIsSslServer( const QSslCertificate &cert );
//! Get whether a certificate is probably used for a client identity
static bool certificateIsSslClient( const QSslCertificate &cert );
//! Get short strings describing an SSL error
static QString sslErrorEnumString( QSslError::SslError errenum );
/**
* Get short strings describing SSL errors.
* \note not available in Python bindings
*/
static QList<QPair<QSslError::SslError, QString> > sslErrorEnumStrings() SIP_SKIP;
/**
* \brief certIsCurrent checks if \a cert is viable for its not before and not after dates
* \param cert certificate to be checked
*/
static bool certIsCurrent( const QSslCertificate &cert );
/**
* \brief certViabilityErrors checks basic characteristics (validity dates, blacklisting, etc.) of given \a cert
* \param cert certificate to be checked
* \return list of QSslError (will return NO ERRORS if a null QSslCertificate is passed)
*/
static QList<QSslError> certViabilityErrors( const QSslCertificate &cert );
/**
* \brief certIsViable checks for viability errors of \a cert and whether it is NULL
* \param cert certificate to be checked
* \return false if cert is NULL or has viability errors
*/
static bool certIsViable( const QSslCertificate &cert );
/**
* \brief validateCertChain validates the given \a certificateChain
* \param certificateChain list of certificates to be checked, with leaf first and with optional root CA last
* \param hostName (optional) name of the host to be verified
* \param trustRootCa if true the CA will be added to the trusted CAs for this validation check
* \return list of QSslError, if the list is empty then the cert chain is valid
*/
static QList<QSslError> validateCertChain( const QList<QSslCertificate> &certificateChain,
const QString &hostName = QString(),
bool trustRootCa = false ) ;
/**
* \brief validatePKIBundle validate the PKI bundle by checking the certificate chain, the
* expiration and effective dates, optionally trusts the root CA
* \param bundle
* \param useIntermediates if true the intermediate certs are also checked
* \param trustRootCa if true the CA will be added to the trusted CAs for this validation check (if useIntermediates is false)
* this option is ignored and set to false
* \return a list of error strings, if the list is empty then the PKI bundle is valid
*/
static QStringList validatePKIBundle( QgsPkiBundle &bundle, bool useIntermediates = true, bool trustRootCa = false );
private:
static void appendDirSegment_( QStringList &dirname, const QString &segment, QString value );
};
#endif // QGSAUTHCERTUTILS_H