前言
PKI(public key infrastructure)标准可以分为第一代标准和第二代标准。
第一代PKI标准主要包括:
- 美国RSA公司的公钥加密标准(
Public Key Cryptography Standards,PKCS
)系列 - 国际电信联盟的ITU-T
X.509
- IETF组织的公钥基础设施X.509(Public Key Infrastructure X.509,PKIX)标准系列
- 无线应用协议(Wireless Application Protocol ,WAP)论坛的无线公钥基础设施(Wireless Public Key Infrastructure,WPKI)标准等。
第一代PKI标准主要是基于抽象语法符号(Abstract Syntax Notation One,ASN.1)编码的,实现比较困难,这也在一定程度上影响了标准的推广。
第二代PKI标准,略。
CA中心普遍采用的规范是X.509系列和PKCS系列。
一、ASN.1 - 数据结构描述语言
引用自Wiki:
ASN.1 is a standard interface description language for defining data structures that can be serialized and deserialized in a cross-platform way.
也就是说ASN.1是一种用来定义数据结构的接口描述语言,它不是二进制,也不是文件格式,看下面的例子你就会明白了:
1 | FooQuestion ::= SEQUENCE { |
这段代码定义了FooQuestion的数据结构,下面是FooQuestion这个数据接口的某个具体的数据:
1 | myQuestion FooQuestion ::= SEQUENCE { |
ASN.1用在很多地方比如下面要讲的 X.509 和 PKCS group of cryptography standards。
二、文件编码格式
2.1 DER编码格式
引用自Wiki:
ASN.1 is closely associated with a set of encoding rules that specify how to represent a data structure as a series of bytes
意思是ASN.1有一套关联的编码规则,这些编码规则用来规定如何用二进制来表示数据结构,DER是其中一种。
把上面的FooQuestion的例子用DER编码则是(16进制):
1 | 30 13 02 01 05 16 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f |
翻译过来就是:
1 | 30 — type tag indicating SEQUENCE |
看到这里你应该对DER编码格式有一个比较好的认识了。
我们可以使用openssl命令来看一下pkcs1.der的输出:
1 | openssl asn1parse -i -in pkcs1.der -inform DER |
里面将RSA私钥的所有数据都输出了。
2.2 PEM编码格式
引用自Wiki:
Privacy-Enhanced Mail (PEM) is a de facto file format for storing and sending cryptographic keys, certificates, and other data, based on a set of 1993 IETF standards defining “privacy-enhanced mail.”
PEM是一个用来存储和发送密码学key、证书和其他数据的文件格式的事实标准。许多使用ASN.1的密码学标准(比如X.509和PKCS)都使用DER编码,而DER编码的内容是二进制的,不适合与邮件传输(早期Email不能发送附件),因此使用PEM把二进制内容转换成ASCII码。文件内容的格式像下面这样:
1 | -----BEGIN label----- |
label用来区分内容到底是什么类型,下面会讲。
和PEM相关的RFC有很多,与本文内容相关的则是RFC7468,这里面规定了很多label,不过要注意不是所有label都会有对应的RFC或Specification,这些label只是一种约定俗成。
PEM实际上就是把DER编码的文件的二进制内容用base64编码一下,然后加上 -----BEGIN label-----
这样的头和 -----END label-----
这样的尾,中间则是DER文件的Base64编码。
我们可以通过下面的方法验证这个结论,先生成一个RSA Private Key,编码格式是PEM格式:
1 | openssl genrsa -out key.pem |
查看一下文件内容,可以看到label是RSA PRIVATE KEY
:
1 | -----BEGIN RSA PRIVATE KEY----- |
在—–BEGIN—–与—–END—–之间内容的顶部,有时使用一些头定义对信息进行封装,这些头信息格式如下(不一定都需要,可选的):
1 | Proc-Type,4: ENCRYPTED |
其中,第一个头信息标注了该文件是否进行了加密,该头信息可能的值包括:
- ENCRYPTED(信息已经加密和签名)
- MIC-ONLY(信息经过数字签名但没有加密)
- MIC-CLEAR(信息经过数字签名但是没有加密、也没有进行编码,可使用非PEM格式阅读)
- CLEAR(信息没有签名和加密并且没有进行编码,该项好象是openssl自身的扩展,但是并没有真正实现);
第二个头信息标注了加密的算法以及使用的ivec参量,ivec其实在这儿提供的应该是一个随机产生的数据序列,与块加密算法中要使用到的初始化变量(IV)不一样。
2.3 DER与PEM的格式转换
我们可以把PEM格式转换成DER格式:
1 | openssl rsa -in key.pem -outform der -out key.der |
如果你这个时候看一下文件内容会发现都是二进制。然后我们把DER文件的内容Base64一下,会看到内容和PEM文件一样(忽略头尾和换行):
1 | base64 -i key.der -o key.der.base64 |
三、证书、密码学Key格式标准
上面讲到的PEM是对证书、密码学Key文件的一种编码方式,下面举例这些证书、密码学Key文件格式:
3.1 X.509标准
引用自Wiki :
In cryptography, X.509 is a standard defining the format of public key certificates. X.509 certificates are used in many Internet protocols, including TLS/SSL, which is the basis for HTTPS, the secure protocol for browsing the web.
X.509是一个 Public Key Certificates 的格式标准,TLS/SSL使用它,TLS/SSL是HTTPS的基础所以HTTPS也使用它。而所谓 Public Key Certificates又被称为 Digital Certificate(数字证书) 或 Identity Certificate(身份证书)。
An X.509 certificate contains a public key and an identity (a hostname, or an organization, or an individual), and is either signed by a certificate authority or self-signed.
一个X.509 Certificate包含一个Public Key和一个身份信息,它要么是被CA签发的要么是自签发的。
下面这种张图就是一个X.509 Certificate:
事实上X.509 Certificate这个名词通常指代的是IETF的PKIX Certificate和CRL Profile,见RFC5280。所以当你看到PKIX Certificate字样的时候可以认为就是X.509 Certificate。
3.2 PKCS(公钥加密标准)系列
PKCS是由美国RSA数据安全公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。
引用自Wiki:
In cryptography, PKCS stands for “Public Key Cryptography Standards”
前面提到的X.509是定义Public Key Certificates的格式的标准,看上去和PKCS有点像,但实际上不同,PKCS是Public Key密码学标准。此外Public-Key Cryptography虽然名字看上去只涉及Public Key,实际上也涉及Priviate Key,因此PKCS也涉及Private Key。
到1999年底,PKCS已经公布了以下标准:
- PKCS#1:定义RSA公开密钥算法加密和签名机制,主要用于组织PKCS#7中所描述的数字签名和数字信封。专门用来加密/解密。
- PKCS#3:定义Diffie-Hellman密钥交换协议。
- PKCS#5:描述一种利用从口令派生出来的安全密钥加密字符串的方法。使用MD2或MD5 从口令中派生密钥,并采用DES-CBC模式加密。主要用于加密从一个计算机传送到另一个计算机的私人密钥,不能用于加密消息。
- PKCS#6:描述了公钥证书的标准语法,主要描述X.509证书的扩展格式。
- PKCS#7:定义一种通用的消息语法,包括数字签名和加密等用于增强的加密机制,PKCS#7与PEM兼容,所以不需其他密码操作,就可以将加密的消息转换成PEM消息。
- PKCS#8:描述私有密钥信息格式,该信息包括公开密钥算法的私有密钥以及可选的属性集等。 JAVA加密只支持 PKCS8。
- PKCS#9:定义一些用于PKCS#6证书扩展、PKCS#7数字签名和PKCS#8私钥加密信息的属性类型。
- PKCS#10:描述证书请求语法。
- PKCS#11:称为Cyptoki,定义了一套独立于技术的程序设计接口,用于智能卡和PCMCIA卡之类的加密设备。
- PKCS#12:描述个人信息交换语法标准。描述了将用户公钥、私钥、证书和其他相关信息打包的语法。
- PKCS#13:椭圆曲线密码体制标准。
- PKCS#14:伪随机数生成标准。
- PKCS#15:密码令牌信息格式标准。
下面挑讲PKCS #1、PKCS #8、PKCS #12。
3.2.1 PKCS #1
PKCS #1,RSA Cryptography Standard,定义了RSA Public Key和Private Key数学属性和格式,详见RFC8017。
详细的介绍了RSA算法的计算过程,包括:key的产生,key的结构,对数字加密/解密/签名/验证签名的过程、对应算法。
key:关于key,分别记录了private和public的详细结构,以及存储哪些内容。并且在附录里面推荐了ASN.1 Syntax中的存储结构。注:没有规定实际的物理文件存储结构,比如pem等。
加密/解密:详细描述了加密/解密的算法。包括,首先针对字符串,怎么转化成数字,之后,怎么根据数字进行加密。 这里可以看出,标准中没有对超长字符串处理的说明。而转化出的字符串的长度,全都是key的模长度k。
在字符串转化成数字过程中,需要增加填充字符,所以,分成了两种不同算法:RSAES-OAEP(现有标准) RSAES-PKCS1-v1_5(兼容过去标准)。在实际加密过程中,就只有一种算法了无论在加密还是签名过程中,都会进行hash操作,hash操作没有自己定义,而是从附录中可以选择需要的hash方式。
PKCS1填充方式:在进行RSA运算时需要将源数据D转化为Encryption block(EB)。其中pkcs1padding V1.5的填充模式按照以下方式进行。
PKCS1填充方式
格式:
1 | 格式:加密块EB = 00 + 块类型BT + 填充字符PS + 00 + 数据D //以下描述均以十六进制字符串来描述 |
说明:
- EB:为转化后Hex进制表示的数据块,长度为128个字节(密钥1024位的情况下)
- 00:为了确保块转换为整数的时候 小于模数
- BT(Block Type):
- 用一个字节表示,在目前的版本上,有三个值00 01 02。如果使用公钥操作,BT永远为02,如果用私钥操作则可能为00或01。
- 块类型为00,数据开头必须不能是00,因为填充的也是00,将无法解析。
- 块类型为01或02,块可以被准确解析,因为不会是00来填充。
- 如果是私钥操作,且明文数据是以00字节开头,那么建议把BT的值设置为01,否则,BT为00,PS为00,D开头为00,数据无法被分隔开
- PS(Padding String):
- 为填充位,PS由k-3-D这么多个字节构成,k表示密钥的字节长度,如果我们用1024bit的RSA密钥,这个长度就是1024/8=128,D表示明文数据D的字节长度
- 对于BT为00的,则这些字节全部为00
- 对于BT为01的,这些值全部为FF
- 对于BT为02的,这些字节的值随机产生但不能是0字节(非00)
- 填充长度至少为8个字节
- 00:用于分开 PS 和 D
- D:数据原文(HEX十六进制)
- PS至少为8个字节,所以D <= k-11
- 当我们使用128字节密钥对数据进行加密时,明文数据的长度不能超过过128-11=117字节
- 当RSA要加密数据大于 k-11字节时怎么办呢?把明文数据按照D的最大长度分块然后逐块加密,最后把密文拼起来就行。
注意:加密块EB长度 = RSA密钥key的长度。比如密钥1024bit,即128字节,加密块也会被填充为128字节
示例:下面的数据需要填充74个FF才够128个字节。
1 | 加密块 = 00 + 01 + FF(74个) + 00 + ">3031300d060960864801650304020105000420b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9(51字节) |
此外还有其他标准里定义的填充方式,比如PKCS5Padding、PKCS7Padding,不再赘述
3.2.2 PKCS #8
PKCS #8,Private-Key Information Syntax Standard,详细的描述了私钥的存储格式。用于加密、非加密地存储Private Certificate Keypairs(不限于RSA),详见RFC5858。
3.2.3 PKCS #12
PKCS #12定义了通常用来存储Private Keys和Public Key Certificates(例如前面提到的X.509)的文件格式,使用基于密码的对称密钥进行保护。注意上述Private Keys和Public Key Certificates是复数形式,这意味着PKCS #12文件实际上是一个Keystore,PKCS #12文件可以被用做 Java Key Store(JKS),详见RFC7292。
如果你用自己的CA所签发了一个证书,运行下列命令可以生成PKCS #12 keystore:
1 | openssl pkcs12 -export \ |
PKCS #12一般不导出PEM编码格式。
四、常见的证书标准格式(PEM编码)
当你不知道你的PEM文件内容是什么格式的可以根据下面查询。
4.1 X.509 Certificate
RFC7468 - Textual Encoding of Certificates
1 | -----BEGIN CERTIFICATE----- |
4.2 X.509 Certificate Subject Public Key Info
RFC7468 - Textual Encoding of Subject Public Key Info
1 | -----BEGIN PUBLIC KEY----- |
4.3 PKCS #1 Private Key
没有RFC或权威Specification,该格式有时候被称为traditional format、SSLeay format(见SO)
1 | -----BEGIN RSA PRIVATE KEY----- |
4.4 PKCS #1 Public Key
同上没有RFC或权威Specification
1 | -----BEGIN RSA PUBLIC KEY----- |
4.5 PKCS #8 Unencrypted Private Key
RFC7468 - One Asymmetric Key and the Textual Encoding of PKCS #8 Private Key Info
1 | -----BEGIN PRIVATE KEY----- |
4.6 PKCS #8 Encrypted Private Key
RFC7468 - Textual Encoding of PKCS #8 Encrypted Private Key Info
1 | -----BEGIN ENCRYPTED PRIVATE KEY----- |
4.7 RSA私钥PKCS1与PKCS8格式区别?
PKCS1:全名《Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications》最新版本2.2 (rfc8017, 有兴趣的同学可以读一下) ,从名称上可以看出它是针对RSA算法的一个规范。里面包含了RSA加密、解密、签名验签等所有的内容,当然也包含了私钥的格式。PKCS1的1.1版本是1991年发布的。
PKCS8:全名《Public-Key Cryptography Standards (PKCS) #8: Private-Key Information Syntax Specification》最新版本1.2,从名称上可以看出它是一个专门用来存储私钥的文件格式规范。PKCS1的1.2版本是2008年发布的。
它们两个有重合的部分,都定义了私钥的存储,那他们到底有什么关系呢?下面来验证一下
第一步:产生RSA私钥
1 | openssl genrsa -out pkcs1.pem 1024 |
此时当前目录下就有一个pkcs1.pem文件,查看文件内容可以看到
1 | -----BEGIN RSA PRIVATE KEY----- |
第二步:将私钥转为PKCS8
1 | openssl pkcs8 -topk8 -inform PEM -in pkcs1.pem -outform PEM -nocrypt -out pkcs8.pem |
此时当前目录下会多出一个pkcs8.pem文件,查看文件内容可以看到
1 | -----BEGIN PRIVATE KEY----- |
区别
那么之间有什么区别呢?从PEM格式的内容中看不出来什么规律,我们需要将两个PEM格式的文件转换为DER格式,这个二进制的数据容易做对比。
1 | $ openssl rsa -in pkcs1.pem -out pkcs1.der -outform DER |
现在我们得到了两个DER格式的文件,我们可以先用hexdemp看看两个文件的内容:
1 | cat pkcs1.der | hexdump |
此时如果从后往前看的话,其实可以发现PKCS8仅比PKCS1多了一个26自己的头,剩余的内容均完全一致。
我们可以使用以下的命令进行验证:
1 | openssl asn1parse -i -in pkcs8.der -inform DER |
可以看到 22:d=1 hl=4 l= 608 prim: OCTET STRING 这一行的输出就是pkcs1.der的内容。而上面的内容就是pkcs8比pkcs1多的内容。其实最主要的就是算法信息,这也是pkcs8能存储各种算法私钥的原因。
总结
从时间顺序上可以看出来,PKCS标准也是在不断发展的,PKCS1的标准是先于PKCS8出来的,那时候甚至非对称算法还只有RSA是成熟的,在计算机领域刚刚得到应用,PKCS专门将RSA的算法作为一个标准输出就不难理解了。随着加密算法的发展才有了PKCS8出来专门做存储密钥这样一件事情。
五、常见的证书后缀
作为文件形式存在的证书一般有这几种后缀:(证书中包含公钥,以及公钥颁发机构、版本号、算法等信息,可以以X.509为例看一下证书内容)
带有私钥的证书:(一般都有密码保护,使用的是DER编码)
.pfx
常用于Windows上的 IIS服务器.p12
常用于MAC OS、iOS中(PKCS#12由PFX进化而来的用于交换公共的和私有的对象的标准格式).jks
Java Key Storage,这是Java的专利,JAVA的专属格式,一般用于 Tomcat 服务器。
不带私钥的证书:
.cer/crt
编码方式不一定,可能是DER也可能是PEM.pem
都是PEM编码格式.der
都是DER编码格式.p7b
以树状展示证书链(certificate chain),同时也支持单个证书,不含私钥
补充
- .der文件一般只放证书,
不含私钥
- .pem文件中可以放证书或者私钥,或者两者都有,pem如果只含私钥的话,一般用
.key
扩展名,而且可以有密码保护 - .csr,即证书签名请求(Certificate Signing Request, CSR),这个并不是证书。
- 在创建加解密的公钥私钥对时,我们也可以生成对应的CSR文件。CSR中包含了公钥和标识名称(Distinguished Name)等信息。
- 如果想让此公钥变成证书来使用,需要把CSR文件提交给证书颁发机构,机构会使用其根证书私钥签名,就生成了证书文件,然后颁发给申请者。
- 做过iOS APP的朋友都应该知道是怎么向苹果申请开发者证书的吧。
可以使用命令行工具、代码、在线网站等实现不同后缀格式、不同证书标准之间的转换,比如这个网站http://www.metools.info/