openssl 密钥生成和解析

最近,在写java读取key的时候遇到了一个version的问题。不解,就各种找资料,这里总结一下.

ANS.1编码规则

openssl的数据编码规则是基于ans.1:

ASN.1(=Abstract Syntax Notation One),是一种结构化的描述语言,包括两部分,数据描述语言和数据编码规则。

  • 数据描述语言标准:语言标准允许用户自定义的基本数据类型,并可以通过简单的数据类型组成更复杂的数据类型。
  • 数据编码规则:这些编码方法规定了将数字对象转换成应用程序能够处理、保存、传输的二进制形式的一组规则。

标准ASN.1编码规则有规范编码规则(CER,Canonical Encoding Rules)、唯一编码规则(DER,Distinguished Encoding Rules)、压缩编码规则(PER,Packed Encoding Rules)和XML编码规则(XER,XML Encoding Rules)。

所谓的规则,就是数据流编解码的方式。编码:将数据结构(可能是不同的,异构的)变成数据流;解码:其他应用读取解析,获得相应数据。

Step 1:数据格式定义

对于普通数据类型,我们将数据直接转化成数据流进行传输即可;对于复杂数据格式,需要引入一个数字对象的概念,通过将复杂数据结构转化成数据对象。

ASN.1的第一部分---数据描述语言标准,这个标准定义了一些基本的数据类型,如果我们使用到复杂的数据结构,asn.1还允许通过简单数据类型组成复杂的数据类型(x.509)。

Step 2:数据流转化

数据传输需要将数据(结构或者对象)转化成数据流(二进制流)进行传输。

ASN.1的第二部分--编码规则,编码方法规定了将数字对象转换成应用程序能够处理、保存、传输的二进制形式的一组规则。

所以,ASN.1标准就是规定了把数据转化成数据对象,又规定数据对象编码为二进制流的方法。

openssl使用的是asn.1的der编码规则,保证每个asn.1对象使用der编码获得的二进制编码是唯一的。

openssl使用pem作为基本的文件编码格式,pem和der的关系如下图所示,其中几种加密环节是可选的:


image.png

从本质上来说,openssl是pem编码就是在der编码的技术上进行Base64编码,然后添加一些头尾信息组成,可以通过openssl指令对der和pem进行格式转换。

证书编码格式

常见的证书编码格式有三种X.509证书,PKCS#12证书和PKCS#7证书。

X.509证书

最常用的证书格式,它仅包含了公钥信息而没有私钥信息,一个openssl签发经过PEM编码的X.509证书格式如下:

-----BEGIN CERTIFICATE-----
XXX
-----END CERTIFICATE-----

中间部分就是经过PEM编码的X509证书。除了上述形式的头尾格式,还可能出现以下两种不同的标识符。

-----BEGIN X.509 CERTIFICATE----
XXX
-----END X.509 CERTIFICATE-----

或者

-----BEGIN TRUSTED  CERTIFICATE-----
XXX
-----END TRUSTED  CERTIFICATE-----

X.509证书文件的后缀名经常是der,cer或者crt。openssl的指令x509提供了对X.509证书进行格式转换的方法。

PKCS#12证书

PKCS12证书可以包含一个或者多个证书,并且还可以包含证书对应的私钥。openssl的pkcs12指令可以将X.509格式的证书和私钥封装成PKCS#12格式的证书,也可以将PKCS#12证书转换成X.509证书。

PKCS#12证书的后缀名通常是p12或者pdx。

PKCS#7证书:

PKCS#7可以封装一个或者多个X.509证书或者PKCS#6证书,并且可以包含CRL信息。PKCS#7证书中也不包含私钥信息。openssl提供了crl2pkcs7和pkcs7两个指令来生成和处理PKCS#7文件,可以使用他们在X.509证书和PKCS#7证书之间进行转换和处理

PKCS#7证书的后缀名是p7b

密钥编码

openssl有多种形式的密钥,openssl提供PEM和DER两种编码方式对这些密钥进行编码,并提供相关指令可以使用户在这两种格式之间进行转换。

openssl密钥大致可以分为两种,一种是可以公开的,例如公钥,一种是不能公开的,比对私钥。反映在编码上,有的密钥需要加密,有的密钥就不需要加密。一个经过加密的PEM编码密钥文件会在PEM文件中增加一些头信息,表明密钥的加密状态,加密算法及初始化向量等信息

openssl指令提供了对密钥加密的功能,并提供了多种可选的对称加密算法,比如DES和DES3。当对密钥进行加密的时候通常需要用户输入口令,这里的口令并非直接用来作为加密的密钥,而是根据这个口令使用一系列HASH操作来生成一个用户加密密钥数据的密钥。当读取这类密钥的时候,同样需要输入同样的口令。

这里再详细的说下DER和PEM。

DER

DER就是密钥的二进制表述格式。

Distinguished Encoding Rules (DER) is a binary serialization of ASN.1 format. It is often used for cryptographic data such as certificates, but has other uses.

PEM

PEM格式就是对DER编码转码为base64字符格式。通过base64解码可以还原DER格式。

A PEM file is plain text. It contain one or more objects, such as certificates or keys, which may not all be the same type. Each object is delimited by lines similar to “—–BEGIN …—–” and “—–END …—–”. Data that is not between such lines is ignored, and is sometimes used for comments, or for a human-readable dump of the encoded data.

Following the “BEGIN” and “END” keywords is a name (such as “CERTIFICATE”) that can be used as an identifier for the type of object.

The data between the delimiter lines starts with an optional email-like header section, followed by base64-encoded payload data. After decoding, the payload data is in DER format.

PEM 是明文格式,可以包含证书或者是密钥;其内容通常是以类似 “—–BEGIN …—–” 开头 “—–END …—–” 为结尾的这样的格式进行描述的。
因为DER是纯二进制格式,对人不友好,所以一般都用PEM进行存储。这里主要介绍PEM格式的公私钥。

公钥PEM

PKCS #1

PKCS #1 标准是专门为 RSA 密钥进行定义的,其对应的 PEM 文件格式如下,

-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----

上面的内容 BASE64 ENCODED DATA 指的就是 ANS.1 的 DER 的 Base64 编码。其ASN.1的格式为:

RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}

各个filed的含义:

  • modulus 是RSA的合数模n。
  • publicExponent 是RSA公开幂e。
PKCS #8

PKCS#8 标准定义了一个密钥格式的通用方案,它不仅仅为 RSA 所使用,同样也可以被其它密钥所使用。其对应的PEM文件格式如下:

-----BEGIN PUBLIC KEY-----
BASE64 ENCODED DATA
-----END PUBLIC KEY-----

注意,这里就没有 RSA 字样了,因为 PKCS#8 是一个通用型的秘钥格式方案;其中的 BASE64 ENCODED DATA 所标注的内容为 PEM 格式中对 DER 原始二进制进行的 BASE64 编码:

PublicKeyInfo ::= SEQUENCE {
  algorithm       AlgorithmIdentifier,
  PublicKey       BIT STRING
}
AlgorithmIdentifier ::= SEQUENCE {
  algorithm       OBJECT IDENTIFIER,
  parameters      ANY DEFINED BY algorithm OPTIONAL
}

PKCS#8 虽然名字叫做 Private-Key Information Syntax Specification,但是实际上,可以看到,它同样可以用作 Public Key 的格式定义;而 PKCS#8 是站在 PKCS#7 CMS 的基础之上进行编码格式定义的。

私钥PEM

PKCS #1

PKCS#1 是专门为 RSA 所涉及的,其对应的 PEM 格式如下:

-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----

原始的 DER 格式结构,既是 ASN.1 的数据结构:

RSAPrivateKey ::= SEQUENCE {
  version           Version,
  modulus           INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d
  prime1            INTEGER,  -- p
  prime2            INTEGER,  -- q
  exponent1         INTEGER,  -- d mod (p-1)
  exponent2         INTEGER,  -- d mod (q-1)
  coefficient       INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

其中各个字段的含义为:

  • version 是版本号,两个素数为0,如果使用了多素数,则版本号应该是1。
    Version ::= INTEGER { two-prime(0), multi(1) }
    (CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})
  • modulus 是RSA合数模n。
  • publicExponent 是RSA的公开幂e。
  • privateExponent 是RSA的私有幂d。
  • prime1 是n的素数因子p。
  • prime2 是n的素数因子q。
  • exponent1 等于d mod (p − 1)。
  • exponent2 等于d mod (q − 1)。
  • coefficient 是CRT系数 q–1 mod p。
  • otherPrimeInfos 按顺序包含了其它素数r3, …, ru的信息。如果version是0 ,它应该被忽略;而如果version是1,它应该至少包含OtherPrimeInfo的一个实例。
