backport EVPPKey file/stream load/save capabilities

This commit is contained in:
Alex Fabijanic
2017-09-19 22:04:32 -05:00
parent dbd82953cb
commit ab90c48da2
55 changed files with 1244 additions and 832 deletions

View File

@@ -77,11 +77,11 @@ ECKeyImpl::ECKeyImpl(const std::string& publicKeyFile,
const std::string& privateKeyFile,
const std::string& privateKeyPassphrase): KeyPairImpl("ec", KT_EC_IMPL), _pEC(0)
{
if (loadKey(PEM_read_PrivateKey, privateKeyFile, privateKeyPassphrase))
if (EVPPKey::loadKey(&_pEC, PEM_read_PrivateKey, EVP_PKEY_get1_EC_KEY, privateKeyFile, privateKeyPassphrase))
return; // private key is enough
// no private key, this must be public key only, otherwise throw
if (!loadKey(PEM_read_PUBKEY, publicKeyFile))
if (!EVPPKey::loadKey(&_pEC, PEM_read_PUBKEY, EVP_PKEY_get1_EC_KEY, publicKeyFile))
{
throw OpenSSLException("ECKeyImpl(const string&, const string&, const string&");
}
@@ -92,11 +92,11 @@ ECKeyImpl::ECKeyImpl(std::istream* pPublicKeyStream,
std::istream* pPrivateKeyStream,
const std::string& privateKeyPassphrase): KeyPairImpl("ec", KT_EC_IMPL), _pEC(0)
{
if (loadKey(PEM_read_bio_PrivateKey, pPrivateKeyStream, privateKeyPassphrase))
if (EVPPKey::loadKey(&_pEC, PEM_read_bio_PrivateKey, EVP_PKEY_get1_EC_KEY, pPrivateKeyStream, privateKeyPassphrase))
return; // private key is enough
// no private key, this must be public key only, otherwise throw
if (!loadKey(PEM_read_bio_PUBKEY, pPublicKeyStream))
if (!EVPPKey::loadKey(&_pEC, PEM_read_bio_PUBKEY, EVP_PKEY_get1_EC_KEY, pPublicKeyStream))
{
throw OpenSSLException("ECKeyImpl(istream*, istream*, const string&");
}
@@ -119,84 +119,6 @@ void ECKeyImpl::freeEC()
}
int ECKeyImpl::passCB(char* buf, int size, int, void* pass)
{
int len = (int) strlen((char*)pass);
if (len > size) len = size;
memcpy(buf, pass, len);
return len;
}
bool ECKeyImpl::loadKey(PEM_read_Key_fn func, const std::string& keyFile, const std::string& pass)
{
if (!keyFile.empty())
{
EVP_PKEY* pKey = EVP_PKEY_new();
if (pKey)
{
FILE* pFile = fopen(keyFile.c_str(), "r");
if (pFile)
{
func(pFile, &pKey, passCB, pass.empty() ? (void*)0 : (void*)pass.c_str());
_pEC = EVP_PKEY_get1_EC_KEY(pKey);
EVP_PKEY_free(pKey);
if (!_pEC) goto error;
return true;
}
else
{
EVP_PKEY_free(pKey);
throw IOException("ECKeyImpl, cannot open file", keyFile);
}
}
else goto error;
}
return false;
error:
throw OpenSSLException("ECKeyImpl(const string&, const string&, const string&)");
}
bool ECKeyImpl::loadKey(PEM_read_bio_Key_fn func, std::istream* pIstr, const std::string& pass)
{
if (pIstr)
{
std::ostringstream ostr;
Poco::StreamCopier::copyStream(*pIstr, ostr);
std::string key = ostr.str();
BIO *pBIO = BIO_new_mem_buf(const_cast<char*>(key.data()), static_cast<int>(key.size()));
if (pBIO)
{
EVP_PKEY* pKey = EVP_PKEY_new();
if (pKey)
{
if (func(pBIO, &pKey, passCB, pass.empty() ? (void*)0 : (void*)pass.c_str()))
{
_pEC = EVP_PKEY_get1_EC_KEY(pKey);
EVP_PKEY_free(pKey);
BIO_free(pBIO);
if (!_pEC) goto error;
return true;
}
goto error;
}
else
{
BIO_free(pBIO);
goto error;
}
}
else goto error;
}
return false;
error:
throw OpenSSLException("ECKeyImpl::ECKeyImpl(const string&, const string&, const string&)");
}
int ECKeyImpl::size() const
{
int sz = -1;
@@ -211,14 +133,14 @@ int ECKeyImpl::size() const
}
std::string ECKeyImpl::groupName() const
int ECKeyImpl::groupId() const
{
if (_pEC)
{
const EC_GROUP* ecGroup = EC_KEY_get0_group(_pEC);
if (ecGroup)
{
return OBJ_nid2sn(EC_GROUP_get_curve_name(ecGroup));
return EC_GROUP_get_curve_name(ecGroup);
}
else
{
@@ -229,129 +151,4 @@ std::string ECKeyImpl::groupName() const
}
void ECKeyImpl::save(const std::string& publicKeyFile,
const std::string& privateKeyFile,
const std::string& privateKeyPassphrase)
{
if (!publicKeyFile.empty())
{
BIO* bio = BIO_new(BIO_s_file());
if (!bio) throw Poco::IOException("Cannot create BIO for writing public key file", publicKeyFile);
try
{
if (BIO_write_filename(bio, const_cast<char*>(publicKeyFile.c_str())))
{
EVPPKey pKey(_pEC);
if (!PEM_write_bio_PUBKEY(bio, pKey))
{
throw Poco::WriteFileException("Failed to write public key to file", publicKeyFile);
}
}
else throw Poco::CreateFileException("Cannot create public key file");
}
catch (...)
{
BIO_free(bio);
throw;
}
BIO_free(bio);
}
if (!privateKeyFile.empty())
{
BIO* bio = BIO_new(BIO_s_file());
if (!bio) throw Poco::IOException("Cannot create BIO for writing private key file", privateKeyFile);
try
{
if (BIO_write_filename(bio, const_cast<char*>(privateKeyFile.c_str())))
{
EVPPKey pKey(_pEC);
int rc = 0;
if (privateKeyPassphrase.empty())
{
rc = PEM_write_bio_PrivateKey(bio, pKey, 0, 0, 0, 0, 0);
}
else
{
rc = PEM_write_bio_PrivateKey(bio, pKey, EVP_des_ede3_cbc(),
reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())),
static_cast<int>(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(bio);
throw;
}
BIO_free(bio);
}
}
void ECKeyImpl::save(std::ostream* pPublicKeyStream,
std::ostream* pPrivateKeyStream,
const std::string& privateKeyPassphrase)
{
if (pPublicKeyStream)
{
BIO* bio = BIO_new(BIO_s_mem());
if (!bio) throw Poco::IOException("Cannot create BIO for writing public key");
EVPPKey pKey(_pEC);
if (!PEM_write_bio_PUBKEY(bio, pKey))
{
BIO_free(bio);
throw Poco::WriteFileException("Failed to write public key to stream");
}
char* pData;
long size = BIO_get_mem_data(bio, &pData);
pPublicKeyStream->write(pData, static_cast<std::streamsize>(size));
BIO_free(bio);
}
if (pPrivateKeyStream)
{
BIO* bio = BIO_new(BIO_s_mem());
if (!bio) throw Poco::IOException("Cannot create BIO for writing public key");
EVPPKey pKey(_pEC);
int rc = 0;
if (privateKeyPassphrase.empty())
rc = PEM_write_bio_PrivateKey(bio, pKey, 0, 0, 0, 0, 0);
else
rc = PEM_write_bio_PrivateKey(bio, pKey, EVP_des_ede3_cbc(),
reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())),
static_cast<int>(privateKeyPassphrase.length()), 0, 0);
if (!rc)
{
BIO_free(bio);
throw Poco::FileException("Failed to write private key to stream");
}
char* pData;
long size = BIO_get_mem_data(bio, &pData);
pPrivateKeyStream->write(pData, static_cast<std::streamsize>(size));
BIO_free(bio);
}
}
ECKeyImpl::ByteVec ECKeyImpl::convertToByteVec(const BIGNUM* bn)
{
int numBytes = BN_num_bytes(bn);
ByteVec byteVector(numBytes);
ByteVec::value_type* buffer = new ByteVec::value_type[numBytes];
BN_bn2bin(bn, buffer);
for (int i = 0; i < numBytes; ++i)
byteVector[i] = buffer[i];
delete [] buffer;
return byteVector;
}
} } // namespace Poco::Crypto

View File

@@ -41,6 +41,36 @@ EVPPKey::EVPPKey(EVP_PKEY* pEVPPKey): _pEVPPKey(0)
}
EVPPKey::EVPPKey(const std::string& publicKeyFile,
const std::string& privateKeyFile,
const std::string& privateKeyPassphrase): _pEVPPKey(0)
{
if (loadKey(&_pEVPPKey, PEM_read_PrivateKey, (EVP_PKEY_get_Key_fn)0, privateKeyFile, privateKeyPassphrase))
return; // private key is enough
// no private key, this must be public key only, otherwise throw
if (!loadKey(&_pEVPPKey, PEM_read_PUBKEY, (EVP_PKEY_get_Key_fn)0, publicKeyFile))
{
throw OpenSSLException("ECKeyImpl(const string&, const string&, const string&");
}
}
EVPPKey::EVPPKey(std::istream* pPublicKeyStream,
std::istream* pPrivateKeyStream,
const std::string& privateKeyPassphrase): _pEVPPKey(0)
{
if (loadKey(&_pEVPPKey, PEM_read_bio_PrivateKey, (EVP_PKEY_get_Key_fn)0, pPrivateKeyStream, privateKeyPassphrase))
return; // private key is enough
// no private key, this must be public key only, otherwise throw
if (!loadKey(&_pEVPPKey, PEM_read_bio_PUBKEY, (EVP_PKEY_get_Key_fn)0, pPublicKeyStream))
{
throw OpenSSLException("ECKeyImpl(istream*, istream*, const string&");
}
}
EVPPKey::EVPPKey(const EVPPKey& other)
{
duplicate(other._pEVPPKey);
@@ -77,6 +107,106 @@ EVPPKey::~EVPPKey()
}
void EVPPKey::save(const std::string& publicKeyFile, const std::string& privateKeyFile, const std::string& privateKeyPassphrase)
{
if (!publicKeyFile.empty())
{
BIO* bio = BIO_new(BIO_s_file());
if (!bio) throw Poco::IOException("Cannot create BIO for writing public key file", publicKeyFile);
try
{
if (BIO_write_filename(bio, const_cast<char*>(publicKeyFile.c_str())))
{
if (!PEM_write_bio_PUBKEY(bio, _pEVPPKey))
{
throw Poco::WriteFileException("Failed to write public key to file", publicKeyFile);
}
}
else throw Poco::CreateFileException("Cannot create public key file");
}
catch (...)
{
BIO_free(bio);
throw;
}
BIO_free(bio);
}
if (!privateKeyFile.empty())
{
BIO* bio = BIO_new(BIO_s_file());
if (!bio) throw Poco::IOException("Cannot create BIO for writing private key file", privateKeyFile);
try
{
if (BIO_write_filename(bio, const_cast<char*>(privateKeyFile.c_str())))
{
int rc = 0;
if (privateKeyPassphrase.empty())
{
rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, 0, 0, 0, 0, 0);
}
else
{
rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, EVP_des_ede3_cbc(),
reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())),
static_cast<int>(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(bio);
throw;
}
BIO_free(bio);
}
}
void EVPPKey::save(std::ostream* pPublicKeyStream, std::ostream* pPrivateKeyStream, const std::string& privateKeyPassphrase)
{
if (pPublicKeyStream)
{
BIO* bio = BIO_new(BIO_s_mem());
if (!bio) throw Poco::IOException("Cannot create BIO for writing public key");
if (!PEM_write_bio_PUBKEY(bio, _pEVPPKey))
{
BIO_free(bio);
throw Poco::WriteFileException("Failed to write public key to stream");
}
char* pData;
long size = BIO_get_mem_data(bio, &pData);
pPublicKeyStream->write(pData, static_cast<std::streamsize>(size));
BIO_free(bio);
}
if (pPrivateKeyStream)
{
BIO* bio = BIO_new(BIO_s_mem());
if (!bio) throw Poco::IOException("Cannot create BIO for writing public key");
int rc = 0;
if (privateKeyPassphrase.empty())
rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, 0, 0, 0, 0, 0);
else
rc = PEM_write_bio_PrivateKey(bio, _pEVPPKey, EVP_des_ede3_cbc(),
reinterpret_cast<unsigned char*>(const_cast<char*>(privateKeyPassphrase.c_str())),
static_cast<int>(privateKeyPassphrase.length()), 0, 0);
if (!rc)
{
BIO_free(bio);
throw Poco::FileException("Failed to write private key to stream");
}
char* pData;
long size = BIO_get_mem_data(bio, &pData);
pPrivateKeyStream->write(pData, static_cast<std::streamsize>(size));
BIO_free(bio);
}
}
void EVPPKey::duplicate(EVP_PKEY* pEVPPKey)
{
if (!pEVPPKey) throw NullPointerException("EVPPKey::duplicate(): "
@@ -129,7 +259,7 @@ void EVPPKey::newECKey(const char* ecCurveName)
EC_KEY_free(pEC);
return;
err:
throw OpenSSLException();
throw OpenSSLException("EVPPKey:newECKey()");
}
@@ -148,4 +278,14 @@ void EVPPKey::setKey(RSAKey* pKey)
setKey(pKey->impl()->getRSA());
}
int EVPPKey::passCB(char* buf, int size, int, void* pass)
{
int len = (int) strlen((char*)pass);
if (len > size) len = size;
memcpy(buf, pass, len);
return len;
}
} } // namespace Poco::Crypto

View File

@@ -96,7 +96,7 @@ RSAKeyImpl::RSAKeyImpl(const std::string& publicKeyFile,
_pRSA(0)
{
poco_assert_dbg(_pRSA == 0);
_pRSA = RSA_new();
if (!publicKeyFile.empty())
{
@@ -162,7 +162,7 @@ RSAKeyImpl::RSAKeyImpl(std::istream* pPublicKeyStream,
_pRSA(0)
{
poco_assert_dbg(_pRSA == 0);
_pRSA = RSA_new();
if (pPublicKeyStream)
{