X.509证书的编码及解析:程序解析以及winhex模板解析

时间:2024-04-30 13:05:08

一、证书的整体结构:证书内容、签名算法、签名结果。

用ASN.1语法描述如下:

Certificate::=SEQUENCE{
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}

其中,签名算法为CA对tbsCertificate进行签名所使用的算法;类型为AlgorithmIdentifier,其ASN.1语法描述如下:

AlgorithmIdentifier::=SEQUENCE{
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}

其中,algorithm给出了算法的OID;可选的parameters给出算法的参数。

需要注意,algorithm同时说明了杂凑算法和数字签名算法,常见的有:(1)MD5wihRSAEncryption, MD5 Hash函数和RSA签名算法配合使用,OID为1.2.840.113549.1.1.4。(2)SHA1withRSAEncryption, SHA-1 Hash函数和RSA签名算法配合使用,OID为1.2.840.113549.1.1.5。

签名结果是CA对tbsCertificate进行签名的结果,类型为BIT STRING。

证书内容是需要被CA签名的信息,ASN.1语法描述如下:

X.509证书的编码及解析:程序解析以及winhex模板解析
TBSCertificate::=SEQUENCE{
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
extensions [3] EXPLICIT Extensions OPTIONAL
}
X.509证书的编码及解析:程序解析以及winhex模板解析

其中,issuerUniqueID和subjectUniqueID只能在版本2或者3中出现;extensions只能在版本3中出现。

下面我们逐一说明TBSCertificate中的每一个字段。

1>版本号

版本(version)为整数格式。到目前为止,证书格式的版本只有v1、v2、v3,分别用整数0、1、2表示。

其类型Version的ASN.1描述如下:

Version::=INTEGER {v1(0),v2(1),v3(2)}

目前最常用的版本是v3。

2>序列号

证书序列号(serialNumber)为整数格式。

其类型CertificateSerialNumber的ASN.1描述如下:

CertificateSerialNumber::=INTEGER

证书序列号用来在某一个CA范围内唯一地标识一张证书。由此,“签发者”和“证书序列号”配合起来就能唯一地标识一张数字证书。在很多PKI的通信协议中使用的就是这种方式。

RFC 3280标准要求证书序列号必须是正整数,且长度不应该大于20字节。

3>签名算法

签名算法(signature)给出了CA签发证书时所使用的数字签名算法,它的类型与signatureAlgorithm的类型相同,都为AlgorithmIdentifier,它们的值必须一致,否则该证书无效。

4>签发者和主体

证书的签发者(issuer)和证书主体(subject)分别标识了签发证书的CA实体和证书持有者实体,两者类型均为Name。ASN.1描述如下:

X.509证书的编码及解析:程序解析以及winhex模板解析
Name::=CHOICE{
RDNSequence
}
RDNSequence::=SEQUENCE OF RelativeDistinguishedName
RelativeDistinguishedName::=SET OF AttributeTypeAndValue
AttributeTypeAndValue::=SEQUENCE{
type AttributeType,
value AttributeValue
}
AttributeType::=OBJECT IDENTIFIER
AttributeValue::=ANY DEFINED BY AttributeType
X.509证书的编码及解析:程序解析以及winhex模板解析

证书的签发者和证书主体用X.509 DN表示,DN是由RDN构成的序列。RDN用“属性类型=属性值”的形式表示。常用的属性类型名称以及简写如下:

常用RDN属性类型
属性类型名称 含义 简写
Common Name 通用名称 CN
Organizational Unit name 机构单元名称 OU
Organization name 机构名 O
Locality 地理位置 L
State or province name 州/省名 S
Country 国名 C

5>有效期

证书有效期(validity)给出证书的有效使用期,包含起、止两个时间值。时间值可以使用UTCTime或者GeneralizedTime的形式表示。ASN.1描述如下:

X.509证书的编码及解析:程序解析以及winhex模板解析
Validity::=SEQUENCE{
notBefore Time,
notAfter Time
}
Time::=CHOICE{
utcTime UTCTime,
generalTime GeneralizedTime
}
X.509证书的编码及解析:程序解析以及winhex模板解析

6>主体公钥信息

主体公钥信息(subjectPublicKeyInfo)给出了证书所绑定的加密算法和公钥。其ASN.1描述如下:

SubjectPublicKeyInfo::=SEQUENCE{
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING
}

其中,algorithm表示被绑定的、证书主体持有的公钥密码算法;subjectPublicKey是具体的公钥数据,内容和格式依算法不同而异。对于RSA算法,它包含公钥参数e和n。

7>签发者唯一标识符和主体唯一标识符

签发者唯一标识符(issuerUniqueID)和主体唯一标识符(subjectUniqueID)给出了证书签发者和证书主体的唯一标识符。UniqueIdentifier类型的ASN.1描述如下:

