类 OpenSSL::PKey::EC

OpenSSL::PKey::EC 提供对椭圆曲线数字签名算法 (ECDSA) 和椭圆曲线 Diffie-Hellman (ECDH) 的访问。

密钥交换

ec1 = OpenSSL::PKey::EC.generate("prime256v1")
ec2 = OpenSSL::PKey::EC.generate("prime256v1")
# ec1 and ec2 have own private key respectively
shared_key1 = ec1.dh_compute_key(ec2.public_key)
shared_key2 = ec2.dh_compute_key(ec1.public_key)

p shared_key1 == shared_key2 #=> true

常量

EXPLICIT_CURVE
NAMED_CURVE

公共类方法

builtin_curves → [[sn, comment], ...] 点击切换源代码

通过 OpenSSL 获取所有预定义曲线的列表。曲线名称以 sn 返回。

请参阅 OpenSSL 文档以了解 EC_get_builtin_curves()。

static VALUE ossl_s_builtin_curves(VALUE self)
{
    EC_builtin_curve *curves = NULL;
    int n;
    int crv_len = rb_long2int(EC_get_builtin_curves(NULL, 0));
    VALUE ary, ret;

    curves = ALLOCA_N(EC_builtin_curve, crv_len);
    if (curves == NULL)
        return Qnil;
    if (!EC_get_builtin_curves(curves, crv_len))
        ossl_raise(rb_eRuntimeError, "EC_get_builtin_curves");

    ret = rb_ary_new2(crv_len);

    for (n = 0; n < crv_len; n++) {
        const char *sname = OBJ_nid2sn(curves[n].nid);
        const char *comment = curves[n].comment;

        ary = rb_ary_new2(2);
        rb_ary_push(ary, rb_str_new2(sname));
        rb_ary_push(ary, comment ? rb_str_new2(comment) : Qnil);
        rb_ary_push(ret, ary);
    }

    return ret;
}
generate(ec_group) → ec 点击切换源代码
generate(string) → ec

创建一个新的 EC 实例,其中包含新的随机私钥和公钥。

static VALUE
ossl_ec_key_s_generate(VALUE klass, VALUE arg)
{
    EVP_PKEY *pkey;
    EC_KEY *ec;
    VALUE obj;

    obj = rb_obj_alloc(klass);

    ec = ec_key_new_from_group(arg);
    pkey = EVP_PKEY_new();
    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
        EVP_PKEY_free(pkey);
        EC_KEY_free(ec);
        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
    }
    RTYPEDDATA_DATA(obj) = pkey;

    if (!EC_KEY_generate_key(ec))
        ossl_raise(eECError, "EC_KEY_generate_key");

    return obj;
}
OpenSSL::PKey::EC.new 点击切换源代码
OpenSSL::PKey::EC.new(ec_key)
OpenSSL::PKey::EC.new(ec_group)
OpenSSL::PKey::EC.new("secp112r1")
OpenSSL::PKey::EC.new(pem_string [, pwd])
OpenSSL::PKey::EC.new(der_string)

从给定的参数创建一个新的 EC 对象。

static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self)
{
    EVP_PKEY *pkey;
    EC_KEY *ec;
    BIO *in;
    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");

    rb_scan_args(argc, argv, "02", &arg, &pass);
    if (NIL_P(arg)) {
        if (!(ec = EC_KEY_new()))
            ossl_raise(eECError, "EC_KEY_new");
        goto legacy;
    }
    else if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
        ec = ec_key_new_from_group(arg);
        goto legacy;
    }

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

    pkey = ossl_pkey_read_generic(in, pass);
    BIO_free(in);
    if (!pkey) {
        ossl_clear_error();
        ec = ec_key_new_from_group(arg);
        goto legacy;
    }

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

  legacy:
    pkey = EVP_PKEY_new();
    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
        EVP_PKEY_free(pkey);
        EC_KEY_free(ec);
        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
    }
    RTYPEDDATA_DATA(self) = pkey;
    return self;
}

公共实例方法

check_key → true 点击切换源代码

如果密钥无效,则引发异常。

另请参阅手册页 EVP_PKEY_public_check(3)。

