1//
2// EVPPKey.cpp
3//
4//
5// Library: Crypto
6// Package: CryptoCore
7// Module: EVPPKey
8//
9// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
10// and Contributors.
11//
12// SPDX-License-Identifier: BSL-1.0
13//
14
15
16#include "Poco/Crypto/EVPPKey.h"
17#include "Poco/Crypto/ECKey.h"
18#include "Poco/Crypto/RSAKey.h"
19#include "Poco/NumberFormatter.h"
20
21
22namespace Poco {
23namespace Crypto {
24
25
26EVPPKey::EVPPKey(const std::string& ecCurveName)
27{
28 newECKey(ecCurveName.c_str());
29 poco_check_ptr(_pEVPPKey);
30}
31
32
33EVPPKey::EVPPKey(const char* ecCurveName)
34{
35 newECKey(ecCurveName);
36 poco_check_ptr(_pEVPPKey);
37}
38
39
40EVPPKey::EVPPKey(EVP_PKEY* pEVPPKey)
41{
42 duplicate(pEVPPKey, &_pEVPPKey);
43 poco_check_ptr(_pEVPPKey);
44}
45
46
47EVPPKey::EVPPKey(const std::string& publicKeyFile,
48 const std::string& privateKeyFile,
49 const std::string& privateKeyPassphrase): _pEVPPKey(0)
50{
51 if (loadKey(&_pEVPPKey, PEM_read_PrivateKey, (EVP_PKEY_get_Key_fn)0, privateKeyFile, privateKeyPassphrase))
52 {
53 poco_check_ptr(_pEVPPKey);
54 return; // private key is enough
55 }
56
57 // no private key, this must be public key only, otherwise throw
58 if (!loadKey(&_pEVPPKey, PEM_read_PUBKEY, (EVP_PKEY_get_Key_fn)0, publicKeyFile))
59 {
60 throw OpenSSLException("ECKeyImpl(const string&, const string&, const string&");
61 }
62 poco_check_ptr(_pEVPPKey);
63}
64
65
66EVPPKey::EVPPKey(std::istream* pPublicKeyStream,
67 std::istream* pPrivateKeyStream,
68 const std::string& privateKeyPassphrase): _pEVPPKey(0)
69{
70 if (loadKey(&_pEVPPKey, PEM_read_bio_PrivateKey, (EVP_PKEY_get_Key_fn)0, pPrivateKeyStream, privateKeyPassphrase))
71 {
72 poco_check_ptr(_pEVPPKey);
73 return; // private key is enough
74 }
75
76 // no private key, this must be public key only, otherwise throw
77 if (!loadKey(&_pEVPPKey, PEM_read_bio_PUBKEY, (EVP_PKEY_get_Key_fn)0, pPublicKeyStream))
78 {
79 throw OpenSSLException("ECKeyImpl(istream*, istream*, const string&");
80 }
81 poco_check_ptr(_pEVPPKey);
82}
83
84
85EVPPKey::EVPPKey(const EVPPKey& other): _pEVPPKey(0)
86{
87 duplicate(other._pEVPPKey, &_pEVPPKey);
88 poco_check_ptr(_pEVPPKey);
89}
90
91
92EVPPKey::EVPPKey(EVPPKey&& other): _pEVPPKey(other._pEVPPKey)
93{
94 other._pEVPPKey = nullptr;
95 poco_check_ptr(_pEVPPKey);
96}
97
98
99EVPPKey& EVPPKey::operator=(const EVPPKey& other)
100{
101 duplicate(other._pEVPPKey, &_pEVPPKey);
102 return *this;
103}
104
105
106EVPPKey& EVPPKey::operator=(EVPPKey&& other)
107{
108 _pEVPPKey = other._pEVPPKey;
109 other._pEVPPKey = nullptr;
110 poco_check_ptr(_pEVPPKey);
111 return *this;
112}
113
114
115EVPPKey::~EVPPKey()
116{
117 if (_pEVPPKey) EVP_PKEY_free(_pEVPPKey);
118}
119
120
121void EVPPKey::save(const std::string& publicKeyFile,
122 const std::string& privateKeyFile,
123 const std::string& privateKeyPassphrase) const
124{
125 if (!publicKeyFile.empty() && (publicKeyFile != privateKeyFile))
126 {
127 BIO* bio = BIO_new(BIO_s_file());
128 if (!bio) throw Poco::IOException("Cannot create BIO for writing public key file", publicKeyFile);
129 try
130 {
131 if (BIO_write_filename(bio, const_cast<char*>(publicKeyFile.c_str())))
132 {
133 if (!PEM_write_bio_PUBKEY(bio, _pEVPPKey))
134 {
135 throw Poco::WriteFileException("Failed to write public key to file", publicKeyFile);
136 }
137 }
138 else throw Poco::CreateFileException("Cannot create public key file");
139 }
140 catch (...)
141 {
142 BIO_free(bio);
143 throw;
144 }
145 BIO_free(bio);
146 }
147
148 if (!privateKeyFile.empty())
149 {
150 BIO* bio = BIO_new(BIO_s_file());
151 if (!bio) throw Poco::IOException("Cannot create BIO for writing private key file", privateKeyFile);
152 try
153 {
154 if (BIO_write_filename(bio, const_cast<char*>(privateKeyFile.c_str())))
155 {
156 int rc = 0;
157 if (privateKeyPassphrase.empty())
158 {
159 rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, 0, 0, 0, 0, 0);
160 }
161 else
162 {
163 rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, EVP_des_ede3_cbc(),
164 reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())),
165 static_cast<int>(privateKeyPassphrase.length()), 0, 0);
166 }
167 if (!rc)
168 throw Poco::FileException("Failed to write private key to file", privateKeyFile);
169 }
170 else throw Poco::CreateFileException("Cannot create private key file", privateKeyFile);
171 }
172 catch (...)
173 {
174 BIO_free(bio);
175 throw;
176 }
177 BIO_free(bio);
178 }
179}
180
181
182void EVPPKey::save(std::ostream* pPublicKeyStream,
183 std::ostream* pPrivateKeyStream,
184 const std::string& privateKeyPassphrase) const
185{
186 if (pPublicKeyStream && (pPublicKeyStream != pPrivateKeyStream))
187 {
188 BIO* bio = BIO_new(BIO_s_mem());
189 if (!bio) throw Poco::IOException("Cannot create BIO for writing public key");
190 if (!PEM_write_bio_PUBKEY(bio, _pEVPPKey))
191 {
192 BIO_free(bio);
193 throw Poco::WriteFileException("Failed to write public key to stream");
194 }
195 char* pData;
196 long size = BIO_get_mem_data(bio, &pData);
197 pPublicKeyStream->write(pData, static_cast<std::streamsize>(size));
198 BIO_free(bio);
199 }
200
201 if (pPrivateKeyStream)
202 {
203 BIO* bio = BIO_new(BIO_s_mem());
204 if (!bio) throw Poco::IOException("Cannot create BIO for writing public key");
205 int rc = 0;
206 if (privateKeyPassphrase.empty())
207 rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, 0, 0, 0, 0, 0);
208 else
209 rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, EVP_des_ede3_cbc(),
210 reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())),
211 static_cast<int>(privateKeyPassphrase.length()), 0, 0);
212 if (!rc)
213 {
214 BIO_free(bio);
215 throw Poco::FileException("Failed to write private key to stream");
216 }
217 char* pData;
218 long size = BIO_get_mem_data(bio, &pData);
219 pPrivateKeyStream->write(pData, static_cast<std::streamsize>(size));
220 BIO_free(bio);
221 }
222}
223
224
225EVP_PKEY* EVPPKey::duplicate(const EVP_PKEY* pFromKey, EVP_PKEY** pToKey)
226{
227 poco_check_ptr(pToKey);
228
229 if (!pFromKey) throw NullPointerException("EVPPKey::duplicate(): "
230 "provided key pointer is null.");
231
232 *pToKey = EVP_PKEY_new();
233 if (!*pToKey) throw NullPointerException("EVPPKey::duplicate(): "
234 "EVP_PKEY_new() returned null.");
235
236 int keyType = type(pFromKey);
237 switch (keyType)
238 {
239 case EVP_PKEY_RSA:
240 {
241 RSA* pRSA = EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pFromKey));
242 if (pRSA)
243 {
244 EVP_PKEY_set1_RSA(*pToKey, pRSA);
245 RSA_free(pRSA);
246 }
247 else throw OpenSSLException("EVPPKey::duplicate(): EVP_PKEY_get1_RSA()");
248 break;
249 }
250 case EVP_PKEY_EC:
251 {
252 EC_KEY* pEC = EVP_PKEY_get1_EC_KEY(const_cast<EVP_PKEY*>(pFromKey));
253 if (pEC)
254 {
255 EVP_PKEY_set1_EC_KEY(*pToKey, pEC);
256 EC_KEY_free(pEC);
257 int cmp = EVP_PKEY_cmp_parameters(*pToKey, pFromKey);
258 if (cmp < 0)
259 throw OpenSSLException("EVPPKey::duplicate(): EVP_PKEY_cmp_parameters()");
260 if (0 == cmp)
261 {
262 if(!EVP_PKEY_copy_parameters(*pToKey, pFromKey))
263 throw OpenSSLException("EVPPKey::duplicate(): EVP_PKEY_copy_parameters()");
264 }
265 }
266 else throw OpenSSLException();
267 break;
268 }
269 default:
270 throw NotImplementedException("EVPPKey:duplicate(); Key type: " +
271 NumberFormatter::format(keyType));
272 }
273 return *pToKey;
274}
275
276
277void EVPPKey::newECKey(const char* ecCurveName)
278{
279 int curveID = OBJ_txt2nid(ecCurveName);
280 EC_KEY* pEC = EC_KEY_new_by_curve_name(curveID);
281 if (!pEC) goto err;
282 if (!EC_KEY_generate_key(pEC)) goto err;
283 _pEVPPKey = EVP_PKEY_new();
284 if (!_pEVPPKey) goto err;
285 if (!EVP_PKEY_set1_EC_KEY(_pEVPPKey, pEC)) goto err;
286 EC_KEY_free(pEC);
287 return;
288err:
289 throw OpenSSLException("EVPPKey:newECKey()");
290}
291
292
293void EVPPKey::setKey(ECKey* pKey)
294{
295 poco_check_ptr(pKey);
296 poco_check_ptr(pKey->impl());
297 setKey(pKey->impl()->getECKey());
298}
299
300
301void EVPPKey::setKey(RSAKey* pKey)
302{
303 poco_check_ptr(pKey);
304 poco_check_ptr(pKey->impl());
305 setKey(pKey->impl()->getRSA());
306}
307
308
309int EVPPKey::passCB(char* buf, int size, int, void* pass)
310{
311 if (pass)
312 {
313 int len = (int)std::strlen((char*)pass);
314 if(len > size) len = size;
315 std::memcpy(buf, pass, len);
316 return len;
317 }
318 return 0;
319}
320
321
322} } // namespace Poco::Crypto
323