module OpenSSL::ASN1
抽象语法标记一 (Abstract Syntax Notation One, 或 ASN.1) 是一种描述数据结构的符号语法,定义在 ITU-T X.680 中。ASN.1 本身不强制任何编码或解析规则,但通常 ASN.1 数据结构使用区分编码规则 (Distinguished Encoding Rules, DER) 或较少使用的基本编码规则 (Basic Encoding Rules, BER) 进行编码,这些规则在 ITU-T X.690 中进行了描述。DER 和 BER 编码是二进制的标签-长度-值 (Tag-Length-Value, TLV) 编码,与其他流行的数据描述格式(如 XML、JSON 等)相比非常简洁。ASN.1 数据结构在加密应用中非常常见,例如 X.509 公钥证书或证书撤销列表 (CRLs) 都定义在 ASN.1 中并使用 DER 编码。ASN.1、DER 和 BER 是应用密码学的构建块。 ASN1 模块提供了必要的类,允许生成 ASN.1 数据结构以及使用 DER 编码对其进行编码的方法。decode 方法允许解析任意 BER/DER 编码的数据到 Ruby 对象,然后可以随意修改和重新编码。
ASN.1 类层次结构
表示 ASN.1 结构的基类是 ASN1Data。 ASN1Data 提供了属性来读取和设置特定 ASN.1 项的标签、标签类以及最终的值。解析时,任何带标签的值(隐式或显式)都将由 ASN1Data 实例表示,因为它们的“真实类型”只能通过 ASN.1 类型声明中的带外信息来确定。由于在编码类型时通常已知此信息,因此 ASN1Data 的所有子类都提供了一个额外的标签属性,允许隐式 (:IMPLICIT) 或显式 (:EXPLICIT) 编码值。
Constructive
Constructive 类正如其名,是所有构造编码的基类,即那些由多个值组成的编码,与仅包含单个值的“原始”编码相反。Constructive 的值始终是一个 Array。
ASN1::Set 和 ASN1::Sequence
最常见的构造编码是 SET 和 SEQUENCE,因此 Constructive 类有两个子类分别代表它们。
Primitive
这是所有原始值的超类。 Primitive 本身在解析 ASN.1 数据时不会被使用,所有值要么是 Primitive 的相应子类的实例,要么是 ASN1Data 的实例(如果值被隐式或显式标记)。请参考 Primitive 文档以获取有关子类及其各自 ASN.1 数据类型到 Ruby 对象映射的详细信息。
标签的可选值
在构造 ASN1Data 对象时,ASN.1 类型定义可能要求某些元素被隐式或显式标记。这可以通过为 ASN1Data 的子类手动设置标签属性来实现。使用符号 :IMPLICIT 进行隐式标记,如果元素需要显式标记,则使用 :EXPLICIT。
标签类的可选值
可以创建任意 ASN1Data 对象,这些对象也支持 PRIVATE 或 APPLICATION 标签类。标签类属性的可选值包括:
-
:UNIVERSAL(未标记值的默认值) -
:CONTEXT_SPECIFIC(已标记值的默认值) -
:APPLICATION -
:PRIVATE
标签常量
为每个通用标签定义了一个常量
-
OpenSSL::ASN1::EOC (0)
-
OpenSSL::ASN1::BOOLEAN (1)
-
OpenSSL::ASN1::INTEGER (2)
-
OpenSSL::ASN1::BIT_STRING (3)
-
OpenSSL::ASN1::OCTET_STRING (4)
-
OpenSSL::ASN1::NULL (5)
-
OpenSSL::ASN1::OBJECT (6)
-
OpenSSL::ASN1::ENUMERATED (10)
-
OpenSSL::ASN1::UTF8STRING (12)
-
OpenSSL::ASN1::SEQUENCE (16)
-
OpenSSL::ASN1::SET (17)
-
OpenSSL::ASN1::NUMERICSTRING (18)
-
OpenSSL::ASN1::PRINTABLESTRING (19)
-
OpenSSL::ASN1::T61STRING (20)
-
OpenSSL::ASN1::VIDEOTEXSTRING (21)
-
OpenSSL::ASN1::IA5STRING (22)
-
OpenSSL::ASN1::UTCTIME (23)
-
OpenSSL::ASN1::GENERALIZEDTIME (24)
-
OpenSSL::ASN1::GRAPHICSTRING (25)
-
OpenSSL::ASN1::ISO64STRING (26)
-
OpenSSL::ASN1::GENERALSTRING (27)
-
OpenSSL::ASN1::UNIVERSALSTRING (28)
-
OpenSSL::ASN1::BMPSTRING (30)
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
Constants
- UNIVERSAL_TAG_NAME
-
Array,在标签的索引处存储标签名。
Public Class Methods
Source
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;
}
Source
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;
}
与 decode 类似,区别在于 decode 期望 der 中表示一个单独的值。而 decode_all 则解码 der 中排列的一系列连续 BER/DER 值,并将它们返回为一个数组。
示例
ders = File.binread('asn1data_seq') asn1_ary = OpenSSL::ASN1.decode_all(ders)
Source
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;
}
如果给定了块,它将打印遇到的每个元素。块参数(按顺序)是:
-
depth: 递归深度,每遇到一个构造值加一 (
)Integer -
offset: 当前字节偏移量 (
)Integer -
header length: 标签和长度头部的总字节长度。(
)Integer -
length: 整个数据的总体剩余长度 (
)Integer -
constructed: 该值是否为构造的 (布尔值)
-
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