模块 OpenSSL::ASN1

抽象语法标记一 (或 ASN.1) 是一种用于描述数据结构的符号语法,定义在 ITU-T X.680 中。ASN.1 本身不强制任何编码或解析规则,但通常 ASN.1 数据结构使用 ITU-T X.690 中描述的区分 Encoding 规则 (DER) 或较少使用的基本 Encoding 规则 (BER) 进行编码。DER 和 BER 编码是二进制标签-长度-值 (TLV) 编码,与其他流行的数据描述格式(如 XML、JSON 等)相比,它们非常简洁。ASN.1 数据结构在密码学应用中非常常见,例如 X.509 公钥证书或证书吊销列表 (CRL) 都是用 ASN.1 定义并使用 DER 编码的。ASN.1、DER 和 BER 是应用密码学的基石。ASN1 模块提供了必要的类,允许生成 ASN.1 数据结构,并提供使用 DER 编码对其进行编码的方法。decode 方法允许将任意 BER/DER 编码的数据解析为 Ruby 对象,然后可以随意修改和重新编码。

ASN.1 类层次结构

表示 ASN.1 结构的基类是 ASN1DataASN1Data 提供了属性来读取和设置特定 ASN.1 项目的标签标签类以及最终的。在解析时,任何带标签的值(隐式或显式)都将由 ASN1Data 实例表示,因为它们的“真实类型”只能使用来自 ASN.1 类型声明的带外信息来确定。由于此信息通常在编码类型时已知,因此 ASN1Data 的所有子类都提供了一个额外的属性tagging,允许以隐式 (:IMPLICIT) 或显式 (:EXPLICIT) 方式编码值。

Constructive

Constructive 正如其名称所示,是所有构造编码的基类,即由多个值组成的编码,与仅包含单个值的“原始”编码相反。一个 Constructive 的值始终是一个 Array

ASN1::Set 和 ASN1::Sequence

最常见的构造编码是 SET 和 SEQUENCE,因此 Constructive 有两个子类分别代表它们。

Primitive

这是所有原始值的超类。Primitive 本身在解析 ASN.1 数据时不会使用,所有值要么是 Primitive 的相应子类的实例,要么是 ASN1Data 的实例,如果该值是隐式或显式标记的。请参阅 Primitive 文档以了解有关子类及其 ASN.1 数据类型到 Ruby 对象的相应映射的详细信息。

tagging 的可能值

在构造 ASN1Data 对象时,ASN.1 类型定义可能要求某些元素以隐式或显式方式标记。这可以通过为 ASN1Data 的子类手动设置tagging 属性来实现。如果元素需要隐式标记,则使用符号 :IMPLICIT;如果元素需要显式标记,则使用 :EXPLICIT

tag_class 的可能值

可以创建任意 ASN1Data 对象,这些对象还支持 PRIVATE 或 APPLICATION 标签类。tag_class 属性的可能值为

标签常量

每个通用标签都定义了一个常量

UNIVERSAL_TAG_NAME 常量

一个 Array,它存储给定标签号的名称。这些名称与另外定义的标签常量的名称相同,例如 UNIVERSAL_TAG_NAME[2] = "INTEGER"OpenSSL::ASN1::INTEGER = 2

示例用法

解码和查看 DER 编码文件

require 'openssl'
require 'pp'
der = File.binread('data.der')
asn1 = OpenSSL::ASN1.decode(der)
pp der

创建 ASN.1 结构并进行 DER 编码

require 'openssl'
version = OpenSSL::ASN1::Integer.new(1)
# Explicitly 0-tagged implies context-specific tag class
serial = OpenSSL::ASN1::Integer.new(12345, 0, :EXPLICIT, :CONTEXT_SPECIFIC)
name = OpenSSL::ASN1::PrintableString.new('Data 1')
sequence = OpenSSL::ASN1::Sequence.new( [ version, serial, name ] )
der = sequence.to_der

常量

UNIVERSAL_TAG_NAME