static VALUE ossl_ec_key_check_key(VALUE self)
{
#ifdef HAVE_EVP_PKEY_CHECK
    EVP_PKEY *pkey;
    EVP_PKEY_CTX *pctx;
    const EC_KEY *ec;

    GetPKey(self, pkey);
    GetEC(self, ec);
    pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
    if (!pctx)
        ossl_raise(eECError, "EVP_PKEY_CTX_new");

    if (EC_KEY_get0_private_key(ec) != NULL) {
        if (EVP_PKEY_check(pctx) != 1) {
            EVP_PKEY_CTX_free(pctx);
            ossl_raise(eECError, "EVP_PKEY_check");
        }
    }
    else {
        if (EVP_PKEY_public_check(pctx) != 1) {
            EVP_PKEY_CTX_free(pctx);
            ossl_raise(eECError, "EVP_PKEY_public_check");
        }
    }

    EVP_PKEY_CTX_free(pctx);
#else
    EC_KEY *ec;

    GetEC(self, ec);
    if (EC_KEY_check_key(ec) != 1)
        ossl_raise(eECError, "EC_KEY_check_key");
#endif

    return Qtrue;
}
dh_compute_key(pubkey) → string 点击切换源代码

通过 ECDH 推导出共享密钥。pubkey 必须是 OpenSSL::PKey::EC::Point 的实例,并且必须属于同一个组。

此方法是为了向后兼容而提供的,它在内部调用 derive

# File openssl/lib/openssl/pkey.rb, line 284
def dh_compute_key(pubkey)
  obj = OpenSSL::ASN1.Sequence([
    OpenSSL::ASN1.Sequence([
      OpenSSL::ASN1.ObjectId("id-ecPublicKey"),
      group.to_der,
    ]),
    OpenSSL::ASN1.BitString(pubkey.to_octet_string(:uncompressed)),
  ])
  derive(OpenSSL::PKey.read(obj.to_der))
end
dsa_sign_asn1(data) → String 点击切换源代码

在版本 3.0 中已弃用。请考虑使用 PKey::PKey#sign_rawPKey::PKey#verify_raw 代替。

# File openssl/lib/openssl/pkey.rb, line 259
def dsa_sign_asn1(data)
  sign_raw(nil, data)
rescue OpenSSL::PKey::PKeyError
  raise OpenSSL::PKey::ECError, $!.message
end
dsa_verify_asn1(data, sig) → true | false 点击切换源代码

在版本 3.0 中已弃用。请考虑使用 PKey::PKey#sign_rawPKey::PKey#verify_raw 代替。

# File openssl/lib/openssl/pkey.rb, line 270
def dsa_verify_asn1(data, sig)
  verify_raw(nil, sig, data)
rescue OpenSSL::PKey::PKeyError
  raise OpenSSL::PKey::ECError, $!.message
end
export([cipher, password]) → String 点击切换源代码

将私钥或公钥序列化为 PEM 编码。

当密钥仅包含公钥组件时

将其序列化为 X.509 SubjectPublicKeyInfo。参数 cipherpassword 将被忽略。

PEM 编码的密钥将如下所示

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

请考虑使用 public_to_pem 代替。这将密钥序列化为 X.509 SubjectPublicKeyInfo,无论它是公钥还是私钥。

当密钥包含私钥组件,并且没有给出参数时

将其序列化为 SEC 1/RFC 5915 ECPrivateKey。

PEM 编码的密钥将如下所示

-----BEGIN EC PRIVATE KEY-----
[...]
-----END EC PRIVATE KEY-----
当密钥包含私钥组件,并且给出了 cipherpassword

将其序列化为 SEC 1/RFC 5915 ECPrivateKey,并在 OpenSSL 的传统 PEM 加密格式中对其进行加密。cipher 必须是 OpenSSL::Cipher.new 理解的密码名称,或 OpenSSL::Cipher 的实例。

加密的 PEM 编码密钥将如下所示

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

[...]
-----END EC PRIVATE KEY-----

请注意,此格式使用 MD5 来推导出加密密钥,因此在符合 FIPS 的系统上将不可用。

此方法保留用于兼容性。 这只应在需要 SEC 1/RFC 5915 ECPrivateKey 格式时使用。

请考虑使用 public_to_pem (X.509 SubjectPublicKeyInfo) 或 private_to_pem (PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo) 代替。

