类型与类型定义
类型列表
基础类型的标签号
Tag | 类型 | Tag | 类型 | Tag | 类型 |
---|---|---|---|---|---|
1 (hex:01) | BER保留 | 12(hex:0C) | UTF8String | 23(hex:17) | UTCTime |
2 (hex:02) | BOOLEAN | 13(hex:0D) | RELATIVE-OID | 24(hex:18) | GeneralizedTime |
3(hex:03) | INTEGER | 14(hex:0E) | 保留 | 25(hex:10) | GraphicString |
4(hex:04) | BIT STRING | 15(hex:0F) | 保留 | 26(hex:1A) | VisibleString,ISO646String |
5(hex:05) | NULL | 16(hex:10) | SEQUENCE,SEQUENCE OF | 27(hex:1B) | GeneralString |
6(hex:06) | OBJECT IDENTIFIER | 17(hex:11) | SET,SET OF | 28(hex:1C) | UniversalString |
7(hex:07) | ObjectDescripion | 18(hex:12) | NumericString | 29(hex:1D) | CHARACTER STRING |
8(hex:08) | EXTERNAL,INSTANCE OF | 19(hex:13) | PrintableString | 30(hex:1E) | BMPString |
9(hex:09) | REAL | 20(hex:14) | TeletexString,T61String | 31(hex:1F) | 保留 |
10(hex:0A) | ENUMERATED | 21(hex:15) | VideotexString | ||
11(hex:0B) | EMBEDDED PDV | 22(hex:16) | IA5String |
Tag Class(标签类别)
标签类别用于标识标签的上下文,共有 4 种类别:
类别 | 值(二进制) | 值(十六进制) | 描述 |
---|---|---|---|
Universal | 00 |
0x00 |
通用标签,用于 ASN.1 内置类型(如 INTEGER 、SEQUENCE 等)。 |
Application | 01 |
0x40 |
应用标签,由应用程序定义。 |
Context-specific | 10 |
0x80 |
上下文特定标签,用于特定上下文中定义的类型。 |
Private | 11 |
0xC0 |
私有标签,由私有协议定义。 |
- 如果标签是 通用类型(如
INTEGER
、SEQUENCE
),则Tag Class = 0x00
。 - 如果标签是 应用类型(如
[APPLICATION n]
),则Tag Class = 0x40
。 - 如果标签是 上下文特定类型(如
[n]
),则Tag Class = 0x80
。 - 如果标签是 私有类型,则
Tag Class = 0xC0
。
Primitive/Constructed(原始/构造)
Primitive/Constructed
用于标识数据的编码方式:
类型 | 值(二进制) | 值(十六进制) | 描述 |
---|---|---|---|
Primitive | 0 |
0x00 |
原始类型,表示数据是简单的值(如 INTEGER 、OCTET STRING )。 |
Constructed | 1 |
0x20 |
构造类型,表示数据是复杂的结构(如 SEQUENCE 、SEQUENCE OF )。 |
- 如果数据类型是 原始类型(如
INTEGER
、OCTET STRING
),则Primitive/Constructed = 0x00
。 - 如果数据类型是 构造类型(如
SEQUENCE
、SET
),则Primitive/Constructed = 0x20
。
ASN.1中的类型
内建数据类型
类型 | 含义 |
---|---|
NULL | 只包含一个值NULL,用于传送一个报告或者作为CHOICE类型中某些值 |
INTEGER | 全部整数(包括正数和负数) |
REAL | 实数,表示浮点数 |
ENUMERATED | 标识符的枚举(实例状态机的状态) |
BIT STRING | 比特串 |
OCTET STRING | 字节串 |
OBJECT IDENTIFIER,RELATIVE-OID | 一个实体的标识符,它在一个全世界范围树状结构中注册 |
EXTERNAL, EMBEDDED PDV | 表示层上下文交换类型 |
…String(除了BIT STRING、OCTET STRING外) | 各种字符串,有NumericString、PrintableString、VisibleStirng、ISO64String、IA5String、TeletexStirng、T61String、VideotexString、GraphicString、GeneralString、UniversalString、BMPString和UTF8String |
CHARACTER STRING | 允许为字符串协商一个明确的字符表 |
UTCTime, GeneralizedTime | 日期 |
组合类型
类型 | 含义 |
---|---|
CHOICE | 在类型中选择(相当于C中的联合) |
SEQUENCE | 由不同类型的值组成一个有序的结构(相当于C中的结构体) |
SET | 由不同类型的值组成一个无序的结构 |
SEQUENCE OF | 由相同类型的值组成一个有序的结构(相当于C中的数组) |
SET OF | 由相同类型的值组成一个无序的结构 |
基本类型
类型 | UNIVERSAL Tag | 取值 |
---|---|---|
BOOLEAN | 1 | TRUE,FALSE |
NULL | 5 | NULL |
INTEGER | 2 | 整数 |
ENUMERATED | 10 | 类型定义中列出的成员 |
REAL | 9 | 实数 |
BIT STRING | 3 | 比特串 |
OCTET STRING | 4 | 八位组串,字节流 |
OBJECT IDENTIFIER | 6 | |
RELATIVE-OID | 13 |
字符串类型
Known-multiple Character String Type指字符串中每个字符编码所占字节数都一样。
类型名字 | Tag | 字符表 | ESC | Multi |
---|---|---|---|---|
NumericString | 18 | 字符“0”到“9”,空格 | 是 | |
PrintableString | 19 | 字符“A” 到 “Z”,“a”到“z”,“0”到“9”,空格,单引号(’),圆括号(( ,)),加号(+),逗号(,),减号(-),点(.),斜杠(/),冒号(:),等号(=),问号(?) | 是 | |
VisibleStringISO646String | 26 | [ISOReg] entry no. 6; space | 是 | |
IA5String | 22 | [ISOReg] entry no. 1 & 6;space, delete | 是 | |
TeletexStringT61String | 20 | [ISOReg] entry no. 6, 87, 102,103, 106, 107, 126, 144, 150,153, 156, 164, 165, 168; space, delete | 是 | |
VideotexString | 21 | [ISOReg] entry no. 1, 13, 72,73, 87, 89, 102, 108, 126, 128,129, 144, 150, 153, 164, 165,168; space, delete | 是 | |
GraphicString | 25 | all the graphical sets (called`G’) of [ISOReg]; space | 是 | |
GeneralString | 27 | all the graphical sets (calledG') and all the control characters(called C’) of [ISOReg];space, delete |
是 | |
UniversalString | 28 | [ISO10646-1] | 是 | |
BMPString | 30 | the basic multilingual plane[ISO10646-1] (65,536 cells) | 是 | |
UTF8String | 12 | [ISO10646-1] |
类型 | 编码标识 | 备注 |
---|---|---|
BMPString(UNICODE_STRING) | 0x1E | 基本多语言平面 (BMP) 是一种字符编码,包含通用字符集 (UCS) 的第一个平面。 共有17架编号为 0 到 16 的飞机。 BMP 占用平面 0,包含从 0x0000 到 0xFFFF 的 65,536 个码位。 这是 Unicode 字符映射的一部分,到目前为止,大多数字符分配都已在此处。 它包括拉丁语、中东语、亚洲语、非洲语和其他语言。 |
IA5String(IA5_STRING) | 0x16 | 国际字母数字 5 (IA5) 通常等效于 ASCII 字母表,但不同的版本可以包括特定于区域语言的重音或其他字符。 以下示例演示 AlternativeNames 证书扩展的 ASN.1 定义中使用的 IA5String 类型 |
PrintableString(PRINTABLE_STRING) | 0x13 | PrintableString 数据类型最初旨在表示可用于大型机输入终端的有限字符集,但仍常用。 它包含以下字符:A-Z a-z 0-9 ' ( ) + , - . / : = ? [space] |
TeletexString | 0x14 | TeletexString 和相关 T61String 数据类型在 8 位 (编码,或复合字符) 的 16 位编码。 它们都有0x14的标记号。 它们未得到广泛使用。 |
UTF8String(UTF8_STRING) | 0x0C | 8 位 UCS/Unicode 转换格式 (UTF-8) 是一种长度可变的字符编码,可以将任何通用字符表示为 Unicode 字符,同时允许初始码位与 ASCII 保持一致。 UTF-8 使用 1 到 4 个字节。 标记号为0x0C。 |
八进制字符串(OCTET_STRING) | 0x04 | 八进制字符串是任意大的字节数组。 但是,与 BIT STRING 类型不同,无法为字符串中的特定位和字节分配名称。 八位字节一词是一种独立于平台的方式来引用内存词。 在证书注册 API 的上下文中,八位字节和字节可互换 |
BIT STRING | 0x03 | 位或二进制字符串是任意长度的位数组。 特定位可以通过带圆括号的整数和分配的名称进行标识 |
字符串数据类型使用实例
对于以下ASN.1定义
MyHTTP |
示例数据1
使用以下Java代码
Person person = new Person(); |
生成的数据如下:
30 (27) |
示例数据2
对于另一组数据
Person person = new Person(); |
得到的数据如下
原始的hex= 302D8001010C06E5BCA0E4B88902011204030102030301001204414141411303424242140443434343160447474747 |
示例数据3
Person person = new Person(); |
得到的数据结果如下
原始的hex= 305A8001010C06E5BCA0E4B8890201120403010203030100120FE695B0E5AD97E5AD97E7ACA6E4B8B21312E58FAFE68993E58DB0E5AD97E7ACA6E4B8B2140FE794B5E4BCA0E5AD97E7ACA6E4B8B2160C494135E5AD97E7ACA6E4B8B2 |
UTF8String
UTF8String “张三” 转换为 hex E5BCA0E4B889
- UTF8String 使用UTF-8编码,这是一种可变长度编码,支持所有Unicode字符。
- “张” 的UTF-8编码:
- “张” 的Unicode码点是U+5F20。
- UTF-8编码规则:对于U+0800到U+FFFF的字符,使用3字节:
1110xxxx 10xxxxxx 10xxxxxx
。 - U+5F20的二进制:0101 1111 0010 0000。
- 分割为三部分:0101、111100、100000(从高位到低位)。
- 编码后:
- 第一个字节:11100101 → 0xE5
- 第二个字节:10111100 → 0xBC
- 第三个字节:10100000 → 0xA0
- 所以 “张” 的UTF-8编码为
E5 BC A0
。
- “三” 的UTF-8编码:
- “三” 的Unicode码点是U+4E09。
- U+4E09的二进制:0100 1110 0000 1001。
- 分割为三部分:0100、111000、001001。
- 编码后:
- 第一个字节:11100100 → 0xE4
- 第二个字节:10111000 → 0xB8
- 第三个字节:10001001 → 0x89
- 所以 “三” 的UTF-8编码为
E4 B8 89
。
- 因此,”张三” 的完整hex编码为
E5 BC A0 E4 B8 89
,即E5BCA0E4B889
(长度6字节)。
NumericString
NumericString “AAAA” 转换为 hex 41414141
NumericString 理论上只允许数字(0-9)和空格,但实际编码中有时会处理其他字符(如字母),但不符合标准。在您的示例中,”AAAA” 被编码为NumericString,这可能是一种简化或错误使用。
转换基于ASCII编码:每个字符的ASCII值直接转换为hex。
- ‘A’ 的ASCII值为十进制65,十六进制41。
- 因此,”AAAA” 的hex编码为
41 41 41 41
,即41414141
(长度4字节)。
NumericString类型的数据123对应的hex值为313233
在ASN.1编码中,NumericString类型的数据使用ASCII编码来表示每个字符。ASCII(American Standard Code for Information Interchange)是一种常见的字符编码标准,其中每个字符对应一个唯一的数字值(十六进制)。
对于数字字符”123”:
- 字符 ‘1’ 的ASCII值为十进制49,十六进制31。
- 字符 ‘2’ 的ASCII值为十进制50,十六进制32。
字符 ‘3’ 的ASCII值为十进制51,十六进制33。
因此,字符串”123”在编码为NumericString时,其值部分直接转换为hex值313233。
详细转换过程:
NumericString类型定义:NumericString只允许包含数字(0-9)和空格。在ASN.1编码中,它使用标签0x12(但标签本身不包含在值部分中)。
编码规则:当编码NumericString时,每个字符都使用其ASCII值的十六进制形式表示。
示例”123”:
- 取每个字符的ASCII值:’1’ → 0x31, ‘2’ → 0x32, ‘3’ → 0x33。
- 组合这些值得到313233。
NumericString “数字字符串” 转换为 hex E695B0E5AD97E5AD97E7ACA6E4B8B2
- 字符串: “数字字符串”(注意:根据hex值解码,实际字符串可能为”数字符串”或包含重复字符,但这里以您提供的字符串为准)。
- 编码规则: NumericString标准只允许数字(0-9)和空格,但这里中文字符使用了UTF-8编码。
- 转换过程:
- 每个中文字符转换为UTF-8字节序列:
- “数” -> UTF-8:
E6 95 B0
- “字” -> UTF-8:
E5 AD 97
- “符” -> UTF-8:
E7 AC A6
- “串” -> UTF-8:
E4 B8 B2
- “数” -> UTF-8:
- 最终hex值:
E695B0
(数) +E5AD97
(字) +E5AD97
(字) +E7ACA6
(符) +E4B8B2
(串) =E695B0E5AD97E5AD97E7ACA6E4B8B2
- 每个中文字符转换为UTF-8字节序列:
- 说明: 这不符合NumericString标准,建议使用UTF8String中文字符。
PrintableString
PrintableString “BBB” 转换为 hex 424242
- PrintableString 允许特定的可打印字符,包括大写字母(A-Z)、数字(0-9)和某些符号(如空格、单引号、括号等)。
- 转换基于ASCII编码:每个字符的ASCII值直接转换为hex。
- ‘B’ 的ASCII值为十进制66,十六进制42。
- 因此,”BBB” 的hex编码为
42 42 42
,即424242
(长度3字节)。
PrintableString “可打印字符串” 转换为 hex E58FAFE68993E58DB0E5AD97E7ACA6E4B8B2
- 字符串: “可打印字符串”
- 编码规则: PrintableString标准只允许可打印ASCII字符(如字母、数字、空格和特定符号),但这里中文字符使用了UTF-8编码。
- 转换过程:
- 每个中文字符转换为UTF-8字节序列:
- “可” -> UTF-8:
E5 8F AF
- “打” -> UTF-8:
E6 89 93
- “印” -> UTF-8:
E5 8D B0
- “字” -> UTF-8:
E5 AD 97
- “符” -> UTF-8:
E7 AC A6
- “串” -> UTF-8:
E4 B8 B2
- “可” -> UTF-8:
- 最终hex值:
E58FAF
(可) +E68993
(打) +E58DB0
(印) +E5AD97
(字) +E7ACA6
(符) +E4B8B2
(串) =E58FAFE68993E58DB0E5AD97E7ACA6E4B8B2
- 每个中文字符转换为UTF-8字节序列:
- 说明: 这不符合PrintableString标准,建议使用UTF8String中文字符。
TeletexString
TeletexString “CCCC” 转换为 hex 43434343
- TeletexString 通常使用ISO/IEC 8859-1编码(Latin-1),这是一种8位编码,与ASCII兼容(前128字符相同)。
- 转换基于ASCII或Latin-1编码:每个字符的编码值直接转换为hex。
- ‘C’ 的ASCII值为十进制67,十六进制43。
- 因此,”CCCC” 的hex编码为
43 43 43 43
,即43434343
(长度4字节)。
TeletexString “电传字符串” 转换为 hex E794B5E4BCA0E5AD97E7ACA6E4B8B2
- 字符串: “电传字符串”
- 编码规则: TeletexString标准通常使用T.61或ISO-8859-1编码,但这里中文字符使用了UTF-8编码。
- 转换过程:
- 每个中文字符转换为UTF-8字节序列:
- “电” -> UTF-8:
E7 94 B5
- “传” -> UTF-8:
E4 BC A0
- “字” -> UTF-8:
E5 AD 97
- “符” -> UTF-8:
E7 AC A6
- “串” -> UTF-8:
E4 B8 B2
- “电” -> UTF-8:
- 最终hex值:
E794B5
(电) +E4BCA0
(传) +E5AD97
(字) +E7ACA6
(符) +E4B8B2
(串) =E794B5E4BCA0E5AD97E7ACA6E4B8B2
- 每个中文字符转换为UTF-8字节序列:
IA5String
IA5String “GGGG” 转换为 hex 47474747
- IA5String 使用ASCII编码,支持国际字母数字字符(但实际是ASCII子集)。
- 转换基于ASCII编码:每个字符的ASCII值直接转换为hex。
- ‘G’ 的ASCII值为十进制71,十六进制47。
- 因此,”GGGG” 的hex编码为
47 47 47 47
,即47474747
(长度4字节)。
IA5String “IA5字符串” 转换为 hex 494135E5AD97E7ACA6E4B8B2
- 字符串: “IA5字符串”
- 编码规则: IA5String标准只允许ASCII字符(0-127),但这里中文字符使用了UTF-8编码,而英文字符”IA5”使用ASCII编码。
- 转换过程:
- 英文字符部分”IA5”使用ASCII编码:
- “I” -> ASCII:
49
(hex) - “A” -> ASCII:
41
(hex) - “5” -> ASCII:
35
(hex)
- “I” -> ASCII:
- 中文字符部分”字符串”使用UTF-8编码:
- “字” -> UTF-8:
E5 AD 97
- “符” -> UTF-8:
E7 AC A6
- “串” -> UTF-8:
E4 B8 B2
- “字” -> UTF-8:
- 最终hex值:
49
(I) +41
(A) +35
(5) +E5AD97
(字) +E7ACA6
(符) +E4B8B2
(串) =494135E5AD97E7ACA6E4B8B2
- 英文字符部分”IA5”使用ASCII编码:
- 说明: 这不符合IA5String标准,因为中文字符不是ASCII。建议将整个字符串使用UTF8String编码
整体ASN.1数据实例解析
您提供的hex数据是一个SEQUENCE(标签30),总长度2D(十进制45字节)。内部字段包括:
80 01 01
:上下文特定标签[0],长度1,值01(整数1)。0C 06 E5BCA0E4B889
:UTF8String(标签0C),长度6,值”张三”的UTF-8编码。02 01 12
:INTEGER(标签02),长度1,值12(十进制18)。04 03 010203
:OCTET STRING(标签04),长度3,值010203。03 01 00
:BIT STRING(标签03),长度1,值00(表示3比特位,但值为0)。12 04 41414141
:NumericString(标签12),长度4,值”AAAA”的ASCII编码。13 03 424242
:PrintableString(标签13),长度3,值”BBB”的ASCII编码。14 04 43434343
:TeletexString(标签14),长度4,值”CCCC”的ASCII编码。16 04 47474747
:IA5String(标签16),长度4,值”GGGG”的ASCII编码。
总结
字符串到hex的转换依赖于字符的编码标准:
- UTF8String使用UTF-8编码。
- NumericString、PrintableString、TeletexString和IA5String通常使用ASCII编码(或Latin-1编码,但前128字符相同)。
- 每个字符的ASCII值直接转换为十六进制字节。
转换解析示例
package org.example; |
得到的运行结果为
字符=I ASCII=73 , ASCII=73 , HEX=49 |
UTF-8编码原理
UTF-8是一种可变长度的字符编码标准,用于表示Unicode字符。汉字“字”的Unicode码点是U+5B57(十六进制),其二进制表示为:
- Unicode码点 U+5B57:
0101 1011 0101 0111
(二进制)
根据UTF-8编码规则,对于U+0800到U+FFFF范围内的字符(如汉字),使用三字节编码模式:
- 第一字节以
1110
开头,后跟Unicode码点的前4位。 - 第二字节以
10
开头,后跟Unicode码点的中间6位。 - 第三字节以
10
开头,后跟Unicode码点的最后6位。
具体编码过程:
- Unicode码点 U+5B57 的二进制分割:前4位
0101
,中间6位101101
,最后6位010111
。 - 第一字节:
1110
+0101
=11100101
(二进制) → 十六进制0xE5
,无符号十进制229
。 - 第二字节:
10
+101101
=10101101
(二进制) → 十六进制0xAD
,无符号十进制173
。 - 第三字节:
10
+010111
=10010111
(二进制) → 十六进制0x97
,无符号十进制151
。
因此,汉字“字”的UTF-8编码为三个字节:0xE5
, 0xAD
, 0x97
(十六进制),对应无符号十进制值 229
, 173
, 151
。
为什么显示为负数(-27, -83, -105)
在计算机系统中,字节(8位)通常可以被解释为无符号整数(范围0到255)或有符号整数(范围-128到127)。当您看到负数值时,通常是因为这些字节值被解释为有符号整数(采用二的补码表示法)。
转换规则:无符号字节值大于127时,在有符号表示中会变为负数。具体计算为:
- 有符号值 = 无符号值 - 256(因为256是模数)
应用到这里:
- 无符号值
229
→ 有符号值: 229 - 256 = -27 - 无符号值
173
→ 有符号值: 173 - 256 = -83 - 无符号值
151
→ 有符号值: 151 - 256 = -105
因此,如果您在编程环境或工具中看到这些字节被作为有符号整数处理(例如在Java的byte
类型中,默认是有符号的),它们就会显示为-27、-83、-105。但这并不影响实际的编码数据,因为存储时仍然是原始的字节值(0xE5, 0xAD, 0x97)。
为什么这些值不在ASCII码对照表中
ASCII码(American Standard Code for Information Interchange)是一种7位编码标准,只定义值从0到127的字符(包括英文字母、数字、标点符号和控制字符)。UTF-8编码的汉字使用多字节,且每个字节的值通常大于127(即超出ASCII范围),因此这些字节值不会出现在ASCII码对照表中。
- ASCII码只覆盖0-127的值,例如字母’A’的ASCII值是65(十六进制0x41)。
UTF-8编码中,汉字使用三字节,每个字节的值都在128以上(如229、173、151),所以它们不是ASCII字符,而是UTF-8编码的一部分。
汉字“字”的UTF-8编码字节为无符号值229、173、151(十六进制E5、AD、97)。
- 当这些字节被解释为有符号整数时,它们显示为-27、-83、-105,这是数值表示方式的差异,并不改变实际编码。
- 这些值不在ASCII码对照表中,因为ASCII仅限于基本英文字符,而汉字需要多字节UTF-8编码。
日期时间类型
MyHTTP |
java代码如下
Person person = new Person(); |
生成的数据如下:
30 (23) |
上述数据相当于
person Person ::= { |
- id: 这是一个整数字段,带有标签
[0]
。示例值为123
,是一个任意选择的整数。 - uTCTime: 这是一个
UTCTime
类型的字段,表示时间。格式为YYMMDDhhmmssZ
,其中YY
是年份的后两位,MM
是月份,DD
是日期,hh
是小时,mm
是分钟,ss
是秒,Z
表示UTC时间。示例值"230101120000Z"
对应2023年1月1日12:00:00 UTC。 - generalizedTime: 这是一个
GeneralizedTime
类型的字段,也表示时间。格式为YYYYMMDDhhmmssZ
,其中YYYY
是四位年份。示例值"20230101120000Z"
同样对应2023年1月1日12:00:00 UTC。
例如 32 35 30 32 31 39 30 39 32 36 32 35 5A
;转换为ASCII字符串:250219092625Z
按UTCTime格式YYMMDDHHMMSSZ
解析:
25
→ 年份 202502
→ 月份 2月19
→ 日期 19日09
→ 小时 09时26
→ 分钟 26分25
→ 秒 25秒Z
→ UTC时区
最终时间:2025-02-19 09:26:25 UTC
非常好的问题!这涉及到 ASN.1 时间类型中一个非常重要且常见的细节。
Z
的含义
在 UTCTime
和 GeneralizedTime
类型中,后缀字母 Z
表示 “Zulu Time”。
- Zulu 是航空和军事领域中对 协调世界时(UTC) 的代号。
- 因此,
Z
的含义是:该时间字符串所表示的时间是 UTC 时区 的时间,其相对于 UTC 的时区偏移量为+00:00
。
简单来说:Z
就等于 +00:00
,代表零时区,即格林威治标准时间。
示例中的 "230101120000Z"
和 "20230101120000Z"
都明确表示这是一个 UTC 时间(2023年1月1日12:00:00 UTC)。
如何表示东八区时间(北京时间)
东八区(例如北京时间)比 UTC 时间 早8个小时。在 ASN.1 中,你不能使用 Z
,而是需要使用一个 时区偏移量(Time Zone Offset) 来明确表示与 UTC 的差异。
时区偏移量有两种表示方式:
+HHMM
(UTC之后,东区)-HHMM
(UTC之前,西区)
对于 东八区,偏移量为 +0800
。
因此,你的示例数据如果要表示为本地东八区时间,需要做如下修改:
UTCTime 的表示
- 原始(UTC):
"230101120000Z"
- 东八区:
"230101200000+0800"
解释:
- 因为东八区比 UTC 早8小时,所以本地时间
20:00:00
对应的是 UTC 时间12:00:00
。 - 将时间部分从
120000
(12:00:00 UTC) 改为200000
(20:00:00 Local)。 - 将后缀从
Z
改为+0800
,明确指出这个20:00:00
是东八区的本地时间。
GeneralizedTime 的表示
- 原始(UTC):
"20230101120000Z"
- 东八区:
"20230101200000+0800"
解释:
- 同理,将时间从
120000
改为200000
。 - 将后缀
Z
替换为+0800
。
总结与关键点
情况 | 格式示例 | 含义 |
---|---|---|
UTC 时间 | "230101120000Z" 或 "20230101120000Z" |
2023年1月1日12:00:00 UTC |
东八区时间 | "230101200000+0800" 或 "20230101200000+0800" |
2023年1月1日20:00:00 东八区(北京时间) (这个时间点等同于UTC的12:00:00) |
北美东部标准时间(UTC-5) | "230101070000-0500" |
2023年1月1日07:00:00 UTC-5 |
重要注意事项:
UTCTime
的限制:UTCTime
只能表示年份后两位。按照最新的规范,YY
为 50-99 表示 1950-1999,YY
为 00-49 表示 2000-2049。如果你需要表示 2050 年之后的时间,必须使用GeneralizedTime
。- 可读性:使用
Z
是表示 UTC 时间最简洁、最清晰的方式。只要有可能,强烈推荐将时间转换为 UTC 并使用Z
后缀进行存储和传输,这可以避免时区转换带来的所有歧义和错误。 - 编码规则:在 DER(Distinguished Encoding Rules)编码中,
UTCTime
必须使用Z
格式,而不能使用本地时间加偏移量的格式。GeneralizedTime
则允许两种格式,但同样推荐使用Z
格式以确保唯一性。
在你的代码或协议中,最佳实践是:
在内部处理和存储时,始终使用 UTC 时间。只在需要显示给用户时,才将其转换为本地时间。。
类型定义
<新类型的名字> ::= <类型描述> |
其中:
- <新类型的名字>是一个以大写字母开头的标识符;
- <类型描述>是基于内建类型或在其它地方定义的类型。
如:
Married ::= BOOLEAN |
注意:在SEQUENCE
和SET
等(好像应该是所有组合类型的)定义中,最后一个成员结尾没有逗号“,
”。
为了接收方能正确解码,发送方为每个值的类型附加一个数,称为tag,在描述中以“[]
”标识。缺省情况下,编码器会使用universal
的tag。很多时候,缺省情况下不能消除所有的模糊性,有必要明确指出各成员的tag。如:
Coordinates ::= SET |
注意:ASN.1允许递归式的类型分配(Assignment),但我们应当保证其中包含至少一个非递归的值,因为编码规则无法处理无限的值。当然,绝大多数结构类型的成员都终结于简单类型。
为了准确描述一个类型,我们需要对值的集合进行一定的限制。这用到子类型约束,在类型之后用圆括号进行标识。
如:
Lottery-number ::= INTERGER(1..49) |
有约束的类型当然还是类型,可以用在任何一个可以使用类型的地方。
最后,因为版本升级,在新的描述中,出现新的成员被加入到SEQUENCE
、SET
或者CHOICE
或者在上述类型基础上添加约束而衍生的子类型时,两个连接的机器(特别是在开放网络中)不一定使用的是相同版本描述而生成的编解码器。为了防止一方因收到过多或者过少数据而出现错误,ASN.1中用符号“…
”来标记可能以后是其它类型的地方。这样即使是旧的编解码器也不会因为描述扩充而导致编解码错误。如:
Type ::= SEQUENCE |
以后新的版本中,描述可能为:
Type ::= SEQUENCE |
注意:新加入的类型成员要嵌套在“[[]]
”中。
自动标签AUTOMATIC TAGS
AUTOMATIC TAGS
的核心目的是自动化地为 SEQUENCE
、SET
或 CHOICE
类型中的成员分配标签。它主要解决了两个问题:
- 避免手动分配标签的繁琐和错误:当一个结构体中有大量可选字段时,手动为每个字段分配唯一的
[0]
,[1]
,[2]
… 既枯燥又容易出错(如重复使用同一个标签号)。 - 确保编码清晰无歧义:特别是在处理多个
OPTIONAL
或DEFAULT
字段时,编码器(如 BER/DER)需要依靠唯一的上下文特定标签(Context-specific Tags) 来识别哪个字段存在,哪个字段不存在。AUTOMATIC TAGS
保证了这些标签的唯一性。
基本定义
可以在模块定义时,声明模块全局Tag模式。可以是EXPLICIT TAGS
、IMPLICIT TAGS
和AUTOMATIC TAGS
。
IMPLICIT TAGS
:模块内所有SEQUENCE
、SET
和CHOICE
的成员都是IMPLICIT
模式(除非它是CHOICE
类型、开放类型或者一个参数类型)。但不影响IMPORTS的内容。
AUTOMATIC TAGS
:模块内所有SEQUENCE
、SET
和CHOICE
类型ASN.1编译器会自动从0开始,步长为1进行自动编码。而其中的成员则用IMPLICIT
模式,除非它是CHOICE
类型、开放类型或者一个参数类型。
下面两个定义是等效的:
M DEFINITIONS AUTOMATIC TAGS ::= |
M DEFINITIONS ::= |
如果使用全局AUTOMATIC TAGS
模式时,在描述中给一个类型指定了一个Tag,那么对这个类型的AUTOMATIC TAGS
就关闭。如果一个SEQUENCE
或者SET
类型包含COMPONENTS OF
条目,而且这个条目在自动编号前展开;但自动标号又要求在展开前进行,ASN.1编译器要自己检查这种冲突。
对新的协议现在都推荐使用AUTOMATIC TAGS
。
开启与关闭的触发条件
对于以下ASN.1定义
MyHTTP |
使用如下java代码
Person person = new Person(); |
生成的数据为
原始的hex= 30208006E5BCA0E4B889A1058003E794B78201128305656D61696C840570686F6E65 |
修改上面的ASN.1定义为
MyHTTP |
此时生成的数据为
30 (20) |
如果使用全局AUTOMATIC TAGS
模式时,在描述中给一个类型指定了一个Tag,那么对这个类型的AUTOMATIC TAGS
就关闭
工作机制
当你在模块定义中使用 AUTOMATIC TAGS
时,编译器或编码器会遵循一个简单的规则:
自动为
SEQUENCE
、SET
或CHOICE
类型中的每一个组件分配一个上下文特定的标签号,从[0]
开始,按定义顺序依次递增。
重要的是,这种自动分配的本质是隐式标签(IMPLICIT)。这意味着在编码时,字段的原始通用标签(如 UNIVERSAL 2 for INTEGER)会被新分配的上下文特定标签所替换,从而产生更紧凑的编码。
示例 1:没有使用 AUTOMATIC TAGS (手动分配标签)
在这种方式下,你必须手动为每个需要区分的可选字段分配唯一的标签号。
PersonManual DEFINITIONS EXPLICIT TAGS ::= BEGIN |
痛点:如果以后在 age
和 email
之间添加一个新的可选字段 nickname
,你必须重新编排后面所有的标签号(email
变成 [2]
, phone
变成 [3]
),这非常容易出错且难以维护。
示例 2:使用 AUTOMATIC TAGS (自动分配标签)
现在,我们让 ASN.1 编译器来自动处理标签分配。
PersonAuto DEFINITIONS AUTOMATIC TAGS ::= BEGIN |
优势:
- 无需手动管理:你不用再写
[0]
,[1]
。编译器会自动完成。 - 易于扩展:在
age
和email
之间插入nickname UTF8String OPTIONAL
,它会自动获得标签[1]
,而email
和phone
会自动变为[2]
和[3]
。代码维护变得非常简单。 - 避免冲突:绝对保证不会出现标签号重复的错误。
示例 3:混合类型与CHOICE类型
AUTOMATIC TAGS
同样完美处理混合类型和选择类型。
ComplexExample DEFINITIONS AUTOMATIC TAGS ::= BEGIN |
代码分析:
Address
:street
,city
:非可选字段,通常不需要标签。zipCode
:我们手动指定了显式标签[0] EXPLICIT
(因为模块是AUTOMATIC TAGS
,但被[0]
覆盖了规则)。country
:这是一个DEFAULT
字段。注意:虽然DEFAULT
字段在编码中通常可以省略(如果取默认值),但为了在出现时能够被识别,它也需要一个标签。AUTOMATIC TAGS
会为它自动分配一个标签(在这个例子中,由于zipCode
是手动[0]
,country
可能会被分配到[1]
)。
ContactMethod
(CHOICE):email
,phone
,postalMail
:自动获得标签[0]
,[1]
,[2]
。fax
:手动覆盖,使用显式标签[5]
。这意味着编码器会创建一个外层标签[5]
,里面再包裹UTF8String
的通用标签和值。这打破了自动的连续性,但提供了灵活性。
UserRecord
:id
,primaryContact
:非可选字段。backupContact
:是一个可选字段,因此被自动分配了标签[1]
(因为它是序列中第一个可选/DEFAULT组件)。isVerified
:是一个DEFAULT
字段,如果它被自动分配标签,会是[2]
。
使用以下java代码
UserRecord userRecord = new UserRecord(); |
生成的数据为
原始的hex= 3026800101A11EA21C0C067374726565740C0463697479800301E2400C07636F756E7472798301FF |
特性 | 描述 |
---|---|
目的 | 自动化管理 SEQUENCE /SET /CHOICE 中组件的上下文特定标签,防止冲突,简化维护。 |
机制 | 从 [0] 开始,按定义顺序为需要标签的组件(如 OPTIONAL /DEFAULT )自动分配标签。 |
本质 | 自动分配的标签是隐式(IMPLICIT) 的,会替换原始类型标签,编码更紧凑。 |
优势 | 1. 免去手动分配和管理标签的麻烦。 2. 避免标签号冲突错误。 3. 使数据结构更容易扩展和修改。 |
注意 | 解码方必须拥有完全相同的 ASN.1 schema(模式定义),因为类型信息不再包含在编码数据中。 |
简单来说:AUTOMATIC TAGS
是一个“一劳永逸”的配置,它通过让编译器接管繁琐的标签分配工作,使你能够更专注于数据结构的逻辑设计,从而编写出更清晰、更健壮、更易于扩展的 ASN.1 规范。
隐式编码与显式编码
隐式编码 (IMPLICIT Encoding)
核心思想:替换标签。 使用新的上下文特定标签直接替换数据类型的原始通用标签。
- 编码方式: 编码器直接使用新指定的标签(如
[5]
)作为 T,数据的值作为 V 进行编码。原始数据类型的标识信息(通用标签)不会出现在编码字节流中。 - 解码要求: 解码器必须预先知道这个特定标签(如
[5]
)对应的是什么数据类型。它无法从编码数据本身推断出类型。协议规范(ASN.1 模式)就是解码器所需的“说明书”。 - 优点: 编码更紧凑,体积更小,因为没有内层标签的额外开销。
- 缺点: 缺乏自描述性,灵活性差。如果协议变更(例如将
[5]
从 INTEGER 改为 STRING),新旧版本将无法兼容。
ASN.1 定义
MyModule DEFINITIONS IMPLICIT TAGS ::= BEGIN |
(注意:在 IMPLICIT TAGS
模块中,[0]
默认就是隐式的,这里的 IMPLICIT
关键字可写可不写,只是为了清晰)。
假设我们有数据:name = "Alice"
, id = 123
。
对于 id
字段:
- 其原始类型是
INTEGER
(通用标签 = UNIVERSAL 2)。 - 我们应用了隐式标签
[0]
。
编码结果:30 (0D)
0C (05) 416C696365
80 (01) 7B
81 (01) 02
请注意,编码后的数据中完全没有 UNIVERSAL 2
这个标签的踪影。
典型应用场景:
- 带宽或存储极其敏感的领域:例如早期的移动通信协议(如 GSM)、卫星通信、某些物联网(IoT)设备协议。每个字节都很宝贵,需要极力缩减开销。
- 封闭的、高度优化的系统:通信双方由同一实体严格控制,协议规范几乎不会变动,可以牺牲灵活性来换取性能。
显式编码 (EXPLICIT Encoding)
核心思想:包装标签。 新的上下文特定标签作为一个外壳,包裹住完整的原始数据类型的 TLV 结构。
- 编码方式: 编码器先像没有上下文标签一样,对数据进行正常编码(得到一个完整的 TLV)。然后,它再创建一个新的、外层的 TLV,其中 T 就是新的上下文特定标签(如
[5]
),V 则是刚才编码得到的整个内层 TLV。 - 解码要求: 解码器看到外层标签
[5]
,知道需要解码一个上下文特定的元素。它接着解码内层的 V 字段,而内层 V 字段本身又是一个完整的 TLV,包含了完整的类型信息。解码器可以检查内层标签是否符合预期,或者甚至处理未知的新类型(如果协议允许扩展)。 - 优点: 自我描述,灵活性强,向后兼容性好。数据类型信息完整地保存在编码中。
- 缺点: 编码体积更大,因为有额外的外层标签和长度字节。
ASN.1 定义:
MyModule DEFINITIONS EXPLICIT TAGS ::= BEGIN |
(同样,在 EXPLICIT TAGS
模块中,[0]
默认就是显式的)。
2. 编码过程:
同样数据:name = "Alice"
, id = 123
。
对于 id
字段:
- 先对值
123
进行编码:T_inner = UNIVERSAL 2
,L_inner = 1
,V_inner = 0x7B
。 - 然后将整个 inner TLV 作为值,外面再套一个外壳:
T_outer = Context-Specific Tag [0]
L_outer = Length of the inner TLV
V_outer = The inner TLV (T_inner + L_inner + V_inner)
编码结果:原始的hex= 30110C05416C696365A00302017BA103020102
格式化后的TLV结构:
30 (11)
0C (05) 416C696365
A0 (03)
02 (01) 7B
A1 (03)
02 (01) 02
3. 典型应用场景:
- 需要强兼容性和扩展性的协议:例如 X.509 公钥证书、LDAP、SNMP。这些标准广泛使用显式编码。如果未来版本想将
id
字段从 INTEGER 改为 STRING,旧解码器虽然不认识新类型,但能看到内层的 STRING 标签,可以更好地处理错误或忽略未知字段。新解码器则能正确解析。 - 网络协议和加密标准:这些领域数据结构的复杂性高,且各方实现可能不同,显式编码提供的自描述特性大大降低了互操作性的难度。
- 绝大多数新项目:显式编码是 ASN.1 默认的标签设置,因为它更安全、更灵活。除非有极其严格的效率要求,否则首选显式编码。
对比总结表
特性 | 隐式编码 (IMPLICIT) | 显式编码 (EXPLICIT) |
---|---|---|
机制 | 替换原始通用标签 | 包装完整的原始TLV |
编码结构 | [新标签] + Length + Value |
[新标签] + Length + (原标签 + Length + Value ) |
数据体积 | 更小(无额外开销) | 更大(有外层包装开销) |
自描述性 | 无。接收方必须预知类型。 | 有。类型信息包含在编码中。 |
灵活性 | 差。类型固定,难以扩展和兼容。 | 好。易于处理可选字段和未来类型变更。 |
安全性 | 较低。错误配置容易导致解码错误。 | 较高。编码数据自身包含类型检查信息。 |
典型应用 | 专有协议、极限优化场景、旧系统 | 行业标准(X.509, LDAP, SNMP)、新项目、网络协议 |
AUTOMATIC TAGS
的作用是自动分配标签号,但它分配标签的方式是隐式的。这意味着:
MyModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN |
这个 age
字段将会使用隐式编码。AUTOMATIC TAGS
带来了标签管理的便利,但继承了隐式编码的优缺点(紧凑但不自描述)。
- 默认选择显式编码(EXPLICIT TAGS):这是最安全、最通用的选择,适用于绝大多数应用场景,尤其是在开放系统和网络协议中。
- 仅在确有必要时选择隐式编码:只有当性能分析表明编码开销是主要瓶颈,并且你能够严格控制协议规范的演化和所有实现时,才考虑使用隐式编码。
标准 ASCII 码对照表
二进制 | 八进制 | 十进制 | 十六进制 | 字符/缩写 | 解释 | |
---|---|---|---|---|---|---|
00000000 | 000 | 0 | 00 | NUL (NULL) | 空字符 | |
00000001 | 001 | 1 | 01 | SOH (Start Of Headling) | 标题开始 | |
00000010 | 002 | 2 | 02 | STX (Start Of Text) | 正文开始 | |
00000011 | 003 | 3 | 03 | ETX (End Of Text) | 正文结束 | |
00000100 | 004 | 4 | 04 | EOT (End Of Transmission) | 传输结束 | |
00000101 | 005 | 5 | 05 | ENQ (Enquiry) | 请求 | |
00000110 | 006 | 6 | 06 | ACK (Acknowledge) | 回应/响应/收到通知 | |
00000111 | 007 | 7 | 07 | BEL (Bell) | 响铃 | |
00001000 | 010 | 8 | 08 | BS (Backspace) | 退格 | |
00001001 | 011 | 9 | 09 | HT (Horizontal Tab) | 水平制表符 | |
00001010 | 012 | 10 | 0A | LF/NL(Line Feed/New Line) | 换行键 | |
00001011 | 013 | 11 | 0B | VT (Vertical Tab) | 垂直制表符 | |
00001100 | 014 | 12 | 0C | FF/NP (Form Feed/New Page) | 换页键 | |
00001101 | 015 | 13 | 0D | CR (Carriage Return) | 回车键 | |
00001110 | 016 | 14 | 0E | SO (Shift Out) | 不用切换 | |
00001111 | 017 | 15 | 0F | SI (Shift In) | 启用切换 | |
00010000 | 020 | 16 | 10 | DLE (Data Link Escape) | 数据链路转义 | |
00010001 | 021 | 17 | 11 | DC1/XON (Device Control 1/Transmission On) | 设备控制1/传输开始 | |
00010010 | 022 | 18 | 12 | DC2 (Device Control 2) | 设备控制2 | |
00010011 | 023 | 19 | 13 | DC3/XOFF (Device Control 3/Transmission Off) | 设备控制3/传输中断 | |
00010100 | 024 | 20 | 14 | DC4 (Device Control 4) | 设备控制4 | |
00010101 | 025 | 21 | 15 | NAK (Negative Acknowledge) | 无响应/非正常响应/拒绝接收 | |
00010110 | 026 | 22 | 16 | SYN (Synchronous Idle) | 同步空闲 | |
00010111 | 027 | 23 | 17 | ETB (End of Transmission Block) | 传输块结束/块传输终止 | |
00011000 | 030 | 24 | 18 | CAN (Cancel) | 取消 | |
00011001 | 031 | 25 | 19 | EM (End of Medium) | 已到介质末端/介质存储已满/介质中断 | |
00011010 | 032 | 26 | 1A | SUB (Substitute) | 替补/替换 | |
00011011 | 033 | 27 | 1B | ESC (Escape) | 逃离/取消 | |
00011100 | 034 | 28 | 1C | FS (File Separator) | 文件分割符 | |
00011101 | 035 | 29 | 1D | GS (Group Separator) | 组分隔符/分组符 | |
00011110 | 036 | 30 | 1E | RS (Record Separator) | 记录分离符 | |
00011111 | 037 | 31 | 1F | US (Unit Separator) | 单元分隔符 | |
00100000 | 040 | 32 | 20 | (Space) | 空格 | |
00100001 | 041 | 33 | 21 | ! | ||
00100010 | 042 | 34 | 22 | “ | ||
00100011 | 043 | 35 | 23 | # | ||
00100100 | 044 | 36 | 24 | $ | ||
00100101 | 045 | 37 | 25 | % | ||
00100110 | 046 | 38 | 26 | & | ||
00100111 | 047 | 39 | 27 | ‘ | ||
00101000 | 050 | 40 | 28 | ( | ||
00101001 | 051 | 41 | 29 | ) | ||
00101010 | 052 | 42 | 2A | * | ||
00101011 | 053 | 43 | 2B | + | ||
00101100 | 054 | 44 | 2C | , | ||
00101101 | 055 | 45 | 2D | - | ||
00101110 | 056 | 46 | 2E | . | ||
00101111 | 057 | 47 | 2F | / | ||
00110000 | 060 | 48 | 30 | 0 | ||
00110001 | 061 | 49 | 31 | 1 | ||
00110010 | 062 | 50 | 32 | 2 | ||
00110011 | 063 | 51 | 33 | 3 | ||
00110100 | 064 | 52 | 34 | 4 | ||
00110101 | 065 | 53 | 35 | 5 | ||
00110110 | 066 | 54 | 36 | 6 | ||
00110111 | 067 | 55 | 37 | 7 | ||
00111000 | 070 | 56 | 38 | 8 | ||
00111001 | 071 | 57 | 39 | 9 | ||
00111010 | 072 | 58 | 3A | : | ||
00111011 | 073 | 59 | 3B | ; | ||
00111100 | 074 | 60 | 3C | < | ||
00111101 | 075 | 61 | 3D | = | ||
00111110 | 076 | 62 | 3E | > | ||
00111111 | 077 | 63 | 3F | ? | ||
01000000 | 100 | 64 | 40 | @ | ||
01000001 | 101 | 65 | 41 | A | ||
01000010 | 102 | 66 | 42 | B | ||
01000011 | 103 | 67 | 43 | C | ||
01000100 | 104 | 68 | 44 | D | ||
01000101 | 105 | 69 | 45 | E | ||
01000110 | 106 | 70 | 46 | F | ||
01000111 | 107 | 71 | 47 | G | ||
01001000 | 110 | 72 | 48 | H | ||
01001001 | 111 | 73 | 49 | I | ||
01001010 | 112 | 74 | 4A | J | ||
01001011 | 113 | 75 | 4B | K | ||
01001100 | 114 | 76 | 4C | L | ||
01001101 | 115 | 77 | 4D | M | ||
01001110 | 116 | 78 | 4E | N | ||
01001111 | 117 | 79 | 4F | O | ||
01010000 | 120 | 80 | 50 | P | ||
01010001 | 121 | 81 | 51 | Q | ||
01010010 | 122 | 82 | 52 | R | ||
01010011 | 123 | 83 | 53 | S | ||
01010100 | 124 | 84 | 54 | T | ||
01010101 | 125 | 85 | 55 | U | ||
01010110 | 126 | 86 | 56 | V | ||
01010111 | 127 | 87 | 57 | W | ||
01011000 | 130 | 88 | 58 | X | ||
01011001 | 131 | 89 | 59 | Y | ||
01011010 | 132 | 90 | 5A | Z | ||
01011011 | 133 | 91 | 5B | [ | ||
01011100 | 134 | 92 | 5C | \ | ||
01011101 | 135 | 93 | 5D | ] | ||
01011110 | 136 | 94 | 5E | ^ | ||
01011111 | 137 | 95 | 5F | _ | ||
01100000 | 140 | 96 | 60 | ` | ||
01100001 | 141 | 97 | 61 | a | ||
01100010 | 142 | 98 | 62 | b | ||
01100011 | 143 | 99 | 63 | c | ||
01100100 | 144 | 100 | 64 | d | ||
01100101 | 145 | 101 | 65 | e | ||
01100110 | 146 | 102 | 66 | f | ||
01100111 | 147 | 103 | 67 | g | ||
01101000 | 150 | 104 | 68 | h | ||
01101001 | 151 | 105 | 69 | i | ||
01101010 | 152 | 106 | 6A | j | ||
01101011 | 153 | 107 | 6B | k | ||
01101100 | 154 | 108 | 6C | l | ||
01101101 | 155 | 109 | 6D | m | ||
01101110 | 156 | 110 | 6E | n | ||
01101111 | 157 | 111 | 6F | o | ||
01110000 | 160 | 112 | 70 | p | ||
01110001 | 161 | 113 | 71 | q | ||
01110010 | 162 | 114 | 72 | r | ||
01110011 | 163 | 115 | 73 | s | ||
01110100 | 164 | 116 | 74 | t | ||
01110101 | 165 | 117 | 75 | u | ||
01110110 | 166 | 118 | 76 | v | ||
01110111 | 167 | 119 | 77 | w | ||
01111000 | 170 | 120 | 78 | x | ||
01111001 | 171 | 121 | 79 | y | ||
01111010 | 172 | 122 | 7A | z | ||
01111011 | 173 | 123 | 7B | { | ||
01111100 | 174 | 124 | 7C | \ | ||
01111101 | 175 | 125 | 7D | } | ||
01111110 | 176 | 126 | 7E | ~ | ||
01111111 | 177 | 127 | 7F | DEL (Delete) | 删除 |