Array 在标签的索引处存储标签名称。

公共类方法

OpenSSL::ASN1.decode(der) → ASN1Data click to toggle source

解码 BER 或 DER 编码的值并创建一个 ASN1Data 实例。der 可以是 String 或任何具有 .to_der 方法的对象,该方法将其转换为 BER/DER 编码的字符串。

示例

der = File.binread('asn1data')
asn1 = OpenSSL::ASN1.decode(der)
static VALUE
ossl_asn1_decode(VALUE self, VALUE obj)
{
    VALUE ret;
    unsigned char *p;
    VALUE tmp;
    long len, read = 0, offset = 0;

    obj = ossl_to_der_if_possible(obj);
    tmp = rb_str_new4(StringValue(obj));
    p = (unsigned char *)RSTRING_PTR(tmp);
    len = RSTRING_LEN(tmp);
    ret = ossl_asn1_decode0(&p, len, &offset, 0, 0, &read);
    RB_GC_GUARD(tmp);
    int_ossl_decode_sanity_check(len, read, offset);
    return ret;
}
OpenSSL::ASN1.decode_all(der) → ASN1Data 数组 click to toggle source

与 decode 类似,不同之处在于 decode 预期 der 中表示一个不同的值。相反,decode_all 解码 der 中排列的一系列连续的 BER/DER 值,并将它们作为数组返回。

示例

ders = File.binread('asn1data_seq')
asn1_ary = OpenSSL::ASN1.decode_all(ders)
static VALUE
ossl_asn1_decode_all(VALUE self, VALUE obj)
{
    VALUE ary, val;
    unsigned char *p;
    long len, tmp_len = 0, read = 0, offset = 0;
    VALUE tmp;

    obj = ossl_to_der_if_possible(obj);
    tmp = rb_str_new4(StringValue(obj));
    p = (unsigned char *)RSTRING_PTR(tmp);
    len = RSTRING_LEN(tmp);
    tmp_len = len;
    ary = rb_ary_new();
    while (tmp_len > 0) {
        long tmp_read = 0;
        val = ossl_asn1_decode0(&p, tmp_len, &offset, 0, 0, &tmp_read);
        rb_ary_push(ary, val);
        read += tmp_read;
        tmp_len -= tmp_read;
    }
    RB_GC_GUARD(tmp);
    int_ossl_decode_sanity_check(len, read, offset);
    return ary;
}
OpenSSL::ASN1.traverse(asn1) → nil click to toggle source

如果给定一个块,它将打印出遇到的每个元素。块参数(按顺序)为

  • depth: 递归深度,遇到每个构造值时加一 (Integer)

  • offset: 当前字节偏移量 (Integer)

  • header length: 标签和长度头的组合长度(以字节为单位)。(Integer)

  • length: 整个数据的剩余总长度 (Integer)

  • constructed: 此值是否为构造值 (Boolean)

  • tag_class: 当前标签类 (Symbol)

  • tag: 当前标签号 (Integer)

示例

der = File.binread('asn1data.der')
OpenSSL::ASN1.traverse(der) do | depth, offset, header_len, length, constructed, tag_class, tag|
  puts "Depth: #{depth} Offset: #{offset} Length: #{length}"
  puts "Header length: #{header_len} Tag: #{tag} Tag class: #{tag_class} Constructed: #{constructed}"
end
static VALUE
ossl_asn1_traverse(VALUE self, VALUE obj)
{
    unsigned char *p;
    VALUE tmp;
    long len, read = 0, offset = 0;

    obj = ossl_to_der_if_possible(obj);
    tmp = rb_str_new4(StringValue(obj));
    p = (unsigned char *)RSTRING_PTR(tmp);
    len = RSTRING_LEN(tmp);
    ossl_asn1_decode0(&p, len, &offset, 0, 1, &read);
    RB_GC_GUARD(tmp);
    int_ossl_decode_sanity_check(len, read, offset);
    return Qnil;
}