X.509证书的编码及解析:程序解析以及winhex模板解析
一、证书的整体结构:证书内容、签名算法、签名结果。
用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用“属性类型=属性值”的形式表示。常用的属性类型名称以及简写如下:
属性类型名称 | 含义 | 简写 |
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):https://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函数,这个地方对我的证书依赖性太强,没有去特意解决。不过我想加几个变量监控下就可以解决。这里先纸上谈兵好了。同时也得承认,自己编程能力需要提高了。