Security ASN.1如何对对象标识符进行编码?
我很难理解ASN.1的基本概念 如果一个类型是一个OID,那么相应的数字是否在二进制数据中进行了编码 例如,在这个定义中:Security ASN.1如何对对象标识符进行编码?,security,encoding,binary,asn.1,Security,Encoding,Binary,Asn.1,我很难理解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
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数据的最佳方法是粘贴到在线解码器中,例如,如果您的所有数字都小于或等于127,那么您非常幸运,因为它们可以用单个八位字节表示。棘手的部分是当您有更大的常用数字时,例如
1.2.840.113549.1.1.5(sha1withrsa加密)
。这些示例侧重于解码,但编码恰恰相反
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编码的巨大资源 第一个八位组的值为40*value1+value2。(这是明确的, 由于value1限制为值0、1和2;value2限制为 当值1为0或1时,范围为0至39;根据X.208,n为 始终至少2个。) 以下八位字节(如果有)编码值3。。。, 瓦伦。每个值以128为基数进行编码,最高有效位在前, 尽可能少的数字,每个数字的最高有效位 值的编码设置为“1”时,除最后一个外的八位字节 RSA Data Security,Inc.对象的BER编码的第一个八位组 标识符是40*1+2=42=2a16。840=6*128的编码+ 4816是8648,编码为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编码:):
- 每个OID组件编码为一个或多个字节(八位字节)
- OID编码只是这些OID组件编码的串联
- 前两个组件以特殊方式编码(见下文)
- 如果OID分量二进制值小于7位,则编码仅为一个八位字节,保存分量值(注意,最重要的位,最左边的位,将始终为0)
- 否则,如果它有8位或更多位,则该值将“扩展”为多个八位字节-将二进制表示形式拆分为7位字节(从右开始),如果需要,将第一个字节左键加上零,并通过添加最高有效位(左)1从这些七位字节中形成八位字节,但最后一个字节中的位0除外
- 前两个分量(X.Y)被编码为一个值为40*X+Y的单个分量
这是对ITU-T建议X.690,第8.19章8.19的改写。这是上述of的简化Python 3实现。对象标识符的字符串形式转换为ASN.1 DER或BER形式
def encode\u variable\u length\u quantity(v:int)->列表:
#从最低有效位开始,将其分成7位一组
#对于除最低位以外的所有其他7位组,请将MSB设置为1
m=0x00
输出=[]
当v>=0x80时:
输出.插入(0,(v&0x7f)| m)
v=v>>7
m=0x80
输出.插入(0,v | m)
返回输出
def encode_oid_字符串(oid_str:str)->元组:
a=[oid_str.split('.')中x的int(x)]
oid=[a[0]*40+a[1]#前两项由a1*40+a2编码
#余数是可变长度的量
对于[2:]中的n:
oid.extend(编码\变量\长度\数量(n))
oid.插入(0,长度(oid))#添加长度
oid.insert(0,0x06)#添加类型(0x06表示对象标识符)
返回元组(oid)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
oid=编码oid字符串(“1.2.840.10045.3.1.7”)
打印(oid)
+1用于asn.1解码器。谢谢如果我当时知道的话…:-)但我如何知道特定OID的有效值?就我而言,OID 1.3.6.1.5.5.7.48.1.5的值(据我所知)为04020500。它有具体的解释吗?在cntext中,很难分辨04020500是什么。它看起来根本不像一个OID。它是1.3.6.1.5.5.7.48.1.5的值。但我找不到这个OID的合法值。RFC说这个OID应该有空值。我不确定04020500是否以某种方式表示空值04020500是一个八位字节字符串(04),两个字节(02)编码一个空值(05,长度为00)。我不确定您解码前两个数字的代码是否正确。考虑{ 2 100×3 }的T-ReC-X.690的例子。因此,第一个字节将是180。因此,要解码,如果第一个字节小于80,您的解决方案似乎可以工作。任何大于或等于80的数字都意味着第一个数字是2,第二个数字可以通过减去80找到。“模40”代码是错误的。价值观