1// Copyright (C) 2016 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
4
5/*!
6 \class QSslKey
7 \brief The QSslKey class provides an interface for private and public keys.
8 \since 4.3
9
10 \reentrant
11 \ingroup network
12 \ingroup ssl
13 \ingroup shared
14 \inmodule QtNetwork
15
16 QSslKey provides a simple API for managing keys.
17
18 \sa QSslSocket, QSslCertificate, QSslCipher
19*/
20
21#include "qssl_p.h"
22#include "qsslkey.h"
23#include "qsslkey_p.h"
24#include "qsslsocket.h"
25#include "qsslsocket_p.h"
26#include "qtlsbackend_p.h"
27
28#include <QtCore/qatomic.h>
29#include <QtCore/qbytearray.h>
30#include <QtCore/qiodevice.h>
31#ifndef QT_NO_DEBUG_STREAM
32#include <QtCore/qdebug.h>
33#endif
34
35QT_BEGIN_NAMESPACE
36
37/*!
38 \fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
39 bool deepClear)
40 \internal
41
42 Allocates a new rsa or dsa struct and decodes \a pem into it
43 according to the current algorithm and type.
44
45 If \a deepClear is true, the rsa/dsa struct is freed if it is was
46 already allocated, otherwise we "leak" memory (which is exactly
47 what we want for copy construction).
48
49 If \a passPhrase is non-empty, it will be used for decrypting
50 \a pem.
51*/
52
53/*!
54 \internal
55*/
56QSslKeyPrivate::QSslKeyPrivate()
57{
58 const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
59 if (!tlsBackend)
60 return;
61 backend.reset(p: tlsBackend->createKey());
62 if (backend.get())
63 backend->clear(deepClear: false /*not deep clear*/);
64 else
65 qCWarning(lcSsl, "Active TLS backend does not support key creation");
66}
67
68/*!
69 \internal
70*/
71QSslKeyPrivate::~QSslKeyPrivate()
72{
73 if (backend.get())
74 backend->clear(deepClear: true /*deep clear*/);
75}
76
77QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
78{
79 if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
80 const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey());
81 return cryptor->decrypt(cipher, data, passPhrase: key, iv);
82 }
83
84 return {};
85}
86
87QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
88{
89 if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
90 const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey());
91 return cryptor->encrypt(cipher, data, key, iv);
92 }
93
94 return {};
95}
96
97/*!
98 Constructs a null key.
99
100 \sa isNull()
101*/
102QSslKey::QSslKey()
103 : d(new QSslKeyPrivate)
104{
105}
106
107/*!
108 Constructs a QSslKey by decoding the string in the byte array
109 \a encoded using a specified \a algorithm and \a encoding format.
110 \a type specifies whether the key is public or private.
111
112 If the key is encrypted then \a passPhrase is used to decrypt it.
113
114 After construction, use isNull() to check if \a encoded contained
115 a valid key.
116*/
117QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
118 QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
119 : d(new QSslKeyPrivate)
120{
121 if (auto *tlsKey = d->backend.get()) {
122 if (encoding == QSsl::Der)
123 tlsKey->decodeDer(type, algorithm, der: encoded, passPhrase, deepClear: true /*deep clear*/);
124 else
125 tlsKey->decodePem(type, algorithm, pem: encoded, passPhrase, deepClear: true /*deep clear*/);
126 }
127}
128
129/*!
130 Constructs a QSslKey by reading and decoding data from a
131 \a device using a specified \a algorithm and \a encoding format.
132 \a type specifies whether the key is public or private.
133
134 If the key is encrypted then \a passPhrase is used to decrypt it.
135
136 After construction, use isNull() to check if \a device provided
137 a valid key.
138*/
139QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
140 QSsl::KeyType type, const QByteArray &passPhrase)
141 : d(new QSslKeyPrivate)
142{
143 QByteArray encoded;
144 if (device)
145 encoded = device->readAll();
146
147 if (auto *tlsKey = d->backend.get()) {
148 if (encoding == QSsl::Der)
149 tlsKey->decodeDer(type, algorithm, der: encoded, passPhrase, deepClear: true /*deep clear*/);
150 else
151 tlsKey->decodePem(type, algorithm, pem: encoded, passPhrase, deepClear: true /*deep clear*/);
152 }
153}
154
155/*!
156 \since 5.0
157 Constructs a QSslKey from a valid native key \a handle.
158 \a type specifies whether the key is public or private.
159
160 QSslKey will take ownership for this key and you must not
161 free the key using the native library.
162*/
163QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
164 : d(new QSslKeyPrivate)
165{
166 if (auto *tlsKey = d->backend.get())
167 tlsKey->fromHandle(handle, type);
168}
169
170/*!
171 Constructs an identical copy of \a other.
172*/
173QSslKey::QSslKey(const QSslKey &other) : d(other.d)
174{
175}
176
177QSslKey::QSslKey(QSslKey &&other) noexcept
178 : d(nullptr)
179{
180 qSwap(value1&: d, value2&: other.d);
181}
182
183QSslKey &QSslKey::operator=(QSslKey &&other) noexcept
184{
185 if (this == &other)
186 return *this;
187
188 // If no one else is referencing the key data we want to make sure
189 // before we swap the d-ptr that it is not left in memory.
190 d.reset();
191 qSwap(value1&: d, value2&: other.d);
192 return *this;
193}
194
195/*!
196 Destroys the QSslKey object.
197*/
198QSslKey::~QSslKey()
199{
200}
201
202/*!
203 Copies the contents of \a other into this key, making the two keys
204 identical.
205
206 Returns a reference to this QSslKey.
207*/
208QSslKey &QSslKey::operator=(const QSslKey &other)
209{
210 d = other.d;
211 return *this;
212}
213
214/*!
215 \fn void QSslKey::swap(QSslKey &other)
216 \since 5.0
217
218 Swaps this ssl key with \a other. This function is very fast and
219 never fails.
220*/
221
222/*!
223 Returns \c true if this is a null key; otherwise false.
224
225 \sa clear()
226*/
227bool QSslKey::isNull() const
228{
229 if (const auto *tlsKey = d->backend.get())
230 return tlsKey->isNull();
231
232 return true;
233}
234
235/*!
236 Clears the contents of this key, making it a null key.
237
238 \sa isNull()
239*/
240void QSslKey::clear()
241{
242 d = new QSslKeyPrivate;
243}
244
245/*!
246 Returns the length of the key in bits, or -1 if the key is null.
247*/
248int QSslKey::length() const
249{
250 if (const auto *tlsKey = d->backend.get())
251 return tlsKey->length();
252
253 return -1;
254}
255
256/*!
257 Returns the type of the key (i.e., PublicKey or PrivateKey).
258*/
259QSsl::KeyType QSslKey::type() const
260{
261 if (const auto *tlsKey = d->backend.get())
262 return tlsKey->type();
263
264 return QSsl::PublicKey;
265}
266
267/*!
268 Returns the key algorithm.
269*/
270QSsl::KeyAlgorithm QSslKey::algorithm() const
271{
272 if (const auto *tlsKey = d->backend.get())
273 return tlsKey->algorithm();
274
275 return QSsl::Opaque;
276}
277
278/*!
279 Returns the key in DER encoding.
280
281 The \a passPhrase argument should be omitted as DER cannot be
282 encrypted. It will be removed in a future version of Qt.
283*/
284QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
285{
286 if (isNull() || algorithm() == QSsl::Opaque)
287 return {};
288
289 // Encrypted DER is nonsense, see QTBUG-41038.
290 if (type() == QSsl::PrivateKey && !passPhrase.isEmpty())
291 return {};
292
293 QMap<QByteArray, QByteArray> headers;
294 if (const auto *tlsKey = d->backend.get())
295 return tlsKey->derFromPem(pem: toPem(passPhrase), headers: &headers);
296
297 return {};
298}
299
300/*!
301 Returns the key in PEM encoding. The result is encrypted with
302 \a passPhrase if the key is a private key and \a passPhrase is
303 non-empty.
304*/
305QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
306{
307 if (const auto *tlsKey = d->backend.get())
308 return tlsKey->toPem(passPhrase);
309
310 return {};
311}
312
313/*!
314 Returns a pointer to the native key handle, if there is
315 one, else \nullptr.
316
317 You can use this handle together with the native API to access
318 extended information about the key.
319
320 \warning Use of this function has a high probability of being
321 non-portable, and its return value may vary across platforms, and
322 between minor Qt releases.
323*/
324Qt::HANDLE QSslKey::handle() const
325{
326 if (d->backend.get())
327 return d->backend->handle();
328
329 return nullptr;
330}
331
332/*!
333 Returns \c true if this key is equal to \a other; otherwise returns \c false.
334*/
335bool QSslKey::operator==(const QSslKey &other) const
336{
337 if (isNull())
338 return other.isNull();
339 if (other.isNull())
340 return isNull();
341 if (algorithm() != other.algorithm())
342 return false;
343 if (type() != other.type())
344 return false;
345 if (length() != other.length())
346 return false;
347 if (algorithm() == QSsl::Opaque)
348 return handle() == other.handle();
349 return toDer() == other.toDer();
350}
351
352/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
353
354 Returns \c true if this key is not equal to key \a other; otherwise
355 returns \c false.
356*/
357
358#ifndef QT_NO_DEBUG_STREAM
359QDebug operator<<(QDebug debug, const QSslKey &key)
360{
361 QDebugStateSaver saver(debug);
362 debug.resetFormat().nospace();
363 debug << "QSslKey("
364 << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
365 << ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" :
366 (key.algorithm() == QSsl::Rsa ? "RSA" :
367 (key.algorithm() == QSsl::Dsa ? "DSA" :
368 (key.algorithm() == QSsl::Dh ? "DH" : "EC"))))
369 << ", " << key.length()
370 << ')';
371 return debug;
372}
373#endif
374
375QT_END_NAMESPACE
376

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