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.

### Constants

NO_PADDING
PKCS1_OAEP_PADDING
PKCS1_PADDING
SSLV23_PADDING

### Public Class Methods

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

Generates an RSA keypair.

See also `OpenSSL::PKey.generate_key`.

`size`

  The desired key size in bits.

`exponent`

  An odd `Integer`, normally 3, 17, or 65537.

    # File ext/openssl/lib/openssl/pkey.rb, line 343
    def generate(size, exp = 0x10001, &blk)
      OpenSSL::PKey.generate_key("RSA", {
        "rsa_keygen_bits" => size,
        "rsa_keygen_pubexp" => exp,
      }, &blk)
    end

new → rsa click to toggle source
new(encoded_key [, passphrase]) → rsa
new(encoded_key) { passphrase } → rsa
new(size [, exponent]) → rsa

Generates or loads an RSA keypair.

If called without arguments, creates a new instance with no key components set. They can be set individually by `set_key`, `set_factors`, and `set_crt_params`.

If called with a `String`, tries to parse as DER or PEM encoding of an RSA key. Note that, if passphrase is not specified but the key is encrypted with a passphrase, OpenSSL will prompt for it. See also `OpenSSL::PKey.read` which can parse keys of any kinds.

If called with a number, generates a new key pair. This form works as an alias of `RSA.generate`.

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 = NULL;
        VALUE arg, pass;
        int type;

        TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
        if (pkey)
            rb_raise(rb_eTypeError, "pkey already initialized");

        /* The RSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
        rb_scan_args(argc, argv, "02", &arg, &pass);
        if (argc == 0) {
            rsa = RSA_new();
            if (!rsa)
                ossl_raise(eRSAError, "RSA_new");
            goto legacy;
        }

        pass = ossl_pem_passwd_value(pass);
        arg = ossl_to_der_if_possible(arg);
        in = ossl_obj2bio(&arg);

        /* First try RSAPublicKey format */
        rsa = d2i_RSAPublicKey_bio(in, NULL);
        if (rsa)
            goto legacy;
        OSSL_BIO_reset(in);
        rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);
        if (rsa)
            goto legacy;
        OSSL_BIO_reset(in);

        /* Use the generic routine */
        pkey = ossl_pkey_read_generic(in, pass);
        BIO_free(in);
        if (!pkey)
            ossl_raise(eRSAError, "Neither PUB key nor PRIV key");

        type = EVP_PKEY_base_id(pkey);
        if (type != EVP_PKEY_RSA) {
            EVP_PKEY_free(pkey);
            rb_raise(eRSAError, "incorrect pkey type: %s", OBJ_nid2sn(type));
        }
        RTYPEDDATA_DATA(self) = pkey;
        return self;

      legacy:
        BIO_free(in);
        pkey = EVP_PKEY_new();
        if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
            EVP_PKEY_free(pkey);
            RSA_free(rsa);
            ossl_raise(eRSAError, "EVP_PKEY_assign_RSA");
        }
        RTYPEDDATA_DATA(self) = pkey;
        return self;
    }

### Public Instance Methods

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)
    {
        if (can_export_rsaprivatekey(self))
            return ossl_pkey_export_traditional(argc, argv, self, 0);
        else
            return ossl_pkey_export_spki(self, 0);
    }

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;

        TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
        if (pkey)
            rb_raise(rb_eTypeError, "pkey already initialized");
        GetRSA(other, rsa);

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

        pkey = EVP_PKEY_new();
        if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa_new) != 1) {
            RSA_free(rsa_new);
            ossl_raise(eRSAError, "EVP_PKEY_assign_RSA");
        }
        RTYPEDDATA_DATA(self) = pkey;

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

Deprecated in version 3.0. Consider using `PKey::PKey#encrypt` and `PKey::PKey#decrypt` instead.

    # File ext/openssl/lib/openssl/pkey.rb, line 435
    def private_decrypt(data, padding = PKCS1_PADDING)
      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
      private? or raise OpenSSL::PKey::RSAError, "private key needed."
      begin
        decrypt(data, {
          "rsa_padding_mode" => translate_padding_mode(padding),
        })
      rescue OpenSSL::PKey::PKeyError
        raise OpenSSL::PKey::RSAError, $!.message
      end
    end

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

Deprecated in version 3.0. Consider using `PKey::PKey#sign_raw` and `PKey::PKey#verify_raw`, and `PKey::PKey#verify_recover` instead.

    # File ext/openssl/lib/openssl/pkey.rb, line 372
    def private_encrypt(string, padding = PKCS1_PADDING)
      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
      private? or raise OpenSSL::PKey::RSAError, "private key needed."
      begin
        sign_raw(nil, string, {
          "rsa_padding_mode" => translate_padding_mode(padding),
        })
      rescue OpenSSL::PKey::PKeyError
        raise OpenSSL::PKey::RSAError, $!.message
      end
    end

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

Deprecated in version 3.0. Consider using `PKey::PKey#sign_raw` and `PKey::PKey#verify_raw`, and `PKey::PKey#verify_recover` instead.

    # File ext/openssl/lib/openssl/pkey.rb, line 394
    def public_decrypt(string, padding = PKCS1_PADDING)
      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
      begin
        verify_recover(nil, string, {
          "rsa_padding_mode" => translate_padding_mode(padding),
        })
      rescue OpenSSL::PKey::PKeyError
        raise OpenSSL::PKey::RSAError, $!.message
      end
    end

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

Deprecated in version 3.0. Consider using `PKey::PKey#encrypt` and `PKey::PKey#decrypt` instead.

    # File ext/openssl/lib/openssl/pkey.rb, line 415
    def public_encrypt(data, padding = PKCS1_PADDING)
      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
      begin
        encrypt(data, {
          "rsa_padding_mode" => translate_padding_mode(padding),
        })
      rescue OpenSSL::PKey::PKeyError
        raise OpenSSL::PKey::RSAError, $!.message
      end
    end

public_key → rsanew click to toggle source

Returns a new `RSA` instance that carries just the public key components.

This method is provided for backwards compatibility. In most cases, there is no need to call this method.

For the purpose of serializing the public key, to PEM or DER encoding of X.509 SubjectPublicKeyInfo format, check `PKey#public_to_pem` and `PKey#public_to_der`.

    # File ext/openssl/lib/openssl/pkey.rb, line 327
    def public_key
      OpenSSL::PKey.read(public_to_der)
    end

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 = OpenSSL::PKey.read(pkey.public_to_der)
    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)
    {
        if (can_export_rsaprivatekey(self))
            return ossl_pkey_export_traditional(0, NULL, self, 1);
        else
            return ossl_pkey_export_spki(self, 1);
    }

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

### Private Instance Methods

translate_padding_mode(num) click to toggle source

    # File ext/openssl/lib/openssl/pkey.rb, line 452
            def translate_padding_mode(num)
      case num
      when PKCS1_PADDING
        "pkcs1"
      when SSLV23_PADDING
        "sslv23"
      when NO_PADDING
        "none"
      when PKCS1_OAEP_PADDING
        "oaep"
      else
        raise OpenSSL::PKey::PKeyError, "unsupported padding mode"
      end
    end