UniqueIdentifier::=BIT STRING

二、证书编码

针对ASN.1的语法,编码可以采用“TLV”方式,即依次对数据的类型(type)、长度(length)、值(value)编码,这样就可以完整地表示一个特定类型的数据。“TLV”方式的编码有多种,下面介绍DER这种编码方式。都是big-endian字节序。

1.简单类型的编码

1>BOOLEAN:01

布尔类型,两种取值:TRUE(0xFF)、FALSE(0x00)。

编码为:

        T      L      V
TRUE 01 01 FF
FALSE 01 01 00

2>INTEGER:02

整数类型。两种情况:

第一种,数据长度不大于0x7F,称为“短形式”,length占1字节,直接把长度赋给length。举例:0x123456的DER编码为:

T   L   V
02 03 12 34 56

第二种,数据长度大于0x7F,称为“长形式”,把数据长度L表示为字节码,计算其长度n,然后把n与0x80进行“位或”运算的结果赋给length的第一个字节。举例:0x1234...34(长0100字节),即n=2,编码为:

T   L           V
02 82 01 00 12 34 ... 34

此外,对于整数,还有正负的问题。规定value的最高位表示符号---0(+) 1(-)  负数用补码表示。

1)对于正数,如最高位为1,则向左扩展00。

2)对于负数,如其补码的最高位为0,则向左扩展FF。

3>BIT STRING:03

比特串的长度可能不是8的倍数,而DER编码以字节为单位。故而,如果需要,则在比特串的最后填若干位“0”,使其长度达到8的倍数;在最前面增加1字节,写明填充的位数。特别注意:value部分的第一字节,即表示填充位数的那个字节,也要计入数据的总长度。如果不需要填充,则第一字节也需要用00来表示填充位数。举例:1011010010编码为:

T   L   V
03 03 06 B4 80

4>OCTET STRING:04

字节码串。举例:AB CD EF 01 23的编码为:

T   L   V
04 05 AB CD EF 01 23

5>NULL:05

编码是固定的,value部分为空,一共两字节:

T   L
05 00

6>OBJECT IDENTIFIER:06

对象标识符(OID),是一个用“.”隔开的非负整数组成的序列。下面说下OID的编码设计:设OID=V1.V2.V3.V4.V5....Vn,则DER编码的value部分规则如下:(1)计算40*V1+V2作为第一字节;(2)将Vi(i>=3)表示为128进制,每一个128进制位作为一个字节,再将除最后一个字节外的所有字节的最高位置1;(3)依次排列,就得到了value部分。举例:OID=1.2.840.11359.1.1的编码如下:

X.509证书的编码及解析:程序解析以及winhex模板解析

说明:Vi的最后一个字节不对最高位置1,系统以此来识别这里是这个字段的最后一字节。

7>PrintableString:13

表示任意长度的ASCII字符串。举例:“Hello, world”的编码为:

T   L   V
13 0C 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64

8>UTCTime:17

表示时间,可以用GMT格林威治时间(结尾标“Z”)来表示,或者是用本地时间和相对于GMT的偏移量来表示。

UTCTime的格式如下多种:
YYMMDDhhmmZ
YYMMDDhhmm+hh'mm'
YYMMDDhhmm-hh'mm'
YYMMDDhhmmssZ
YYMMDDhhmmss+hh'mm'
YYMMDDhhmmss-hh'mm'

其中,

YY:年的最后2位
MM:月,01-12
DD:日,01-31
hh:小时,00-23
mm:分钟,00-59
ss:秒,00-59
Z/+/-:Z表示GMT时间,+/-表示本地时间与GMT时间的差距
hh’:与GMT的差
mm’:与GMT的差

举例:北京时间2008年8月8日晚8时表示成UTCTime为:080808120000Z 或 080808200000-0800 其编码为:

T   L   V
17 0D 30 38 30 38 30 38 31 32 30 30 30 30 5A

T L V
17 11 30 38 30 38 30 38 32 30 30 30 30 30 2D 30 38 30 30

9>GeneralizedTime:18

与UTCTime类似,差别只在于用4位数字表示“年”,以及“秒”可精确到千分位。举例:北京时间2008年8月8日晚8时1分2.345秒表示成GeneralizedTime为:20080808120102.345Z 或 20080808200102.345-0800 其编码为:

T   L   V
18 13 32 30 30 38 30 38 30 38 31 32 30 31 30 32 2E 33 34 35 5A

T L V
18 17 32 30 30 38 30 38 30 38 32 30 30 31 30 32 2E 33 34 35 2D 30 38 30 30

2.构造类型数据的编码

1>序列构造类型:30

SEQUENCE与SEQUENCE OF的type相同,都是30。value部分为序列内所有项目的编码的依次排列。length为这些项目编码的总长度。举例:一天中几次温度测量的结果:temperatureInADay SEQUENCE(7) OF INTEGER::={21,15,5,-2,5,10,5}, 其DER编码为:

X.509证书的编码及解析:程序解析以及winhex模板解析
T   L   V
30 15 02 01 15
02 01 0F
02 01 05
02 01 FE
02 01 05
02 01 0A
02 01 05
X.509证书的编码及解析:程序解析以及winhex模板解析

构造类型的定义中,常常包含CHOICE、ANY、OPTIONAL、DEFAULT等关键字,其编码规则如下:

(1)CHOICE

多选一,按照实际选中的类型编码。举例:

Time::=CHOICE{
utcTime UTCTime,
generalizedTime GeneralizedTime
}

若实际用到的类型是UTCTime,则数据用UTCTime的编码规则编码。

(2)ANY

类型依赖于另一个域的值,则按照实际类型编码。举例:

AlgorithmIdentifier::=SEQUENCE{
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}

若algorithm的值表示RSA,则parameters按RSA算法的参数类型编码;若algorithm的值表示Diffie-Hellman算法,则parameters按Diffie-Hellman算法的参数类型编码。

(3)OPTIONAL

所标记的字段在实际中可能存在,也可能不存在。如果有值,则编码;如果无值,则直接跳过。举例:

AlgorithmIdentifier::=SEQUENCE{
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}

实际中,如果没有参数parameters,则相当于

AlgorithmIdentifier::=SEQUENCE{
algorithm OBJECT IDENTIFIER
}

(4)DEFAULT

如果所标记的字段在实际中正好等于缺省值,则可以编码也可以不编码,相当于是OPTIONAL;如果不等于缺省值,则应该如实编码。举例:

Certificate::=SEQUENCE{
version Version DEFAULT 0
......
}

若version的值恰好等于0(缺省值),则可以不编码;否则,必须按其类型编码。

2>集合构造类型:31

SET和SET OF的type都是31,value部分包括集合内所有项目的编码,length为其总长度。需要注意的是,集合构造类型中的各字段是并列的,逻辑上不分先后,但为了编码的唯一性,在DER编码中,编码的排列是有一定顺序的。SET按标签的顺序排列。举例:

Name::=SET{
surname [0] PrintableString,
mid-name [1] PrintableString,
first-name [2] PrintableString
}

编码时则按照surname,mid-name,first-name的顺序。

SET OF按字典升序排列,即将各项目的DER结果看做字节码从小到大排列。举例:一天中几次温度测量的结果:temperatureInADay SET(7) OF INTEGER::={21,15,5,-2,5,10,5}, 其DER编码为:

X.509证书的编码及解析:程序解析以及winhex模板解析
T   L   V
30 15 02 01 05
02 01 05
02 01 05
02 01 0A
02 01 0F
02 01 15
02 01 FE
X.509证书的编码及解析:程序解析以及winhex模板解析

由于排序需要一定的时间和空间代价,故而实际情况中,应避免使用集合构造类型。

3.标签

仅仅以上的编码规则是不够的,会有些出现歧义的情况。比如:有相邻的字段属于相同的数据类型。type相同,则根据编码的排列顺序来区分他们。一旦其中有字段是可选的,解码时就不能再仅仅根据排列顺序来判断下一个是哪个字段了,产生歧义。故而,引入了标签,目的是把相同的type标签为不同的type,以便区分。

标签分为隐式标签和显式标签两种。分别如下:

隐式标签:

举例:

X.509证书的编码及解析:程序解析以及winhex模板解析
Contact::=SEQUENCE{
name PrintableString,
sex BOOLEAN,
title [0] IMPLICIT PrintableString OPTIONAL,
locality [1] IMPLICIT PrintableString OPTIONAL,
telephone [2] IMPLICIT PrintableString OPTIONAL,
fax [3] IMPLICIT PrintableString OPTIONAL
}
X.509证书的编码及解析:程序解析以及winhex模板解析

DER编码时,对于加了标签的项目,按如下规则编码:

对于简单类型,type=80+tag序号;对于构造类型,type=A0+tag序号。length和value不变。

例如,上例中如果项目fax被赋值为“86-10-12345678”,则编码为

T   L   V
83 0E 38 36 2D 31 30 2D 31 32 33 34 35 36 37 38

显式标签:

举例:(隐式标签的例子)

X.509证书的编码及解析:程序解析以及winhex模板解析
Record::=SEQUENCE{
......
time [1] IMPLICIT Time OPTIONAL,
......
}
Time::=CHOICE{
utcTime UTCTime,
generalizedTime GeneralizedTime
}
X.509证书的编码及解析:程序解析以及winhex模板解析

