ruby - 如何在 Ruby 中解析 ASN1
问题描述
仍然对 ASN1 有点困惑。我正在使用 ruby openssl 解析 RFC3161 时间戳响应,所以我知道规范。我很确定结构已正确加载,但我对如何找到我想要的部件/键感到困惑。我知道有序列(有序)和集合(无序/唯一)。
我看到我们可以调用asn1_object.value[0].value[0]
,但这很笨拙,我希望如果我知道键或标题,我可以像 JSON 或 YAML 一样对待它,然后调用 asn1_object['TSTInfo']['serialNumber']
. 它不是这样工作的吗?
我们是否应该使用序列的属性,这些属性应该是有序的并且已经知道它们的位置,因为我们也有规范和长度?这对我来说很奇怪,但也许那是因为我以前没有使用过很多 TLV 格式。不幸的是,openssl 的文档很少,只是如何OpenSSL::ASN1.decode(der)
通过索引加载对象和访问值。还有一种traverse
方法,但这不会产生标题名称或我认为在这种情况下使用的任何东西。
#<OpenSSL::ASN1::Sequence:0x000055fdee1a2c68
@indefinite_length=false,
@tag=16,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
[#<OpenSSL::ASN1::ObjectId:0x000055fdee1a2ec0
@indefinite_length=false,
@tag=6,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value="1.2.840.113549.1.9.52">,
#<OpenSSL::ASN1::Set:0x000055fdee1a2c90
@indefinite_length=false,
@tag=17,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
[#<OpenSSL::ASN1::Sequence:0x000055fdee1a2cb8
@indefinite_length=false,
@tag=16,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
[#<OpenSSL::ASN1::Sequence:0x000055fdee1a2da8
@indefinite_length=false,
@tag=16,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
@value=
[#<OpenSSL::ASN1::ObjectId:0x000055fdee1a3190
@indefinite_length=false,
@tag=6,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value="signingTime">,
#<OpenSSL::ASN1::Set:0x000055fdee1a30f0
@indefinite_length=false,
@tag=17,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
[#<OpenSSL::ASN1::UTCTime:0x000055fdee1a3118
@indefinite_length=false,
@tag=23,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=2018-12-19 01:49:08 UTC>]>]>
解决方案
让我在这里澄清一点;我只是发现了一个矛盾。OP说他knows the specification
还提到了the signingTime value is two below the declaration
就是strange
。
这使我得出结论,OP 意味着他知道格式是由于规范,但实际上并不知道规范本身。
让我们一起阅读其中一些,这样你就会知道如何做。
让我们回答为什么有时间价值的问题?
签名时间在RFC5652 第 11.3 节中提到;从那里,我们将知道它是一种属性(因为它在第 11 节下)。属性类型的结构也在同一文档的5.3 节中提到
属性 ::= SEQUENCE {
attrType OBJECT IDENTIFIER,
attrValues SET OF AttributeValue }
所以它基本上是一个对象标识符的序列,后跟一个集合。
第 11.3 节还指定集合必须恰好包含 1 个成员。
这正是我们在这里看到的:
[
#<OpenSSL::ASN1::ObjectId:0x000055fdee1a3190
@indefinite_length=false,
@tag=6,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value="signingTime">,
#<OpenSSL::ASN1::Set:0x000055fdee1a30f0
@indefinite_length=false,
@tag=17,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=[
#<OpenSSL::ASN1::UTCTime:0x000055fdee1a3118
@indefinite_length=false,
@tag=23,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=2018-12-19 01:49:08 UTC>
]>
]
如前所述,OpenSSL::ASN1.decode
将 der 解码为结构化的 ruby 对象,并没有考虑每个字段的含义。因此,您需要阅读规范并自己计算字段。这很乏味,但无论如何都不是不可能的。
由于官方库提供的遍历函数几乎没用,所以我最终自己实现了它。不过,它仍然只是一个工作原型。
class TraverseASN1
class StopIteration < ::StandardError
def initialize(result = nil)
super(nil)
@result = result
end
attr_reader :result
end
def call(
decoded,
_depth: 0,
_sequence: nil,
_parents: [],
&traversal_block
)
traversal_block.call(
decoded,
depth: _depth,
sequence: _sequence,
parents: _parents
)
value = decoded.value
return unless value.is_a?(::Array)
value.each_with_index do |sub_token, sequence|
call(
sub_token,
_depth: (_depth + 1),
_sequence: sequence,
_parents: [*_parents, decoded],
&traversal_block
)
end
end
end
这就是我使用它的方式:
target_element = begin
TraverseASN1.new.call(asn1) do |current, depth:, parents:, **_unused_args|
next unless depth == 2
next unless current.is_a?(::OpenSSL::ASN1::ObjectId)
next unless current.value == 'pkcs7-signedData'
raise TraverseASN1::StopIteration.new(parents.last)
end
rescue TraverseASN1::StopIteration => e
e.result
end
一旦你知道了格式,开始破解东西就相当简单了。
推荐阅读
- javascript - 如何在 laravel 7 框架中使用 ajax 自动刷新我的页面
- javascript - 如何在jquery中同时输入和删除两个输入字段
- scala - SCALA:monix 可观察方法
- javascript - 数字只计数一次,而不是无限次
- reactjs - ReactJS 后端请求和代理
- python - 我的摩尔斯电码程序不能在二维数组中工作,还有其他方法吗?
- pine-script - TradingView – Pine Script 中单个订单的多个止盈
- indexing - Power Query 自定义函数,没有增量,一切都是 1/0:为什么?
- rust - 对指针和它指向的结构的生命周期参数使用相同的生命周期
- mysql - 在数据库中使用高级计算或让客户端应用程序执行