我无法理解 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 编码的一个很好的描述。
但分析 ASN.1 数据的最佳方法是粘贴到在线解码器中,例如http://lapo.it/asn1js/。
如果您的所有数字都小于或等于 127,那么您非常幸运,因为它们每个都可以用一个八位字节表示。棘手的部分是当您有较大的常见数字时,例如1.2.840.113549.1.1.5 (sha1WithRsaEncryption)
. 这些示例侧重于解码,但编码正好相反。
1.前两个“数字”用一个字节表示
您可以通过将第一个字节读入整数来解码
var firstByteNumber = 42;
var firstDigit = firstByteNumber / 40;
var secondDigit = firstByteNumber % 40;
产生值
1.2
2. 后续字节使用可变长度数量表示,也称为基数 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;当 value1 为 0 或 1 时,value2 被限制在 0 到 39 的范围内;并且根据 X.208,n 始终至少为 2。)
以下八位字节(如果有)对 value3、...、valuen 进行编码。每个值都以 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;
}
}
假人的 OID 编码 :) :
这是对 ITU-T 建议X.690第8.19章的改写
这是上述的一个简单的 Python 3 实现,resp。将对象标识符的字符串形式转换为 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)