#include #include #include #include #include #include #include #include #include #include #include #include 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() ) { 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<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& 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: "<= 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() ) { 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<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& 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(); 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 >(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 >(max_depth) ); } } // fc