365 lines
12 KiB
C++
Executable file
365 lines
12 KiB
C++
Executable file
#include <fc/crypto/pke.hpp>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/sha.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/err.h>
|
|
#include <assert.h>
|
|
#include <fc/crypto/base64.hpp>
|
|
#include <fc/io/sstream.hpp>
|
|
#include <fc/io/stdio.hpp>
|
|
#include <fc/exception/exception.hpp>
|
|
#include <fc/variant.hpp>
|
|
|
|
namespace fc {
|
|
|
|
namespace detail {
|
|
class pke_impl
|
|
{
|
|
public:
|
|
pke_impl():rsa(nullptr){}
|
|
~pke_impl()
|
|
{
|
|
if( rsa != nullptr )
|
|
RSA_free(rsa);
|
|
}
|
|
RSA* rsa;
|
|
};
|
|
} // detail
|
|
|
|
public_key::operator bool()const { return !!my; }
|
|
private_key::operator bool()const { return !!my; }
|
|
|
|
public_key::public_key()
|
|
{}
|
|
|
|
public_key::public_key( const bytes& d )
|
|
:my( std::make_shared<detail::pke_impl>() )
|
|
{
|
|
string pem = "-----BEGIN RSA PUBLIC KEY-----\n";
|
|
auto b64 = fc::base64_encode( (const unsigned char*)d.data(), d.size() );
|
|
for( size_t i = 0; i < b64.size(); i += 64 )
|
|
pem += b64.substr( i, 64 ) + "\n";
|
|
pem += "-----END RSA PUBLIC KEY-----\n";
|
|
// fc::cerr<<pem;
|
|
|
|
BIO* mem = (BIO*)BIO_new_mem_buf( (void*)pem.c_str(), pem.size() );
|
|
my->rsa = PEM_read_bio_RSAPublicKey(mem, NULL, NULL, NULL );
|
|
BIO_free(mem);
|
|
}
|
|
public_key::public_key( const public_key& k )
|
|
:my(k.my)
|
|
{
|
|
}
|
|
|
|
public_key::public_key( public_key&& k )
|
|
:my(std::move(k.my))
|
|
{
|
|
}
|
|
|
|
public_key::~public_key() { }
|
|
|
|
public_key& public_key::operator=(const public_key& p )
|
|
{
|
|
my = p.my; return *this;
|
|
}
|
|
public_key& public_key::operator=( public_key&& p )
|
|
{
|
|
my = std::move(p.my); return *this;
|
|
}
|
|
bool public_key::verify( const sha1& digest, const array<char,2048/8>& sig )const
|
|
{
|
|
return 0 != RSA_verify( NID_sha1, (const uint8_t*)&digest, 20,
|
|
(uint8_t*)&sig, 2048/8, my->rsa );
|
|
}
|
|
|
|
bool public_key::verify( const sha1& digest, const signature& sig )const
|
|
{
|
|
assert( sig.size() == 2048/8 );
|
|
return 0 != RSA_verify( NID_sha1, (const uint8_t*)&digest, 20,
|
|
(uint8_t*)sig.data(), 2048/8, my->rsa );
|
|
}
|
|
bool public_key::verify( const sha256& digest, const signature& sig )const
|
|
{
|
|
assert( sig.size() == 2048/8 );
|
|
return 0 != RSA_verify( NID_sha256, (const uint8_t*)&digest, 32,
|
|
(uint8_t*)sig.data(), 2048/8, my->rsa );
|
|
}
|
|
bytes public_key::encrypt( const char* b, size_t l )const
|
|
{
|
|
FC_ASSERT( my && my->rsa );
|
|
bytes out( RSA_size(my->rsa) ); //, char(0) );
|
|
int rtn = RSA_public_encrypt( l,
|
|
(unsigned char*)b,
|
|
(unsigned char*)out.data(),
|
|
my->rsa, RSA_PKCS1_OAEP_PADDING );
|
|
if( rtn >= 0 ) {
|
|
out.resize(rtn);
|
|
return out;
|
|
}
|
|
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
|
|
bytes public_key::encrypt( const bytes& in )const
|
|
{
|
|
FC_ASSERT( my && my->rsa );
|
|
bytes out( RSA_size(my->rsa) ); //, char(0) );
|
|
int rtn = RSA_public_encrypt( in.size(),
|
|
(unsigned char*)in.data(),
|
|
(unsigned char*)out.data(),
|
|
my->rsa, RSA_PKCS1_OAEP_PADDING );
|
|
fc::cerr<<"rtn: "<<rtn<<"\n";
|
|
if( rtn >= 0 ) {
|
|
out.resize(rtn);
|
|
return out;
|
|
}
|
|
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
bytes public_key::decrypt( const bytes& in )const
|
|
{
|
|
FC_ASSERT( my && my->rsa );
|
|
bytes out( RSA_size(my->rsa) );//, char(0) );
|
|
int rtn = RSA_public_decrypt( in.size(),
|
|
(unsigned char*)in.data(),
|
|
(unsigned char*)out.data(),
|
|
my->rsa, RSA_PKCS1_OAEP_PADDING );
|
|
if( rtn >= 0 ) {
|
|
out.resize(rtn);
|
|
return out;
|
|
}
|
|
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
|
|
bytes public_key::serialize()const
|
|
{
|
|
bytes ba;
|
|
if( !my ) { return ba; }
|
|
|
|
BIO *mem = BIO_new(BIO_s_mem());
|
|
int e = PEM_write_bio_RSAPublicKey( mem, my->rsa );
|
|
if( e != 1 )
|
|
{
|
|
BIO_free(mem);
|
|
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
char* dat;
|
|
uint32_t l = BIO_get_mem_data( mem, &dat );
|
|
|
|
fc::stringstream ss( string( dat, l ) );
|
|
fc::stringstream key;
|
|
fc::string tmp;
|
|
fc::getline( ss, tmp );
|
|
fc::getline( ss, tmp );
|
|
while( tmp.size() && tmp[0] != '-' )
|
|
{
|
|
key << tmp;
|
|
fc::getline( ss, tmp );
|
|
}
|
|
auto str = key.str();
|
|
str = fc::base64_decode( str );
|
|
ba = bytes( str.begin(), str.end() );
|
|
|
|
BIO_free(mem);
|
|
return ba;
|
|
}
|
|
|
|
private_key::private_key()
|
|
{
|
|
}
|
|
private_key::private_key( const bytes& d )
|
|
:my( std::make_shared<detail::pke_impl>() )
|
|
{
|
|
|
|
string pem = "-----BEGIN RSA PRIVATE KEY-----\n";
|
|
auto b64 = fc::base64_encode( (const unsigned char*)d.data(), d.size() );
|
|
for( size_t i = 0; i < b64.size(); i += 64 )
|
|
pem += b64.substr( i, 64 ) + "\n";
|
|
pem += "-----END RSA PRIVATE KEY-----\n";
|
|
// fc::cerr<<pem;
|
|
|
|
BIO* mem = (BIO*)BIO_new_mem_buf( (void*)pem.c_str(), pem.size() );
|
|
my->rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL );
|
|
BIO_free(mem);
|
|
|
|
FC_ASSERT( my->rsa, "read private key" );
|
|
}
|
|
|
|
private_key::private_key( const private_key& k )
|
|
:my(k.my)
|
|
{
|
|
}
|
|
private_key::private_key( private_key&& k )
|
|
:my(std::move(k.my) )
|
|
{
|
|
}
|
|
private_key::~private_key() { }
|
|
|
|
private_key& private_key::operator=(const private_key& p )
|
|
{
|
|
my = p.my; return *this;
|
|
}
|
|
private_key& private_key::operator=(private_key&& p )
|
|
{
|
|
my = std::move(p.my); return *this;
|
|
}
|
|
|
|
void private_key::sign( const sha1& digest, array<char,2048/8>& sig )const
|
|
{
|
|
FC_ASSERT( (size_t(RSA_size(my->rsa)) <= sizeof(sig)), "Invalid RSA size" );
|
|
uint32_t slen = 0;
|
|
if( 1 != RSA_sign( NID_sha1, (uint8_t*)&digest,
|
|
20, (unsigned char*)&sig, &slen, my->rsa ) )
|
|
{
|
|
FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
}
|
|
|
|
signature private_key::sign( const sha1& digest )const
|
|
{
|
|
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
|
|
signature sig;
|
|
sig.resize( RSA_size(my->rsa) );
|
|
|
|
uint32_t slen = 0;
|
|
if( 1 != RSA_sign( NID_sha1, (uint8_t*)digest.data(),
|
|
20, (unsigned char*)sig.data(), &slen, my->rsa ) )
|
|
{
|
|
FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
return sig;
|
|
}
|
|
signature private_key::sign( const sha256& digest )const
|
|
{
|
|
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
|
|
signature sig;
|
|
sig.resize( RSA_size(my->rsa) );
|
|
|
|
uint32_t slen = 0;
|
|
if( 1 != RSA_sign( NID_sha256, (uint8_t*)digest.data(),
|
|
32, (unsigned char*)sig.data(), &slen, my->rsa ) )
|
|
{
|
|
FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
return sig;
|
|
}
|
|
|
|
|
|
bytes private_key::encrypt( const bytes& in )const
|
|
{
|
|
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
|
|
bytes out;
|
|
out.resize( RSA_size(my->rsa) );
|
|
int rtn = RSA_private_encrypt( in.size(),
|
|
(unsigned char*)in.data(),
|
|
(unsigned char*)out.data(),
|
|
my->rsa, RSA_PKCS1_OAEP_PADDING );
|
|
if( rtn >= 0 ) {
|
|
out.resize(rtn);
|
|
return out;
|
|
}
|
|
|
|
FC_THROW_EXCEPTION( exception, "encrypt failed" );
|
|
}
|
|
|
|
bytes private_key::decrypt( const char* in, size_t l )const
|
|
{
|
|
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
|
|
bytes out;
|
|
out.resize( RSA_size(my->rsa) );
|
|
int rtn = RSA_private_decrypt( l,
|
|
(unsigned char*)in,
|
|
(unsigned char*)out.data(),
|
|
my->rsa, RSA_PKCS1_OAEP_PADDING );
|
|
if( rtn >= 0 ) {
|
|
out.resize(rtn);
|
|
return out;
|
|
}
|
|
FC_THROW_EXCEPTION( exception, "decrypt failed" );
|
|
}
|
|
bytes private_key::decrypt( const bytes& in )const
|
|
{
|
|
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
|
|
bytes out;
|
|
out.resize( RSA_size(my->rsa) );
|
|
int rtn = RSA_private_decrypt( in.size(),
|
|
(unsigned char*)in.data(),
|
|
(unsigned char*)out.data(),
|
|
my->rsa, RSA_PKCS1_OAEP_PADDING );
|
|
if( rtn >= 0 ) {
|
|
out.resize(rtn);
|
|
return out;
|
|
}
|
|
FC_THROW_EXCEPTION( exception, "decrypt failed" );
|
|
}
|
|
|
|
bytes private_key::serialize()const
|
|
{
|
|
bytes ba;
|
|
if( !my ) { return ba; }
|
|
|
|
BIO *mem = BIO_new(BIO_s_mem());
|
|
int e = PEM_write_bio_RSAPrivateKey( mem, my->rsa, NULL, NULL, 0, NULL, NULL );
|
|
if( e != 1 )
|
|
{
|
|
BIO_free(mem);
|
|
FC_THROW_EXCEPTION( exception, "Error writing private key, ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
|
|
}
|
|
char* dat;
|
|
uint32_t l = BIO_get_mem_data( mem, &dat );
|
|
// return bytes( dat, dat + l );
|
|
|
|
stringstream ss( string( dat, l ) );
|
|
stringstream key;
|
|
string tmp;
|
|
fc::getline( ss, tmp );
|
|
fc::getline( ss, tmp );
|
|
|
|
while( tmp.size() && tmp[0] != '-' )
|
|
{
|
|
key << tmp;
|
|
fc::getline( ss, tmp );
|
|
}
|
|
auto str = key.str();
|
|
str = fc::base64_decode( str );
|
|
ba = bytes( str.begin(), str.end() );
|
|
// ba = bytes( dat, dat + l );
|
|
BIO_free(mem);
|
|
return ba;
|
|
}
|
|
|
|
void generate_key_pair( public_key& pub, private_key& priv )
|
|
{
|
|
static bool init = true;
|
|
if( init ) { ERR_load_crypto_strings(); init = false; }
|
|
|
|
pub.my = std::make_shared<detail::pke_impl>();
|
|
priv.my = pub.my;
|
|
pub.my->rsa = RSA_generate_key( 2048, 65537, NULL, NULL );
|
|
}
|
|
|
|
/** encodes the big int as base64 string, or a number */
|
|
void to_variant( const public_key& bi, variant& v, uint32_t max_depth )
|
|
{
|
|
v = bi.serialize();
|
|
}
|
|
|
|
/** decodes the big int as base64 string, or a number */
|
|
void from_variant( const variant& v, public_key& bi, uint32_t max_depth )
|
|
{
|
|
bi = public_key( v.as<std::vector<char> >(max_depth) );
|
|
}
|
|
|
|
|
|
/** encodes the big int as base64 string, or a number */
|
|
void to_variant( const private_key& bi, variant& v, uint32_t max_depth )
|
|
{
|
|
v = bi.serialize();
|
|
}
|
|
|
|
/** decodes the big int as base64 string, or a number */
|
|
void from_variant( const variant& v, private_key& bi, uint32_t max_depth )
|
|
{
|
|
bi = private_key( v.as<std::vector<char> >(max_depth) );
|
|
}
|
|
|
|
} // fc
|