1 | // |
2 | // RSAKeyImpl.cpp |
3 | // |
4 | // Library: Crypto |
5 | // Package: RSA |
6 | // Module: RSAKeyImpl |
7 | // |
8 | // Copyright (c) 2008, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Crypto/RSAKeyImpl.h" |
16 | #include "Poco/Crypto/X509Certificate.h" |
17 | #include "Poco/Crypto/PKCS12Container.h" |
18 | #include "Poco/FileStream.h" |
19 | #include "Poco/StreamCopier.h" |
20 | #include <sstream> |
21 | #include <openssl/pem.h> |
22 | #include <openssl/rsa.h> |
23 | #include <openssl/evp.h> |
24 | #if OPENSSL_VERSION_NUMBER >= 0x00908000L |
25 | #include <openssl/bn.h> |
26 | #endif |
27 | |
28 | |
29 | namespace Poco { |
30 | namespace Crypto { |
31 | |
32 | |
33 | RSAKeyImpl::RSAKeyImpl(const EVPPKey& key): |
34 | KeyPairImpl("rsa" , KT_RSA_IMPL), |
35 | _pRSA(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>((const EVP_PKEY*)key))) |
36 | { |
37 | if (!_pRSA) throw OpenSSLException(); |
38 | } |
39 | |
40 | |
41 | RSAKeyImpl::RSAKeyImpl(const X509Certificate& cert): |
42 | KeyPairImpl("rsa" , KT_RSA_IMPL), |
43 | _pRSA(0) |
44 | { |
45 | const X509* pCert = cert.certificate(); |
46 | EVP_PKEY* pKey = X509_get_pubkey(const_cast<X509*>(pCert)); |
47 | if (pKey) |
48 | { |
49 | _pRSA = EVP_PKEY_get1_RSA(pKey); |
50 | EVP_PKEY_free(pKey); |
51 | } |
52 | else |
53 | throw OpenSSLException("RSAKeyImpl(const X509Certificate&)" ); |
54 | } |
55 | |
56 | |
57 | RSAKeyImpl::RSAKeyImpl(const PKCS12Container& cont): |
58 | KeyPairImpl("rsa" , KT_RSA_IMPL), |
59 | _pRSA(0) |
60 | { |
61 | EVPPKey key = cont.getKey(); |
62 | _pRSA = EVP_PKEY_get1_RSA(key); |
63 | } |
64 | |
65 | |
66 | RSAKeyImpl::RSAKeyImpl(int keyLength, unsigned long exponent): KeyPairImpl("rsa" , KT_RSA_IMPL), |
67 | _pRSA(0) |
68 | { |
69 | #if OPENSSL_VERSION_NUMBER >= 0x00908000L |
70 | _pRSA = RSA_new(); |
71 | int ret = 0; |
72 | BIGNUM* bn = 0; |
73 | try |
74 | { |
75 | bn = BN_new(); |
76 | BN_set_word(bn, exponent); |
77 | ret = RSA_generate_key_ex(_pRSA, keyLength, bn, 0); |
78 | BN_free(bn); |
79 | } |
80 | catch (...) |
81 | { |
82 | BN_free(bn); |
83 | throw; |
84 | } |
85 | if (!ret) throw Poco::InvalidArgumentException("Failed to create RSA context" ); |
86 | #else |
87 | _pRSA = RSA_generate_key(keyLength, exponent, 0, 0); |
88 | if (!_pRSA) throw Poco::InvalidArgumentException("Failed to create RSA context" ); |
89 | #endif |
90 | } |
91 | |
92 | |
93 | RSAKeyImpl::RSAKeyImpl(const std::string& publicKeyFile, |
94 | const std::string& privateKeyFile, |
95 | const std::string& privateKeyPassphrase): KeyPairImpl("rsa" , KT_RSA_IMPL), |
96 | _pRSA(0) |
97 | { |
98 | poco_assert_dbg(_pRSA == 0); |
99 | |
100 | _pRSA = RSA_new(); |
101 | if (!publicKeyFile.empty()) |
102 | { |
103 | BIO* bio = BIO_new(BIO_s_file()); |
104 | if (!bio) throw Poco::IOException("Cannot create BIO for reading public key" , publicKeyFile); |
105 | int rc = BIO_read_filename(bio, publicKeyFile.c_str()); |
106 | if (rc) |
107 | { |
108 | RSA* pubKey = PEM_read_bio_RSAPublicKey(bio, &_pRSA, 0, 0); |
109 | if (!pubKey) |
110 | { |
111 | int rc = BIO_reset(bio); |
112 | // BIO_reset() normally returns 1 for success and 0 or -1 for failure. |
113 | // File BIOs are an exception, they return 0 for success and -1 for failure. |
114 | if (rc != 0) throw Poco::FileException("Failed to load public key" , publicKeyFile); |
115 | pubKey = PEM_read_bio_RSA_PUBKEY(bio, &_pRSA, 0, 0); |
116 | } |
117 | BIO_free(bio); |
118 | if (!pubKey) |
119 | { |
120 | freeRSA(); |
121 | throw Poco::FileException("Failed to load public key" , publicKeyFile); |
122 | } |
123 | } |
124 | else |
125 | { |
126 | freeRSA(); |
127 | throw Poco::FileNotFoundException("Public key file" , publicKeyFile); |
128 | } |
129 | } |
130 | |
131 | if (!privateKeyFile.empty()) |
132 | { |
133 | BIO* bio = BIO_new(BIO_s_file()); |
134 | if (!bio) throw Poco::IOException("Cannot create BIO for reading private key" , privateKeyFile); |
135 | int rc = BIO_read_filename(bio, privateKeyFile.c_str()); |
136 | if (rc) |
137 | { |
138 | RSA* privKey = 0; |
139 | if (privateKeyPassphrase.empty()) |
140 | privKey = PEM_read_bio_RSAPrivateKey(bio, &_pRSA, 0, 0); |
141 | else |
142 | privKey = PEM_read_bio_RSAPrivateKey(bio, &_pRSA, 0, const_cast<char*>(privateKeyPassphrase.c_str())); |
143 | BIO_free(bio); |
144 | if (!privKey) |
145 | { |
146 | freeRSA(); |
147 | throw Poco::FileException("Failed to load private key" , privateKeyFile); |
148 | } |
149 | } |
150 | else |
151 | { |
152 | freeRSA(); |
153 | throw Poco::FileNotFoundException("Private key file" , privateKeyFile); |
154 | } |
155 | } |
156 | } |
157 | |
158 | |
159 | RSAKeyImpl::RSAKeyImpl(std::istream* pPublicKeyStream, |
160 | std::istream* pPrivateKeyStream, |
161 | const std::string& privateKeyPassphrase): KeyPairImpl("rsa" , KT_RSA_IMPL), |
162 | _pRSA(0) |
163 | { |
164 | poco_assert_dbg(_pRSA == 0); |
165 | |
166 | _pRSA = RSA_new(); |
167 | if (pPublicKeyStream) |
168 | { |
169 | std::string publicKeyData; |
170 | Poco::StreamCopier::copyToString(*pPublicKeyStream, publicKeyData); |
171 | BIO* bio = BIO_new_mem_buf(const_cast<char*>(publicKeyData.data()), static_cast<int>(publicKeyData.size())); |
172 | if (!bio) throw Poco::IOException("Cannot create BIO for reading public key" ); |
173 | RSA* publicKey = PEM_read_bio_RSAPublicKey(bio, &_pRSA, 0, 0); |
174 | if (!publicKey) |
175 | { |
176 | int rc = BIO_reset(bio); |
177 | // BIO_reset() normally returns 1 for success and 0 or -1 for failure. |
178 | // File BIOs are an exception, they return 0 for success and -1 for failure. |
179 | if (rc != 1) throw Poco::FileException("Failed to load public key" ); |
180 | publicKey = PEM_read_bio_RSA_PUBKEY(bio, &_pRSA, 0, 0); |
181 | } |
182 | BIO_free(bio); |
183 | if (!publicKey) |
184 | { |
185 | freeRSA(); |
186 | throw Poco::FileException("Failed to load public key" ); |
187 | } |
188 | } |
189 | |
190 | if (pPrivateKeyStream) |
191 | { |
192 | std::string privateKeyData; |
193 | Poco::StreamCopier::copyToString(*pPrivateKeyStream, privateKeyData); |
194 | BIO* bio = BIO_new_mem_buf(const_cast<char*>(privateKeyData.data()), static_cast<int>(privateKeyData.size())); |
195 | if (!bio) throw Poco::IOException("Cannot create BIO for reading private key" ); |
196 | RSA* privateKey = 0; |
197 | if (privateKeyPassphrase.empty()) |
198 | privateKey = PEM_read_bio_RSAPrivateKey(bio, &_pRSA, 0, 0); |
199 | else |
200 | privateKey = PEM_read_bio_RSAPrivateKey(bio, &_pRSA, 0, const_cast<char*>(privateKeyPassphrase.c_str())); |
201 | BIO_free(bio); |
202 | if (!privateKey) |
203 | { |
204 | freeRSA(); |
205 | throw Poco::FileException("Failed to load private key" ); |
206 | } |
207 | } |
208 | } |
209 | |
210 | |
211 | RSAKeyImpl::~RSAKeyImpl() |
212 | { |
213 | freeRSA(); |
214 | } |
215 | |
216 | |
217 | void RSAKeyImpl::freeRSA() |
218 | { |
219 | if (_pRSA) RSA_free(_pRSA); |
220 | _pRSA = 0; |
221 | } |
222 | |
223 | |
224 | int RSAKeyImpl::size() const |
225 | { |
226 | return RSA_size(_pRSA); |
227 | } |
228 | |
229 | |
230 | RSAKeyImpl::ByteVec RSAKeyImpl::modulus() const |
231 | { |
232 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) |
233 | const BIGNUM* n = 0; |
234 | const BIGNUM* e = 0; |
235 | const BIGNUM* d = 0; |
236 | RSA_get0_key(_pRSA, &n, &e, &d); |
237 | return convertToByteVec(n); |
238 | #else |
239 | return convertToByteVec(_pRSA->n); |
240 | #endif |
241 | } |
242 | |
243 | |
244 | RSAKeyImpl::ByteVec RSAKeyImpl::encryptionExponent() const |
245 | { |
246 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) |
247 | const BIGNUM* n = 0; |
248 | const BIGNUM* e = 0; |
249 | const BIGNUM* d = 0; |
250 | RSA_get0_key(_pRSA, &n, &e, &d); |
251 | return convertToByteVec(e); |
252 | #else |
253 | return convertToByteVec(_pRSA->e); |
254 | #endif |
255 | } |
256 | |
257 | |
258 | RSAKeyImpl::ByteVec RSAKeyImpl::decryptionExponent() const |
259 | { |
260 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) |
261 | const BIGNUM* n = 0; |
262 | const BIGNUM* e = 0; |
263 | const BIGNUM* d = 0; |
264 | RSA_get0_key(_pRSA, &n, &e, &d); |
265 | return convertToByteVec(d); |
266 | #else |
267 | return convertToByteVec(_pRSA->d); |
268 | #endif |
269 | } |
270 | |
271 | |
272 | void RSAKeyImpl::save(const std::string& publicKeyFile, |
273 | const std::string& privateKeyFile, |
274 | const std::string& privateKeyPassphrase) const |
275 | { |
276 | if (!publicKeyFile.empty()) |
277 | { |
278 | BIO* bio = BIO_new(BIO_s_file()); |
279 | if (!bio) throw Poco::IOException("Cannot create BIO for writing public key file" , publicKeyFile); |
280 | try |
281 | { |
282 | if (BIO_write_filename(bio, const_cast<char*>(publicKeyFile.c_str()))) |
283 | { |
284 | if (!PEM_write_bio_RSAPublicKey(bio, _pRSA)) |
285 | throw Poco::WriteFileException("Failed to write public key to file" , publicKeyFile); |
286 | } |
287 | else throw Poco::CreateFileException("Cannot create public key file" ); |
288 | } |
289 | catch (...) |
290 | { |
291 | BIO_free(bio); |
292 | throw; |
293 | } |
294 | BIO_free(bio); |
295 | } |
296 | |
297 | if (!privateKeyFile.empty()) |
298 | { |
299 | BIO* bio = BIO_new(BIO_s_file()); |
300 | if (!bio) throw Poco::IOException("Cannot create BIO for writing private key file" , privateKeyFile); |
301 | try |
302 | { |
303 | if (BIO_write_filename(bio, const_cast<char*>(privateKeyFile.c_str()))) |
304 | { |
305 | int rc = 0; |
306 | if (privateKeyPassphrase.empty()) |
307 | rc = PEM_write_bio_RSAPrivateKey(bio, _pRSA, 0, 0, 0, 0, 0); |
308 | else |
309 | rc = PEM_write_bio_RSAPrivateKey(bio, _pRSA, EVP_des_ede3_cbc(), |
310 | reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())), |
311 | static_cast<int>(privateKeyPassphrase.length()), 0, 0); |
312 | if (!rc) throw Poco::FileException("Failed to write private key to file" , privateKeyFile); |
313 | } |
314 | else throw Poco::CreateFileException("Cannot create private key file" , privateKeyFile); |
315 | } |
316 | catch (...) |
317 | { |
318 | BIO_free(bio); |
319 | throw; |
320 | } |
321 | BIO_free(bio); |
322 | } |
323 | } |
324 | |
325 | |
326 | void RSAKeyImpl::save(std::ostream* pPublicKeyStream, |
327 | std::ostream* pPrivateKeyStream, |
328 | const std::string& privateKeyPassphrase) const |
329 | { |
330 | if (pPublicKeyStream) |
331 | { |
332 | BIO* bio = BIO_new(BIO_s_mem()); |
333 | if (!bio) throw Poco::IOException("Cannot create BIO for writing public key" ); |
334 | if (!PEM_write_bio_RSAPublicKey(bio, _pRSA)) |
335 | { |
336 | BIO_free(bio); |
337 | throw Poco::WriteFileException("Failed to write public key to stream" ); |
338 | } |
339 | char* pData; |
340 | long size = BIO_get_mem_data(bio, &pData); |
341 | pPublicKeyStream->write(pData, static_cast<std::streamsize>(size)); |
342 | BIO_free(bio); |
343 | } |
344 | |
345 | if (pPrivateKeyStream) |
346 | { |
347 | BIO* bio = BIO_new(BIO_s_mem()); |
348 | if (!bio) throw Poco::IOException("Cannot create BIO for writing public key" ); |
349 | int rc = 0; |
350 | if (privateKeyPassphrase.empty()) |
351 | rc = PEM_write_bio_RSAPrivateKey(bio, _pRSA, 0, 0, 0, 0, 0); |
352 | else |
353 | rc = PEM_write_bio_RSAPrivateKey(bio, _pRSA, EVP_des_ede3_cbc(), |
354 | reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())), |
355 | static_cast<int>(privateKeyPassphrase.length()), 0, 0); |
356 | if (!rc) |
357 | { |
358 | BIO_free(bio); |
359 | throw Poco::FileException("Failed to write private key to stream" ); |
360 | } |
361 | char* pData; |
362 | long size = BIO_get_mem_data(bio, &pData); |
363 | pPrivateKeyStream->write(pData, static_cast<std::streamsize>(size)); |
364 | BIO_free(bio); |
365 | } |
366 | } |
367 | |
368 | |
369 | RSAKeyImpl::ByteVec RSAKeyImpl::convertToByteVec(const BIGNUM* bn) |
370 | { |
371 | int numBytes = BN_num_bytes(bn); |
372 | ByteVec byteVector(numBytes); |
373 | |
374 | ByteVec::value_type* buffer = new ByteVec::value_type[numBytes]; |
375 | BN_bn2bin(bn, buffer); |
376 | |
377 | for (int i = 0; i < numBytes; ++i) |
378 | byteVector[i] = buffer[i]; |
379 | |
380 | delete [] buffer; |
381 | |
382 | return byteVector; |
383 | } |
384 | |
385 | |
386 | } } // namespace Poco::Crypto |
387 | |