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
29namespace Poco {
30namespace Crypto {
31
32
33RSAKeyImpl::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
41RSAKeyImpl::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
57RSAKeyImpl::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
66RSAKeyImpl::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
93RSAKeyImpl::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
159RSAKeyImpl::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
211RSAKeyImpl::~RSAKeyImpl()
212{
213 freeRSA();
214}
215
216
217void RSAKeyImpl::freeRSA()
218{
219 if (_pRSA) RSA_free(_pRSA);
220 _pRSA = 0;
221}
222
223
224int RSAKeyImpl::size() const
225{
226 return RSA_size(_pRSA);
227}
228
229
230RSAKeyImpl::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
244RSAKeyImpl::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
258RSAKeyImpl::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
272void 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
326void 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
369RSAKeyImpl::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