class OpenSSL::PKey::RSA - RDoc Documentation

# class OpenSSL::PKey::RSA

`RSA` is an asymmetric public key algorithm that has been formalized in RFC 3447. It is in widespread use in public key infrastructures (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.

### Public Class Methods

generate(size) → RSA instance click to toggle source
generate(size, exponent) → RSA instance

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_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;
    }

new(key_size) → RSA instance click to toggle source
new(encoded_key) → RSA instance
new(encoded_key, pass_phrase) → RSA instance

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_initialize(int argc, VALUE *argv, VALUE self)
    {
        EVP_PKEY *pkey;
        RSA *rsa;
        BIO *in;
        VALUE arg, pass;

        GetPKey(self, pkey);
        if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) {
            rsa = RSA_new();
        }
        else if (RB_INTEGER_TYPE_P(arg)) {
            rsa = rsa_generate(NUM2INT(arg), NIL_P(pass) ? RSA_F4 : NUM2ULONG(pass));
            if (!rsa) ossl_raise(eRSAError, NULL);
        }
        else {
            pass = ossl_pem_passwd_value(pass);
            arg = ossl_to_der_if_possible(arg);
            in = ossl_obj2bio(&arg);
            rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass);
            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;
    }

### Public Instance Methods

blinding_off!() click to toggle source

    static VALUE
    ossl_rsa_blinding_off(VALUE self)
    {
        RSA *rsa;

        GetRSA(self, rsa);
        RSA_blinding_off(rsa);

        return self;
    }

blinding_on!() click to toggle source

    static VALUE
    ossl_rsa_blinding_on(VALUE self)
    {
        RSA *rsa;

        GetRSA(self, rsa);

        if (RSA_blinding_on(rsa, ossl_bn_ctx) != 1) {
            ossl_raise(eRSAError, NULL);
        }
        return self;
    }

