ASN.1长度编码(使用BER)

3

这应该是相当基础的问题,但我已经困扰了一段时间,所以我想问问周围的人,提前感谢您的帮助。

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

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

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

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

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

Tag is: 001 10000

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

但是在解决方案中,我看到:

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

而我似乎找不出原因。
4个回答

3
在 BER 和 DER 编码规则中,每个数据元素都被编码为一个标签-长度-值序列。
当讨论非构建类型(例如 INTEGER 或 IA5String)时,值是按照该类型的规则编码的实际值。
当谈论构建类型(例如 SEQUENCE 或 SET)时,值是包含在构建类型中的不同字段的 BER/DER 编码值。
有了这个理解,我们可以看看您的类型。
User::=SEQUENCE {
    userid [0] IA5String,
    password [1] IMPLICIT IA5String}

以及您的数据价值

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

我们可以开始对文本进行编码。
首先是 SEQUENCE 标签,它的十六进制值是 0x30,然后是长度,我们还不知道长度。现在我们需要对构造的 SEQUENCE 进行编码。所以我们开始对不同的字段进行编码。
我们需要对 userid 字段进行编码。这是一个标记类型,这里取决于全局的 EXPLICITIMPLICIT 选项,它可能是构建或非构建的: - 如果是 EXPLICIT,则会有标记为 0xA0 的标签(用于构建上下文 0),然后是长度和标记类型:IA5String 是标签 0x16(UNIVERSAL 22),其长度为 0x04,其值为 0x75 73 65 72 - 如果是 IMPLICIT,则会有标记为 0x80 的标签(用于非构建上下文 0),长度为 0x04,值为 75 73 65 72
最后,我们需要对密码进行编码,在这种情况下,我们没有疑问,添加 IMPLICIT 关键字以强制隐式标记。因此,我们将具有标记为 0x81(用于非构建上下文 1),长度为 0x04,值为 70 61 73 73。
因此,总结一下(假设全局 IMPLICIT):
30 0c
   80 04 75 73 65 72
   81 04 70 61 73 73

总计14字节

或者如果是全局EXPLICIT

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} 则值: {name "Smith", ok TRUE} 可以编码为: 序列长度 内容 3016 0A16 IA5String 长度 内容 1616 0516 "Smith" Boolean 长度 内容 0116 0116 FF16 ... - AVA
@AVA,抱歉我不明白你的意思。我了解你在参考X.209第13页的示例或者X.690第10页的示例。两个示例都是一样的,它们在序列的TL上使用了2个字节,在"Smith"的TLV上使用了7个字节,在TRUE的TLV上使用了3个字节,总共12个字节。 - jsantander

0
请注意,序列的元素是带标签的,第一个元素明确指定了标签和长度(意味着在完整的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。

0
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;
    }

}

0

userid 负载为 4 字节,加上负载长度 (4) 和标签 (IA5String) 的 1 字节。这将产生 6 字节的 TLV。password 值为 4 字节,加上负载长度 (4) 和标签 (IA5String) 的 1 字节。SEQUENCE 的负载大小为 12 字节。添加长度字节 (12) 和标签 (SEQUENCE),您将得到 14 字节的结构。

有关更多信息,请访问 Microsoft 网站: DER 传输语法, 编码长度和值字节


嘿,你能再详细解释一下吗?你在负载长度和标签上添加了+2个字节,但为什么?长度不应该是我们要传输的数据的大小(因此只有8个字节)吗?例如,如果我想传输数字45,那么长度只有01,因为45 = 101101,因此只有1个八位字节。感谢你的帮助! - RedFoxxie
请查看提供的链接。每个组件都以TLV三元组进行编码。每个三元组由标签(用于确定存储在负载中的内容)、一个或多个字节来标识负载长度和实际负载数据组成。 - Crypt32
不,无法发送纯8字节,因为数据接收方将无法区分“userid”和“password”字段。如果您想将45作为整数发送,则编码后的数据将为3个字节:0x02 0x01 0x2d,其中0x02是标签(INTEGER),0x01是有效载荷长度(1个字节),0x2d是实际数据。 - Crypt32
我觉得我表达不太清楚,对此感到抱歉。 我不理解的只是第一个案例中的长度。如果我想传输数字45,那么很简单:TAG - 000 00010 (02) Length - 000 00001(01,因为45小于1个八位字节) Value - 00101101(45)我的问题是,“用户”和“密码”都有4个字节的长度。 因此,有效负载应该是8个字节,但实际上是14个字节。我想知道如何查看剩余的6个字节来自哪里以及原因。在第一个问题上: TAG - 001 10000(16-SEQUENCE) 长度 - 它是000 01110,但是怎么会这样?谢谢 - RedFoxxie
@RedFoxxie,你忽略了序列中字段的标签长度开销。我希望我的答案提供了正确的细节水平... - jsantander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接