模块 OpenSSL::ASN1
抽象语法表示法一 (ASN.1) 是一种用于描述数据结构的表示法语法,定义于 ITU-T X.680。ASN.1 本身不强制任何编码或解析规则,但通常 ASN.1 数据结构使用 ITU-T X.690 中描述的“可区分”编码
规则 (DER) 或较少使用的基本编码
规则 (BER) 进行编码。与 XML、JSON
等其他流行的类似数据描述格式相比,DER 和 BER 编码是相当简洁的二进制标签-长度-值 (TLV) 编码。ASN.1 数据结构在加密应用中非常常见,例如 X.509 公钥证书或证书撤销列表 (CRL) 都是在 ASN.1 中定义并进行 DER 编码的。ASN.1、DER 和 BER 是应用密码学的基石。ASN1
模块提供了必要的类,允许生成 ASN.1 数据结构并使用 DER 编码对它们进行编码。decode 方法允许将任意 BER/DER 编码的数据解析为 Ruby 对象,然后可以随意修改和重新编码。
ASN.1 类层次结构¶ ↑
表示 ASN.1 结构体的基类是 ASN1Data
。ASN1Data
提供属性来读取和设置特定 ASN.1 项的tag、tag_class 和最终的value。在解析时,任何标记的值(隐式或显式)都将由 ASN1Data
实例表示,因为它们的“真实类型”只能使用来自 ASN.1 类型声明的带外信息来确定。由于此信息在编码类型时通常已知,因此 ASN1Data
的所有子类都提供一个额外的属性 tagging,允许隐式 (:IMPLICIT
) 或显式 (:EXPLICIT
) 编码值。
Constructive
¶ ↑
Constructive
,顾名思义,是所有构造编码的基类,即由多个值组成的编码,与只有一个值的“基本”编码相反。Constructive
的值始终是 Array
。
ASN1::Set
和 ASN1::Sequence¶ ↑
最常见的构造编码是 SET 和 SEQUENCE,这就是为什么有两个 Constructive
的子类来表示它们每个的原因。
Primitive
¶ ↑
这是所有基本值的超类。解析 ASN.1 数据时不会使用 Primitive
本身,所有值都是 Primitive
的相应子类的实例,或者如果值是隐式或显式标记的,则它们是 ASN1Data
的实例。请参阅 Primitive
文档,了解有关子类及其各自的 ASN.1 数据类型到 Ruby 对象映射的详细信息。
tagging 的可能值¶ ↑
在构造 ASN1Data
对象时,ASN.1 类型定义可能要求某些元素被隐式或显式标记。这可以通过手动设置 ASN1Data
子类的 tagging 属性来实现。如果元素需要显式标记,请使用符号 :IMPLICIT
进行隐式标记,使用 :EXPLICIT
。
tag_class 的可能值¶ ↑
可以创建也支持 PRIVATE 或 APPLICATION 标签类的任意 ASN1Data
对象。tag_class 属性的可能值是
-
: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
常量
- CLASS_TAG_MAP
- UNIVERSAL_TAG_NAME
-
在标签索引处存储标签名称的
Array
。
公共类方法
来源
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; }
解码 BER 或 DER 编码的值并创建 ASN1Data
实例。der 可以是 String
或任何具有 .to_der
方法将其转换为 BER/DER 编码的 String 的对象。
示例¶ ↑
der = File.binread('asn1data') asn1 = OpenSSL::ASN1.decode(der)
来源
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)
来源
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; }
如果给定了块,它会打印出遇到的每个元素。块参数是(按此顺序)
-
深度:递归深度,每遇到一个构造的值加一 (
Integer
) -
偏移量:当前字节偏移量 (
Integer
) -
标头长度:标签和长度标头的组合字节长度。(
Integer
) -
长度:整个数据的总剩余长度 (
Integer
) -
已构造:此值是否已构造 (
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