PKCS #8
未加密

PEM格式:

-----BEGIN PRIVATE KEY-----
BASE64 ENCODED DATA
-----END PRIVATE KEY-----

对应的DER:

PrivateKeyInfo ::= SEQUENCE {
  version         Version,
  algorithm       AlgorithmIdentifier,
  PrivateKey      BIT STRING
}
AlgorithmIdentifier ::= SEQUENCE {
  algorithm       OBJECT IDENTIFIER,
  parameters      ANY DEFINED BY algorithm OPTIONAL
}
加密

针对私钥的内容进行加密。
PEM格式:

-----BEGIN ENCRYPTED PRIVATE KEY-----
BASE64 ENCODED DATA
-----END ENCRYPTED PRIVATE KEY-----

DER格式:

EncryptedPrivateKeyInfo ::= SEQUENCE {
  encryptionAlgorithm  EncryptionAlgorithmIdentifier,
  encryptedData        EncryptedData
}
EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
EncryptedData ::= OCTET STRING

openssl操作

生成私钥

openssl有多种方法生成私钥:

  • genrsa生成RSA密钥。
  • req在生成req证书请求时同时产生密钥。
  • genpkey除了可以生成RSA密钥外,还可以生成DSA、DH密钥。

使用genpkey命令生成RSA私钥文件,选择DES-EDE3-CBC算法进行加密,口令是1234:
openssl genpkey -algorithm RSA -out privatekey.pem -pass pass:1234 -des-ede3-cbc

生成私钥文件,其密钥长度为1024。

这里还可以直接生成Openssl ASN格式的key:

-----BEGIN RSA PRIVATE KEY-----  
Proc-Type: 4,ENCRYPTED  
DEK-Info:DES-EDE3-CBC,4D5D1AF13367D726  
BASE64私钥内容  
-----END RSA PRIVATE KEY----- 

还有很多格式,比如微软的PVK等。Openssl ASN格式在加密私钥数据时只能用MD5算法生成key,而且只迭代计算了1次。所以从1.0.0开始Openssl把PKCS#8格式作为默认格式,可以为私钥文件提供更好的安全性和扩展性。

genrsa生成ASN格式的key;
rsa生成或者转化为PVK格式:openssl rsa -in privatekey.pem -out privatekey.pvk -outform PVK

解析私钥

使用asn1parse命令读取私钥SAN.1结构,其中-i表示使用缩进格式。

openssl asn1parse -i -in privatekey.pem 

其ASN.1输出格式为:

    0:d=0  hl=3 l= 135 cons: SEQUENCE          
    3:d=1  hl=2 l=   1 prim:  INTEGER           :01
    6:d=1  hl=2 l=  19 cons:  SEQUENCE          
    8:d=2  hl=2 l=   7 prim:   OBJECT            :id-ecPublicKey
   17:d=2  hl=2 l=   8 prim:   OBJECT            :prime256v1
   27:d=1  hl=2 l= 109 prim:  OCTET STRING      [HEX DUMP]:306B02010104204B42FE59E4C16B8BDD516E23AE880BBBC2662D28DD6B461C0364D639F691FBCCA144034200040EE3526C590136A6DD9467F0C17487296BF8A70D9E14E783F955C21D5E0212B04A9D45C18B073F9CBEFAE94CD79D69EC8D4529DD9392CC209FE7A70D90B9AFE9

每一行的意义大概为:

  • 0 表示节点在整个文件中的偏移长度
  • d=0 表示节点深度
  • hl=3 表示节点头字节长度
  • l=135 表示节点数据字节长度
  • cons 表示该节点为结构节点,表示包含子节点或者子结构数据
  • prim 表示该节点为原始节点,包含数据
  • SEQUENCE、OCTET STRING等都是ASN.1中定义的数据类型,具体可以参考ASN.1格式说明

最后一个节点OCTET STRING [HEX DUMP],就是加密后的私钥数据。

参考链接

推荐阅读更多精彩内容