export([cipher, pass_phrase]) → PEM-format String click to toggle source

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` instance.

    static VALUE
    ossl_rsa_export(int argc, VALUE *argv, VALUE self)
    {
        RSA *rsa;
        const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
        BIO *out;
        const EVP_CIPHER *ciph = NULL;
        VALUE cipher, pass, str;

        GetRSA(self, rsa);

        rb_scan_args(argc, argv, "02", &cipher, &pass);

        if (!NIL_P(cipher)) {
            ciph = ossl_evp_get_cipherbyname(cipher);
            pass = ossl_pem_passwd_value(pass);
        }
        if (!(out = BIO_new(BIO_s_mem()))) {
            ossl_raise(eRSAError, NULL);
        }
        RSA_get0_key(rsa, &n, &e, &d);
        RSA_get0_factors(rsa, &p, &q);
        RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
        if (n && e && d && p && q && dmp1 && dmq1 && iqmp) {
            if (!PEM_write_bio_RSAPrivateKey(out, rsa, ciph, NULL, 0,
                                             ossl_pem_passwd_cb, (void *)pass)) {
                BIO_free(out);
                ossl_raise(eRSAError, NULL);
            }
        } else {
            if (!PEM_write_bio_RSA_PUBKEY(out, rsa)) {
                BIO_free(out);
                ossl_raise(eRSAError, NULL);
            }
        }
        str = ossl_membio2str(out);

        return str;
    }

Also aliased as: to_pem, to_s
initialize_copy(p1) click to toggle source

    static VALUE
    ossl_rsa_initialize_copy(VALUE self, VALUE other)
    {
        EVP_PKEY *pkey;
        RSA *rsa, *rsa_new;

        GetPKey(self, pkey);
        if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
            ossl_raise(eRSAError, "RSA already initialized");
        GetRSA(other, rsa);

        rsa_new = ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey, (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa);
        if (!rsa_new)
            ossl_raise(eRSAError, "ASN1_dup");

        EVP_PKEY_assign_RSA(pkey, rsa_new);

        return self;
    }

params → hash click to toggle source

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_get_params(VALUE self)
    {
        RSA *rsa;
        VALUE hash;
        const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;

        GetRSA(self, rsa);
        RSA_get0_key(rsa, &n, &e, &d);
        RSA_get0_factors(rsa, &p, &q);
        RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);

        hash = rb_hash_new();
        rb_hash_aset(hash, rb_str_new2("n"), ossl_bn_new(n));
        rb_hash_aset(hash, rb_str_new2("e"), ossl_bn_new(e));
        rb_hash_aset(hash, rb_str_new2("d"), ossl_bn_new(d));
        rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p));
        rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q));
        rb_hash_aset(hash, rb_str_new2("dmp1"), ossl_bn_new(dmp1));
        rb_hash_aset(hash, rb_str_new2("dmq1"), ossl_bn_new(dmq1));
        rb_hash_aset(hash, rb_str_new2("iqmp"), ossl_bn_new(iqmp));

        return hash;
    }

private? → true | false click to toggle source

Does this keypair contain a private key?

    static VALUE
    ossl_rsa_is_private(VALUE self)
    {
        RSA *rsa;

        GetRSA(self, rsa);

        return RSA_PRIVATE(self, rsa) ? Qtrue : Qfalse;
    }

private_decrypt(string) → String click to toggle source
private_decrypt(string, padding) → String

Decrypt string, which has been encrypted with the public key, with the private key. padding defaults to PKCS1_PADDING.

    static VALUE
    ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
    {
        RSA *rsa;
        const BIGNUM *rsa_n;
        int buf_len, pad;
        VALUE str, buffer, padding;

        GetRSA(self, rsa);
        RSA_get0_key(rsa, &rsa_n, NULL, NULL);
        if (!rsa_n)
            ossl_raise(eRSAError, "incomplete RSA");
        if (!RSA_PRIVATE(self, 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, RSA_size(rsa));
        buf_len = RSA_private_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
                                      (unsigned char *)RSTRING_PTR(str), rsa, pad);
        if (buf_len < 0) ossl_raise(eRSAError, NULL);
        rb_str_set_len(str, buf_len);

        return str;
    }

private_encrypt(string) → String click to toggle source
private_encrypt(string, padding) → String

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_private_encrypt(int argc, VALUE *argv, VALUE self)
    {
        RSA *rsa;
        const BIGNUM *rsa_n;
        int buf_len, pad;
        VALUE str, buffer, padding;

        GetRSA(self, rsa);
        RSA_get0_key(rsa, &rsa_n, NULL, NULL);
        if (!rsa_n)
            ossl_raise(eRSAError, "incomplete RSA");
        if (!RSA_PRIVATE(self, 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, RSA_size(rsa));
        buf_len = RSA_private_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
                                      (unsigned char *)RSTRING_PTR(str), rsa, pad);
        if (buf_len < 0) ossl_raise(eRSAError, NULL);
        rb_str_set_len(str, buf_len);

        return str;
    }

public? → true click to toggle source

The return value is always `true` since every private key is also a public key.

    static VALUE
    ossl_rsa_is_public(VALUE self)
    {
        RSA *rsa;

        GetRSA(self, rsa);
        /*
         * This method should check for n and e.  BUG.
         */
        (void)rsa;
        return Qtrue;
    }

public_decrypt(string) → String click to toggle source
public_decrypt(string, padding) → String

Decrypt string, which has been encrypted with the private key, with the public key. padding defaults to PKCS1_PADDING.

    static VALUE
    ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self)
    {
        RSA *rsa;
        const BIGNUM *rsa_n;
        int buf_len, pad;
        VALUE str, buffer, padding;

        GetRSA(self, rsa);
        RSA_get0_key(rsa, &rsa_n, NULL, NULL);
        if (!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, RSA_size(rsa));
        buf_len = RSA_public_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
                                     (unsigned char *)RSTRING_PTR(str), rsa, pad);
        if (buf_len < 0) ossl_raise(eRSAError, NULL);
        rb_str_set_len(str, buf_len);

        return str;
    }

public_encrypt(string) → String click to toggle source
public_encrypt(string, padding) → String

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_public_encrypt(int argc, VALUE *argv, VALUE self)
    {
        RSA *rsa;
        const BIGNUM *rsa_n;
        int buf_len, pad;
        VALUE str, buffer, padding;

        GetRSA(self, rsa);
        RSA_get0_key(rsa, &rsa_n, NULL, NULL);
        if (!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, RSA_size(rsa));
        buf_len = RSA_public_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
                                     (unsigned char *)RSTRING_PTR(str), rsa, pad);
        if (buf_len < 0) ossl_raise(eRSAError, NULL);
        rb_str_set_len(str, buf_len);

        return str;
    }

public_key → RSA click to toggle source

Makes new `RSA` instance containing the public key from the private key.

    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(EVP_PKEY_get0_RSA(pkey));
        obj = rsa_instance(rb_obj_class(self), rsa);
        if (obj == Qfalse) {
            RSA_free(rsa);
            ossl_raise(eRSAError, NULL);
        }
        return obj;
    }

set_crt_params(dmp1, dmq1, iqmp) → self

Sets dmp1, dmq1, iqmp for the `RSA` instance. They are calculated by `d mod (p - 1)`, `d mod (q - 1)` and `q^(-1) mod p` respectively.

set_factors(p, q) → self

Sets p, q for the `RSA` instance.

set_key(n, e, d) → self

Sets n, e, d for the `RSA` instance.

sign_pss(digest, data, salt_length:, mgf1_hash:) → String click to toggle source

Signs data using the Probabilistic Signature Scheme (RSA-PSS) and returns the calculated signature.

`RSAError` will be raised if an error occurs.

See `verify_pss` for the verification operation.

### Parameters¶ ↑

digest

  A `String` containing the message digest algorithm name.

data

  A `String`. The data to be signed.

salt_length

  The length in octets of the salt. Two special values are reserved: `:digest` means the digest length, and `:max` means the maximum possible length for the combination of the private key and the selected message digest algorithm.

mgf1_hash

  The hash algorithm used in MGF1 (the currently supported mask generation function (MGF)).

### Example¶ ↑

    data = "Sign me!"
    pkey = OpenSSL::PKey::RSA.new(2048)
    signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
    pub_key = pkey.public_key
    puts pub_key.verify_pss("SHA256", signature, data,
                            salt_length: :auto, mgf1_hash: "SHA256") # => true

    static VALUE
    ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self)
    {
        VALUE digest, data, options, kwargs[2], signature;
        static ID kwargs_ids[2];
        EVP_PKEY *pkey;
        EVP_PKEY_CTX *pkey_ctx;
        const EVP_MD *md, *mgf1md;
        EVP_MD_CTX *md_ctx;
        size_t buf_len;
        int salt_len;

        if (!kwargs_ids[0]) {
            kwargs_ids[0] = rb_intern_const("salt_length");
            kwargs_ids[1] = rb_intern_const("mgf1_hash");
        }
        rb_scan_args(argc, argv, "2:", &digest, &data, &options);
        rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs);
        if (kwargs[0] == ID2SYM(rb_intern("max")))
            salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */
        else if (kwargs[0] == ID2SYM(rb_intern("digest")))
            salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */
        else
            salt_len = NUM2INT(kwargs[0]);
        mgf1md = ossl_evp_get_digestbyname(kwargs[1]);

        pkey = GetPrivPKeyPtr(self);
        buf_len = EVP_PKEY_size(pkey);
        md = ossl_evp_get_digestbyname(digest);
        StringValue(data);
        signature = rb_str_new(NULL, (long)buf_len);

        md_ctx = EVP_MD_CTX_new();
        if (!md_ctx)
            goto err;

        if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1)
            goto err;

        if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1)
            goto err;

        if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1)
            goto err;

        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1)
            goto err;

        if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1)
            goto err;

        if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1)
            goto err;

        rb_str_set_len(signature, (long)buf_len);

        EVP_MD_CTX_free(md_ctx);
        return signature;

      err:
        EVP_MD_CTX_free(md_ctx);
        ossl_raise(eRSAError, NULL);
    }

to_der → DER-format String click to toggle source

Outputs this keypair in DER encoding.

    static VALUE
    ossl_rsa_to_der(VALUE self)
    {
        RSA *rsa;
        const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
        int (*i2d_func)(const RSA *, unsigned char **);
        unsigned char *ptr;
        long len;
        VALUE str;

        GetRSA(self, rsa);
        RSA_get0_key(rsa, &n, &e, &d);
        RSA_get0_factors(rsa, &p, &q);
        RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
        if (n && e && d && p && q && dmp1 && dmq1 && iqmp)
            i2d_func = i2d_RSAPrivateKey;
        else
            i2d_func = (int (*)(const RSA *, unsigned char **))i2d_RSA_PUBKEY;
        if((len = i2d_func(rsa, NULL)) <= 0)
            ossl_raise(eRSAError, NULL);
        str = rb_str_new(0, len);
        ptr = (unsigned char *)RSTRING_PTR(str);
        if(i2d_func(rsa, &ptr) < 0)
            ossl_raise(eRSAError, NULL);
        ossl_str_adjust(str, ptr);

        return str;
    }

to_pem([cipher, pass_phrase]) → PEM-format String

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` instance.

