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

Last change on this file since 352 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;