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

用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语法描述如下:

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
}

其中,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描述如下:

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 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描述如下:

Validity::=SEQUENCE{
notBefore Time,
notAfter Time
}
Time::=CHOICE{
utcTime UTCTime,
generalTime GeneralizedTime
}

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的编码如下:

说明: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编码为:

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

构造类型的定义中,常常包含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编码为:

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

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

3.标签

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

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

隐式标签:

举例:

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
}

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

显式标签:

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

Record::=SEQUENCE{
......
time [1] IMPLICIT Time OPTIONAL,
......
}
Time::=CHOICE{
utcTime UTCTime,
generalizedTime GeneralizedTime
}

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

Record::=SEQUENCE{
......
time [1] EXPLICIT Time OPTIONAL,
......
}
Time::=CHOICE{
utcTime UTCTime,
generalizedTime GeneralizedTime
}

编码规则如下:

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

代码:

  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 }

结果截图:

四、证书解析 winhex自制模板

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

代码:

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

X.509证书的编码及解析:程序解析以及winhex模板解析的更多相关文章

  1. PHP模板解析入门

    学习:李炎恢PHP视频第二季 模板引擎的特点: 1.鼓励分离:让更个系统的可读性和维护性得到提高. 2.促进分工:使得程序员和美工去专心处理自己的设计. 3.比PHP更容易解析:编译文件和缓存文件加载 ...

  2. X.509证书生成

    创建数字证书 用户对数字证书的认可决定于对证书颁发机构的信任,所以证书颁发机构决定了数字证书的可用范围.由于官方认可的数字证书颁发机构,比如VeriSign.Thawte(OpenSSL),具有普遍的 ...

  3. openssl ans.1编码规则分析及证书密钥编码方式

    1 数据编码格式 openssl的数据编码规则是基于ans.1的,ans.1是什么 ? 先上高大上的解释 ASN.1(Abstract Syntax Notation One), 是一种结构化的描述语 ...

  4. 使用OpenSSL工具制作X.509证书的方法及其注意事项总结

    版权声明:本文为博主原创文章.转载请注明出处. https://blog.csdn.net/Ping_Fani07/article/details/21622545 怎样使用OpenSSL工具生成根证 ...

  5. 基于X.509证书和SSL协议的身份认证过程实现(OpenSSL可以自己产生证书,有TCP通过SSL进行实际安全通讯的实际编程代码)good

    上周帮一个童鞋做一个数字认证的实验,要求是编程实现一个基于X.509证书认证的过程,唉!可怜我那点薄弱的计算机网络安全的知识啊!只得恶补一下了. 首先来看看什么是X.509.所谓X.509其实是一种非 ...

  6. 什么是 X.509 证书以及它是如何工作的?

    X.509 证书是基于广泛接受的国际电信联盟 (ITU) X.509 标准的数字证书,该标准定义了公钥基础设施 (PKI) 证书的格式. 它们用于管理互联网通信和计算机网络中的身份和安全. 它们不显眼 ...

  7. X.509证书_生成X.509协议的证书

    用法:1. 用NOTE打开,修改按实际情况脚本中的(1)~ (6)处参数2. 找一台含JVM环境的WIN机器3. 双击执行后,会生成一对密钥4. 请确保当前使用的JDK版本为6.0!!! @echo ...

  8. 利用X.509证书对XML进行加密和签名

    综述       XML加密和签名技术应用非常广泛. ASP.NET 使用XML加密对配置信息进行加密:InfoPath使用XML签名对表单进行签名:Web服务使用XML加密和签名对SOAP消息进行加 ...

  9. HTTP证书申请,设置应用程序服务器使用HTTPS

    HTTP证书申请,设置应用程序服务器使用HTTPS https://certs.godaddy.com/repository/ 根证书和中级证书下载地址(godaddy) ######Godaddy购 ...

随机推荐

  1. imx6------watchdog导致不进系统

    刚上板子,把大部分驱动都停了,不过watchdog的驱动没停,当时想没应用程序所以watchdog不用管,没想到 就是watchdog卡住了,有程序open了watchdog但是没有write,结果t ...

  2. 彻底搞懂JavaScript中的继承

    你应该知道,JavaScript是一门基于原型链的语言,而我们今天的主题 -- "继承"就和"原型链"这一概念息息相关.甚至可以说,所谓的"原型链&q ...

  3. Installing VirtualBox DKMS in Kali 2.0

    Kali linux is one of the mainly used operating system by the Ethical hackers and information securit ...

  4. IIS Express 添加MIME映射

    IIS Express没有提供图形化界面来配置MIME类型,但提供了配置MIME类型的命令行工具. IIS Express中添加MIME映射.操作如下: 在DOS窗口下进入IIS Express安装目 ...

  5. 配置github的SSH key及GitHub项目上传方式一——使用终端命令行

    GitHub是一个开源的大仓库,我们经常从github上下载项目进行学习和研究,下面是一个完整的步骤——往GitHub上传一个新项目. 一.注册GitHub账号 1.注册GitHub账号,地址:htt ...

  6. 递推-练习1--noi1760 菲波那契数列(2)

    递推-练习1--noi1760 菲波那契数列(2) 一.心得 二.题目 1760:菲波那契数列(2) 总时间限制:  1000ms 内存限制:  65536kB 描述 菲波那契数列是指这样的数列: 数 ...

  7. activemq、rabbitmq、kafka原理和比较

    一.activemq 虽然是java写的消息队列,但是提供Java, C, C++, C#, Ruby, Perl, Python, PHP各种客户端,所以语言上是没什么问题的.配置和使用,基本上是j ...

  8. 51nod1347思维

    1347 旋转字符串 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题  收藏  关注 S[0...n-1]是一个长度为n的字符串,定义旋转函数Left(S)=S[1… ...

  9. itembase协同过滤的详细介绍

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:https://www.cnblogs.com/by-dream/p/9016289.html 前言 通常我们在网购的时候会遇到这 ...

  10. Sizzle源码分析:二 词法分析

    上一篇我们了解了Sizzle的整体流程,下面我开始一点点分析各个流程,我们进行查询的第一步就是词法分析tokenize,同样先了解下思路,如果是#div_test > span input[ch ...