假设time被赋值为UTCTime类型的值080808120000Z,而由于隐式标签的type编码覆盖了表示这一类型的type编码,导致编码时无法判断time究竟是哪种类型,造成混乱。于是这里需要使用显式标签。运用显式标签,上例描述为:

X.509证书的编码及解析:程序解析以及winhex模板解析
Record::=SEQUENCE{
......
time [1] EXPLICIT Time OPTIONAL,
......
}
Time::=CHOICE{
utcTime UTCTime,
generalizedTime GeneralizedTime
}
X.509证书的编码及解析:程序解析以及winhex模板解析

编码规则如下:

T           L                       V
A0+Tag序号 原TLV格式编码的总长度 原TLV格式编码

上例中time=080808120000Z的编码为:

T   L   V
A1 0F 17 0D 30 38 30 38 30 38 31 32 30 30 30 30 5A

事实上,显式标签就是在原编码外再封装一层。

三、证书解析 C程序

附件(证书ca.cer):http://files.cnblogs.com/files/jiu0821/ca.cer.zip

代码:

X.509证书的编码及解析:程序解析以及winhex模板解析
  1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4
5 typedef struct L{
6 int len,tag;
7 L(){}
8 L(int len,int tag){
9 this->len=len;
10 this->tag=tag;
11 }
12 }Len;
13 typedef struct{
14 char s1[50],s2[50];
15 }TLV;
16 typedef struct{
17 char s1[50],s2[5000];
18 }TLV2;
19 struct SignatureAlgorithm{
20 TLV algorithm;
21 TLV parameters;
22 };
23 struct subjectPublicKey{
24 TLV algorithm;
25 TLV parameters;
26 TLV2 PKey;
27 };
28 struct signatureArray{
29 char s1[50],s2[50];
30 }sA[7],is[6];
31 struct SignatureValue{
32 TLV2 signatureValue;
33 };
34 struct TbsCertificate{
35 TLV version;
36 TLV serialNumber;
37 struct SignatureAlgorithm signature;
38 struct signatureArray issuer_[6];
39 TLV validity[2];
40 struct signatureArray subject_[6];
41 struct subjectPublicKey subjectPublicKeyInfo;
42 TLV issuerUniqueID;
43 TLV subjectUniqueID;
44 TLV extensions;
45 };
46 struct X509cer{
47 struct TbsCertificate cat;
48 struct SignatureAlgorithm casa;
49 struct SignatureValue casv;
50 }ca_cer;//证书ca.cer的结构
51
52 char s[5000];
53 int nc,tis;
54 bool bk=1;
55 bool btag=1;//0-隐式 1-显式
56 FILE *fp;
57
58 void sAfill();//绑定OID
59 void isFill();//绑定RDN
60 void fill(int);//switch结构,把证书结构的各字段调用tlv函数的序号与证书结构内容绑定一起,对ca_cer结构进行填充
61 Len tlv();//TLV匹配的递归
62 void bitfill(int);//从文件里获取连续字节码(字符串),赋给字符串s
63 void output();//依次输出ca_cer内容
64
65 int main(){
66 char *filename="D:\\exercise_cpp\\ca.cer";
67 fp=fopen(filename,"rb");
68 if(fp==NULL){
69 puts("can't open the file!");
70 exit(0);
71 }
72 sAfill();
73 isFill();
74 tlv();
75 fclose(fp);
76 output();
77 return 0;
78 }
79 void sAfill(){
80 strcpy(sA[0].s1,"1.2.840.10040.4.1");
81 strcpy(sA[0].s2,"DSA");
82 strcpy(sA[1].s1,"1.2.840.10040.4.3");
83 strcpy(sA[1].s2,"sha1DSA");
84 strcpy(sA[2].s1,"1.2.840.113549.1.1.1");
85 strcpy(sA[2].s2,"RSA");
86 strcpy(sA[3].s1,"1.2.840.113549.1.1.2");
87 strcpy(sA[3].s2,"md2RSA");
88 strcpy(sA[4].s1,"1.2.840.113549.1.1.3");
89 strcpy(sA[4].s2,"md4RSA");
90 strcpy(sA[5].s1,"1.2.840.113549.1.1.4");
91 strcpy(sA[5].s2,"md5RSA");
92 strcpy(sA[6].s1,"1.2.840.113549.1.1.5");
93 strcpy(sA[6].s2,"sha1RSA");
94 }
95 void isFill(){
96 strcpy(is[0].s1,"2.5.4.6");
97 strcpy(is[0].s2,"Country ");
98 strcpy(is[1].s1,"2.5.4.8");
99 strcpy(is[1].s2,"Sate or province name ");
100 strcpy(is[2].s1,"2.5.4.7");
101 strcpy(is[2].s2,"Locality ");
102 strcpy(is[3].s1,"2.5.4.10");
103 strcpy(is[3].s2,"Organization name ");
104 strcpy(is[4].s1,"2.5.4.11");
105 strcpy(is[4].s2,"Organizational Unit name ");
106 strcpy(is[5].s1,"2.5.4.3");
107 strcpy(is[5].s2,"Common Name ");
108 }
109 void fill(int n){
110 switch(n){//表示第几次调用tlv
111 case 4:
112 strcpy(ca_cer.cat.version.s1,"version: ");
113 if(strcmp(s,"0")==0) strcpy(s,"v1");
114 else if(strcmp(s,"1")==0) strcpy(s,"v2");
115 else strcpy(s,"v3");
116 strcpy(ca_cer.cat.version.s2,s);
117 break;
118 case 5:
119 strcpy(ca_cer.cat.serialNumber.s1,"serialNumber: ");
120 strcpy(ca_cer.cat.serialNumber.s2,s);
121 break;
122 case 7:
123 strcpy(ca_cer.cat.signature.algorithm.s1,"name of algorithm of signature: ");
124 for(int i=0;i<7;i++){
125 if(strcmp(s,sA[i].s1)==0){
126 strcpy(ca_cer.cat.signature.algorithm.s2,sA[i].s2);
127 break;
128 }
129 }
130 break;
131 case 8:
132 strcpy(ca_cer.cat.signature.parameters.s1,"parameters of signature: ");
133 strcpy(ca_cer.cat.signature.parameters.s2,s);
134 break;
135 case 12:
136 for(int i=0;i<6;i++){
137 if(strcmp(s,is[i].s1)==0){
138 strcpy(ca_cer.cat.issuer_[i].s1,is[i].s2);
139 strcat(ca_cer.cat.issuer_[i].s1,"of issuer:\t");
140 tis=i;
141 break;
142 }
143 }
144 break;
145 case 13:
146 strcpy(ca_cer.cat.issuer_[tis].s2,s);
147 break;
148 case 16:
149 for(int i=0;i<6;i++){
150 if(strcmp(s,is[i].s1)==0){
151 strcpy(ca_cer.cat.issuer_[i].s1,is[i].s2);
152 strcat(ca_cer.cat.issuer_[i].s1,"of issuer:\t");
153 tis=i;
154 break;
155 }
156 }
157 break;
158 case 17:
159 strcpy(ca_cer.cat.issuer_[tis].s2,s);
160 break;
161 case 20:
162 for(int i=0;i<6;i++){
163 if(strcmp(s,is[i].s1)==0){
164 strcpy(ca_cer.cat.issuer_[i].s1,is[i].s2);
165 strcat(ca_cer.cat.issuer_[i].s1,"of issuer:\t");
166 tis=i;
167 break;
168 }
169 }
170 break;
171 case 21:
172 strcpy(ca_cer.cat.issuer_[tis].s2,s);
173 break;
174 case 24:
175 for(int i=0;i<6;i++){
176 if(strcmp(s,is[i].s1)==0){
177 strcpy(ca_cer.cat.issuer_[i].s1,is[i].s2);
178 strcat(ca_cer.cat.issuer_[i].s1,"of issuer:\t");
179 tis=i;
180 break;
181 }
182 }
183 break;
184 case 25:
185 strcpy(ca_cer.cat.issuer_[tis].s2,s);
186 break;
187 case 28:
188 for(int i=0;i<6;i++){
189 if(strcmp(s,is[i].s1)==0){
190 strcpy(ca_cer.cat.issuer_[i].s1,is[i].s2);
191 strcat(ca_cer.cat.issuer_[i].s1,"of issuer:\t");
192 tis=i;
193 break;
194 }
195 }
196 break;
197 case 29:
198 strcpy(ca_cer.cat.issuer_[tis].s2,s);
199 break;
200 case 32:
201 for(int i=0;i<6;i++){
202 if(strcmp(s,is[i].s1)==0){
203 strcpy(ca_cer.cat.issuer_[i].s1,is[i].s2);
204 strcat(ca_cer.cat.issuer_[i].s1,"of issuer:\t");
205 tis=i;
206 break;
207 }
208 }
209 break;
210 case 33:
211 strcpy(ca_cer.cat.issuer_[tis].s2,s);
212 break;
213 case 35:
214 strcpy(ca_cer.cat.validity[0].s1,"the begin of validity: ");
215 strcpy(ca_cer.cat.validity[0].s2,s);
216 break;
217 case 36:
218 strcpy(ca_cer.cat.validity[1].s1,"the end of validity: ");
219 strcpy(ca_cer.cat.validity[1].s2,s);
220 break;
221 case 40:
222 for(int i=0;i<6;i++){
223 if(strcmp(s,is[i].s1)==0){
224 strcpy(ca_cer.cat.subject_[i].s1,is[i].s2);
225 strcat(ca_cer.cat.subject_[i].s1,"of subject:\t");
226 tis=i;
227 break;
228 }
229 }
230 break;
231 case 41:
232 strcpy(ca_cer.cat.subject_[tis].s2,s);
233 break;
234 case 44:
235 for(int i=0;i<6;i++){
236 if(strcmp(s,is[i].s1)==0){
237 strcpy(ca_cer.cat.subject_[i].s1,is[i].s2);
238 strcat(ca_cer.cat.subject_[i].s1,"of subject:\t");
239 tis=i;
240 break;
241 }
242 }
243 break;
244 case 45:
245 strcpy(ca_cer.cat.subject_[tis].s2,s);
246 break;
247 case 48:
248 for(int i=0;i<6;i++){
249 if(strcmp(s,is[i].s1)==0){
250 strcpy(ca_cer.cat.subject_[i].s1,is[i].s2);
251 strcat(ca_cer.cat.subject_[i].s1,"of subject:\t");
252 tis=i;
253 break;
254 }
255 }
256 break;
257 case 49:
258 strcpy(ca_cer.cat.subject_[tis].s2,s);
259 break;
260 case 52:
261 for(int i=0;i<6;i++){
262 if(strcmp(s,is[i].s1)==0){
263 strcpy(ca_cer.cat.subject_[i].s1,is[i].s2);
264 strcat(ca_cer.cat.subject_[i].s1,"of subject:\t");
265 tis=i;
266 break;
267 }
268 }
269 break;
270 case 53:
271 strcpy(ca_cer.cat.subject_[tis].s2,s);
272 break;
273 case 56:
274 for(int i=0;i<6;i++){
275 if(strcmp(s,is[i].s1)==0){
276 strcpy(ca_cer.cat.subject_[i].s1,is[i].s2);
277 strcat(ca_cer.cat.subject_[i].s1,"of subject:\t");
278 tis=i;
279 break;
280 }
281 }
282 break;
283 case 57:
284 strcpy(ca_cer.cat.subject_[tis].s2,s);
285 break;
286 case 60:
287 for(int i=0;i<6;i++){
288 if(strcmp(s,is[i].s1)==0){
289 strcpy(ca_cer.cat.subject_[i].s1,is[i].s2);
290 strcat(ca_cer.cat.subject_[i].s1,"of subject:\t");
291 tis=i;
292 break;
293 }
294 }
295 break;
296 case 61:
297 strcpy(ca_cer.cat.subject_[tis].s2,s);
298 break;
299 case 64:
300 strcpy(ca_cer.cat.subjectPublicKeyInfo.algorithm.s1,"name of algorithm of subjectPublicKey: ");
301 for(int i=0;i<7;i++){
302 if(strcmp(s,sA[i].s1)==0){
303 strcpy(ca_cer.cat.subjectPublicKeyInfo.algorithm.s2,sA[i].s2);
304 break;
305 }
306 }
307 break;
308 case 65:
309 strcpy(ca_cer.cat.subjectPublicKeyInfo.parameters.s1,"parameters of algorithm of subjectPublicKey: ");
310 strcpy(ca_cer.cat.subjectPublicKeyInfo.parameters.s2,s);
311 break;
312 case 66:
313 strcpy(ca_cer.cat.subjectPublicKeyInfo.PKey.s1,"subjectPublicKey: ");
314 strcpy(ca_cer.cat.subjectPublicKeyInfo.PKey.s2,s);
315 break;
316 case 69:
317 strcpy(ca_cer.casa.algorithm.s1,"name of signatureAlgorithm: ");
318 for(int i=0;i<7;i++){
319 if(strcmp(s,sA[i].s1)==0){
320 strcpy(ca_cer.casa.algorithm.s2,sA[i].s2);
321 break;
322 }
323 }
324 break;
325 case 70:
326 strcpy(ca_cer.casa.parameters.s1,"parameters of signatureAlgorithm: ");
327 strcpy(ca_cer.casa.parameters.s2,s);
328 break;
329 case 71:
330 strcpy(ca_cer.casv.signatureValue.s1,"signatureValue: ");
331 strcpy(ca_cer.casv.signatureValue.s2,s);
332 bk=0;
333 break;
334 }
335 }
336 Len tlv(){
337 if(bk==0) return Len(1000,0);
338 nc++;
339 bool b=true;
340 unsigned char type=fgetc(fp);//type
341 unsigned char len0=fgetc(fp);//len
342 int len=len0;
343 int lem=0;
344 if(type<0xa0){
345 if(type==1){
346 unsigned char vc=fgetc(fp);
347 if(vc==0) strcpy(s,"FALSE");
348 else strcpy(s,"TRUE");
349 }else if(type==2){
350 if(len0>0x80){
351 int tn2=len0-0x80;
352 unsigned char tl;
353 len=0;
354 for(int i=0;i<tn2;i++){
355 tl=fgetc(fp);
356 len*=256;
357 len+=tl;
358 }
359 }
360 bitfill(len);
361 }else if(type==3){
362 if(len0>0x80){
363 int tn2=len0-0x80;
364 unsigned char tl;
365 len=0;
366 for(int i=0;i<tn2;i++){
367 tl=fgetc(fp);
368 len*=256;
369 len+=tl;
370 }
371 }
372 bitfill(len);
373 }else if(type==4){
374 if(len0>0x80){
375 int tn2=len0-0x80;
376 unsigned char tl;
377 len=0;
378 for(int i=0;i<tn2;i++){
379 tl=fgetc(fp);
380 len*=256;
381 len+=tl;
382 }
383 }
384 bitfill(len);
385 }else if(type==5){
386 strcpy(s,"NULL");
387 }else if(type==6){
388 strcpy(s,"");
389 int dd=len0;
390 unsigned char tl=fgetc(fp);
391 int d=tl/40;
392 char ts2[10];
393 sprintf(ts2,"%d",d);
394 strcat(s,ts2);
395 strcat(s,".");
396 d=tl-d*40;
397 sprintf(ts2,"%d",d);
398 strcat(s,ts2);
399 for(int i=1;i<dd;i++){
400 strcat(s,".");
401 i--;
402 int t=0;
403 while(1){
404 tl=fgetc(fp);
405 i++;
406 bool b2=false;
407 if(tl&0x80){
408 b2=true;
409 }
410 if(b2){
411 tl&=0x7f;
412 }
413 t*=128;
414 t+=tl;
415 if(!b2) break;
416 }
417 sprintf(ts2,"%d",t);
418 strcat(s,ts2);
419 }
420 }else if(type==0x13){
421 int d=len0;
422 fread(s,1,d,fp);
423 s[d]='\0';
424 }else if(type==0x17||type==0x18){
425 int d=len0;
426 fread(s,1,d,fp);
427 s[d]='\0';
428 }else if(type==0x30||type==0x31){
429 b=false;
430 if(len0>0x80){
431 len=0;
432 len0-=0x80;
433 unsigned char tl;
434 for(int i=0;i<len0;i++){
435 tl=fgetc(fp);
436 len*=256;
437 len+=tl;
438 }
439 }
440 int dlen=len;
441 while(dlen>0){
442 dlen-=tlv().len;
443 }
444 }else{
445 printf("the cer has errors!\n");
446 exit(0);
447 }
448 }else{
449 b=false;
450 lem=type-0xa0;
451 if(len0>0x80){
452 int tn2=len0-0x80;
453 unsigned char tl;
454 len=0;
455 for(int i=0;i<tn2;i++){
456 tl=fgetc(fp);
457 len*=256;
458 len+=tl;
459 }
460 }
461 if(btag){
462 //这里做个简化,对扩展域进行忽略处理。
463 if(nc==67) fseek(fp,len,SEEK_CUR);
464 else tlv();
465 }else{
466 //这里不作具体实现,依具体类型的证书而定
467 }
468 }
469 if(b) fill(nc);
470 return Len(len,lem);
471 }
472 void bitfill(int dd){
473 strcpy(s,"");
474 for(int i=0;i<dd;i++){
475 unsigned char tl=fgetc(fp);
476 int d=tl;
477 char ts2[10];
478 sprintf(ts2,"%02x",d);
479 strcat(s,ts2);
480 }
481 }
482 void output(){
483 puts("ca.cer解析如下:");
484 printf("【版本】%s%s\n",ca_cer.cat.version.s1,ca_cer.cat.version.s2);
485 printf("【序列号】%s%s\n",ca_cer.cat.serialNumber.s1,ca_cer.cat.serialNumber.s2);
486 printf("【签名算法】%s%s\n",ca_cer.cat.signature.algorithm.s1,ca_cer.cat.signature.algorithm.s2);
487 printf("【签名算法的参数】%s%s\n",ca_cer.cat.signature.parameters.s1,ca_cer.cat.signature.parameters.s2);
488 printf("【签发者标识信息】issuer\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n",ca_cer.cat.issuer_[0].s1,ca_cer.cat.issuer_[0].s2,ca_cer.cat.issuer_[1].s1,ca_cer.cat.issuer_[1].s2,ca_cer.cat.issuer_[2].s1,ca_cer.cat.issuer_[2].s2,ca_cer.cat.issuer_[3].s1,ca_cer.cat.issuer_[3].s2,ca_cer.cat.issuer_[4].s1,ca_cer.cat.issuer_[4].s2,ca_cer.cat.issuer_[5].s1,ca_cer.cat.issuer_[5].s2);
489 printf("【有效期】validity: %s-%s\n",ca_cer.cat.validity[0].s2,ca_cer.cat.validity[1].s2);
490 printf("【主体标识信息】subject\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n",ca_cer.cat.subject_[0].s1,ca_cer.cat.subject_[0].s2,ca_cer.cat.subject_[1].s1,ca_cer.cat.subject_[1].s2,ca_cer.cat.subject_[2].s1,ca_cer.cat.subject_[2].s2,ca_cer.cat.subject_[3].s1,ca_cer.cat.subject_[3].s2,ca_cer.cat.subject_[4].s1,ca_cer.cat.subject_[4].s2,ca_cer.cat.subject_[5].s1,ca_cer.cat.subject_[5].s2);
491 printf("【公钥的加密算法】%s%s\n",ca_cer.cat.subjectPublicKeyInfo.algorithm.s1,ca_cer.cat.subjectPublicKeyInfo.algorithm.s2);
492 printf("【公钥的加密算法参数】%s%s\n",ca_cer.cat.subjectPublicKeyInfo.parameters.s1,ca_cer.cat.subjectPublicKeyInfo.parameters.s2);
493 printf("【公钥数据】%s%s\n",ca_cer.cat.subjectPublicKeyInfo.PKey.s1,ca_cer.cat.subjectPublicKeyInfo.PKey.s2);
494 printf("【签发者唯一标识符】issuerUniqueID: 无\n");
495 printf("【主体唯一标识符】subjectUniqueID: 无\n");
496 printf("【扩展】extendsions: 省略\n");
497 printf("【签名算法】%s%s\n",ca_cer.casa.algorithm.s1,ca_cer.casa.algorithm.s2);
498 printf("【签名算法的参数】%s%s\n",ca_cer.casa.parameters.s1,ca_cer.casa.parameters.s2);
499 printf("【签名结果】%s%s\n",ca_cer.casv.signatureValue.s1,ca_cer.casv.signatureValue.s2);
500 }
X.509证书的编码及解析:程序解析以及winhex模板解析

