source: trunk/src/network/ssl/qsslkey.cpp@ 788

Last change on this file since 788 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 12.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42
43/*!
44 \class QSslKey
45 \brief The QSslKey class provides an interface for private and public keys.
46 \since 4.3
47
48 \reentrant
49 \ingroup network
50 \ingroup ssl
51 \inmodule QtNetwork
52
53 QSslKey provides a simple API for managing keys.
54
55 \sa QSslSocket, QSslCertificate, QSslCipher
56*/
57
58#include "qsslsocket_openssl_symbols_p.h"
59#include "qsslkey.h"
60#include "qsslkey_p.h"
61#include "qsslsocket.h"
62#include "qsslsocket_p.h"
63
64#include <QtCore/qatomic.h>
65#include <QtCore/qbytearray.h>
66#include <QtCore/qiodevice.h>
67#ifndef QT_NO_DEBUG_STREAM
68#include <QtCore/qdebug.h>
69
70QT_BEGIN_NAMESPACE
71#endif
72
73
74/*!
75 \internal
76 */
77void QSslKeyPrivate::clear(bool deep)
78{
79 isNull = true;
80 if (!QSslSocket::supportsSsl())
81 return;
82 if (rsa) {
83 if (deep)
84 q_RSA_free(rsa);
85 rsa = 0;
86 }
87 if (dsa) {
88 if (deep)
89 q_DSA_free(dsa);
90 dsa = 0;
91 }
92}
93
94/*!
95 \internal
96
97 Allocates a new rsa or dsa struct and decodes \a pem into it
98 according to the current algorithm and type.
99
100 If \a deepClear is true, the rsa/dsa struct is freed if it is was
101 already allocated, otherwise we "leak" memory (which is exactly
102 what we want for copy construction).
103
104 If \a passPhrase is non-empty, it will be used for decrypting
105 \a pem.
106*/
107void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
108 bool deepClear)
109{
110 if (pem.isEmpty())
111 return;
112
113 clear(deepClear);
114
115 if (!QSslSocket::supportsSsl())
116 return;
117
118 BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
119 if (!bio)
120 return;
121
122 void *phrase = (void *)passPhrase.constData();
123
124 if (algorithm == QSsl::Rsa) {
125 RSA *result = (type == QSsl::PublicKey)
126 ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase)
127 : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase);
128 if (rsa && rsa == result)
129 isNull = false;
130 } else {
131 DSA *result = (type == QSsl::PublicKey)
132 ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase)
133 : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase);
134 if (dsa && dsa == result)
135 isNull = false;
136 }
137
138 q_BIO_free(bio);
139}
140
141/*!
142 Constructs a null key.
143
144 \sa isNull()
145*/
146QSslKey::QSslKey()
147 : d(new QSslKeyPrivate)
148{
149}
150
151/*!
152 \internal
153*/
154QByteArray QSslKeyPrivate::pemHeader() const
155{
156 // ### use QByteArray::fromRawData() instead
157 if (type == QSsl::PublicKey)
158 return QByteArray("-----BEGIN PUBLIC KEY-----\n");
159 else if (algorithm == QSsl::Rsa)
160 return QByteArray("-----BEGIN RSA PRIVATE KEY-----\n");
161 return QByteArray("-----BEGIN DSA PRIVATE KEY-----\n");
162}
163
164/*!
165 \internal
166*/
167QByteArray QSslKeyPrivate::pemFooter() const
168{
169 // ### use QByteArray::fromRawData() instead
170 if (type == QSsl::PublicKey)
171 return QByteArray("-----END PUBLIC KEY-----\n");
172 else if (algorithm == QSsl::Rsa)
173 return QByteArray("-----END RSA PRIVATE KEY-----\n");
174 return QByteArray("-----END DSA PRIVATE KEY-----\n");
175}
176
177/*!
178 \internal
179
180 Returns a DER key formatted as PEM.
181*/
182QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
183{
184 QByteArray pem(der.toBase64());
185
186 const int lineWidth = 64; // RFC 1421
187 const int newLines = pem.size() / lineWidth;
188 const bool rem = pem.size() % lineWidth;
189
190 // ### optimize
191 for (int i = 0; i < newLines; ++i)
192 pem.insert((i + 1) * lineWidth + i, '\n');
193 if (rem)
194 pem.append('\n'); // ###
195
196 pem.prepend(pemHeader());
197 pem.append(pemFooter());
198
199 return pem;
200}
201
202/*!
203 \internal
204
205 Returns a PEM key formatted as DER.
206*/
207QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const
208{
209 const QByteArray header = pemHeader();
210 const QByteArray footer = pemFooter();
211
212 QByteArray der(pem);
213
214 const int headerIndex = der.indexOf(header);
215 const int footerIndex = der.indexOf(footer);
216 if (headerIndex == -1 || footerIndex == -1)
217 return QByteArray();
218
219 der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
220
221 return QByteArray::fromBase64(der); // ignores newlines
222}
223
224/*!
225 Constructs a QSslKey by decoding the string in the byte array
226 \a encoded using a specified \a algorithm and \a encoding format.
227 If the encoded key is encrypted, \a passPhrase is used to decrypt
228 it. \a type specifies whether the key is public or private.
229
230 After construction, use isNull() to check if \a encoded contained
231 a valid key.
232*/
233QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
234 QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
235 : d(new QSslKeyPrivate)
236{
237 d->type = type;
238 d->algorithm = algorithm;
239 d->decodePem((encoding == QSsl::Der)
240 ? d->pemFromDer(encoded) : encoded,
241 passPhrase);
242}
243
244/*!
245 Constructs a QSslKey by reading and decoding data from a
246 \a device using a specified \a algorithm and \a encoding format.
247 If the encoded key is encrypted, \a passPhrase is used to decrypt
248 it. \a type specifies whether the key is public or private.
249
250 After construction, use isNull() to check if \a device provided
251 a valid key.
252*/
253QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
254 QSsl::KeyType type, const QByteArray &passPhrase)
255 : d(new QSslKeyPrivate)
256{
257 QByteArray encoded;
258 if (device)
259 encoded = device->readAll();
260 d->type = type;
261 d->algorithm = algorithm;
262 d->decodePem((encoding == QSsl::Der) ?
263 d->pemFromDer(encoded) : encoded,
264 passPhrase);
265}
266
267/*!
268 Constructs an identical copy of \a other.
269*/
270QSslKey::QSslKey(const QSslKey &other) : d(other.d)
271{
272}
273
274/*!
275 Destroys the QSslKey object.
276*/
277QSslKey::~QSslKey()
278{
279}
280
281/*!
282 Copies the contents of \a other into this key, making the two keys
283 identical.
284
285 Returns a reference to this QSslKey.
286*/
287QSslKey &QSslKey::operator=(const QSslKey &other)
288{
289 d = other.d;
290 return *this;
291}
292
293/*!
294 Returns true if this is a null key; otherwise false.
295
296 \sa clear()
297*/
298bool QSslKey::isNull() const
299{
300 return d->isNull;
301}
302
303/*!
304 Clears the contents of this key, making it a null key.
305
306 \sa isNull()
307*/
308void QSslKey::clear()
309{
310 d = new QSslKeyPrivate;
311}
312
313/*!
314 Returns the length of the key in bits, or -1 if the key is null.
315*/
316int QSslKey::length() const
317{
318 if (d->isNull)
319 return -1;
320 return (d->algorithm == QSsl::Rsa)
321 ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
322}
323
324/*!
325 Returns the type of the key (i.e., PublicKey or PrivateKey).
326*/
327QSsl::KeyType QSslKey::type() const
328{
329 return d->type;
330}
331
332/*!
333 Returns the key algorithm.
334*/
335QSsl::KeyAlgorithm QSslKey::algorithm() const
336{
337 return d->algorithm;
338}
339
340/*!
341 Returns the key in DER encoding. The result is encrypted with
342 \a passPhrase if the key is a private key and \a passPhrase is
343 non-empty.
344*/
345// ### autotest failure for non-empty passPhrase and private key
346QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
347{
348 if (d->isNull)
349 return QByteArray();
350 return d->derFromPem(toPem(passPhrase));
351}
352
353/*!
354 Returns the key in PEM encoding. The result is encrypted with
355 \a passPhrase if the key is a private key and \a passPhrase is
356 non-empty.
357*/
358QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
359{
360 if (!QSslSocket::supportsSsl() || d->isNull)
361 return QByteArray();
362
363 BIO *bio = q_BIO_new(q_BIO_s_mem());
364 if (!bio)
365 return QByteArray();
366
367 bool fail = false;
368
369 if (d->algorithm == QSsl::Rsa) {
370 if (d->type == QSsl::PublicKey) {
371 if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa))
372 fail = true;
373 } else {
374 if (!q_PEM_write_bio_RSAPrivateKey(
375 bio, d->rsa,
376 // ### the cipher should be selectable in the API:
377 passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
378 (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
379 fail = true;
380 }
381 }
382 } else {
383 if (d->type == QSsl::PublicKey) {
384 if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa))
385 fail = true;
386 } else {
387 if (!q_PEM_write_bio_DSAPrivateKey(
388 bio, d->dsa,
389 // ### the cipher should be selectable in the API:
390 passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
391 (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
392 fail = true;
393 }
394 }
395 }
396
397 QByteArray pem;
398 if (!fail) {
399 char *data;
400 long size = q_BIO_get_mem_data(bio, &data);
401 pem = QByteArray(data, size);
402 }
403 q_BIO_free(bio);
404 return pem;
405}
406
407/*!
408 Returns a pointer to the native key handle, if it is available;
409 otherwise a null pointer is returned.
410
411 You can use this handle together with the native API to access
412 extended information about the key.
413
414 \warning Use of this function has a high probability of being
415 non-portable, and its return value may vary across platforms, and
416 between minor Qt releases.
417*/
418Qt::HANDLE QSslKey::handle() const
419{
420 return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa);
421}
422
423/*!
424 Returns true if this key is equal to \a other; otherwise returns false.
425*/
426bool QSslKey::operator==(const QSslKey &other) const
427{
428 if (isNull())
429 return other.isNull();
430 if (other.isNull())
431 return isNull();
432 if (algorithm() != other.algorithm())
433 return false;
434 if (type() != other.type())
435 return false;
436 if (length() != other.length())
437 return false;
438 return toDer() == other.toDer();
439}
440
441/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
442
443 Returns true if this key is not equal to key \a other; otherwise
444 returns false.
445*/
446
447#ifndef QT_NO_DEBUG_STREAM
448class QDebug;
449QDebug operator<<(QDebug debug, const QSslKey &key)
450{
451 debug << "QSslKey("
452 << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
453 << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA")
454 << ", " << key.length()
455 << ')';
456 return debug;
457}
458#endif
459
460QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.