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 \memberswap{ssl key}
218*/
219
220/*!
221 Returns \c true if this is a null key; otherwise false.
222
223 \sa clear()
224*/
225bool QSslKey::isNull() const
226{
227 if (const auto *tlsKey = d->backend.get())
228 return tlsKey->isNull();
229
230 return true;
231}
232
233/*!
234 Clears the contents of this key, making it a null key.
235
236 \sa isNull()
237*/
238void QSslKey::clear()
239{
240 d = new QSslKeyPrivate;
241}
242
243/*!
244 Returns the length of the key in bits, or -1 if the key is null.
245*/
246int QSslKey::length() const
247{
248 if (const auto *tlsKey = d->backend.get())
249 return tlsKey->length();
250
251 return -1;
252}
253
254/*!
255 Returns the type of the key (i.e., PublicKey or PrivateKey).
256*/
257QSsl::KeyType QSslKey::type() const
258{
259 if (const auto *tlsKey = d->backend.get())
260 return tlsKey->type();
261
262 return QSsl::PublicKey;
263}
264
265/*!
266 Returns the key algorithm.
267*/
268QSsl::KeyAlgorithm QSslKey::algorithm() const
269{
270 if (const auto *tlsKey = d->backend.get())
271 return tlsKey->algorithm();
272
273 return QSsl::Opaque;
274}
275
276/*!
277 Returns the key in DER encoding.
278
279 The \a passPhrase argument should be omitted as DER cannot be
280 encrypted. It will be removed in a future version of Qt.
281*/
282QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
283{
284 if (isNull() || algorithm() == QSsl::Opaque)
285 return {};
286
287 // Encrypted DER is nonsense, see QTBUG-41038.
288 if (type() == QSsl::PrivateKey && !passPhrase.isEmpty())
289 return {};
290
291 QMap<QByteArray, QByteArray> headers;
292 if (const auto *tlsKey = d->backend.get())
293 return tlsKey->derFromPem(pem: toPem(passPhrase), headers: &headers);
294
295 return {};
296}
297
298/*!
299 Returns the key in PEM encoding. The result is encrypted with
300 \a passPhrase if the key is a private key and \a passPhrase is
301 non-empty.
302*/
303QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
304{
305 if (const auto *tlsKey = d->backend.get())
306 return tlsKey->toPem(passPhrase);
307
308 return {};
309}
310
311/*!
312 Returns a pointer to the native key handle, if there is
313 one, else \nullptr.
314
315 You can use this handle together with the native API to access
316 extended information about the key.
317
318 \warning Use of this function has a high probability of being
319 non-portable, and its return value may vary across platforms, and
320 between minor Qt releases.
321*/
322Qt::HANDLE QSslKey::handle() const
323{
324 if (d->backend.get())
325 return d->backend->handle();
326
327 return nullptr;
328}
329
330/*!
331 Returns \c true if this key is equal to \a other; otherwise returns \c false.
332*/
333bool QSslKey::operator==(const QSslKey &other) const
334{
335 if (isNull())
336 return other.isNull();
337 if (other.isNull())
338 return isNull();
339 if (algorithm() != other.algorithm())
340 return false;
341 if (type() != other.type())
342 return false;
343 if (length() != other.length())
344 return false;
345 if (algorithm() == QSsl::Opaque)
346 return handle() == other.handle();
347 return toDer() == other.toDer();
348}
349
350/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
351
352 Returns \c true if this key is not equal to key \a other; otherwise
353 returns \c false.
354*/
355
356#ifndef QT_NO_DEBUG_STREAM
357QDebug operator<<(QDebug debug, const QSslKey &key)
358{
359 QDebugStateSaver saver(debug);
360 debug.resetFormat().nospace();
361 debug << "QSslKey("
362 << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
363 << ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" :
364 (key.algorithm() == QSsl::Rsa ? "RSA" :
365 (key.algorithm() == QSsl::Dsa ? "DSA" :
366 (key.algorithm() == QSsl::Dh ? "DH" : "EC"))))
367 << ", " << key.length()
368 << ')';
369 return debug;
370}
371#endif
372
373QT_END_NAMESPACE
374

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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