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