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

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

trunk: Merged in qt 4.6.1 sources.

File size: 12.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 = passPhrase.isEmpty()
123 ? (void *)0
124 : (void *)passPhrase.constData();
125
126 if (algorithm == QSsl::Rsa) {
127 RSA *result = (type == QSsl::PublicKey)
128 ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase)
129 : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase);
130 if (rsa && rsa == result)
131 isNull = false;
132 } else {
133 DSA *result = (type == QSsl::PublicKey)
134 ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase)
135 : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase);
136 if (dsa && dsa == result)
137 isNull = false;
138 }
139
140 q_BIO_free(bio);
141}
142
143/*!
144 Constructs a null key.
145
146 \sa isNull()
147*/
148QSslKey::QSslKey()
149 : d(new QSslKeyPrivate)
150{
151}
152
153/*!
154 \internal
155*/
156QByteArray QSslKeyPrivate::pemHeader() const
157{
158 // ### use QByteArray::fromRawData() instead
159 if (type == QSsl::PublicKey)
160 return QByteArray("-----BEGIN PUBLIC KEY-----\n");
161 else if (algorithm == QSsl::Rsa)
162 return QByteArray("-----BEGIN RSA PRIVATE KEY-----\n");
163 return QByteArray("-----BEGIN DSA PRIVATE KEY-----\n");
164}
165
166/*!
167 \internal
168*/
169QByteArray QSslKeyPrivate::pemFooter() const
170{
171 // ### use QByteArray::fromRawData() instead
172 if (type == QSsl::PublicKey)
173 return QByteArray("-----END PUBLIC KEY-----\n");
174 else if (algorithm == QSsl::Rsa)
175 return QByteArray("-----END RSA PRIVATE KEY-----\n");
176 return QByteArray("-----END DSA PRIVATE KEY-----\n");
177}
178
179/*!
180 \internal
181
182 Returns a DER key formatted as PEM.
183*/
184QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
185{
186 QByteArray pem(der.toBase64());
187
188 const int lineWidth = 64; // RFC 1421
189 const int newLines = pem.size() / lineWidth;
190 const bool rem = pem.size() % lineWidth;
191
192 // ### optimize
193 for (int i = 0; i < newLines; ++i)
194 pem.insert((i + 1) * lineWidth + i, '\n');
195 if (rem)
196 pem.append('\n'); // ###
197
198 pem.prepend(pemHeader());
199 pem.append(pemFooter());
200
201 return pem;
202}
203
204/*!
205 \internal
206
207 Returns a PEM key formatted as DER.
208*/
209QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const
210{
211 const QByteArray header = pemHeader();
212 const QByteArray footer = pemFooter();
213
214 QByteArray der(pem);
215
216 const int headerIndex = der.indexOf(header);
217 const int footerIndex = der.indexOf(footer);
218 if (headerIndex == -1 || footerIndex == -1)
219 return QByteArray();
220
221 der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
222
223 return QByteArray::fromBase64(der); // ignores newlines
224}
225
226/*!
227 Constructs a QSslKey by decoding the string in the byte array
228 \a encoded using a specified \a algorithm and \a encoding format.
229 If the encoded key is encrypted, \a passPhrase is used to decrypt
230 it. \a type specifies whether the key is public or private.
231
232 After construction, use isNull() to check if \a encoded contained
233 a valid key.
234*/
235QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
236 QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
237 : d(new QSslKeyPrivate)
238{
239 d->type = type;
240 d->algorithm = algorithm;
241 d->decodePem((encoding == QSsl::Der)
242 ? d->pemFromDer(encoded) : encoded,
243 passPhrase);
244}
245
246/*!
247 Constructs a QSslKey by reading and decoding data from a
248 \a device using a specified \a algorithm and \a encoding format.
249 If the encoded key is encrypted, \a passPhrase is used to decrypt
250 it. \a type specifies whether the key is public or private.
251
252 After construction, use isNull() to check if \a device provided
253 a valid key.
254*/
255QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
256 QSsl::KeyType type, const QByteArray &passPhrase)
257 : d(new QSslKeyPrivate)
258{
259 QByteArray encoded;
260 if (device)
261 encoded = device->readAll();
262 d->type = type;
263 d->algorithm = algorithm;
264 d->decodePem((encoding == QSsl::Der) ?
265 d->pemFromDer(encoded) : encoded,
266 passPhrase);
267}
268
269/*!
270 Constructs an identical copy of \a other.
271*/
272QSslKey::QSslKey(const QSslKey &other) : d(other.d)
273{
274}
275
276/*!
277 Destroys the QSslKey object.
278*/
279QSslKey::~QSslKey()
280{
281}
282
283/*!
284 Copies the contents of \a other into this key, making the two keys
285 identical.
286
287 Returns a reference to this QSslKey.
288*/
289QSslKey &QSslKey::operator=(const QSslKey &other)
290{
291 d = other.d;
292 return *this;
293}
294
295/*!
296 Returns true if this is a null key; otherwise false.
297
298 \sa clear()
299*/
300bool QSslKey::isNull() const
301{
302 return d->isNull;
303}
304
305/*!
306 Clears the contents of this key, making it a null key.
307
308 \sa isNull()
309*/
310void QSslKey::clear()
311{
312 d = new QSslKeyPrivate;
313}
314
315/*!
316 Returns the length of the key in bits, or -1 if the key is null.
317*/
318int QSslKey::length() const
319{
320 if (d->isNull)
321 return -1;
322 return (d->algorithm == QSsl::Rsa)
323 ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
324}
325
326/*!
327 Returns the type of the key (i.e., PublicKey or PrivateKey).
328*/
329QSsl::KeyType QSslKey::type() const
330{
331 return d->type;
332}
333
334/*!
335 Returns the key algorithm.
336*/
337QSsl::KeyAlgorithm QSslKey::algorithm() const
338{
339 return d->algorithm;
340}
341
342/*!
343 Returns the key in DER encoding. The result is encrypted with
344 \a passPhrase if the key is a private key and \a passPhrase is
345 non-empty.
346*/
347// ### autotest failure for non-empty passPhrase and private key
348QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
349{
350 if (d->isNull)
351 return QByteArray();
352 return d->derFromPem(toPem(passPhrase));
353}
354
355/*!
356 Returns the key in PEM encoding. The result is encrypted with
357 \a passPhrase if the key is a private key and \a passPhrase is
358 non-empty.
359*/
360QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
361{
362 if (!QSslSocket::supportsSsl() || d->isNull)
363 return QByteArray();
364
365 BIO *bio = q_BIO_new(q_BIO_s_mem());
366 if (!bio)
367 return QByteArray();
368
369 bool fail = false;
370
371 if (d->algorithm == QSsl::Rsa) {
372 if (d->type == QSsl::PublicKey) {
373 if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa))
374 fail = true;
375 } else {
376 if (!q_PEM_write_bio_RSAPrivateKey(
377 bio, d->rsa,
378 // ### the cipher should be selectable in the API:
379 passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
380 (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
381 fail = true;
382 }
383 }
384 } else {
385 if (d->type == QSsl::PublicKey) {
386 if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa))
387 fail = true;
388 } else {
389 if (!q_PEM_write_bio_DSAPrivateKey(
390 bio, d->dsa,
391 // ### the cipher should be selectable in the API:
392 passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
393 (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
394 fail = true;
395 }
396 }
397 }
398
399 QByteArray pem;
400 if (!fail) {
401 char *data;
402 long size = q_BIO_get_mem_data(bio, &data);
403 pem = QByteArray(data, size);
404 }
405 q_BIO_free(bio);
406 return pem;
407}
408
409/*!
410 Returns a pointer to the native key handle, if it is available;
411 otherwise a null pointer is returned.
412
413 You can use this handle together with the native API to access
414 extended information about the key.
415
416 \warning Use of this function has a high probability of being
417 non-portable, and its return value may vary across platforms, and
418 between minor Qt releases.
419*/
420Qt::HANDLE QSslKey::handle() const
421{
422 return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa);
423}
424
425/*!
426 Returns true if this key is equal to \a other; otherwise returns false.
427*/
428bool QSslKey::operator==(const QSslKey &other) const
429{
430 if (isNull())
431 return other.isNull();
432 if (other.isNull())
433 return isNull();
434 if (algorithm() != other.algorithm())
435 return false;
436 if (type() != other.type())
437 return false;
438 if (length() != other.length())
439 return false;
440 return toDer() == other.toDer();
441}
442
443/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
444
445 Returns true if this key is not equal to key \a other; otherwise
446 returns false.
447*/
448
449#ifndef QT_NO_DEBUG_STREAM
450class QDebug;
451QDebug operator<<(QDebug debug, const QSslKey &key)
452{
453 debug << "QSslKey("
454 << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
455 << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA")
456 << ", " << key.length()
457 << ')';
458 return debug;
459}
460#endif
461
462QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.