我无法理解ASN.1的基本概念。
如果类型是OID,相应的数字是否实际上在二进制数据中编码?
例如在这个定义中:
id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
相应的1.3.6.1.5.5.7.48.1是否完全像二进制编码?
我问这个是因为我试图理解我在DER文件(证书)中看到的特定值,即04020500,我不知道如何解释它。
答案 0 :(得分:31)
是的,OID以二进制数据编码。您提到的OID 1.3.6.1.5.5.7.48.1变为2b 06 01 05 05 07 30 01(前两个数字以单字节编码,所有剩余数字也以单个字节编码,因为它们都是小于128)。
找到了一个很好的OID编码描述here。
但分析ASN.1数据的最佳方法是粘贴到在线解码器中,例如http://lapo.it/asn1js/
答案 1 :(得分:17)
如果您的所有数字都小于或等于127,那么您非常幸运,因为它们每个都可以用一个八位字节表示。棘手的部分是当您有更大的数字时,例如1.2.840.113549.1.1.5 (sha1WithRsaEncryption)
。这些例子侧重于解码,但编码正好相反。
<强> 1。前两个“数字”用单个字节表示
您可以通过将第一个字节读入整数来解码
var firstByteNumber = 42;
var firstDigit = firstByteNumber / 40;
var secondDigit = firstByteNumber % 40;
生成值
1.2
<强> 2。后续字节使用Variable Length Quantity表示,也称为base 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
第一个八位字节的值为40 * value1 + value2。 (这是明确的, 因为value1限于值0,1和2; value2仅限于 当value1为0或1时,范围为0到39;根据X.208,n是 总是至少2.)
以下八位字节(如果有)编码value3,..., 值N。每个值编码基数为128,最高有效数字为第一, 尽可能少的数字,以及每个数字的最重要位 八位字节除了值的编码中的最后一个设置为“1”。示例: RSA Data Security,Inc。的BER编码的第一个八位字节 标识符是40 * 1 + 2 = 42 = 2a16。编码为840 = 6 * 128 + 4816是86 48,编码113549 = 6 * 1282 + 7716 * 128 + d16 是86 f7 0d。这导致以下BER编码:
06 06 2a 86 48 86 f7 0d
最后,这是我刚刚在Perl中编写的OID解码器。
sub getOid {
my $bytes = shift;
#first 2 nodes are 'special';
use integer;
my $firstByte = shift @$bytes;
my $number = unpack "C", $firstByte;
my $nodeFirst = $number / 40;
my $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;
}
}
答案 2 :(得分:9)
傻瓜的OID编码:):
这是对ITU-T建议 X.690 的重写, 8.19
答案 3 :(得分:2)
这是上述的简单化的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)