// // RSAKeyImpl.cpp // // $Id: //poco/Main/Crypto/src/RSAKeyImpl.cpp#3 $ // // Library: Crypto // Package: RSA // Module: RSAKeyImpl // // Copyright (c) 2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Poco/Crypto/RSAKeyImpl.h" #include "Poco/Crypto/X509Certificate.h" #include "Poco/FileStream.h" #include "Poco/StreamCopier.h" #include "Poco/TemporaryFile.h" #include #include namespace Poco { namespace Crypto { RSAKeyImpl::RSAKeyImpl(const X509Certificate& cert): _pRSA(0) { const X509* pCert = cert.certificate(); EVP_PKEY* pKey = X509_get_pubkey(const_cast(pCert)); RSA* pRSA = pKey->pkey.rsa; _pRSA = RSAPublicKey_dup(pRSA); } RSAKeyImpl::RSAKeyImpl(int keyLength, unsigned long exponent): _pRSA(0) { #if OPENSSL_VERSION_NUMBER >= 0x00908000L _pRSA = RSA_new(); int ret = 0; BIGNUM* bn = 0; try { bn = BN_new(); BN_set_word(bn, exponent); ret = RSA_generate_key_ex(_pRSA, keyLength, bn, 0); BN_free(bn); } catch (...) { BN_free(bn); throw; } if (!ret) throw Poco::InvalidArgumentException("Failed to create RSA context"); #else _pRSA = RSA_generate_key(keyLength, exponent, 0, 0); if (!_pRSA) throw Poco::InvalidArgumentException("Failed to create RSA context"); #endif } RSAKeyImpl::RSAKeyImpl( const std::string& publicKeyFile, const std::string& privateKeyFile, const std::string& privateKeyPassphrase): _pRSA(0) { init(publicKeyFile, privateKeyFile, privateKeyPassphrase); } RSAKeyImpl::RSAKeyImpl(std::istream* pPublicKeyStream, std::istream* pPrivateKeyStream, const std::string& privateKeyPassphrase): _pRSA(0) { // due to C lib not supporting streams, we create two temporary files std::string publicKeyFile; Poco::TemporaryFile pubFile; if (pPublicKeyStream) { if (!pubFile.createFile()) throw Poco::CreateFileException("Cannot create temporary file for writing public key"); publicKeyFile = pubFile.path(); Poco::FileOutputStream fout(publicKeyFile); Poco::StreamCopier::copyStream(*pPublicKeyStream, fout); } std::string privateKeyFile; Poco::TemporaryFile privFile; if (pPrivateKeyStream) { if (!privFile.createFile()) throw Poco::CreateFileException("Cannot create temporary file for writing private key"); privateKeyFile = privFile.path(); Poco::FileOutputStream fout(privateKeyFile); Poco::StreamCopier::copyStream(*pPrivateKeyStream, fout); } init(publicKeyFile, privateKeyFile, privateKeyPassphrase); } void RSAKeyImpl::init(const std::string& publicKeyFile, const std::string& privateKeyFile, const std::string& privateKeyPassphrase) { poco_assert_dbg(_pRSA == 0); _pRSA = RSA_new(); if (!publicKeyFile.empty()) { BIO* out = BIO_new(BIO_s_file()); if (!out) throw Poco::IOException("Cannot create BIO for reading public key", publicKeyFile); int rc = BIO_read_filename(out, publicKeyFile.c_str()); if (rc) { RSA* pubKey = PEM_read_bio_RSAPublicKey(out, &_pRSA, 0, 0); BIO_free(out); if (!pubKey) { freeRSA(); throw Poco::FileException("Failed to load public key", publicKeyFile); } } else { freeRSA(); throw Poco::FileNotFoundException("Public key file", publicKeyFile); } } if (!privateKeyFile.empty()) { BIO* out = BIO_new(BIO_s_file()); if (!out) throw Poco::IOException("Cannot create BIO for reading private key", privateKeyFile); int rc = BIO_read_filename(out, privateKeyFile.c_str()); if (rc) { RSA* privKey = 0; if (privateKeyPassphrase.empty()) privKey = PEM_read_bio_RSAPrivateKey(out, &_pRSA, 0, 0); else privKey = PEM_read_bio_RSAPrivateKey(out, &_pRSA, 0, const_cast(privateKeyPassphrase.c_str())); BIO_free(out); if (!privKey) { freeRSA(); throw Poco::FileException("Failed to load private key", privateKeyFile); } } else { freeRSA(); throw Poco::FileNotFoundException("Private key file", privateKeyFile); } } } RSAKeyImpl::~RSAKeyImpl() { freeRSA(); } void RSAKeyImpl::freeRSA() { if (_pRSA) RSA_free(_pRSA); _pRSA = 0; } int RSAKeyImpl::size() const { return RSA_size(_pRSA); } void RSAKeyImpl::save(const std::string& publicKeyFile, const std::string& privateKeyFile, const std::string& privateKeyPassphrase) { if (!publicKeyFile.empty()) { BIO* out = BIO_new(BIO_s_file()); if (!out) throw Poco::IOException("Cannot create BIO for writing public key file", publicKeyFile); try { if (BIO_write_filename(out, const_cast(publicKeyFile.c_str()))) { if (!PEM_write_bio_RSAPublicKey(out, _pRSA)) throw Poco::WriteFileException("Failed to write public key to file", publicKeyFile); } else throw Poco::CreateFileException("Cannot create public key file"); } catch (...) { BIO_free(out); throw; } BIO_free(out); } if (!privateKeyFile.empty()) { BIO* out = BIO_new(BIO_s_file()); if (!out) throw Poco::IOException("Cannot create BIO for writing private key file", privateKeyFile); try { if (BIO_write_filename(out, const_cast(privateKeyFile.c_str()))) { int rc = 0; if (privateKeyPassphrase.empty()) rc = PEM_write_bio_RSAPrivateKey(out, _pRSA, EVP_des_ede3_cbc(), 0, 0, 0, 0); else rc = PEM_write_bio_RSAPrivateKey(out, _pRSA, EVP_des_ede3_cbc(), reinterpret_cast(const_cast(privateKeyPassphrase.c_str())), static_cast(privateKeyPassphrase.length()), 0, 0); if (!rc) throw Poco::FileException("Failed to write private key to file", privateKeyFile); } else throw Poco::CreateFileException("Cannot create private key file", privateKeyFile); } catch (...) { BIO_free(out); throw; } BIO_free(out); } } void RSAKeyImpl::save(std::ostream* pPublicKeyStream, std::ostream* pPrivateKeyStream, const std::string& privateKeyPassphrase) { if (!pPublicKeyStream && !pPrivateKeyStream) return; // due to C lib not supporting streams, we create two temporary files std::string publicKeyFile; Poco::TemporaryFile pubFile; if (pPublicKeyStream) { publicKeyFile = pubFile.path(); if (!pubFile.createFile()) throw Poco::CreateFileException("Cannot create temporary public file"); } std::string privateKeyFile; Poco::TemporaryFile privFile; if (pPrivateKeyStream) { privateKeyFile = privFile.path(); if (!privFile.createFile()) throw Poco::FileException("Cannot crate temporary private key file"); } save(publicKeyFile, privateKeyFile, privateKeyPassphrase); // now copy everything from the temp files to the original streams if (pPublicKeyStream) { Poco::FileInputStream istr(publicKeyFile); Poco::StreamCopier::copyStream(istr, *pPublicKeyStream); } if (pPrivateKeyStream) { Poco::FileInputStream istr(privateKeyFile); Poco::StreamCopier::copyStream(istr, *pPrivateKeyStream); } } } } // namespace Poco::Crypto