结果截图:

X.509证书的编码及解析:程序解析以及winhex模板解析

四、证书解析 winhex自制模板

附件(证书ca.cer):同上

代码:

X.509证书的编码及解析:程序解析以及winhex模板解析
 1 template "x.509"
2 description "ca.cer"
3 applies_to file
4 fixed_start 0x00
5 big-endian
6 read-only
7 begin
8 move 2
9 uint16 "size of cer"
10 move 2
11 uint16 "size of info of cer"
12 move 4
13 uint8 "version"
14 move 2
15 hex 16 "serialNumber"
16 move 4
17 hex 9 "signature: sha1RSA"
18 move 15
19 string 2 "Country of issuer"
20 move 11
21 string 2 "Sate or province name of issuer" //04a
22 move 11
23 string 2 "Locality of issuer" //057
24 move 11
25 string 5 "Organization name of issuer" //067
26 move 11
27 string 2 "Organizational Unit name of issuer" //074
28 move 11
29 string 6 "Common Name of issuer" //085
30 move 4
31 string 13 "the begin of validity"
32 move 2
33 string 13 "the end of validity" //0a5
34 move 13
35 string 2 "Country of subject"
36 move 11
37 string 2 "Sate or province name of subject"
38 move 11
39 string 2 "Locality of subject"
40 move 11
41 string 5 "Organization name of subject"
42 move 11
43 string 2 "Organizational Unit name of subject"
44 move 11
45 string 6 "Common Name of subject" //0fc
46 move 8
47 hex 9 "subjectPublicKey's algorithm:RSA" //10d
48 move 6
49 hex 271 "subjectPublicKey"
50 move 188 //2de
51 move 4
52 hex 9 "signatureAlgorithm: sha1RSA"
53 move 6
54 hex 257 "sinatureValue"
55 end
X.509证书的编码及解析:程序解析以及winhex模板解析

结果截图:

X.509证书的编码及解析:程序解析以及winhex模板解析

五、小结
程序写得比较粗糙,本意只是借此来掌握x.509证书的结构,也是想玩一下,后面写得比较花时间,就在证书一些结构的细节上写得比较粗糙,比如整数的正负显示,bit串的补位等以及证书扩展没有分析(里面一些OID不认识,原理与前面类似,就作罢)。因为扩展项省略的原因,导致tlv递归函数前后不是很平衡,便加了一个return语句强行退出。总之,写完这个还是比较高兴的。O(∩_∩)O~而最大的问题在于fill函数,这个地方对我的证书依赖性太强,没有去特意解决。不过我想加几个变量监控下就可以解决。这里先纸上谈兵好了。同时也得承认,自己编程能力需要提高了。