1 | // |
2 | // EVPPKey.h |
3 | // |
4 | // |
5 | // Library: Crypto |
6 | // Package: CryptoCore |
7 | // Module: EVPPKey |
8 | // |
9 | // Definition of the EVPPKey class. |
10 | // |
11 | // Copyright (c) 2008, Applied Informatics Software Engineering GmbH. |
12 | // and Contributors. |
13 | // |
14 | // SPDX-License-Identifier: BSL-1.0 |
15 | // |
16 | |
17 | |
18 | #ifndef Crypto_EVPPKeyImpl_INCLUDED |
19 | #define Crypto_EVPPKeyImpl_INCLUDED |
20 | |
21 | |
22 | #include "Poco/Crypto/Crypto.h" |
23 | #include "Poco/Crypto/CryptoException.h" |
24 | #include "Poco/StreamCopier.h" |
25 | #include <openssl/ec.h> |
26 | #include <openssl/rsa.h> |
27 | #include <openssl/evp.h> |
28 | #include <openssl/pem.h> |
29 | #include <sstream> |
30 | #include <typeinfo> |
31 | |
32 | |
33 | namespace Poco { |
34 | namespace Crypto { |
35 | |
36 | |
37 | class ECKey; |
38 | class RSAKey; |
39 | |
40 | |
41 | class Crypto_API EVPPKey |
42 | /// Utility class for conversion of native keys to EVP. |
43 | /// Currently, only RSA and EC keys are supported. |
44 | { |
45 | public: |
46 | EVPPKey() = delete; |
47 | |
48 | explicit EVPPKey(const std::string& ecCurveName); |
49 | /// Constructs EVPPKey from ECC curve name. |
50 | /// |
51 | /// Only EC keys can be wrapped by an EVPPKey |
52 | /// created using this constructor. |
53 | |
54 | explicit EVPPKey(const char* ecCurveName); |
55 | /// Constructs EVPPKey from ECC curve name. |
56 | /// |
57 | /// Only EC keys can be wrapped by an EVPPKey |
58 | /// created using this constructor. |
59 | |
60 | explicit EVPPKey(EVP_PKEY* pEVPPKey); |
61 | /// Constructs EVPPKey from EVP_PKEY pointer. |
62 | /// The content behind the supplied pointer is internally duplicated. |
63 | |
64 | template<typename K> |
65 | explicit EVPPKey(K* pKey): _pEVPPKey(EVP_PKEY_new()) |
66 | /// Constructs EVPPKey from a "native" OpenSSL (RSA or EC_KEY), |
67 | /// or a Poco wrapper (RSAKey, ECKey) key pointer. |
68 | { |
69 | if (!_pEVPPKey) throw OpenSSLException(); |
70 | setKey(pKey); |
71 | } |
72 | |
73 | EVPPKey(const std::string& publicKeyFile, |
74 | const std::string& privateKeyFile, |
75 | const std::string& privateKeyPassphrase = "" ); |
76 | /// Creates the EVPPKey, by reading public and private key from the given files and |
77 | /// using the given passphrase for the private key. Can only by used for signing if |
78 | /// a private key is available. |
79 | |
80 | EVPPKey(std::istream* pPublicKeyStream, |
81 | std::istream* pPrivateKeyStream, |
82 | const std::string& privateKeyPassphrase = "" ); |
83 | /// Creates the EVPPKey. Can only by used for signing if pPrivKey |
84 | /// is not null. If a private key file is specified, you don't need to |
85 | /// specify a public key file. OpenSSL will auto-create it from the private key. |
86 | |
87 | EVPPKey(const EVPPKey& other); |
88 | /// Copy constructor. |
89 | |
90 | EVPPKey(EVPPKey&& other); |
91 | /// Move constructor. |
92 | |
93 | EVPPKey& operator=(const EVPPKey& other); |
94 | /// Assignment operator. |
95 | |
96 | EVPPKey& operator=(EVPPKey&& other); |
97 | /// Assignment move operator. |
98 | |
99 | ~EVPPKey(); |
100 | /// Destroys the EVPPKey. |
101 | |
102 | bool operator == (const EVPPKey& other) const; |
103 | /// Comparison operator. |
104 | /// Returns true if public key components and parameters |
105 | /// of the other key are equal to this key. |
106 | /// |
107 | /// Works as expected when one key contains only public key, |
108 | /// while the other one contains private (thus also public) key. |
109 | |
110 | bool operator != (const EVPPKey& other) const; |
111 | /// Comparison operator. |
112 | /// Returns true if public key components and parameters |
113 | /// of the other key are different from this key. |
114 | /// |
115 | /// Works as expected when one key contains only public key, |
116 | /// while the other one contains private (thus also public) key. |
117 | |
118 | void save(const std::string& publicKeyFile, |
119 | const std::string& privateKeyFile = "" , |
120 | const std::string& privateKeyPassphrase = "" ) const; |
121 | /// Exports the public and/or private keys to the given files. |
122 | /// |
123 | /// If an empty filename is specified, the corresponding key |
124 | /// is not exported. |
125 | |
126 | void save(std::ostream* pPublicKeyStream, |
127 | std::ostream* pPrivateKeyStream = 0, |
128 | const std::string& privateKeyPassphrase = "" ) const; |
129 | /// Exports the public and/or private key to the given streams. |
130 | /// |
131 | /// If a null pointer is passed for a stream, the corresponding |
132 | /// key is not exported. |
133 | |
134 | int type() const; |
135 | /// Retuns the EVPPKey type NID. |
136 | |
137 | bool isSupported(int type) const; |
138 | /// Returns true if OpenSSL type is supported |
139 | |
140 | operator const EVP_PKEY*() const; |
141 | /// Returns const pointer to the OpenSSL EVP_PKEY structure. |
142 | |
143 | operator EVP_PKEY*(); |
144 | /// Returns pointer to the OpenSSL EVP_PKEY structure. |
145 | |
146 | static EVP_PKEY* duplicate(const EVP_PKEY* pFromKey, EVP_PKEY** pToKey); |
147 | /// Duplicates pFromKey into *pToKey and returns |
148 | // the pointer to duplicated EVP_PKEY. |
149 | |
150 | private: |
151 | static int type(const EVP_PKEY* pEVPPKey); |
152 | void newECKey(const char* group); |
153 | void setKey(ECKey* pKey); |
154 | void setKey(RSAKey* pKey); |
155 | void setKey(EC_KEY* pKey); |
156 | void setKey(RSA* pKey); |
157 | static int passCB(char* buf, int size, int, void* pass); |
158 | |
159 | typedef EVP_PKEY* (*PEM_read_FILE_Key_fn)(FILE*, EVP_PKEY**, pem_password_cb*, void*); |
160 | typedef EVP_PKEY* (*PEM_read_BIO_Key_fn)(BIO*, EVP_PKEY**, pem_password_cb*, void*); |
161 | typedef void* (*EVP_PKEY_get_Key_fn)(EVP_PKEY*); |
162 | |
163 | // The following load*() functions are used by both native and EVP_PKEY type key |
164 | // loading from BIO/FILE. |
165 | // When used for EVP key loading, getFunc is null (ie. native key is not extracted |
166 | // from the loaded EVP_PKEY). |
167 | template <typename K, typename F> |
168 | static bool loadKey(K** ppKey, |
169 | PEM_read_FILE_Key_fn readFunc, |
170 | F getFunc, |
171 | const std::string& keyFile, |
172 | const std::string& pass = "" ) |
173 | { |
174 | poco_assert_dbg (((typeid(K*) == typeid(RSA*) || typeid(K*) == typeid(EC_KEY*)) && getFunc) || |
175 | ((typeid(K*) == typeid(EVP_PKEY*)) && !getFunc)); |
176 | poco_check_ptr (ppKey); |
177 | poco_assert_dbg (!*ppKey); |
178 | |
179 | FILE* pFile = 0; |
180 | if (!keyFile.empty()) |
181 | { |
182 | if (!getFunc) *ppKey = (K*)EVP_PKEY_new(); |
183 | EVP_PKEY* pKey = getFunc ? EVP_PKEY_new() : (EVP_PKEY*)*ppKey; |
184 | if (pKey) |
185 | { |
186 | pFile = fopen(keyFile.c_str(), "r" ); |
187 | if (pFile) |
188 | { |
189 | pem_password_cb* pCB = pass.empty() ? (pem_password_cb*)0 : &passCB; |
190 | void* pPassword = pass.empty() ? (void*)0 : (void*)pass.c_str(); |
191 | if (readFunc(pFile, &pKey, pCB, pPassword)) |
192 | { |
193 | fclose(pFile); pFile = 0; |
194 | if(getFunc) |
195 | { |
196 | *ppKey = (K*)getFunc(pKey); |
197 | EVP_PKEY_free(pKey); |
198 | } |
199 | else |
200 | { |
201 | poco_assert_dbg (typeid(K*) == typeid(EVP_PKEY*)); |
202 | *ppKey = (K*)pKey; |
203 | } |
204 | if(!*ppKey) goto error; |
205 | return true; |
206 | } |
207 | goto error; |
208 | } |
209 | else |
210 | { |
211 | if (getFunc) EVP_PKEY_free(pKey); |
212 | throw IOException("ECKeyImpl, cannot open file" , keyFile); |
213 | } |
214 | } |
215 | else goto error; |
216 | } |
217 | return false; |
218 | |
219 | error: |
220 | if (pFile) fclose(pFile); |
221 | throw OpenSSLException("EVPKey::loadKey(string)" ); |
222 | } |
223 | |
224 | template <typename K, typename F> |
225 | static bool loadKey(K** ppKey, |
226 | PEM_read_BIO_Key_fn readFunc, |
227 | F getFunc, |
228 | std::istream* pIstr, |
229 | const std::string& pass = "" ) |
230 | { |
231 | poco_assert_dbg (((typeid(K*) == typeid(RSA*) || typeid(K*) == typeid(EC_KEY*)) && getFunc) || |
232 | ((typeid(K*) == typeid(EVP_PKEY*)) && !getFunc)); |
233 | poco_check_ptr(ppKey); |
234 | poco_assert_dbg(!*ppKey); |
235 | |
236 | BIO* pBIO = 0; |
237 | if (pIstr) |
238 | { |
239 | std::ostringstream ostr; |
240 | Poco::StreamCopier::copyStream(*pIstr, ostr); |
241 | std::string key = ostr.str(); |
242 | pBIO = BIO_new_mem_buf(const_cast<char*>(key.data()), static_cast<int>(key.size())); |
243 | if (pBIO) |
244 | { |
245 | if (!getFunc) *ppKey = (K*)EVP_PKEY_new(); |
246 | EVP_PKEY* pKey = getFunc ? EVP_PKEY_new() : (EVP_PKEY*)*ppKey; |
247 | if (pKey) |
248 | { |
249 | pem_password_cb* pCB = pass.empty() ? (pem_password_cb*)0 : &passCB; |
250 | void* pPassword = pass.empty() ? (void*)0 : (void*)pass.c_str(); |
251 | if (readFunc(pBIO, &pKey, pCB, pPassword)) |
252 | { |
253 | BIO_free(pBIO); pBIO = 0; |
254 | if (getFunc) |
255 | { |
256 | *ppKey = (K*)getFunc(pKey); |
257 | EVP_PKEY_free(pKey); |
258 | } |
259 | else |
260 | { |
261 | poco_assert_dbg (typeid(K*) == typeid(EVP_PKEY*)); |
262 | *ppKey = (K*)pKey; |
263 | } |
264 | if (!*ppKey) goto error; |
265 | return true; |
266 | } |
267 | if (getFunc) EVP_PKEY_free(pKey); |
268 | goto error; |
269 | } |
270 | else goto error; |
271 | } |
272 | else goto error; |
273 | } |
274 | return false; |
275 | |
276 | error: |
277 | if (pBIO) BIO_free(pBIO); |
278 | throw OpenSSLException("EVPKey::loadKey(stream)" ); |
279 | } |
280 | |
281 | EVP_PKEY* _pEVPPKey = nullptr; |
282 | |
283 | friend class ECKeyImpl; |
284 | friend class RSAKeyImpl; |
285 | }; |
286 | |
287 | |
288 | // |
289 | // inlines |
290 | // |
291 | |
292 | |
293 | inline bool EVPPKey::operator == (const EVPPKey& other) const |
294 | { |
295 | poco_check_ptr (other._pEVPPKey); |
296 | poco_check_ptr (_pEVPPKey); |
297 | return (1 == EVP_PKEY_cmp(_pEVPPKey, other._pEVPPKey)); |
298 | } |
299 | |
300 | |
301 | inline bool EVPPKey::operator != (const EVPPKey& other) const |
302 | { |
303 | return !(other == *this); |
304 | } |
305 | |
306 | |
307 | inline int EVPPKey::type(const EVP_PKEY* pEVPPKey) |
308 | { |
309 | if (!pEVPPKey) return NID_undef; |
310 | |
311 | return EVP_PKEY_type(EVP_PKEY_id(pEVPPKey)); |
312 | } |
313 | |
314 | |
315 | inline int EVPPKey::type() const |
316 | { |
317 | return type(_pEVPPKey); |
318 | } |
319 | |
320 | |
321 | inline bool EVPPKey::isSupported(int type) const |
322 | { |
323 | return type == EVP_PKEY_EC || type == EVP_PKEY_RSA; |
324 | } |
325 | |
326 | |
327 | inline EVPPKey::operator const EVP_PKEY*() const |
328 | /// Returns const pointer to the EVP_PKEY structure. |
329 | { |
330 | return _pEVPPKey; |
331 | } |
332 | |
333 | |
334 | inline EVPPKey::operator EVP_PKEY*() |
335 | /// Returns pointer to the EVP_PKEY structure. |
336 | { |
337 | return _pEVPPKey; |
338 | } |
339 | |
340 | |
341 | inline void EVPPKey::setKey(EC_KEY* pKey) |
342 | { |
343 | if (!EVP_PKEY_set1_EC_KEY(_pEVPPKey, pKey)) |
344 | throw OpenSSLException(); |
345 | } |
346 | |
347 | |
348 | inline void EVPPKey::setKey(RSA* pKey) |
349 | { |
350 | if (!EVP_PKEY_set1_RSA(_pEVPPKey, pKey)) |
351 | throw OpenSSLException(); |
352 | } |
353 | |
354 | |
355 | } } // namespace Poco::Crypto |
356 | |
357 | |
358 | #endif // Crypto_EVPPKeyImpl_INCLUDED |
359 | |