【问题标题】:ASN.1 Length encoding (using BER)ASN.1 长度编码(使用 BER)
【发布时间】:2015-04-16 08:41:55
【问题描述】:

这应该是非常基本的,但是我一直在摸索一段时间,所以我想我应该四处问问,所以提前感谢您的帮助。

所以我的问题是,我有这个序列:

User::=SEQUENCE {
userid [0] IA5String,
password [1] Implicit IA5String}

我想使用 BER 对以下内容进行编码,具有以下值:

{userid = "user", password = "pass"}

所以我们有 3 个“字段”TLV:

Tag is: 001 10000

我的问题是长度本身,应该是08(我认为来自“用户”的04字节加上来自“通过”的04字节)。

但在我的解决方案中:

L -> 0 0 0 0 1 1 1 0 (=14)  0E

我似乎无法找出原因。

【问题讨论】:

    标签: asn.1


    【解决方案1】:

    在BER和DER编码规则中,每个数据元素都被编码为一个Tag-Length-Value序列。

    当谈到非构造类型(例如 INTEGER 或 IA5String)时,值是按照该类型的规则编码的实际值。

    当谈到构造类型(例如 SEQUENCE 或 SET)时,值是构造类型中包含的不同字段的 BER/DER 编码值。

    考虑到这一点,我们可以采用您的类型

    User::=SEQUENCE {
        userid [0] IA5String,
        password [1] IMPLICIT IA5String}
    

    以及您的数据价值

    {userid = "user", password = "pass"}
    

    我们可以开始编码了。

    首先是 SEQUENCE 的标签,即 0x30,然后是长度,我们还不知道。现在我们应该对构造的 SEQUENCE 的值进行编码。所以我们开始对不同的字段进行编码。

    我们需要对userid 字段进行编码。这是一个标记类型,这里取决于全局 EXPLICITIMPLICIT 选项,这可以构造或不构造: - 如果显式,我们将有标签 0xA0(构造上下文 0),长度,然后标记类型:IA5String 是标签 0x16(通用 22),它的长度 0x04 和它的值 0x75 73 65 72 - 如果是 IMPLICIT,我们将有标签 0x80(对于非构造上下文 0)、长度 0x04 和值 75 73 65 72

    最后,我们需要对密码进行编码,在这种情况下我们没有疑问,添加IMPLICIT关键字来强制隐式标记。所以我们将有标签 0x81(对于非构造上下文 1)、长度 0x04 和值 70 61 73 73

    所以总的来说我们有(假设全局隐式)

    30 0c
       80 04 75 73 65 72
       81 04 70 61 73 73
    

    总共 14 个字节

    或者如果是全局显式

    30 0e
       A0 06
          16 04 75 73 65 72
       81 04 70 61 73 73
    

    总共 16 个字节

    【讨论】:

    • 嗨!能否请您说明一下,因为根据您的程序计算的长度为 12 个字节(=1+1+1+1+5+1+1+1)与已发布的 10 个字节不同:...如果类型为:SEQUENCE { name IA5String, ok BOOLEAN} value: {name "Smith", ok TRUE} 可以编码为: Sequence Length Contents 3016 0A16 IA5String Length Contents 1616 0516 "Smith" Boolean Length Contents 0116 0116 FF16 ...
    • @AVA,对不起,我没有关注你。我收集到您参考了 X.209 第 13 页的示例或 X.690 第 10 页的示例。两者是相同的,它们在序列的 TL 上使用 2 个字节 + 在 "Smith" 的 TLV 上使用 7 个字节,在 TRUE 的 TLV 上使用 3 个字节,总共 12 个字节
    【解决方案2】:

    userid 有效载荷为 4 个字节,加上有效载荷长度 (4) 和标签 (IA5String) 的 1 个字节。这会产生 6 个字节的 TLV。 password 值是 4 个字节,加上 1 个字节用于有效载荷长度 (4) 和标签 (IA5String)。 SEQUENCE 的有效负载大小为 12 个字节。加上长度字节(12)和标签(SEQUENCE),你将得到14字节的结构。

    微软网站上的更多信息:DER Transfer SyntaxEncoded Length and Value Bytes

    【讨论】:

    • 嘿,你能详细说明一下吗?您正在为有效负载长度和标签添加 +2 个字节,但为什么呢?长度不应该是我们要传输的数据的大小(因此只有 8 个字节)吗?例如,如果我想传输数字 45,长度仅为01因为45 = 101101,因此只有 1 个八位字节。感谢您的帮助!
    • 查看提供的链接。每个组件都以 TLV 三元组编码。每个三元组由标签(用于确定有效负载中存储的内容)、一个或多个标识有效负载长度和实际有效负载数据的字节组成。
    • 不,不可能发送纯 8 个字节,因为数据接收者将无法区分 useridpassword 字段。如果您想将 45 作为整数发送,那么您的编码数据将是 3 个字节:0x02 0x01 0x2d,其中 0x02 - 是标签(整数),0x01 - 有效负载长度(1 个字节)和 0x2d - 实际数据.
    • 我认为我没有正确表达自己对此的抱歉。我不理解的只是第一个案例的长度。如果我想传输数字 45 非常简单:TAG - 000 00010 (02) Length - 000 00001 (01, because 45 is less than 1 octet) Value - 00101101 (45) 我的问题是“用户”的长度为 04 字节,“通过”也是如此。所以payload应该是08字节,但实际上是14。我想知道我怎样才能看到剩余的 6 个字节来自哪里以及为什么。关于第一个问题:TAG - 001 10000 (16-SEQUENCE) Length - It's 000 01110 但是怎么做?谢谢
    • @RedFoxxie,您缺少序列中字段的标签长度​​的开销。我希望我的回答提供了正确的详细程度......
    【解决方案3】:

    请注意,序列的元素被标记,第一个显式(意味着完整 IA5String 编码之前的“额外”标记/长度),第二个隐式(意味着替换原始 IA5String 标记/长度的标记/长度)。

    所以,完整的编码是 300ea006160475736572810470617373:

    30 CONSTRUCTED SEQUENCE
    0e Length 14
    a0 CONSTRUCTED TAGGED 0
    06 Length 6
    16 IA5String
    04 Length 4
    75 'u'
    73 's'
    65 'e'
    72 'r'
    81 TAGGED 1
    04 Length 4
    70 'p'
    61 'a'
    73 's'
    73 's'
    

    请注意,ASN.1 模块定义可以默认声明标记为隐式,但我假设不是这种情况,因为您提到现有解决方案也为 SEQUENCE 标记提供长度 14。

    【讨论】:

      【解决方案4】:
      package binaryhex;
      
      public class BinaryHex {
      
          public static void main(String[] args) {
              String s = "A8 59 A0 47 A0 15 80 01 01 81 02 01 F4 82 01 01 83 09 31 32 37 2E 30 2E 30 2E 31 81 07 32 33 30 5F 32 32 37 82 0E 32 30 31 36 30 38 32 32 31 34 35 36 31 30 83 01 00 84 01 00 A5 0F 80 03 44 53 4D 81 08 31 32 33 34 35 36 37 38 81 0E 32 30 31 36 30 38 32 32 31 34 35 36 31 30";
              String hexDumpStrWithoutSpace = s.replaceAll("\\s+", "");
              int length = calculateLength(hexDumpStrWithoutSpace);
              System.out.println("LENGTH: " + length);
          }
      
          private static int calculateLength(String hexDumpStrWithoutSpace) {
              int decimalValue = 0;
              boolean tag = false;
              int i = 0;
              while (!tag) {
                  String typeSub = hexDumpStrWithoutSpace.substring(i, i + 2);
                  StringBuilder typeBinSB = new StringBuilder();
                  for (int j = 0; j < typeSub.length(); j++) {
                      typeBinSB.append(hexToBinary("" + typeSub.charAt(j)));
                  }
                  String typeBin = typeBinSB.toString();
                  if (typeBin.charAt(2) == '0') {
                      int tagInt = Integer.parseInt(typeBin.substring(3), 2);
                      System.out.println("TAG: " + tagInt);
                      tag = true;
                  } else {
                      String tagStr = typeBin.substring(3 - i / 2);
                      if (tagStr.equals("11111")) {
                          i = i + 2;
                          continue;
                      } else {
                          int tagInt = Integer.parseInt(tagStr, 2);
                          System.out.println("TAG: " + tagInt);
                          tag = true;
                          i = i + 2;
                      }
      
                  }
              }
      
              for (; i < hexDumpStrWithoutSpace.length();) {
                  String lengthSub = hexDumpStrWithoutSpace.substring(i, i + 2);
      
                  StringBuilder lengthBinSB = new StringBuilder();
                  for (int j = 0; j < lengthSub.length(); j++) {
                      lengthBinSB.append(hexToBinary("" + lengthSub.charAt(j)));
                  }
                  String lengthBin = lengthBinSB.toString();
                  if (lengthBin.charAt(0) == '0') {
                      Integer lengthInt = Integer.parseInt(lengthBin, 2);
                      decimalValue = lengthInt + i;
                      break;
                  } else {
                      Integer lengthOctets = Integer.parseInt(lengthBin.substring(1), 2);
                      StringBuilder toBinSB = new StringBuilder();
      
                      for (int k = 0; k < lengthOctets; k++) {
                          i = i + 2;
                          String toBin = hexDumpStrWithoutSpace.substring(i, i + 2);
                          for (int j = 0; j < toBin.length(); j++) {
                              toBinSB.append(hexToBinary("" + toBin.charAt(j)));
                          }
                      }
                      String lengthResult = toBinSB.toString();
                      Integer lengthValue = Integer.parseInt(lengthResult, 2);
                      decimalValue = lengthValue + i - 2;
                      break;
                  }
              }
      
              return decimalValue;
          }
      
          static String hexToBinary(String hex) {
              int i = Integer.parseInt(hex, 16);
              String bin = Integer.toBinaryString(i);
              if (bin.length() < 4) {
                  while (bin.length() < 4) {
                      bin = "0" + bin;
                  }
              }
              return bin;
          }
      
      }
      

      【讨论】:

        最近更新 更多