| 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 | |