简体   繁体   中英

ASN.1 Length encoding (using BER)

This should be pretty basic but, I've been scratching my head around it for some time, so I thought I should ask around, so thanks in advance for the help.

So my question is, I have this sequence:

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

And I want to encode the following using BER, having the following values:

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

So we have the 3 "fields" TLV:

Tag is: 001 10000

My question is the Length itself, which should be 08 (I think 04 bytes from "user" plus 04 bytes from "pass").

But in the solutions I have:

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

And I can't seem able to find out why.

in BER and DER encoding rules, each data element is encoded as a Tag-Length-Value sequence.

When talking about a not-constructed type (eg INTEGER or IA5String) value is the actual value encoded following the rules for that type.

When talking about a constructed type (eg SEQUENCE or SET) value is the BER/DER encoded value of the different fields included within the constructed type.

With that in mind we can take your type

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

and your data value

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

We can start to encode.

First will go the tag for the SEQUENCE, which is 0x30, then it would go the length, which we don't know yet. Now we should encode the value of the constructed SEQUENCE. So we start encoding the different fields.

We need to encode the userid field. This is a tagged type, here, depending on the global EXPLICIT or IMPLICIT options this could be constructed or not: - If EXPLICIT, we will have the tag 0xA0 (for constructed-context 0), the length and then tagged type: IA5String is tag 0x16 (UNIVERSAL 22), its length 0x04 and its value 0x75 73 65 72 - If IMPLICIT, we will have the tag 0x80 (for non-constructed context 0), the length 0x04 and the value 75 73 65 72

Finally, we need to encode the password, in this case we don't have doubts, IMPLICIT keyword is added to force the implicit tagging. So we will have the tag 0x81 (for non constructed context 1), the length 0x04 and the value 70 61 73 73

So in summary we have (assuming global IMPLICIT)

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

totalling 14 bytes

or if global EXPLICIT

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

totalling 16 bytes

userid payload is 4 bytes, plus 1 byte for payload length (4) and tag (IA5String). This results 6 bytes TLV. password value is 4 bytes, plus 1 byte for payload length (4) and tag (IA5String). Payload size for SEQUENCE is 12 bytes. Add length byte (12) and tag (SEQUENCE) and you will get 14 bytes structure.

More info on Microsoft web site: DER Transfer Syntax , Encoded Length and Value Bytes

Note that the elements of the sequence are tagged, the first one explicitly (meaning an "extra" tag/length before the full IA5String encoding), the second implicitly (meaning a tag/length that replaces the original IA5String tag/length).

So, the full encoding would be 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'

Note that an ASN.1 module definition could declare tags implicit by default, but I'm assuming that's not the case as you mention existing solutions which also give length 14 for the SEQUENCE tag.

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM