Created
July 2, 2020 14:13
-
-
Save jweyrich/476da68dffc1b73abd91366add6f9e4d to your computer and use it in GitHub Desktop.
AbstractKeyGenerator for sslpkix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
//#include <cassert> | |
#include <iostream> | |
#include <openssl/evp.h> | |
#include <openssl/pem.h> | |
#include "sslpkix/iosink.h" | |
#include "sslpkix/common.h" | |
namespace sslpkix { | |
// | |
// NOTE: With OpenSSL, the private key also contains the public key information | |
// | |
class Key { | |
public: | |
typedef EVP_PKEY handle_type; | |
struct Cipher { | |
enum EnumCipher { | |
#ifndef OPENSSL_NO_RSA | |
RSA = 1, | |
#endif | |
#ifndef OPENSSL_NO_DSA | |
DSA = 2, | |
#endif | |
#ifndef OPENSSL_NO_DH | |
DH = 3, // Diffie Hellman | |
#endif | |
#ifndef OPENSSL_NO_EC | |
EC = 4, | |
#endif | |
UNKNOWN = 0 | |
}; | |
}; | |
public: | |
Key() | |
: _handle(NULL) | |
, _is_external_handle(false) | |
{ | |
} | |
Key(const Key& other) | |
: _handle(other._handle) | |
, _is_external_handle(false) | |
{ | |
// Srsly OpenSSL, Y U NO HAVE EVP_PKEY_dup(EVP_PKEY*) ? :-( | |
CRYPTO_add(&_handle->references, 1, CRYPTO_LOCK_EVP_PKEY); | |
if (_handle == NULL) { | |
// std::cerr << "Failed to copy certificate" << std::endl; | |
throw std::bad_alloc(); | |
} | |
//reload_data(); | |
} | |
Key& operator=(Key other) { | |
release(); | |
swap(*this, other); | |
return *this; | |
} | |
friend void swap(Key& a, Key& b) { // nothrow | |
using std::swap; // enable ADL | |
swap(a._handle, b._handle); | |
swap(a._is_external_handle, b._is_external_handle); | |
} | |
virtual ~Key() { | |
release(); | |
} | |
handle_type *handle() const { | |
//assert(_handle != NULL); | |
return _handle; | |
} | |
bool create() { | |
release(); | |
_handle = EVP_PKEY_new(); | |
if (_handle == NULL) | |
std::cerr << "Failed to create key" << std::endl; | |
return _handle != NULL; | |
} | |
Cipher::EnumCipher algorithm() const { | |
int algorithm = EVP_PKEY_type(_handle->type); | |
switch (algorithm) { | |
#ifndef OPENSSL_NO_RSA | |
case EVP_PKEY_RSA: return Cipher::RSA; | |
#endif | |
#ifndef OPENSSL_NO_DSA | |
case EVP_PKEY_DSA: return Cipher::DSA; | |
#endif | |
#ifndef OPENSSL_NO_DH | |
case EVP_PKEY_DH: return Cipher::DH; | |
#endif | |
#ifndef OPENSSL_NO_EC | |
case EVP_PKEY_EC: return Cipher::EC; | |
#endif | |
default: return Cipher::UNKNOWN; | |
} | |
} | |
#ifndef OPENSSL_NO_RSA | |
bool assign(RSA *key) { | |
return EVP_PKEY_assign_RSA(_handle, key) != 0; | |
} | |
bool copy(RSA *key) { | |
return EVP_PKEY_set1_RSA(_handle, key) != 0; | |
} | |
#endif | |
#ifndef OPENSSL_NO_DSA | |
bool assign(DSA *key) { | |
return EVP_PKEY_assign_DSA(_handle, key) != 0; | |
} | |
bool copy(DSA *key) { | |
return EVP_PKEY_set1_DSA(_handle, key) != 0; | |
} | |
#endif | |
#ifndef OPENSSL_NO_DH | |
bool assign(DH *key) { | |
return EVP_PKEY_assign_DH(_handle, key) != 0; | |
} | |
bool copy(DH *key) { | |
return EVP_PKEY_set1_DH(_handle, key) != 0; | |
} | |
#endif | |
#ifndef OPENSSL_NO_EC | |
bool assign(EC_KEY *key) { | |
return EVP_PKEY_assign_EC_KEY(_handle, key) != 0; | |
} | |
bool copy(EC_KEY *key) { | |
return EVP_PKEY_set1_EC_KEY(_handle, key) != 0; | |
} | |
#endif | |
virtual bool load(IoSink& sink UNUSED, const char *password UNUSED) { | |
return false; | |
} | |
virtual bool save(IoSink& sink UNUSED) const { | |
return false; | |
} | |
friend bool operator==(const Key& lhs, const Key& rhs) { | |
// TODO(jweyrich): do we need EVP_PKEY_cmp_parameters() too? | |
return EVP_PKEY_cmp(lhs._handle, rhs._handle) == 1; | |
} | |
friend bool operator!=(const Key& lhs, const Key& rhs) { | |
return !(lhs == rhs); | |
} | |
protected: | |
void release() { | |
if (_handle != NULL && !_is_external_handle) { | |
EVP_PKEY_free(_handle); | |
} | |
_handle = NULL; | |
_is_external_handle = false; | |
} | |
void set_handle(handle_type *handle) { | |
release(); | |
_handle = handle; | |
_is_external_handle = true; | |
} | |
protected: | |
handle_type *_handle; | |
bool _is_external_handle; | |
friend class Certificate; | |
friend class CertificateRequest; | |
}; | |
class PrivateKey : public Key { | |
public: | |
PrivateKey() { | |
} | |
virtual ~PrivateKey() { | |
} | |
virtual bool load(IoSink& sink, const char *password) { | |
release(); | |
_handle = PEM_read_bio_PrivateKey(sink.handle(), NULL, NULL, (void *)password); | |
if (_handle == NULL) | |
std::cerr << "Failed to load private key: " << sink.source() << std::endl; | |
return _handle != NULL; | |
} | |
virtual bool save(IoSink& sink) const { | |
if (_handle == NULL) | |
return false; | |
int ret = PEM_write_bio_PrivateKey(sink.handle(), _handle, NULL, NULL, 0, 0, NULL); | |
if (ret == 0) | |
std::cerr << "Failed to save private key: " << sink.source() << std::endl; | |
return ret != 0; | |
} | |
}; | |
// ------------------------------------------------------------------- | |
template <class SubClassType, class HandleType> | |
class AbstractKeyGenerator { | |
public: | |
typedef HandleType handle_type; | |
typedef SubClassType subclass_type; | |
public: | |
AbstractKeyGenerator() | |
: _handle(NULL) | |
, _is_external_handle(false) | |
{ | |
} | |
virtual ~AbstractKeyGenerator() { | |
release(); | |
} | |
subclass_type& operator=(subclass_type other) { | |
release(); | |
swap(*this, other); | |
return *this; | |
} | |
friend void swap(subclass_type& a, subclass_type& b) { // nothrow | |
using std::swap; // enable ADL | |
swap(a._handle, b._handle); | |
swap(a._is_external_handle, b._is_external_handle); | |
} | |
handle_type *handle() const { | |
//assert(_handle != NULL); | |
return _handle; | |
} | |
virtual bool create() = 0; | |
virtual Key::Cipher::EnumCipher algorithm() const = 0; | |
virtual std::string algorithmName() const = 0; | |
virtual bool assignTo(Key::handle_type *target) = 0; | |
virtual bool copyTo(Key::handle_type *target) = 0; | |
virtual bool generate(uint16_t bits) = 0; | |
protected: | |
virtual void release_handle() = 0; | |
void release() { | |
if (_handle != NULL && !_is_external_handle) { | |
// FIXME(jweyrich): Overrides of 'release_handle' | |
// in subclasses are not available in the destructor of this class. | |
release_handle(); | |
} | |
_handle = NULL; | |
_is_external_handle = false; | |
} | |
void set_handle(handle_type *handle) { | |
release(); | |
_handle = handle; | |
_is_external_handle = true; | |
} | |
protected: | |
handle_type *_handle; | |
bool _is_external_handle; | |
}; | |
class RSAKey : AbstractKeyGenerator<RSAKey, RSA> { | |
public: | |
RSAKey(const RSAKey& other) { | |
// TODO(jweyrich): Implement copy. | |
/* | |
_handle = ...; | |
if (_handle == NULL) { | |
// std::cerr << "Failed to copy RSA key" << std::endl; | |
throw std::bad_alloc(); | |
} | |
//reload_data(); | |
*/ | |
} | |
virtual bool create() { | |
release(); | |
_handle = RSA_new(); | |
if (_handle == NULL) | |
std::cerr << "Failed to create RSA key" << std::endl; | |
return _handle != NULL; | |
} | |
/* | |
bool copy(RSAKey& key) { | |
int ret = PLEASE_IMPLEMENT_THE_COPY(&_handle, key.handle()); | |
if (ret == 0) | |
std::cerr << "Failed to copy RSA key" << std::endl; | |
return ret != 0; | |
} | |
*/ | |
virtual Key::Cipher::EnumCipher algorithm() const { | |
return Key::Cipher::RSA; | |
} | |
virtual std::string algorithmName() const { | |
return "RSA"; | |
} | |
virtual bool assignTo(Key::handle_type *target) { | |
return EVP_PKEY_assign_RSA(target, _handle) != 0; | |
} | |
virtual bool copyTo(Key::handle_type *target) { | |
return EVP_PKEY_set1_RSA(target, _handle) != 0; | |
} | |
virtual bool generate(uint16_t num_bits) { | |
bool success = false; | |
int ret = 0; | |
BN_GENCB callback; | |
BIGNUM *bignum = BN_new(); | |
if (bignum == NULL) { | |
goto do_cleanup; | |
} | |
// Use the fourth Fermat Number | |
ret = BN_set_word(bignum, RSA_F4); | |
if (ret == 0) { | |
goto do_cleanup; | |
} | |
BN_GENCB_set(&callback, prime_generation_callback, NULL); | |
ret = RSA_generate_key_ex( | |
_handle, static_cast<int>(num_bits), bignum, &callback); | |
if (ret == 0) { | |
goto do_cleanup; | |
} | |
success = true; | |
do_cleanup: | |
if (bignum != NULL) | |
BN_free(bignum); | |
return success; | |
} | |
virtual bool load(IoSink& priv, IoSink& pub, const std::string& password) { | |
bool success = false; | |
BIO *bio_pub = pub.handle(); | |
BIO *bio_priv = priv.handle(); | |
pem_password_cb *password_cb = NULL; | |
void *user_data = NULL; | |
RSA *priv_rsa = NULL; | |
RSA *pub_rsa = NULL; | |
priv_rsa = PEM_read_bio_RSAPrivateKey( | |
bio_priv, &_handle, | |
password_cb, user_data); | |
if (priv_rsa == NULL) { | |
goto do_cleanup; | |
} | |
_handle = priv_rsa; | |
pub_rsa = PEM_read_bio_RSAPublicKey( | |
bio_pub, &_handle, | |
password_cb, user_data); | |
if (pub_rsa == NULL) { | |
goto do_cleanup; | |
} | |
_handle = pub_rsa; | |
success = true; | |
do_cleanup: | |
return success; | |
} | |
virtual bool save(IoSink& priv, IoSink& pub, const std::string& password) { | |
bool success = false; | |
BIO *bio_pub = pub.handle(); | |
BIO *bio_priv = priv.handle(); | |
const EVP_CIPHER *cipher = NULL; | |
pem_password_cb *password_cb = NULL; | |
void *user_data = NULL; | |
const unsigned char *tmp_pass = | |
reinterpret_cast<const unsigned char *>(password.c_str()); | |
// Save private key | |
int ret = PEM_write_bio_RSAPrivateKey( | |
bio_priv, _handle, cipher, | |
const_cast<unsigned char *>(tmp_pass), password.length(), | |
password_cb, user_data); | |
if (ret != 1) { | |
goto do_cleanup; | |
} | |
// Save public key | |
ret = PEM_write_bio_RSAPublicKey(bio_pub, _handle); | |
if (ret != 1) { | |
goto do_cleanup; | |
} | |
success = true; | |
do_cleanup: | |
return success; | |
} | |
static int prime_generation_callback(int p, int n, BN_GENCB *arg) { | |
(void)n; | |
(void)arg; | |
char c; | |
switch (p) { | |
default: c = 'B'; break; | |
case 0: c = '.'; break; | |
case 1: c = '+'; break; | |
case 2: c = '*'; break; | |
case 3: c = '\n'; break; | |
} | |
fputc(c, stderr); | |
return 1; | |
} | |
protected: | |
virtual void release_handle() { | |
if (_handle != NULL) { | |
RSA_free(_handle); | |
_handle = NULL; | |
} | |
} | |
}; | |
} // namespace sslpkix |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment