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