static VALUE
ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
{
    OSSL_3_const EC_KEY *ec;

    GetEC(self, ec);
    if (EC_KEY_get0_public_key(ec) == NULL)
        ossl_raise(eECError, "can't export - no public key set");
    if (EC_KEY_get0_private_key(ec))
        return ossl_pkey_export_traditional(argc, argv, self, 0);
    else
        return ossl_pkey_export_spki(self, 0);
}
也称为:to_pem
generate_key()

生成新的随机私钥和公钥。

另请参阅 OpenSSL 文档以了解 EC_KEY_generate_key()

示例

ec = OpenSSL::PKey::EC.new("prime256v1")
p ec.private_key # => nil
ec.generate_key!
p ec.private_key # => #<OpenSSL::BN XXXXXX>
别名:generate_key!
generate_key! => self 点击切换源代码

生成新的随机私钥和公钥。

另请参阅 OpenSSL 文档以了解 EC_KEY_generate_key()

示例

ec = OpenSSL::PKey::EC.new("prime256v1")
p ec.private_key # => nil
ec.generate_key!
p ec.private_key # => #<OpenSSL::BN XXXXXX>
static VALUE ossl_ec_key_generate_key(VALUE self)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
    EC_KEY *ec;

    GetEC(self, ec);
    if (EC_KEY_generate_key(ec) != 1)
        ossl_raise(eECError, "EC_KEY_generate_key");

    return self;
#endif
}
也称为:generate_key
group → group 点击切换源代码

返回与密钥关联的 EC::Group。修改返回的组不会影响密钥

static VALUE
ossl_ec_key_get_group(VALUE self)
{
    OSSL_3_const EC_KEY *ec;
    const EC_GROUP *group;

    GetEC(self, ec);
    group = EC_KEY_get0_group(ec);
    if (!group)
        return Qnil;

    return ec_group_new(group);
}
group = group 点击切换源代码

设置密钥的 EC::Group。组结构在内部被复制,因此在分配给密钥后对的修改不会影响密钥。

static VALUE
ossl_ec_key_set_group(VALUE self, VALUE group_v)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
    EC_KEY *ec;
    EC_GROUP *group;

    GetEC(self, ec);
    GetECGroup(group_v, group);

    if (EC_KEY_set_group(ec, group) != 1)
        ossl_raise(eECError, "EC_KEY_set_group");

    return group_v;
#endif
}
initialize_copy(p1) 点击切换源代码
static VALUE
ossl_ec_key_initialize_copy(VALUE self, VALUE other)
{
    EVP_PKEY *pkey;
    EC_KEY *ec, *ec_new;

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

    ec_new = EC_KEY_dup(ec);
    if (!ec_new)
        ossl_raise(eECError, "EC_KEY_dup");

    pkey = EVP_PKEY_new();
    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec_new) != 1) {
        EC_KEY_free(ec_new);
        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
    }
    RTYPEDDATA_DATA(self) = pkey;

    return self;
}
private? → true 或 false 点击切换源代码

返回此 EC 实例是否具有私钥。可以使用 EC#private_key 获取私钥 (BN)。

static VALUE ossl_ec_key_is_private(VALUE self)
{
    OSSL_3_const EC_KEY *ec;

    GetEC(self, ec);

    return EC_KEY_get0_private_key(ec) ? Qtrue : Qfalse;
}
也称为:private_key?
private_key → OpenSSL::BN 点击切换源代码

请参阅 OpenSSL 文档以了解 EC_KEY_get0_private_key()

static VALUE ossl_ec_key_get_private_key(VALUE self)
{
    OSSL_3_const EC_KEY *ec;
    const BIGNUM *bn;

    GetEC(self, ec);
    if ((bn = EC_KEY_get0_private_key(ec)) == NULL)
        return Qnil;

    return ossl_bn_new(bn);
}
private_key = openssl_bn 点击切换源代码

请参阅 OpenSSL 文档以了解 EC_KEY_set_private_key()

static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
    EC_KEY *ec;
    BIGNUM *bn = NULL;

    GetEC(self, ec);
    if (!NIL_P(private_key))
        bn = GetBNPtr(private_key);

    switch (EC_KEY_set_private_key(ec, bn)) {
    case 1:
        break;
    case 0:
        if (bn == NULL)
            break;
        /* fallthrough */
    default:
        ossl_raise(eECError, "EC_KEY_set_private_key");
    }

    return private_key;
