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

Last change on this file since 385 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 12.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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 io
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 d->ref.ref();
275}
276
277/*!
278 Destroys the QSslKey object.
279*/
280QSslKey::~QSslKey()
281{
282 if (!d->ref.deref())
283 delete d;
284}
285
286/*!
287 Copies the contents of \a other into this key, making the two keys
288 identical.
289
290 Returns a reference to this QSslKey.
291*/
292QSslKey &QSslKey::operator=(const QSslKey &other)
293{
294 qAtomicAssign(d, other.d);
295 return *this;
296}
297
298/*!
299 Returns true if this is a null key; otherwise false.
300
301 \sa clear()
302*/
303bool QSslKey::isNull() const
304{
305 return d->isNull;
306}
307
308/*!
309 Clears the contents of this key, making it a null key.
310
311 \sa isNull()
312*/
313void QSslKey::clear()
314{
315 if (!d->ref.deref()) {
316 delete d;
317 d = new QSslKeyPrivate;
318 }
319}
320
321/*!
322 Returns the length of the key in bits, or -1 if the key is null.
323*/
324int QSslKey::length() const
325{
326 if (d->isNull)
327 return -1;
328 return (d->algorithm == QSsl::Rsa)
329 ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
330}
331
332/*!
333 Returns the type of the key (i.e., PublicKey or PrivateKey).
334*/
335QSsl::KeyType QSslKey::type() const
336{
337 return d->type;
338}
339
340/*!
341 Returns the key algorithm.
342*/
343QSsl::KeyAlgorithm QSslKey::algorithm() const
344{
345 return d->algorithm;
346}
347
348/*!
349 Returns the key in DER encoding. The result is encrypted with
350 \a passPhrase if the key is a private key and \a passPhrase is
351 non-empty.
352*/
353// ### autotest failure for non-empty passPhrase and private key
354QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
355{
356 if (d->isNull)
357 return QByteArray();
358 return d->derFromPem(toPem(passPhrase));
359}
360
361/*!
362 Returns the key in PEM encoding. The result is encrypted with
363 \a passPhrase if the key is a private key and \a passPhrase is
364 non-empty.
365*/
366QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
367{
368 if (!QSslSocket::supportsSsl() || d->isNull)
369 return QByteArray();
370
371 BIO *bio = q_BIO_new(q_BIO_s_mem());
372 if (!bio)
373 return QByteArray();
374
375 bool fail = false;
376
377 if (d->algorithm == QSsl::Rsa) {
378 if (d->type == QSsl::PublicKey) {
379 if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa))
380 fail = true;
381 } else {
382 if (!q_PEM_write_bio_RSAPrivateKey(
383 bio, d->rsa,
384 // ### the cipher should be selectable in the API:
385 passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
386 (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
387 fail = true;
388 }
389 }
390 } else {
391 if (d->type == QSsl::PublicKey) {
392 if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa))
393 fail = true;
394 } else {
395 if (!q_PEM_write_bio_DSAPrivateKey(
396 bio, d->dsa,
397 // ### the cipher should be selectable in the API:
398 passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
399 (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
400 fail = true;
401 }
402 }
403 }
404
405 QByteArray pem;
406 if (!fail) {
407 char *data;
408 long size = q_BIO_get_mem_data(bio, &data);
409 pem = QByteArray(data, size);
410 }
411 q_BIO_free(bio);
412 return pem;
413}
414
415/*!
416 Returns a pointer to the native key handle, if it is available;
417 otherwise a null pointer is returned.
418
419 You can use this handle together with the native API to access
420 extended information about the key.
421
422 \warning Use of this function has a high probability of being
423 non-portable, and its return value may vary across platforms, and
424 between minor Qt releases.
425*/
426Qt::HANDLE QSslKey::handle() const
427{
428 return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa);
429}
430
431/*!
432 Returns true if this key is equal to \a other; otherwise returns false.
433*/
434bool QSslKey::operator==(const QSslKey &other) const
435{
436 if (isNull())
437 return other.isNull();
438 if (other.isNull())
439 return isNull();
440 if (algorithm() != other.algorithm())
441 return false;
442 if (type() != other.type())
443 return false;
444 if (length() != other.length())
445 return false;
446 return toDer() == other.toDer();
447}
448
449/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
450
451 Returns true if this key is not equal to key \a other; otherwise
452 returns false.
453*/
454
455#ifndef QT_NO_DEBUG_STREAM
456class QDebug;
457QDebug operator<<(QDebug debug, const QSslKey &key)
458{
459 debug << "QSslKey("
460 << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
461 << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA")
462 << ", " << key.length()
463 << ")";
464 return debug;
465}
466#endif
467
468QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.