RSA
is an asymmetric public key algorithm that has been formalized in RFC 3447. It is in widespread use in public key infrastuctures (PKI) where certificates (cf. OpenSSL::X509::Certificate
) often are issued on the basis of a public/private RSA
key pair. RSA
is used in a wide field of applications such as secure (symmetric) key exchange, e.g. when establishing a secure TLS/SSL connection. It is also used in various digital signature schemes.
static VALUE
ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass)
{
/* why does this method exist? why can't initialize take an optional exponent? */
RSA *rsa;
VALUE size, exp;
VALUE obj;
rb_scan_args(argc, argv, "11", &size, &exp);
rsa = rsa_generate(NUM2INT(size), NIL_P(exp) ? RSA_F4 : NUM2ULONG(exp)); /* err handled by rsa_instance */
obj = rsa_instance(klass, rsa);
if (obj == Qfalse) {
RSA_free(rsa);
ossl_raise(eRSAError, NULL);
}
return obj;
}
Generates an RSA
keypair. size
is an integer representing the desired key size. Keys smaller than 1024 should be considered insecure. exponent
is an odd number normally 3, 17, or 65537.
static VALUE
ossl_rsa_initialize(int argc, VALUE *argv, VALUE self)
{
EVP_PKEY *pkey;
RSA *rsa;
BIO *in;
char *passwd = NULL;
VALUE arg, pass;
GetPKey(self, pkey);
if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) {
rsa = RSA_new();
}
else if (FIXNUM_P(arg)) {
rsa = rsa_generate(FIX2INT(arg), NIL_P(pass) ? RSA_F4 : NUM2ULONG(pass));
if (!rsa) ossl_raise(eRSAError, NULL);
}
else {
if (!NIL_P(pass)) passwd = StringValuePtr(pass);
arg = ossl_to_der_if_possible(arg);
in = ossl_obj2bio(&arg);
rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd);
if (!rsa) {
OSSL_BIO_reset(in);
rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL);
}
if (!rsa) {
OSSL_BIO_reset(in);
rsa = d2i_RSAPrivateKey_bio(in, NULL);
}
if (!rsa) {
OSSL_BIO_reset(in);
rsa = d2i_RSA_PUBKEY_bio(in, NULL);
}
if (!rsa) {
OSSL_BIO_reset(in);
rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);
}
if (!rsa) {
OSSL_BIO_reset(in);
rsa = d2i_RSAPublicKey_bio(in, NULL);
}
BIO_free(in);
if (!rsa) {
ossl_raise(eRSAError, "Neither PUB key nor PRIV key");
}
}
if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
RSA_free(rsa);
ossl_raise(eRSAError, NULL);
}
return self;
}
Generates or loads an RSA
keypair. If an integer key_size
is given it represents the desired key size. Keys less than 1024 bits should be considered insecure.
A key can instead be loaded from an encoded_key
which must be PEM or DER encoded. A pass_phrase
can be used to decrypt the key. If none is given OpenSSL
will prompt for the pass phrase.
Examples
OpenSSL::PKey::RSA.new 2048 OpenSSL::PKey::RSA.new File.read 'rsa.pem' OpenSSL::PKey::RSA.new File.read('rsa.pem'), 'my pass phrase'
static VALUE
ossl_rsa_blinding_off(VALUE self)
{
EVP_PKEY *pkey;
GetPKeyRSA(self, pkey);
RSA_blinding_off(pkey->pkey.rsa);
return self;
}
me
static VALUE
ossl_rsa_blinding_on(VALUE self)
{
EVP_PKEY *pkey;
GetPKeyRSA(self, pkey);
if (RSA_blinding_on(pkey->pkey.rsa, ossl_bn_ctx) != 1) {
ossl_raise(eRSAError, NULL);
}
return self;
}
static VALUE
ossl_rsa_export(int argc, VALUE *argv, VALUE self)
{
EVP_PKEY *pkey;
BIO *out;
const EVP_CIPHER *ciph = NULL;
char *passwd = NULL;
VALUE cipher, pass, str;
GetPKeyRSA(self, pkey);
rb_scan_args(argc, argv, "02", &cipher, &pass);
if (!NIL_P(cipher)) {
ciph = GetCipherPtr(cipher);
if (!NIL_P(pass)) {
StringValue(pass);
if (RSTRING_LENINT(pass) < OSSL_MIN_PWD_LEN)
ossl_raise(eOSSLError, "OpenSSL requires passwords to be at least four characters long");
passwd = RSTRING_PTR(pass);
}
}
if (!(out = BIO_new(BIO_s_mem()))) {
ossl_raise(eRSAError, NULL);
}
if (RSA_HAS_PRIVATE(pkey->pkey.rsa)) {
if (!PEM_write_bio_RSAPrivateKey(out, pkey->pkey.rsa, ciph,
NULL, 0, ossl_pem_passwd_cb, passwd)) {
BIO_free(out);
ossl_raise(eRSAError, NULL);
}
} else {
if (!PEM_write_bio_RSA_PUBKEY(out, pkey->pkey.rsa)) {
BIO_free(out);
ossl_raise(eRSAError, NULL);
}
}
str = ossl_membio2str(out);
return str;
}
Outputs this keypair in PEM encoding. If cipher
and pass_phrase
are given they will be used to encrypt the key. cipher
must be an OpenSSL::Cipher::Cipher
instance.
static VALUE
ossl_rsa_get_params(VALUE self)
{
EVP_PKEY *pkey;
VALUE hash;
GetPKeyRSA(self, pkey);
hash = rb_hash_new();
rb_hash_aset(hash, rb_str_new2("n"), ossl_bn_new(pkey->pkey.rsa->n));
rb_hash_aset(hash, rb_str_new2("e"), ossl_bn_new(pkey->pkey.rsa->e));
rb_hash_aset(hash, rb_str_new2("d"), ossl_bn_new(pkey->pkey.rsa->d));
rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.rsa->p));
rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.rsa->q));
rb_hash_aset(hash, rb_str_new2("dmp1"), ossl_bn_new(pkey->pkey.rsa->dmp1));
rb_hash_aset(hash, rb_str_new2("dmq1"), ossl_bn_new(pkey->pkey.rsa->dmq1));
rb_hash_aset(hash, rb_str_new2("iqmp"), ossl_bn_new(pkey->pkey.rsa->iqmp));
return hash;
}
THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!!
Stores all parameters of key to the hash. The hash has keys ‘n’, ‘e’, ‘d’, ‘p’, ‘q’, ‘dmp1’, ‘dmq1’, ‘iqmp’.
Don’t use :-)) (It’s up to you)
static VALUE
ossl_rsa_is_private(VALUE self)
{
EVP_PKEY *pkey;
GetPKeyRSA(self, pkey);
return (RSA_PRIVATE(self, pkey->pkey.rsa)) ? Qtrue : Qfalse;
}
Does this keypair contain a private key?
static VALUE
ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
{
EVP_PKEY *pkey;
int buf_len, pad;
VALUE str, buffer, padding;
GetPKeyRSA(self, pkey);
if (!pkey->pkey.rsa->n)
ossl_raise(eRSAError, "incomplete RSA");
if (!RSA_PRIVATE(self, pkey->pkey.rsa))
ossl_raise(eRSAError, "private key needed");
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_private_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), pkey->pkey.rsa,
pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
rb_str_set_len(str, buf_len);
return str;
}
Decrypt string
, which has been encrypted with the public key, with the private key. padding
defaults to PKCS1_PADDING.
static VALUE
ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self)
{
EVP_PKEY *pkey;
int buf_len, pad;
VALUE str, buffer, padding;
GetPKeyRSA(self, pkey);
if (!pkey->pkey.rsa->n)
ossl_raise(eRSAError, "incomplete RSA");
if (!RSA_PRIVATE(self, pkey->pkey.rsa))
ossl_raise(eRSAError, "private key needed");
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_private_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), pkey->pkey.rsa,
pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
rb_str_set_len(str, buf_len);
return str;
}
Encrypt string
with the private key. padding
defaults to PKCS1_PADDING. The encrypted string output can be decrypted using public_decrypt
.
static VALUE
ossl_rsa_is_public(VALUE self)
{
EVP_PKEY *pkey;
GetPKeyRSA(self, pkey);
/*
* This method should check for n and e. BUG.
*/
return Qtrue;
}
The return value is always true since every private key is also a public key.
static VALUE
ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self)
{
EVP_PKEY *pkey;
int buf_len, pad;
VALUE str, buffer, padding;
GetPKeyRSA(self, pkey);
if (!pkey->pkey.rsa->n)
ossl_raise(eRSAError, "incomplete RSA");
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_public_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), pkey->pkey.rsa,
pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
rb_str_set_len(str, buf_len);
return str;
}
Decrypt string
, which has been encrypted with the private key, with the public key. padding
defaults to PKCS1_PADDING.
static VALUE
ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self)
{
EVP_PKEY *pkey;
int buf_len, pad;
VALUE str, buffer, padding;
GetPKeyRSA(self, pkey);
if (!pkey->pkey.rsa->n)
ossl_raise(eRSAError, "incomplete RSA");
rb_scan_args(argc, argv, "11", &buffer, &padding);
pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
StringValue(buffer);
str = rb_str_new(0, ossl_rsa_buf_size(pkey));
buf_len = RSA_public_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
(unsigned char *)RSTRING_PTR(str), pkey->pkey.rsa,
pad);
if (buf_len < 0) ossl_raise(eRSAError, NULL);
rb_str_set_len(str, buf_len);
return str;
}
Encrypt string
with the public key. padding
defaults to PKCS1_PADDING. The encrypted string output can be decrypted using private_decrypt
.
static VALUE
ossl_rsa_to_public_key(VALUE self)
{
EVP_PKEY *pkey;
RSA *rsa;
VALUE obj;
GetPKeyRSA(self, pkey);
/* err check performed by rsa_instance */
rsa = RSAPublicKey_dup(pkey->pkey.rsa);
obj = rsa_instance(CLASS_OF(self), rsa);
if (obj == Qfalse) {
RSA_free(rsa);
ossl_raise(eRSAError, NULL);
}
return obj;
}
Makes new RSA
instance containing the public key from the private key.
static VALUE
ossl_rsa_to_der(VALUE self)
{
EVP_PKEY *pkey;
int (*i2d_func)_((const RSA*, unsigned char**));
unsigned char *p;
long len;
VALUE str;
GetPKeyRSA(self, pkey);
if(RSA_HAS_PRIVATE(pkey->pkey.rsa))
i2d_func = i2d_RSAPrivateKey;
else
i2d_func = (int (*)(const RSA*, unsigned char**))i2d_RSA_PUBKEY;
if((len = i2d_func(pkey->pkey.rsa, NULL)) <= 0)
ossl_raise(eRSAError, NULL);
str = rb_str_new(0, len);
p = (unsigned char *)RSTRING_PTR(str);
if(i2d_func(pkey->pkey.rsa, &p) < 0)
ossl_raise(eRSAError, NULL);
ossl_str_adjust(str, p);
return str;
}
Outputs this keypair in DER encoding.
static VALUE
ossl_rsa_to_text(VALUE self)
{
EVP_PKEY *pkey;
BIO *out;
VALUE str;
GetPKeyRSA(self, pkey);
if (!(out = BIO_new(BIO_s_mem()))) {
ossl_raise(eRSAError, NULL);
}
if (!RSA_print(out, pkey->pkey.rsa, 0)) { /* offset = 0 */
BIO_free(out);
ossl_raise(eRSAError, NULL);
}
str = ossl_membio2str(out);
return str;
}
THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!!
Dumps all parameters of a keypair to a String
Don’t use :-)) (It’s up to you)