证书一般都是x.509格式的证书,然后经过DER编码,DER是TLV编码,然后再经过base64编码后存储的。
我们打开ca.crt会发现是一个base64编码,如果直接在网上在线base64解码,解码后copy到一个文件里,
然后转换成16进制查看,这样是
行不通的,因为会把回车换行弄到里面,0a就变成了0d 0a。而且这时候的16进制某些位是错误的。
正确的方法,应该是,把证书文件,用binary方式,传送到linux下,然后用linux中的base64来进行文件解码。
命令如下:base64 -d -i ca.crt > crt.hex
-d的命令是解密,然后-i是--ignore-garbage When decoding, ignore non-alphabet characters.
Decoding require compliant input by default, use --ignore-garbage to
attempt to recover from non-alphabet characters (such as newlines) in
the encoded stream.
然后再用vim打开crt.hex,这时候再转换成16进制,就可以查看到正常的证书16进制的DER编码了。
证书的格式在RFC2459里,就是x.509.
Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING } 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, -- If present, version shall be v2 or v3 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version shall be v2 or v3 extensions [3] EXPLICIT Extensions OPTIONAL -- If present, version shall be v3 } Version ::= INTEGER { v1(0), v2(1), v3(2) } CertificateSerialNumber ::= INTEGER AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } Validity ::= SEQUENCE { notBefore Time, notAfter Time } Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } UniqueIdentifier ::= BIT STRING Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING } 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 这里只是简单的描述了下,具体的还要去看RFC文档。 Name中各个属性类型的简介: E 就是邮箱 DC 就是 domain component CN 就是common name OU 就是organization unit O 就是 organization L 就是城市名 S 就是省名 C 就是国家名 简单定长方法(Primitive, definite-length method) 这种方法用于简单类型及通过对简单类型使用隐式标签生成的类型。 它要求值的长度是事先预知的。BER编码的部分定义如下: 1. Identifier octets,有两种形式:较小的标签值(标签值在0 和30之间)和较大的标签值(标签值大于等于31) z Low-tag-number form:一个字节。Bit8和bit7表示类(如表2),bit6值为0,表示编码方法为简单化的。 下面给出了标签值。 Class Bit 8 Bit 7 universal 0 0 application 0 1 context-specific 1 0 private 1 1 High-tag-number form :两个或多个octet。第一个octet形式如low-tag-number form,但是bit5-1均为1。 第二个和以后的字节给出标签值,基于128,最高位在先,以便使用尽可能少的数字,除了最后一个字节以外, 每个字节的bit 8都置为1。最后一个字节的为0。 结构化定长编码的tag值的bit6为1 在隐式标签类型就是有关键字 IMPLICIT 定义的, ASN.1 表示法: [[class] number] IMPLICIT Type class = UNIVERSAL | APPLICATION | PRIVATE 其中,Type 是类型,class 是可选的 class 名,number 是类内的标签值,是一个非负整数。 如果没有 class 名称,则标签为 context-specific 类。 context-specific 类型的标签只能出现在 结构类型或CHOICE类型的组件中。 显式的也是如果没有class名称,则标签为 context-specific 类 , 此时tag值如果小于31的,bit8要为1。 a0 a3 暂时不知道 所以a0就是02加上bit8是1,所以是a0,就是显式的integer。用来表示Version的类型。 而a3的出现,是因为有Extensions的出现,他的number值是3,tag是sequence是0x10, 而且又是显示的, 没有class名字的,所以bit8要为1.所以是0xa3。 通过看RFC的文档例子,知道了些DER编码的tag值。 CHOICE和ANY是没有tag值的 0x02 integer 0x03 Bit String 简单编码, 第一个内容字节指出了该bit string凑成8的倍数所缺少的bit数(无用bit,unused bit) 0x04 OCTET String 任意的octet(8 bit)流 0x05 NULL 编码值固定为05 00. 0x06 OBJECT IDENTIFIER=OID :对象标识符,有一列整数构成,用于确定对象,如算法或属性类型 0x10 SEQUENCE and SEQUENCE of 有序集合,of可以为0个 0x11 SET and SET OF 无序集合,of可以为0个 0x13 printableString 0x14 T61String T.61(8 bit)字符的任意流 0x16 IA5String 由IA5(ASCII)字符任意组成的字符流 0x17 UTC time :"coordinated universal time"或者格林威治平均时(GMT)值。 由于 SEQUENCE 类型的标签为16(十进制) ,介于0与 30之间,所以标识符部分采用小标签形式。 因为SEQUENCE 属于universal类,所以Bits 8和7值为 0 。由于采用结构化编码,所以 Bit 6 为 1 长度字节部分采用短型, 内容字节是 attributeType 和attributeValue组件DER编码的串联。 所以 SEQUENCE 的DER编码是0x30,set的DER编码是0x31. 还知道了某些OID的值 2a 86 48 ce 38 04 03 dsa-with-sha 2a 86 48 86 f7 0d 01 01 05 sha1-with-rsa 2a 86 48 86 f7 0d 01 01 01 rsa 06 03 55 04 06 countryname 2.5.4.6 06 03 55 04 0a organizationname 2.5.4.10 06 03 55 04 03 commonname 2.5.4.3 OID的计算方法为: 描述符中组件的整数值: 1. 第一个字节值为 40 ×value1 + value2. (这是唯一的,由于 value1 的值 限制为0, 1,和 2;当value1 为 0或 1时value2 限制在 0至 39之间;根据 X.208,n 总是至少为2.) 2. 如果有后续的字节,编码为 value3,……,valuen。每个值基于128编码,最 高位在先以保证使用尽可能少的数字,除了最后一个字节外,每个 octet 的最 位都置为1。 例: RSA Data Security, Inc.的对象描述符的BER编码的第一个字节是40×1 + 2 = 42 = 2a16。840的编码为6×128 + 4816 即 86 48, 113549 的编码为6⋅1282 + 7716 ⋅128 + d16 是 86 f7 0d。最后的BER编码为: 06 06 2a 86 48 86 f7 0d DER encoding. 简单编码。内容字节与简单的BER编码相同。 证书最后的签名算法标识符要和前面证书里面的标识符完全一样。 最后签名的值如下产生,就是输入是签名的der编码,输出是签名后的der编码: The signatureValue field contains a digital signature computed upon the ASN.1 DER encoded tbsCertificate. The ASN.1 DER encoded tbsCertificate is used as the input to the signature function. This signature value is then ASN.1 encoded as a BIT STRING and included in the Certificate's signature field. The details of this process are specified for each of the supported algorithms in Section 7.2 RSA公钥的der编码都是这样的,30:81:89:02:81:81:<公钥(129字节)>:03:02:01:00:01 长度超过127字节时,就采用长型length格式,第一个字节的Bit 8为1,bit7-1表示后面有多少个字节用于表示实际长度。 其中,30的二进制值为110000,第 6 位的1表示使用了Constructed, definite-length 方法,后面第 1 到 第 5 位的10000为SEQUENCE 符号的编码, 十进制值为16。81的二进制值为10000001,第8位的1表示使用了长编码格式,第一位的1表示该编码包含一个字节,即后面的89,这个值就是整个数值串的长度。 之后的02为INTEGER符号的编码,81表示长编码格式,之后的81表示modulus 的长度,即129个字节。再之后的02表示INTEGER 的编码,03表示使用了短编码格式, 并且指示了publicExponent 的长度为3个字节,之后跟的就是publicExponent 了。 公钥是129字节,因为长度是81,第一个字节是00,具体为什么是00,还不是很清楚,望清楚者留言,不胜感激。 终于知道为什么有的会是00开头了,因为后面第一个字节的第一个bit是1,所以加了一个00.如果后面第一个bit不是0,则不加00.