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 | |
22 | namespace Poco { |
23 | namespace Crypto { |
24 | |
25 | |
26 | EVPPKey::EVPPKey(const std::string& ecCurveName) |
27 | { |
28 | newECKey(ecCurveName.c_str()); |
29 | poco_check_ptr(_pEVPPKey); |
30 | } |
31 | |
32 | |
33 | EVPPKey::EVPPKey(const char* ecCurveName) |
34 | { |
35 | newECKey(ecCurveName); |
36 | poco_check_ptr(_pEVPPKey); |
37 | } |
38 | |
39 | |
40 | EVPPKey::EVPPKey(EVP_PKEY* pEVPPKey) |
41 | { |
42 | duplicate(pEVPPKey, &_pEVPPKey); |
43 | poco_check_ptr(_pEVPPKey); |
44 | } |
45 | |
46 | |
47 | EVPPKey::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 | |
66 | EVPPKey::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 | |
85 | EVPPKey::EVPPKey(const EVPPKey& other): _pEVPPKey(0) |
86 | { |
87 | duplicate(other._pEVPPKey, &_pEVPPKey); |
88 | poco_check_ptr(_pEVPPKey); |
89 | } |
90 | |
91 | |
92 | EVPPKey::EVPPKey(EVPPKey&& other): _pEVPPKey(other._pEVPPKey) |
93 | { |
94 | other._pEVPPKey = nullptr; |
95 | poco_check_ptr(_pEVPPKey); |
96 | } |
97 | |
98 | |
99 | EVPPKey& EVPPKey::operator=(const EVPPKey& other) |
100 | { |
101 | duplicate(other._pEVPPKey, &_pEVPPKey); |
102 | return *this; |
103 | } |
104 | |
105 | |
106 | EVPPKey& EVPPKey::operator=(EVPPKey&& other) |
107 | { |
108 | _pEVPPKey = other._pEVPPKey; |
109 | other._pEVPPKey = nullptr; |
110 | poco_check_ptr(_pEVPPKey); |
111 | return *this; |
112 | } |
113 | |
114 | |
115 | EVPPKey::~EVPPKey() |
116 | { |
117 | if (_pEVPPKey) EVP_PKEY_free(_pEVPPKey); |
118 | } |
119 | |
120 | |
121 | void 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 | |
182 | void 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 | |
225 | EVP_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 | |
277 | void 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; |
288 | err: |
289 | throw OpenSSLException("EVPPKey:newECKey()" ); |
290 | } |
291 | |
292 | |
293 | void EVPPKey::setKey(ECKey* pKey) |
294 | { |
295 | poco_check_ptr(pKey); |
296 | poco_check_ptr(pKey->impl()); |
297 | setKey(pKey->impl()->getECKey()); |
298 | } |
299 | |
300 | |
301 | void EVPPKey::setKey(RSAKey* pKey) |
302 | { |
303 | poco_check_ptr(pKey); |
304 | poco_check_ptr(pKey->impl()); |
305 | setKey(pKey->impl()->getRSA()); |
306 | } |
307 | |
308 | |
309 | int 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 | |