我对ASN.1的基本概念有些困惑。
如果一个类型是OID,那么相应的数字是否实际上会被编码到二进制数据中?
举个例子,在这个定义中:
id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
对应的1.3.6.1.5.5.7.48.1是否会精确地以这种方式编码为二进制?
我之所以问这个问题,是因为我正试图理解DER文件(证书)中看到的特定值,即04020500,我不确定如何解释它。
是的,OID被编码在二进制数据中。你提到的OID 1.3.6.1.5.5.7.48.1 变成了2b 06 01 05 05 07 30 01 (前两个数字被编码为单个字节,所有其余数字也被编码为单个字节,因为它们都小于128)。
有关OID编码的详细描述可以在这里找到:http://msdn.microsoft.com/en-us/library/bb540809%28v=vs.85%29.aspx。
但是,分析ASN.1数据的最佳方法是将其粘贴到在线解码器中,例如:http://lapo.it/asn1js/。
如果您的所有数字都小于或等于127,则它们可以用每个八位字节表示。当您有较大的常见数字时,例如1.2.840.113549.1.1.5 (sha1WithRsaEncryption)
,则使用可变长度解码。这些示例侧重于解码,但编码正好相反。
1. 前两个“数字”由单个字节表示
您可以通过将第一个字节读入整数来进行解码。
if ($firstByte >= 80) {
$nodeFirst = 2;
$nodeSecond = $firstByte - 80;
}
else {
$nodeFirst = $firstByte / 40;
$nodeSecond = $firstByte % 40;
}
$oidText = "$nodeFirst.$nodeSecond"
生成值
1.2
2. 后续的字节使用可变长度数量(VLQ)表示,也称为基于128的表示法。
VLQ有两种形式,
短序列 - 如果八位字节以0开头,则只需使用剩余的7位表示。
长序列 - 如果八位字节以1(最高有效位)开头,则将该八位字节的下一个7位与每个后续八位字节的7位组合,直到遇到最高有效位为0的八位字节(这标志着最后一个八位字节)。
值840将用以下两个字节表示:
10000110
01001000
Combine to 00001101001000 and read as int.
针对BER编码的重要资源,http://luca.ntop.org/Teaching/Appunti/asn1.html 很不错
以下的八位组(如果有的话)编码value3,……,valuen。每个值都使用最少的数字和最高有效位先行编码,基数为128,并且除了值的编码最后一个八位组之外,每个八位组中的最高有效位都设置为"1"。例如:RSA数据安全公司对象标识符的BER编码的第一个八位组是40*1+2=42=2a16。840的编码=6*128+4816,是86 48,113549的编码=6*1282+7716*128+d16是86 f7 0d。这导致以下BER编码:第一个八位字节的值为40*value1+value2。(这是无歧义的,因为value1仅限于0、1和2的值;当value1为0或1时,value2的范围限制在0到39之间;根据X.208标准,n始终至少为2。)
编辑/免责声明: 根据下面的评论修复了第一个八位组,但尚未测试。我现在将保留此代码片段作为一般参考,但不能保证正确性,不建议盲目复制和粘贴 :)。 对于大于128 VLQ的情况,您通常会使用位移来重新对齐位,而不是一串位。
sub getOid {
my $bytes = shift;
#first 2 nodes are 'special';
use integer;
my $firstByte = shift @$bytes;
my $number = unpack "C", $firstByte;
my $nodeFirst;
my $nodeSecond;
if ($number >= 80) {
$nodeFirst = 2;
$nodeSecond = $number - 80;
}
else {
$nodeFirst = $number / 40;
$nodeSecond = $number % 40;
}
my @oidDigits = ($nodeFirst, $nodeSecond);
while (@$bytes) {
my $num = convertFromVLQ($bytes);
push @oidDigits, $num;
}
return join '.', @oidDigits;
}
sub convertFromVLQ {
my $bytes = shift;
my $firstByte = shift @$bytes;
my $bitString = unpack "B*", $firstByte;
my $firstBit = substr $bitString, 0, 1;
my $remainingBits = substr $bitString, 1, 7;
my $remainingByte = pack "B*", '0' . $remainingBits;
my $remainingInt = unpack "C", $remainingByte;
if ($firstBit eq '0') {
return $remainingInt;
}
else {
my $bitBuilder = $remainingBits;
my $nextFirstBit = "1";
while ($nextFirstBit eq "1") {
my $nextByte = shift @$bytes;
my $nextBits = unpack "B*", $nextByte;
$nextFirstBit = substr $nextBits, 0, 1;
my $nextSevenBits = substr $nextBits, 1, 7;
$bitBuilder .= $nextSevenBits;
}
my $MAX_BITS = 32;
my $missingBits = $MAX_BITS - (length $bitBuilder);
my $padding = 0 x $missingBits;
$bitBuilder = $padding . $bitBuilder;
my $finalByte = pack "B*", $bitBuilder;
my $finalNumber = unpack "N", $finalByte;
return $finalNumber;
}
}
OID解码入门 :) :
这是ITU-T建议X.690,第8.19章的改写
这是一个简单的Python 3实现,用于将上述内容或对象标识符的字符串形式转换为ASN.1 DER或BER格式。请注意,此实现仅供参考,可能需要根据具体需求进行修改。
def encode_variable_length_quantity(v:int) -> list:
# Break it up in groups of 7 bits starting from the lowest significant bit
# For all the other groups of 7 bits than lowest one, set the MSB to 1
m = 0x00
output = []
while v >= 0x80:
output.insert(0, (v & 0x7f) | m)
v = v >> 7
m = 0x80
output.insert(0, v | m)
return output
def encode_oid_string(oid_str:str) -> tuple:
a = [int(x) for x in oid_str.split('.')]
oid = [a[0]*40 + a[1]] # First two items are coded by a1*40+a2
# A rest is Variable-length_quantity
for n in a[2:]:
oid.extend(encode_variable_length_quantity(n))
oid.insert(0, len(oid)) # Add a Length
oid.insert(0, 0x06) # Add a Type (0x06 for Object Identifier)
return tuple(oid)
if __name__ == '__main__':
oid = encode_oid_string("1.2.840.10045.3.1.7")
print(oid)