Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtlskey_openssl.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include "qtlskey_openssl_p.h"
7
8#include <QtNetwork/private/qsslkey_p.h>
9
10#include <QtNetwork/qsslsocket.h>
11
12#include <QtCore/qscopeguard.h>
13
15
16namespace QTlsPrivate {
17
19 const QByteArray &passPhrase, bool deepClear)
20{
21 if (der.isEmpty())
22 return;
23
24 keyType = type;
26
27 QMap<QByteArray, QByteArray> headers;
28 const auto pem = pemFromDer(der, headers);
29
30 decodePem(type, algorithm, pem, passPhrase, deepClear);
31}
32
34 const QByteArray &passPhrase, bool deepClear)
35{
36 if (pem.isEmpty())
37 return;
38
39 keyType = type;
41
42 clear(deepClear);
43
44 BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
45 if (!bio)
46 return;
47
48 const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
49
50 void *phrase = const_cast<char *>(passPhrase.data());
51
52#ifdef OPENSSL_NO_DEPRECATED_3_0
53 if (type == QSsl::PublicKey)
54 genericKey = q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase);
55 else
56 genericKey = q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
58 if (keyIsNull)
60#else
61
62 if (algorithm == QSsl::Rsa) {
63 RSA *result = (type == QSsl::PublicKey)
64 ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, nullptr, phrase)
65 : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, phrase);
66 if (rsa && rsa == result)
67 keyIsNull = false;
68 } else if (algorithm == QSsl::Dsa) {
69 DSA *result = (type == QSsl::PublicKey)
70 ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, nullptr, phrase)
71 : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase);
72 if (dsa && dsa == result)
73 keyIsNull = false;
74 } else if (algorithm == QSsl::Dh) {
75 EVP_PKEY *result = (type == QSsl::PublicKey)
76 ? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase)
77 : q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
78 if (result)
80 if (dh)
81 keyIsNull = false;
83#ifndef OPENSSL_NO_EC
84 } else if (algorithm == QSsl::Ec) {
85 EC_KEY *result = (type == QSsl::PublicKey)
86 ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, nullptr, phrase)
87 : q_PEM_read_bio_ECPrivateKey(bio, &ec, nullptr, phrase);
88 if (ec && ec == result)
89 keyIsNull = false;
90#endif // OPENSSL_NO_EC
91 }
92
93#endif // OPENSSL_NO_DEPRECATED_3_0
94}
95
96QByteArray TlsKeyOpenSSL::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
97{
99 QByteArray footer = pemFooter();
100
101 QByteArray der(pem);
102
103 int headerIndex = der.indexOf(header);
104 int footerIndex = der.indexOf(footer, headerIndex + header.size());
105 if (type() != QSsl::PublicKey) {
106 if (headerIndex == -1 || footerIndex == -1) {
107 header = pkcs8Header(true);
108 footer = pkcs8Footer(true);
109 headerIndex = der.indexOf(header);
110 footerIndex = der.indexOf(footer, headerIndex + header.size());
111 }
112 if (headerIndex == -1 || footerIndex == -1) {
113 header = pkcs8Header(false);
114 footer = pkcs8Footer(false);
115 headerIndex = der.indexOf(header);
116 footerIndex = der.indexOf(footer, headerIndex + header.size());
117 }
118 }
119 if (headerIndex == -1 || footerIndex == -1)
120 return QByteArray();
121
122 der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
123
124 if (der.contains("Proc-Type:")) {
125 // taken from QHttpNetworkReplyPrivate::parseHeader
126 int i = 0;
127 while (i < der.size()) {
128 int j = der.indexOf(':', i); // field-name
129 if (j == -1)
130 break;
131 const QByteArray field = der.mid(i, j - i).trimmed();
132 j++;
133 // any number of LWS is allowed before and after the value
135 do {
136 i = der.indexOf('\n', j);
137 if (i == -1)
138 break;
139 if (!value.isEmpty())
140 value += ' ';
141 // check if we have CRLF or only LF
142 bool hasCR = (i && der[i-1] == '\r');
143 int length = i -(hasCR ? 1: 0) - j;
144 value += der.mid(j, length).trimmed();
145 j = ++i;
146 } while (i < der.size() && (der.at(i) == ' ' || der.at(i) == '\t'));
147 if (i == -1)
148 break; // something is wrong
149
150 headers->insert(field, value);
151 }
152 der = der.mid(i);
153 }
154
155 return QByteArray::fromBase64(der); // ignores newlines
156}
157
158void TlsKeyOpenSSL::clear(bool deep)
159{
160 keyIsNull = true;
161
162#ifndef OPENSSL_NO_DEPRECATED_3_0
163 if (algorithm() == QSsl::Rsa && rsa) {
164 if (deep)
166 rsa = nullptr;
167 }
168 if (algorithm() == QSsl::Dsa && dsa) {
169 if (deep)
171 dsa = nullptr;
172 }
173 if (algorithm() == QSsl::Dh && dh) {
174 if (deep)
175 q_DH_free(dh);
176 dh = nullptr;
177 }
178#ifndef OPENSSL_NO_EC
179 if (algorithm() == QSsl::Ec && ec) {
180 if (deep)
182 ec = nullptr;
183 }
184#endif
185#endif // OPENSSL_NO_DEPRECATED_3_0
186
187 if (algorithm() == QSsl::Opaque && opaque) {
188 if (deep)
190 opaque = nullptr;
191 }
192
193 if (genericKey) {
194 // None of the above cleared it. genericKey is either
195 // initialised by PEM read operation, or from X509, and
196 // we are the owners and not sharing. So we free it.
198 genericKey = nullptr;
199 }
200}
201
203{
205 return Qt::HANDLE(opaque);
206
207#ifndef OPENSSL_NO_DEPRECATED_3_0
208 switch (keyAlgorithm) {
209 case QSsl::Rsa:
210 return Qt::HANDLE(rsa);
211 case QSsl::Dsa:
212 return Qt::HANDLE(dsa);
213 case QSsl::Dh:
214 return Qt::HANDLE(dh);
215#ifndef OPENSSL_NO_EC
216 case QSsl::Ec:
217 return Qt::HANDLE(ec);
218#endif
219 default:
220 return Qt::HANDLE(nullptr);
221 }
222#else
223 qCWarning(lcTlsBackend,
224 "This version of OpenSSL disabled direct manipulation with RSA/DSA/DH/EC_KEY structures, consider using QSsl::Opaque instead.");
225 return Qt::HANDLE(genericKey);
226#endif
227}
228
230{
231 if (isNull() || algorithm() == QSsl::Opaque)
232 return -1;
233
234#ifndef OPENSSL_NO_DEPRECATED_3_0
235 switch (algorithm()) {
236 case QSsl::Rsa:
237 return q_RSA_bits(rsa);
238 case QSsl::Dsa:
239 return q_DSA_bits(dsa);
240 case QSsl::Dh:
241 return q_DH_bits(dh);
242#ifndef OPENSSL_NO_EC
243 case QSsl::Ec:
245#endif
246 default:
247 return -1;
248 }
249#else // OPENSSL_NO_DEPRECATED_3_0
251 return q_EVP_PKEY_get_bits(genericKey);
252#endif // OPENSSL_NO_DEPRECATED_3_0
253}
254
256{
258 return {};
259
260 const EVP_CIPHER *cipher = nullptr;
261 if (type() == QSsl::PrivateKey && !passPhrase.isEmpty()) {
262#ifndef OPENSSL_NO_DES
263 cipher = q_EVP_des_ede3_cbc();
264#else
265 return {};
266#endif
267 }
268
269 BIO *bio = q_BIO_new(q_BIO_s_mem());
270 if (!bio)
271 return {};
272
273 const auto bioRaii = qScopeGuard([bio]{q_BIO_free(bio);});
274
275#ifndef OPENSSL_NO_DEPRECATED_3_0
276
277#define write_pubkey(alg, key) q_PEM_write_bio_##alg##_PUBKEY(bio, key)
278#define write_privatekey(alg, key) \
279 q_PEM_write_bio_##alg##PrivateKey(bio, key, cipher, (uchar *)passPhrase.data(), \
280 passPhrase.size(), nullptr, nullptr)
281
282#else
283
284#define write_pubkey(alg, key) q_PEM_write_bio_PUBKEY(bio, genericKey)
285#define write_privatekey(alg, key) \
286 q_PEM_write_bio_PrivateKey_traditional(bio, genericKey, cipher, (uchar *)passPhrase.data(), passPhrase.size(), nullptr, nullptr)
287
288#endif // OPENSSL_NO_DEPRECATED_3_0
289
290 bool fail = false;
291 if (algorithm() == QSsl::Rsa) {
292 if (type() == QSsl::PublicKey) {
293 if (!write_pubkey(RSA, rsa))
294 fail = true;
295 } else if (!write_privatekey(RSA, rsa)) {
296 fail = true;
297 }
298 } else if (algorithm() == QSsl::Dsa) {
299 if (type() == QSsl::PublicKey) {
300 if (!write_pubkey(DSA, dsa))
301 fail = true;
302 } else if (!write_privatekey(DSA, dsa)) {
303 fail = true;
304 }
305 } else if (algorithm() == QSsl::Dh) {
306#ifdef OPENSSL_NO_DEPRECATED_3_0
307 EVP_PKEY *result = genericKey;
308#else
309 EVP_PKEY *result = q_EVP_PKEY_new();
310 const auto guard = qScopeGuard([result]{if (result) q_EVP_PKEY_free(result);});
311 if (!result || !q_EVP_PKEY_set1_DH(result, dh)) {
312 fail = true;
313 } else
314#endif
315 if (type() == QSsl::PublicKey) {
317 fail = true;
318 } else if (!q_PEM_write_bio_PrivateKey(bio, result, cipher, (uchar *)passPhrase.data(),
319 passPhrase.size(), nullptr, nullptr)) {
320 fail = true;
321 }
322#ifndef OPENSSL_NO_EC
323 } else if (algorithm() == QSsl::Ec) {
324 if (type() == QSsl::PublicKey) {
325 if (!write_pubkey(EC, ec))
326 fail = true;
327 } else {
328 if (!write_privatekey(EC, ec))
329 fail = true;
330 }
331#endif
332 } else {
333 fail = true;
334 }
335
336 QByteArray pem;
337 if (!fail) {
338 char *data = nullptr;
339 const long size = q_BIO_get_mem_data(bio, &data);
340 if (size > 0 && data)
341 pem = QByteArray(data, size);
342 } else {
344 }
345
346 return pem;
347}
348
350{
351 EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
352 if (!evpKey || !fromEVP_PKEY(evpKey)) {
353 opaque = evpKey;
355 } else {
356 q_EVP_PKEY_free(evpKey);
357 }
358
359 keyType = expectedType;
360 keyIsNull = !opaque;
361}
362
363bool TlsKeyOpenSSL::fromEVP_PKEY(EVP_PKEY *pkey)
364{
365 if (!pkey)
366 return false;
367
368#ifndef OPENSSL_NO_DEPRECATED_3_0
369#define get_key(key, alg) key = q_EVP_PKEY_get1_##alg(pkey)
370#else
371#define get_key(key, alg) q_EVP_PKEY_up_ref(pkey); genericKey = pkey;
372#endif
373
374 switch (q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey))) {
375 case EVP_PKEY_RSA:
376 keyIsNull = false;
379 get_key(rsa, RSA);
380 return true;
381 case EVP_PKEY_DSA:
382 keyIsNull = false;
385 get_key(dsa, DSA);
386 return true;
387 case EVP_PKEY_DH:
388 keyIsNull = false;
391 get_key(dh, DH);
392 return true;
393#ifndef OPENSSL_NO_EC
394 case EVP_PKEY_EC:
395 keyIsNull = false;
398 get_key(ec, EC_KEY);
399 return true;
400#endif
401 default:;
402 // Unknown key type. This could be handled as opaque, but then
403 // we'd eventually leak memory since we wouldn't be able to free
404 // the underlying EVP_PKEY structure. For now, we won't support
405 // this.
406 }
407
408 return false;
409}
410
412 const QByteArray &key, const QByteArray &iv, bool enc)
413{
414 const EVP_CIPHER *type = nullptr;
415 int i = 0, len = 0;
416
417 switch (cipher) {
418 case Cipher::DesCbc:
419#ifndef OPENSSL_NO_DES
421#endif
422 break;
424#ifndef OPENSSL_NO_DES
426#endif
427 break;
428 case Cipher::Rc2Cbc:
429#ifndef OPENSSL_NO_RC2
431#endif
432 break;
435 break;
438 break;
441 break;
442 }
443
444 if (type == nullptr)
445 return {};
446
448 output.resize(data.size() + EVP_MAX_BLOCK_LENGTH);
449
450 EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new();
452 if (q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc) != 1) {
455 return {};
456 }
457
459 if (cipher == Cipher::Rc2Cbc)
460 q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
461
462 q_EVP_CipherInit_ex(ctx, nullptr, nullptr,
463 reinterpret_cast<const unsigned char *>(key.constData()),
464 reinterpret_cast<const unsigned char *>(iv.constData()),
465 enc);
467 reinterpret_cast<unsigned char *>(output.data()), &len,
468 reinterpret_cast<const unsigned char *>(data.constData()), data.size());
470 reinterpret_cast<unsigned char *>(output.data()) + len, &i);
471 len += i;
472
475
476 return output.left(len);
477}
478
480 const QByteArray &key, const QByteArray &iv) const
481{
482 return doCrypt(cipher, data, key, iv, false);
483}
484
486 const QByteArray &key, const QByteArray &iv) const
487{
488 return doCrypt(cipher, data, key, iv, true);
489}
490
492{
493 TlsKeyOpenSSL *tlsKey = new TlsKeyOpenSSL;
494 std::unique_ptr<TlsKeyOpenSSL> keyRaii(tlsKey);
495
496 tlsKey->keyType = QSsl::PublicKey;
497
498#ifndef OPENSSL_NO_DEPRECATED_3_0
499
500#define get_pubkey(keyName, alg) tlsKey->keyName = q_EVP_PKEY_get1_##alg(pkey)
501
502#else
503
504#define get_pubkey(a, b) tlsKey->genericKey = pkey
505
506#endif
507
508 EVP_PKEY *pkey = q_X509_get_pubkey(x);
509 Q_ASSERT(pkey);
510 const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
511
512 if (keyType == EVP_PKEY_RSA) {
513 get_pubkey(rsa, RSA);
514 tlsKey->keyAlgorithm = QSsl::Rsa;
515 tlsKey->keyIsNull = false;
516 } else if (keyType == EVP_PKEY_DSA) {
517 get_pubkey(dsa, DSA);
518 tlsKey->keyAlgorithm = QSsl::Dsa;
519 tlsKey->keyIsNull = false;
520#ifndef OPENSSL_NO_EC
521 } else if (keyType == EVP_PKEY_EC) {
522 get_pubkey(ec, EC_KEY);
523 tlsKey->keyAlgorithm = QSsl::Ec;
524 tlsKey->keyIsNull = false;
525#endif
526 } else if (keyType == EVP_PKEY_DH) {
527 // DH unsupported (key is null)
528 } else {
529 // error? (key is null)
530 }
531
532#ifndef OPENSSL_NO_DEPRECATED_3_0
533 q_EVP_PKEY_free(pkey);
534#endif
535
536 return keyRaii.release();
537}
538
539} // namespace QTlsPrivate
540
\inmodule QtCore
Definition qbytearray.h:57
QByteArray trimmed() const &
Definition qbytearray.h:262
static QByteArray fromBase64(const QByteArray &base64, Base64Options options=Base64Encoding)
QByteArray mid(qsizetype index, qsizetype len=-1) const &
static bool supportsSsl()
Returns true if this platform supports SSL; otherwise, returns false.
static void logAndClearErrorQueue()
KeyType type() const override
QByteArray pemFromDer(const QByteArray &der, const QMap< QByteArray, QByteArray > &headers) const override
KeyAlgorithm algorithm() const override
static QByteArray pkcs8Footer(bool encrypted)
static QByteArray pkcs8Header(bool encrypted)
bool isNull() const override
void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) override
void fromHandle(Qt::HANDLE opaque, KeyType expectedType) override
void clear(bool deep) override
bool fromEVP_PKEY(EVP_PKEY *pkey)
QByteArray derFromPem(const QByteArray &pem, QMap< QByteArray, QByteArray > *headers) const override
void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, const QByteArray &passPhrase, bool deepClear) override
QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) const override
QByteArray toPem(const QByteArray &passPhrase) const override
int length() const override
Qt::HANDLE handle() const override
QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) const override
static TlsKeyOpenSSL * publicKeyFromX509(X509 *x)
QByteArray pemHeader() const
QByteArray pemFooter() const
EGLContext ctx
b clear()
KeyType
Describes the two types of keys QSslKey supports.
Definition qssl.h:22
@ PublicKey
Definition qssl.h:24
@ PrivateKey
Definition qssl.h:23
KeyAlgorithm
Describes the different key algorithms supported by QSslKey.
Definition qssl.h:34
@ Rsa
Definition qssl.h:36
@ Ec
Definition qssl.h:38
@ Opaque
Definition qssl.h:35
@ Dsa
Definition qssl.h:37
@ Dh
Definition qssl.h:39
Combined button and popup list for selecting options.
Namespace containing onternal types that TLS backends implement.
QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, bool enc)
void * HANDLE
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qCWarning(category,...)
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLuint64EXT * result
[6]
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c)
const EVP_CIPHER * q_EVP_des_cbc()
const EVP_CIPHER * q_EVP_aes_192_cbc()
const EVP_CIPHER * q_EVP_aes_256_cbc()
int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned char *key, const unsigned char *iv, int enc)
int q_EVP_PKEY_base_id(EVP_PKEY *a)
DSA * q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d)
void q_DH_free(DH *dh)
int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl)
EC_KEY * q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d)
const EVP_CIPHER * q_EVP_rc2_cbc()
RSA * q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d)
void q_DSA_free(DSA *a)
int q_DSA_bits(DSA *a)
#define q_BIO_get_mem_data(b, pp)
void q_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a)
int q_EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen)
DSA * q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d)
int q_DH_bits(DH *dh)
EVP_PKEY * q_PEM_read_bio_PrivateKey(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d)
EVP_PKEY * q_EVP_PKEY_new()
RSA * q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d)
void q_EVP_PKEY_free(EVP_PKEY *a)
void q_EC_KEY_free(EC_KEY *ecdh)
BIO * q_BIO_new(const BIO_METHOD *a)
const EVP_CIPHER * q_EVP_des_ede3_cbc()
EVP_PKEY * q_PEM_read_bio_PUBKEY(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d)
EVP_PKEY * q_X509_get_pubkey(X509 *a)
int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc)
const BIO_METHOD * q_BIO_s_mem()
int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b)
int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b)
int q_EC_GROUP_get_degree(const EC_GROUP *g)
int q_RSA_bits(RSA *a)
DH * q_EVP_PKEY_get1_DH(EVP_PKEY *a)
int q_PEM_write_bio_PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d, int e, pem_password_cb *f, void *g)
const EVP_CIPHER * q_EVP_aes_128_cbc()
BIO * q_BIO_new_mem_buf(void *a, int b)
EVP_CIPHER_CTX * q_EVP_CIPHER_CTX_new()
int q_EVP_PKEY_type(int a)
const EC_GROUP * q_EC_KEY_get0_group(const EC_KEY *k)
int q_BIO_free(BIO *a)
int q_EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
void q_RSA_free(RSA *a)
EC_KEY * q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d)
#define write_pubkey(alg, key)
#define write_privatekey(alg, key)
#define get_key(key, alg)
#define get_pubkey(keyName, alg)
unsigned char uchar
Definition qtypes.h:32
QT_BEGIN_NAMESPACE typedef uchar * output