#endif
}
private_key?()

返回此 EC 实例是否具有私钥。可以使用 EC#private_key 获取私钥 (BN)。

别名:private?
public? → true 或 false 点击切换源代码

返回此 EC 实例是否具有公钥。可以使用 EC#public_key 获取公钥 (EC::Point)。

static VALUE ossl_ec_key_is_public(VALUE self)
{
    OSSL_3_const EC_KEY *ec;

    GetEC(self, ec);

    return EC_KEY_get0_public_key(ec) ? Qtrue : Qfalse;
}
也称为:public_key?
public_key → OpenSSL::PKey::EC::Point 点击切换源代码

请参阅 OpenSSL 文档以了解 EC_KEY_get0_public_key()

static VALUE ossl_ec_key_get_public_key(VALUE self)
{
    OSSL_3_const EC_KEY *ec;
    const EC_POINT *point;

    GetEC(self, ec);
    if ((point = EC_KEY_get0_public_key(ec)) == NULL)
        return Qnil;

    return ec_point_new(point, EC_KEY_get0_group(ec));
}
public_key = ec_point 点击切换源代码

请参阅 OpenSSL 文档以了解 EC_KEY_set_public_key()

static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key)
{
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
#else
    EC_KEY *ec;
    EC_POINT *point = NULL;

    GetEC(self, ec);
    if (!NIL_P(public_key))
        GetECPoint(public_key, point);

    switch (EC_KEY_set_public_key(ec, point)) {
    case 1:
        break;
    case 0:
        if (point == NULL)
            break;
        /* fallthrough */
    default:
        ossl_raise(eECError, "EC_KEY_set_public_key");
    }

    return public_key;
#endif
}
public_key?()

返回此 EC 实例是否具有公钥。可以使用 EC#public_key 获取公钥 (EC::Point)。

别名:public?
to_der → String 点击切换源代码

将私钥或公钥序列化为 DER 编码。

有关详细信息,请参见 to_pem

此方法保留用于兼容性。 这只应在需要 SEC 1/RFC 5915 ECPrivateKey 格式时使用。

建议使用 public_to_derprivate_to_der 代替。

static VALUE
ossl_ec_key_to_der(VALUE self)
{
    OSSL_3_const EC_KEY *ec;

    GetEC(self, ec);
    if (EC_KEY_get0_public_key(ec) == NULL)
        ossl_raise(eECError, "can't export - no public key set");
    if (EC_KEY_get0_private_key(ec))
        return ossl_pkey_export_traditional(0, NULL, self, 1);
    else
        return ossl_pkey_export_spki(self, 1);
}
to_pem([cipher, password]) → String

将私钥或公钥序列化为 PEM 编码。

当密钥仅包含公钥组件时

将其序列化为 X.509 SubjectPublicKeyInfo。参数 cipherpassword 将被忽略。

PEM 编码的密钥将如下所示

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

请考虑使用 public_to_pem 代替。这将密钥序列化为 X.509 SubjectPublicKeyInfo,无论它是公钥还是私钥。

当密钥包含私钥组件,并且没有给出参数时

将其序列化为 SEC 1/RFC 5915 ECPrivateKey。

PEM 编码的密钥将如下所示

-----BEGIN EC PRIVATE KEY-----
[...]
-----END EC PRIVATE KEY-----
当密钥包含私钥组件,并且给出了 cipherpassword

将其序列化为 SEC 1/RFC 5915 ECPrivateKey,并在 OpenSSL 的传统 PEM 加密格式中对其进行加密。cipher 必须是 OpenSSL::Cipher.new 理解的密码名称,或 OpenSSL::Cipher 的实例。

加密的 PEM 编码密钥将如下所示

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

[...]
-----END EC PRIVATE KEY-----

请注意,此格式使用 MD5 来推导出加密密钥,因此在符合 FIPS 的系统上将不可用。

此方法保留用于兼容性。 这只应在需要 SEC 1/RFC 5915 ECPrivateKey 格式时使用。

请考虑使用 public_to_pem (X.509 SubjectPublicKeyInfo) 或 private_to_pem (PKCS #8 PrivateKeyInfo 或 EncryptedPrivateKeyInfo) 代替。

别名:export