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

ASN.1 Length encoding (using BER)

这应该是非常基本的,但是,我已经摸索了一段时间,所以我想我应该四处询问,在此先感谢您的帮助。

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

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

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

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

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

Tag is: 001 10000

我的问题是长度本身,它应该是 08(我认为来自“user”的 04 字节加上来自“pass”的 04 字节)。

但在我的解决方案中:

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

而且我似乎无法找出原因。

userid 有效负载为 4 个字节,加上有效负载长度 (4) 和标记 (IA5String) 的 1 个字节。这导致 6 字节 TLV。 password 值是 4 个字节,加上有效负载长度 (4) 和标记 (IA5String) 的 1 个字节。 SEQUENCE 的有效载荷大小为 12 字节。添加长度字节 (12) 和标记 (SEQUENCE),您将得到 14 字节结构。

有关 Microsoft 网站的更多信息:DER Transfer Syntax, Encoded Length and Value Bytes

在 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 - 如果隐含,我们将有标签 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字节

注意序列的元素被标记了,第一个是显式的(意思是 "extra" tag/length 在完整的 IA5String 编码之前),第二个是隐式的(意思是 tag/length替换原来的 IA5String tag/length).

因此,完整编码为 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 模块定义 可以 默认声明隐式标签,但我假设情况并非如此,因为您提到现有的解决方案也给出长度 14 SEQUENCE 标签。

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

}