Alias for: export
to_s([cipher, pass_phrase]) → PEM-format String

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` instance.

Alias for: export
to_text → String click to toggle source

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)

    static VALUE
    ossl_rsa_to_text(VALUE self)
    {
        RSA *rsa;
        BIO *out;
        VALUE str;

        GetRSA(self, rsa);
        if (!(out = BIO_new(BIO_s_mem()))) {
            ossl_raise(eRSAError, NULL);
        }
        if (!RSA_print(out, rsa, 0)) { /* offset = 0 */
            BIO_free(out);
            ossl_raise(eRSAError, NULL);
        }
        str = ossl_membio2str(out);

        return str;
    }

verify_pss(digest, signature, data, salt_length:, mgf1_hash:) → true | false click to toggle source

Verifies data using the Probabilistic Signature Scheme (RSA-PSS).

The return value is `true` if the signature is valid, `false` otherwise. `RSAError` will be raised if an error occurs.

See `sign_pss` for the signing operation and an example code.

### Parameters¶ ↑

digest

  A `String` containing the message digest algorithm name.

data

  A `String`. The data to be signed.

salt_length

  The length in octets of the salt. Two special values are reserved: `:digest` means the digest length, and `:auto` means automatically determining the length based on the signature.

mgf1_hash

  The hash algorithm used in MGF1.

    static VALUE
    ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self)
    {
        VALUE digest, signature, data, options, kwargs[2];
        static ID kwargs_ids[2];
        EVP_PKEY *pkey;
        EVP_PKEY_CTX *pkey_ctx;
        const EVP_MD *md, *mgf1md;
        EVP_MD_CTX *md_ctx;
        int result, salt_len;

        if (!kwargs_ids[0]) {
            kwargs_ids[0] = rb_intern_const("salt_length");
            kwargs_ids[1] = rb_intern_const("mgf1_hash");
        }
        rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options);
        rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs);
        if (kwargs[0] == ID2SYM(rb_intern("auto")))
            salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */
        else if (kwargs[0] == ID2SYM(rb_intern("digest")))
            salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */
        else
            salt_len = NUM2INT(kwargs[0]);
        mgf1md = ossl_evp_get_digestbyname(kwargs[1]);

        GetPKey(self, pkey);
        md = ossl_evp_get_digestbyname(digest);
        StringValue(signature);
        StringValue(data);

        md_ctx = EVP_MD_CTX_new();
        if (!md_ctx)
            goto err;

        if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1)
            goto err;

        if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1)
            goto err;

        if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1)
            goto err;

        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1)
            goto err;

        if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1)
            goto err;

        result = EVP_DigestVerifyFinal(md_ctx,
                                       (unsigned char *)RSTRING_PTR(signature),
                                       RSTRING_LEN(signature));

        switch (result) {
          case 0:
            ossl_clear_error();
            EVP_MD_CTX_free(md_ctx);
            return Qfalse;
          case 1:
            EVP_MD_CTX_free(md_ctx);
            return Qtrue;
          default:
            goto err;
        }

      err:
        EVP_MD_CTX_free(md_ctx);
        ossl_raise(eRSAError, NULL);
    }