🔍

No recent searches

# 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
Source

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

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.

new → rsa
new(encoded_key [, password ]) → rsa
new(encoded_key) { password } → rsa
new(size [, exponent]) → rsa
Source

    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) {
    #ifdef OSSL_HAVE_IMMUTABLE_PKEY
            rb_raise(rb_eArgError, "OpenSSL::PKey::RSA.new cannot be called " \
                     "without arguments; pkeys are immutable with OpenSSL 3.0");
    #else
            rsa = RSA_new();
            if (!rsa)
                ossl_raise(ePKeyError, "RSA_new");
            goto legacy;
    #endif
        }

        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(ePKeyError, "Neither PUB key nor PRIV key");

        type = EVP_PKEY_base_id(pkey);
        if (type != EVP_PKEY_RSA) {
            EVP_PKEY_free(pkey);
            rb_raise(ePKeyError, "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(ePKeyError, "EVP_PKEY_assign_RSA");
        }
        RTYPEDDATA_DATA(self) = pkey;
        return self;
    }

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`. This form is not compatible with `OpenSSL` 3.0 or later.

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

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 password'

### Public Instance Methods

export([cipher, password]) → PEM-format String
Source

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

Serializes a private or public key to a PEM-encoding.

When the key contains public components only

  Serializes it into an X.509 SubjectPublicKeyInfo. The parameters cipher and password are ignored.

  A PEM-encoded key will look like:

      -----BEGIN PUBLIC KEY-----
      [...]
      -----END PUBLIC KEY-----

  Consider using `public_to_pem` instead. This serializes the key into an X.509 SubjectPublicKeyInfo regardless of whether the key is a public key or a private key.

When the key contains private components, and no parameters are given

  Serializes it into a PKCS #1 RSAPrivateKey.

  A PEM-encoded key will look like:

      -----BEGIN RSA PRIVATE KEY-----
      [...]
      -----END RSA PRIVATE KEY-----

When the key contains private components, and cipher and password are given

  Serializes it into a PKCS #1 RSAPrivateKey and encrypts it in OpenSSL’s traditional PEM encryption format. cipher must be a cipher name understood by `OpenSSL::Cipher.new` or an instance of `OpenSSL::Cipher`.

  An encrypted PEM-encoded key will look like:

      -----BEGIN RSA PRIVATE KEY-----
      Proc-Type: 4,ENCRYPTED
      DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0

      [...]
      -----END RSA PRIVATE KEY-----

  Note that this format uses MD5 to derive the encryption key, and hence will not be available on FIPS-compliant systems.

This method is kept for compatibility. This should only be used when the PKCS #1 RSAPrivateKey format is required.

Consider using `public_to_pem` (X.509 SubjectPublicKeyInfo) or `private_to_pem` (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.

Also aliased as: to_pem, to_s
params → hash
Source

    # File ext/openssl/lib/openssl/pkey.rb, line 363
    def params
      %w{n e d p q dmp1 dmq1 iqmp}.map { |name|
        [name, send(name)]
      }.to_h
    end

Stores all parameters of key to a `Hash`.

The hash has keys ‘n’, ‘e’, ‘d’, ‘p’, ‘q’, ‘dmp1’, ‘dmq1’, and ‘iqmp’.

private? → true | false
Source

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

        GetRSA(self, rsa);

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

Does this keypair contain a private key?

private_decrypt(string) → String
private_decrypt(string, padding) → String
Source

    # File ext/openssl/lib/openssl/pkey.rb, line 465
    def private_decrypt(data, padding = PKCS1_PADDING)
      n or raise PKeyError, "incomplete RSA"
      private? or raise PKeyError, "private key needed."
      decrypt(data, {
        "rsa_padding_mode" => translate_padding_mode(padding),
      })
    end

Decrypt `string`, which has been encrypted with the public key, with the private key. `padding` defaults to `PKCS1_PADDING`, which is known to be insecure but is kept for backwards compatibility.

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

private_encrypt(string) → String
private_encrypt(string, padding) → String
Source

    # File ext/openssl/lib/openssl/pkey.rb, line 411
    def private_encrypt(string, padding = PKCS1_PADDING)
      n or raise PKeyError, "incomplete RSA"
      private? or raise PKeyError, "private key needed."
      sign_raw(nil, string, {
        "rsa_padding_mode" => translate_padding_mode(padding),
      })
    end

Encrypt `string` with the private key. `padding` defaults to `PKCS1_PADDING`, which is known to be insecure but is kept for backwards compatibility. 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.

public? → true
Source

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

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

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

public_decrypt(string) → String
public_decrypt(string, padding) → String
Source

    # File ext/openssl/lib/openssl/pkey.rb, line 430
    def public_decrypt(string, padding = PKCS1_PADDING)
      n or raise PKeyError, "incomplete RSA"
      verify_recover(nil, string, {
        "rsa_padding_mode" => translate_padding_mode(padding),
      })
    end

Decrypt `string`, which has been encrypted with the private key, with the public key. `padding` defaults to `PKCS1_PADDING` which is known to be insecure but is kept for backwards compatibility.

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

public_encrypt(string) → String
public_encrypt(string, padding) → String
Source

    # File ext/openssl/lib/openssl/pkey.rb, line 448
    def public_encrypt(data, padding = PKCS1_PADDING)
      n or raise PKeyError, "incomplete RSA"
      encrypt(data, {
        "rsa_padding_mode" => translate_padding_mode(padding),
      })
    end

Encrypt `string` with the public key. `padding` defaults to `PKCS1_PADDING`, which is known to be insecure but is kept for backwards compatibility. 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.

public_key → rsanew
Source

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

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

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
Source

    static VALUE
    ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self)
    {
        VALUE digest, data, options, kwargs[2], signature, mgf1md_holder, md_holder;
        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_md_fetch(kwargs[1], &mgf1md_holder);

        pkey = GetPrivPKeyPtr(self);
        buf_len = EVP_PKEY_size(pkey);
        md = ossl_evp_md_fetch(digest, &md_holder);
        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(ePKeyError, NULL);
    }

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

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

to_der → DER-format String
Source

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

Serializes a private or public key to a DER-encoding.

See `to_pem` for details.

This method is kept for compatibility. This should only be used when the PKCS #1 RSAPrivateKey format is required.

Consider using `public_to_der` or `private_to_der` instead.

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

Serializes a private or public key to a PEM-encoding.

When the key contains public components only

  Serializes it into an X.509 SubjectPublicKeyInfo. The parameters cipher and password are ignored.

  A PEM-encoded key will look like:

      -----BEGIN PUBLIC KEY-----
      [...]
      -----END PUBLIC KEY-----

  Consider using `public_to_pem` instead. This serializes the key into an X.509 SubjectPublicKeyInfo regardless of whether the key is a public key or a private key.

When the key contains private components, and no parameters are given

  Serializes it into a PKCS #1 RSAPrivateKey.

  A PEM-encoded key will look like:

      -----BEGIN RSA PRIVATE KEY-----
      [...]
      -----END RSA PRIVATE KEY-----

When the key contains private components, and cipher and password are given

  Serializes it into a PKCS #1 RSAPrivateKey and encrypts it in OpenSSL’s traditional PEM encryption format. cipher must be a cipher name understood by `OpenSSL::Cipher.new` or an instance of `OpenSSL::Cipher`.

  An encrypted PEM-encoded key will look like:

      -----BEGIN RSA PRIVATE KEY-----
      Proc-Type: 4,ENCRYPTED
      DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0

      [...]
      -----END RSA PRIVATE KEY-----

  Note that this format uses MD5 to derive the encryption key, and hence will not be available on FIPS-compliant systems.

This method is kept for compatibility. This should only be used when the PKCS #1 RSAPrivateKey format is required.

Consider using `public_to_pem` (X.509 SubjectPublicKeyInfo) or `private_to_pem` (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.

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

Serializes a private or public key to a PEM-encoding.

When the key contains public components only

  Serializes it into an X.509 SubjectPublicKeyInfo. The parameters cipher and password are ignored.

  A PEM-encoded key will look like:

      -----BEGIN PUBLIC KEY-----
      [...]
      -----END PUBLIC KEY-----

  Consider using `public_to_pem` instead. This serializes the key into an X.509 SubjectPublicKeyInfo regardless of whether the key is a public key or a private key.

When the key contains private components, and no parameters are given

  Serializes it into a PKCS #1 RSAPrivateKey.

  A PEM-encoded key will look like:

      -----BEGIN RSA PRIVATE KEY-----
      [...]
      -----END RSA PRIVATE KEY-----

When the key contains private components, and cipher and password are given

  Serializes it into a PKCS #1 RSAPrivateKey and encrypts it in OpenSSL’s traditional PEM encryption format. cipher must be a cipher name understood by `OpenSSL::Cipher.new` or an instance of `OpenSSL::Cipher`.

  An encrypted PEM-encoded key will look like:

      -----BEGIN RSA PRIVATE KEY-----
      Proc-Type: 4,ENCRYPTED
      DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0

      [...]
      -----END RSA PRIVATE KEY-----

  Note that this format uses MD5 to derive the encryption key, and hence will not be available on FIPS-compliant systems.

This method is kept for compatibility. This should only be used when the PKCS #1 RSAPrivateKey format is required.

Consider using `public_to_pem` (X.509 SubjectPublicKeyInfo) or `private_to_pem` (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.

Alias for: export
verify_pss(digest, signature, data, salt_length:, mgf1_hash:) → true | false
Source

    static VALUE
    ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self)
    {
        VALUE digest, signature, data, options, kwargs[2], mgf1md_holder, md_holder;
        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_md_fetch(kwargs[1], &mgf1md_holder);

        GetPKey(self, pkey);
        md = ossl_evp_md_fetch(digest, &md_holder);
        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));
        EVP_MD_CTX_free(md_ctx);

        switch (result) {
          case 0:
            ossl_clear_error();
            return Qfalse;
          case 1:
            return Qtrue;
          default:
            ossl_raise(ePKeyError, "EVP_DigestVerifyFinal");
        }

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

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

The return value is `true` if the signature is valid, `false` otherwise. `PKeyError` 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.

### Private Instance Methods

translate_padding_mode (num)
Source

    # File ext/openssl/lib/openssl/pkey.rb, line 478
            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 PKeyError, "unsupported padding mode"
      end
    end