1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40
41/*!
42 \class QSslKey
43 \brief The QSslKey class provides an interface for private and public keys.
44 \since 4.3
45
46 \reentrant
47 \ingroup network
48 \ingroup ssl
49 \ingroup shared
50 \inmodule QtNetwork
51
52 QSslKey provides a simple API for managing keys.
53
54 \sa QSslSocket, QSslCertificate, QSslCipher
55*/
56
57#include "qsslkey.h"
58#include "qsslkey_p.h"
59#ifndef QT_NO_OPENSSL
60#include "qsslsocket_openssl_symbols_p.h"
61#endif
62#include "qsslsocket.h"
63#include "qsslsocket_p.h"
64#include "qasn1element_p.h"
65
66#include <QtCore/qatomic.h>
67#include <QtCore/qbytearray.h>
68#include <QtCore/qiodevice.h>
69#ifndef QT_NO_DEBUG_STREAM
70#include <QtCore/qdebug.h>
71#endif
72
73QT_BEGIN_NAMESPACE
74
75/*!
76 \fn void QSslKeyPrivate::clear(bool deep)
77 \internal
78 */
79
80/*!
81 \fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
82 bool deepClear)
83 \internal
84
85 Allocates a new rsa or dsa struct and decodes \a pem into it
86 according to the current algorithm and type.
87
88 If \a deepClear is true, the rsa/dsa struct is freed if it is was
89 already allocated, otherwise we "leak" memory (which is exactly
90 what we want for copy construction).
91
92 If \a passPhrase is non-empty, it will be used for decrypting
93 \a pem.
94*/
95
96/*!
97 Constructs a null key.
98
99 \sa isNull()
100*/
101QSslKey::QSslKey()
102 : d(new QSslKeyPrivate)
103{
104}
105
106/*!
107 \internal
108*/
109QByteArray QSslKeyPrivate::pemHeader() const
110{
111 if (type == QSsl::PublicKey)
112 return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----");
113 else if (algorithm == QSsl::Rsa)
114 return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----");
115 else if (algorithm == QSsl::Dsa)
116 return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
117 else if (algorithm == QSsl::Ec)
118 return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----");
119 else if (algorithm == QSsl::Dh)
120 return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
121
122 Q_UNREACHABLE();
123 return QByteArray();
124}
125
126static QByteArray pkcs8Header(bool encrypted)
127{
128 return encrypted
129 ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
130 : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
131}
132
133/*!
134 \internal
135*/
136QByteArray QSslKeyPrivate::pemFooter() const
137{
138 if (type == QSsl::PublicKey)
139 return QByteArrayLiteral("-----END PUBLIC KEY-----");
140 else if (algorithm == QSsl::Rsa)
141 return QByteArrayLiteral("-----END RSA PRIVATE KEY-----");
142 else if (algorithm == QSsl::Dsa)
143 return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
144 else if (algorithm == QSsl::Ec)
145 return QByteArrayLiteral("-----END EC PRIVATE KEY-----");
146 else if (algorithm == QSsl::Dh)
147 return QByteArrayLiteral("-----END PRIVATE KEY-----");
148
149 Q_UNREACHABLE();
150 return QByteArray();
151}
152
153static QByteArray pkcs8Footer(bool encrypted)
154{
155 return encrypted
156 ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
157 : QByteArrayLiteral("-----END PRIVATE KEY-----");
158}
159
160/*!
161 \internal
162
163 Returns a DER key formatted as PEM.
164*/
165QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
166{
167 QByteArray pem(der.toBase64());
168
169 const int lineWidth = 64; // RFC 1421
170 const int newLines = pem.size() / lineWidth;
171 const bool rem = pem.size() % lineWidth;
172
173 // ### optimize
174 for (int i = 0; i < newLines; ++i)
175 pem.insert(i: (i + 1) * lineWidth + i, c: '\n');
176 if (rem)
177 pem.append(c: '\n'); // ###
178
179 QByteArray extra;
180 if (!headers.isEmpty()) {
181 QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd();
182 do {
183 --it;
184 extra += it.key() + ": " + it.value() + '\n';
185 } while (it != headers.constBegin());
186 extra += '\n';
187 }
188
189 if (isEncryptedPkcs8(der)) {
190 pem.prepend(a: pkcs8Header(encrypted: true) + '\n' + extra);
191 pem.append(a: pkcs8Footer(encrypted: true) + '\n');
192#if !QT_CONFIG(openssl)
193 } else if (isPkcs8) {
194 pem.prepend(pkcs8Header(false) + '\n' + extra);
195 pem.append(pkcs8Footer(false) + '\n');
196#endif
197 } else {
198 pem.prepend(a: pemHeader() + '\n' + extra);
199 pem.append(a: pemFooter() + '\n');
200 }
201
202 return pem;
203}
204
205/*!
206 \internal
207
208 Returns a PEM key formatted as DER.
209*/
210QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
211{
212 QByteArray header = pemHeader();
213 QByteArray footer = pemFooter();
214
215 QByteArray der(pem);
216
217 int headerIndex = der.indexOf(a: header);
218 int footerIndex = der.indexOf(a: footer, from: headerIndex + header.length());
219 if (type != QSsl::PublicKey) {
220 if (headerIndex == -1 || footerIndex == -1) {
221 header = pkcs8Header(encrypted: true);
222 footer = pkcs8Footer(encrypted: true);
223 headerIndex = der.indexOf(a: header);
224 footerIndex = der.indexOf(a: footer, from: headerIndex + header.length());
225 }
226 if (headerIndex == -1 || footerIndex == -1) {
227 header = pkcs8Header(encrypted: false);
228 footer = pkcs8Footer(encrypted: false);
229 headerIndex = der.indexOf(a: header);
230 footerIndex = der.indexOf(a: footer, from: headerIndex + header.length());
231 }
232 }
233 if (headerIndex == -1 || footerIndex == -1)
234 return QByteArray();
235
236 der = der.mid(index: headerIndex + header.size(), len: footerIndex - (headerIndex + header.size()));
237
238 if (der.contains(c: "Proc-Type:")) {
239 // taken from QHttpNetworkReplyPrivate::parseHeader
240 int i = 0;
241 while (i < der.count()) {
242 int j = der.indexOf(c: ':', from: i); // field-name
243 if (j == -1)
244 break;
245 const QByteArray field = der.mid(index: i, len: j - i).trimmed();
246 j++;
247 // any number of LWS is allowed before and after the value
248 QByteArray value;
249 do {
250 i = der.indexOf(c: '\n', from: j);
251 if (i == -1)
252 break;
253 if (!value.isEmpty())
254 value += ' ';
255 // check if we have CRLF or only LF
256 bool hasCR = (i && der[i-1] == '\r');
257 int length = i -(hasCR ? 1: 0) - j;
258 value += der.mid(index: j, len: length).trimmed();
259 j = ++i;
260 } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t'));
261 if (i == -1)
262 break; // something is wrong
263
264 headers->insert(akey: field, avalue: value);
265 }
266 der = der.mid(index: i);
267 }
268
269 return QByteArray::fromBase64(base64: der); // ignores newlines
270}
271
272bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const
273{
274 static const QVector<QByteArray> pbes1OIds {
275 // PKCS5
276 {PKCS5_MD2_DES_CBC_OID},
277 {PKCS5_MD2_RC2_CBC_OID},
278 {PKCS5_MD5_DES_CBC_OID},
279 {PKCS5_MD5_RC2_CBC_OID},
280 {PKCS5_SHA1_DES_CBC_OID},
281 {PKCS5_SHA1_RC2_CBC_OID},
282 };
283 QAsn1Element elem;
284 if (!elem.read(data: der) || elem.type() != QAsn1Element::SequenceType)
285 return false;
286
287 const QVector<QAsn1Element> items = elem.toVector();
288 if (items.size() != 2
289 || items[0].type() != QAsn1Element::SequenceType
290 || items[1].type() != QAsn1Element::OctetStringType) {
291 return false;
292 }
293
294 const QVector<QAsn1Element> encryptionSchemeContainer = items[0].toVector();
295 if (encryptionSchemeContainer.size() != 2
296 || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
297 || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
298 return false;
299 }
300
301 const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
302 return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID
303 || pbes1OIds.contains(t: encryptionScheme)
304 || encryptionScheme.startsWith(PKCS12_OID);
305}
306
307/*!
308 Constructs a QSslKey by decoding the string in the byte array
309 \a encoded using a specified \a algorithm and \a encoding format.
310 \a type specifies whether the key is public or private.
311
312 If the key is encrypted then \a passPhrase is used to decrypt it.
313
314 After construction, use isNull() to check if \a encoded contained
315 a valid key.
316*/
317QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
318 QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
319 : d(new QSslKeyPrivate)
320{
321 d->type = type;
322 d->algorithm = algorithm;
323 if (encoding == QSsl::Der)
324 d->decodeDer(der: encoded, passPhrase);
325 else
326 d->decodePem(pem: encoded, passPhrase);
327}
328
329/*!
330 Constructs a QSslKey by reading and decoding data from a
331 \a device using a specified \a algorithm and \a encoding format.
332 \a type specifies whether the key is public or private.
333
334 If the key is encrypted then \a passPhrase is used to decrypt it.
335
336 After construction, use isNull() to check if \a device provided
337 a valid key.
338*/
339QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
340 QSsl::KeyType type, const QByteArray &passPhrase)
341 : d(new QSslKeyPrivate)
342{
343 QByteArray encoded;
344 if (device)
345 encoded = device->readAll();
346 d->type = type;
347 d->algorithm = algorithm;
348 if (encoding == QSsl::Der)
349 d->decodeDer(der: encoded, passPhrase);
350 else
351 d->decodePem(pem: encoded, passPhrase);
352}
353
354/*!
355 \since 5.0
356 Constructs a QSslKey from a valid native key \a handle.
357 \a type specifies whether the key is public or private.
358
359 QSslKey will take ownership for this key and you must not
360 free the key using the native library.
361*/
362QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
363 : d(new QSslKeyPrivate)
364{
365#ifndef QT_NO_OPENSSL
366 EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
367 if (!evpKey || !d->fromEVP_PKEY(pkey: evpKey)) {
368 d->opaque = evpKey;
369 d->algorithm = QSsl::Opaque;
370 } else {
371 q_EVP_PKEY_free(a: evpKey);
372 }
373#else
374 d->opaque = handle;
375 d->algorithm = QSsl::Opaque;
376#endif
377 d->type = type;
378 d->isNull = !d->opaque;
379}
380
381/*!
382 Constructs an identical copy of \a other.
383*/
384QSslKey::QSslKey(const QSslKey &other) : d(other.d)
385{
386}
387
388QSslKey::QSslKey(QSslKey &&other) noexcept
389 : d(nullptr)
390{
391 qSwap(value1&: d, value2&: other.d);
392}
393
394QSslKey &QSslKey::operator=(QSslKey &&other) noexcept
395{
396 if (this == &other)
397 return *this;
398
399 // If no one else is referencing the key data we want to make sure
400 // before we swap the d-ptr that it is not left in memory.
401 d.reset();
402 qSwap(value1&: d, value2&: other.d);
403 return *this;
404}
405
406/*!
407 Destroys the QSslKey object.
408*/
409QSslKey::~QSslKey()
410{
411}
412
413/*!
414 Copies the contents of \a other into this key, making the two keys
415 identical.
416
417 Returns a reference to this QSslKey.
418*/
419QSslKey &QSslKey::operator=(const QSslKey &other)
420{
421 d = other.d;
422 return *this;
423}
424
425/*!
426 \fn void QSslKey::swap(QSslKey &other)
427 \since 5.0
428
429 Swaps this ssl key with \a other. This function is very fast and
430 never fails.
431*/
432
433/*!
434 Returns \c true if this is a null key; otherwise false.
435
436 \sa clear()
437*/
438bool QSslKey::isNull() const
439{
440 return d->isNull;
441}
442
443/*!
444 Clears the contents of this key, making it a null key.
445
446 \sa isNull()
447*/
448void QSslKey::clear()
449{
450 d = new QSslKeyPrivate;
451}
452
453/*!
454 Returns the length of the key in bits, or -1 if the key is null.
455*/
456int QSslKey::length() const
457{
458 return d->length();
459}
460
461/*!
462 Returns the type of the key (i.e., PublicKey or PrivateKey).
463*/
464QSsl::KeyType QSslKey::type() const
465{
466 return d->type;
467}
468
469/*!
470 Returns the key algorithm.
471*/
472QSsl::KeyAlgorithm QSslKey::algorithm() const
473{
474 return d->algorithm;
475}
476
477/*!
478 Returns the key in DER encoding.
479
480 The \a passPhrase argument should be omitted as DER cannot be
481 encrypted. It will be removed in a future version of Qt.
482*/
483QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
484{
485 if (d->isNull || d->algorithm == QSsl::Opaque)
486 return QByteArray();
487
488 // Encrypted DER is nonsense, see QTBUG-41038.
489 if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty())
490 return QByteArray();
491
492#ifndef QT_NO_OPENSSL
493 QMap<QByteArray, QByteArray> headers;
494 return d->derFromPem(pem: toPem(passPhrase), headers: &headers);
495#else
496 return d->derData;
497#endif
498}
499
500/*!
501 Returns the key in PEM encoding. The result is encrypted with
502 \a passPhrase if the key is a private key and \a passPhrase is
503 non-empty.
504*/
505QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
506{
507 return d->toPem(passPhrase);
508}
509
510/*!
511 Returns a pointer to the native key handle, if there is
512 one, else \nullptr.
513
514 You can use this handle together with the native API to access
515 extended information about the key.
516
517 \warning Use of this function has a high probability of being
518 non-portable, and its return value may vary across platforms, and
519 between minor Qt releases.
520*/
521Qt::HANDLE QSslKey::handle() const
522{
523 return d->handle();
524}
525
526/*!
527 Returns \c true if this key is equal to \a other; otherwise returns \c false.
528*/
529bool QSslKey::operator==(const QSslKey &other) const
530{
531 if (isNull())
532 return other.isNull();
533 if (other.isNull())
534 return isNull();
535 if (algorithm() != other.algorithm())
536 return false;
537 if (type() != other.type())
538 return false;
539 if (length() != other.length())
540 return false;
541 if (algorithm() == QSsl::Opaque)
542 return handle() == other.handle();
543 return toDer() == other.toDer();
544}
545
546/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
547
548 Returns \c true if this key is not equal to key \a other; otherwise
549 returns \c false.
550*/
551
552#ifndef QT_NO_DEBUG_STREAM
553QDebug operator<<(QDebug debug, const QSslKey &key)
554{
555 QDebugStateSaver saver(debug);
556 debug.resetFormat().nospace();
557 debug << "QSslKey("
558 << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
559 << ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" :
560 (key.algorithm() == QSsl::Rsa ? "RSA" :
561 (key.algorithm() == QSsl::Dsa ? "DSA" :
562 (key.algorithm() == QSsl::Dh ? "DH" : "EC"))))
563 << ", " << key.length()
564 << ')';
565 return debug;
566}
567#endif
568
569QT_END_NAMESPACE
570

source code of qtbase/src/network/ssl/qsslkey_p.cpp