Security ASN.1如何对对象标识符进行编码?

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

我很难理解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数据的最佳方法是粘贴到在线解码器中,例如,如果您的所有数字都小于或等于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”代码是错误的。价值观