最近,在寫java讀取key的時(shí)候遇到了一個(gè)version的問(wèn)題。不解,就各種找資料,這里總結(jié)一下.
ANS.1編碼規(guī)則
openssl的數(shù)據(jù)編碼規(guī)則是基于ans.1:
ASN.1(=Abstract Syntax Notation One),是一種結(jié)構(gòu)化的描述語(yǔ)言,包括兩部分,數(shù)據(jù)描述語(yǔ)言和數(shù)據(jù)編碼規(guī)則。
- 數(shù)據(jù)描述語(yǔ)言標(biāo)準(zhǔn):語(yǔ)言標(biāo)準(zhǔn)允許用戶自定義的基本數(shù)據(jù)類型,并可以通過(guò)簡(jiǎn)單的數(shù)據(jù)類型組成更復(fù)雜的數(shù)據(jù)類型。
- 數(shù)據(jù)編碼規(guī)則:這些編碼方法規(guī)定了將數(shù)字對(duì)象轉(zhuǎn)換成應(yīng)用程序能夠處理、保存、傳輸?shù)亩M(jìn)制形式的一組規(guī)則。
標(biāo)準(zhǔn)ASN.1編碼規(guī)則有規(guī)范編碼規(guī)則(CER,Canonical Encoding Rules)、唯一編碼規(guī)則(DER,Distinguished Encoding Rules)、壓縮編碼規(guī)則(PER,Packed Encoding Rules)和XML編碼規(guī)則(XER,XML Encoding Rules)。
所謂的規(guī)則,就是數(shù)據(jù)流編解碼的方式。編碼:將數(shù)據(jù)結(jié)構(gòu)(可能是不同的,異構(gòu)的)變成數(shù)據(jù)流;解碼:其他應(yīng)用讀取解析,獲得相應(yīng)數(shù)據(jù)。
Step 1:數(shù)據(jù)格式定義
對(duì)于普通數(shù)據(jù)類型,我們將數(shù)據(jù)直接轉(zhuǎn)化成數(shù)據(jù)流進(jìn)行傳輸即可;對(duì)于復(fù)雜數(shù)據(jù)格式,需要引入一個(gè)數(shù)字對(duì)象的概念,通過(guò)將復(fù)雜數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化成數(shù)據(jù)對(duì)象。
ASN.1的第一部分---數(shù)據(jù)描述語(yǔ)言標(biāo)準(zhǔn),這個(gè)標(biāo)準(zhǔn)定義了一些基本的數(shù)據(jù)類型,如果我們使用到復(fù)雜的數(shù)據(jù)結(jié)構(gòu),asn.1還允許通過(guò)簡(jiǎn)單數(shù)據(jù)類型組成復(fù)雜的數(shù)據(jù)類型(x.509)。
Step 2:數(shù)據(jù)流轉(zhuǎn)化
數(shù)據(jù)傳輸需要將數(shù)據(jù)(結(jié)構(gòu)或者對(duì)象)轉(zhuǎn)化成數(shù)據(jù)流(二進(jìn)制流)進(jìn)行傳輸。
ASN.1的第二部分--編碼規(guī)則,編碼方法規(guī)定了將數(shù)字對(duì)象轉(zhuǎn)換成應(yīng)用程序能夠處理、保存、傳輸?shù)亩M(jìn)制形式的一組規(guī)則。
所以,ASN.1標(biāo)準(zhǔn)就是規(guī)定了把數(shù)據(jù)轉(zhuǎn)化成數(shù)據(jù)對(duì)象,又規(guī)定數(shù)據(jù)對(duì)象編碼為二進(jìn)制流的方法。
openssl使用的是asn.1的der編碼規(guī)則,保證每個(gè)asn.1對(duì)象使用der編碼獲得的二進(jìn)制編碼是唯一的。
openssl使用pem作為基本的文件編碼格式,pem和der的關(guān)系如下圖所示,其中幾種加密環(huán)節(jié)是可選的:
從本質(zhì)上來(lái)說(shuō),openssl是pem編碼就是在der編碼的技術(shù)上進(jìn)行Base64編碼,然后添加一些頭尾信息組成,可以通過(guò)openssl指令對(duì)der和pem進(jìn)行格式轉(zhuǎn)換。
證書編碼格式
常見(jiàn)的證書編碼格式有三種X.509證書,PKCS#12證書和PKCS#7證書。
X.509證書
最常用的證書格式,它僅包含了公鑰信息而沒(méi)有私鑰信息,一個(gè)openssl簽發(fā)經(jīng)過(guò)PEM編碼的X.509證書格式如下:
-----BEGIN CERTIFICATE-----
XXX
-----END CERTIFICATE-----
中間部分就是經(jīng)過(guò)PEM編碼的X509證書。除了上述形式的頭尾格式,還可能出現(xiàn)以下兩種不同的標(biāo)識(shí)符。
-----BEGIN X.509 CERTIFICATE----
XXX
-----END X.509 CERTIFICATE-----
或者
-----BEGIN TRUSTED CERTIFICATE-----
XXX
-----END TRUSTED CERTIFICATE-----
X.509證書文件的后綴名經(jīng)常是der,cer或者crt。openssl的指令x509提供了對(duì)X.509證書進(jìn)行格式轉(zhuǎn)換的方法。
PKCS#12證書
PKCS12證書可以包含一個(gè)或者多個(gè)證書,并且還可以包含證書對(duì)應(yīng)的私鑰。openssl的pkcs12指令可以將X.509格式的證書和私鑰封裝成PKCS#12格式的證書,也可以將PKCS#12證書轉(zhuǎn)換成X.509證書。
PKCS#12證書的后綴名通常是p12或者pdx。
PKCS#7證書:
PKCS#7可以封裝一個(gè)或者多個(gè)X.509證書或者PKCS#6證書,并且可以包含CRL信息。PKCS#7證書中也不包含私鑰信息。openssl提供了crl2pkcs7和pkcs7兩個(gè)指令來(lái)生成和處理PKCS#7文件,可以使用他們?cè)赬.509證書和PKCS#7證書之間進(jìn)行轉(zhuǎn)換和處理
PKCS#7證書的后綴名是p7b
密鑰編碼
openssl有多種形式的密鑰,openssl提供PEM和DER兩種編碼方式對(duì)這些密鑰進(jìn)行編碼,并提供相關(guān)指令可以使用戶在這兩種格式之間進(jìn)行轉(zhuǎn)換。
openssl密鑰大致可以分為兩種,一種是可以公開(kāi)的,例如公鑰,一種是不能公開(kāi)的,比對(duì)私鑰。反映在編碼上,有的密鑰需要加密,有的密鑰就不需要加密。一個(gè)經(jīng)過(guò)加密的PEM編碼密鑰文件會(huì)在PEM文件中增加一些頭信息,表明密鑰的加密狀態(tài),加密算法及初始化向量等信息
openssl指令提供了對(duì)密鑰加密的功能,并提供了多種可選的對(duì)稱加密算法,比如DES和DES3。當(dāng)對(duì)密鑰進(jìn)行加密的時(shí)候通常需要用戶輸入口令,這里的口令并非直接用來(lái)作為加密的密鑰,而是根據(jù)這個(gè)口令使用一系列HASH操作來(lái)生成一個(gè)用戶加密密鑰數(shù)據(jù)的密鑰。當(dāng)讀取這類密鑰的時(shí)候,同樣需要輸入同樣的口令。
這里再詳細(xì)的說(shuō)下DER和PEM。
DER
DER就是密鑰的二進(jìn)制表述格式。
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格式就是對(duì)DER編碼轉(zhuǎn)碼為base64字符格式。通過(guò)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 是明文格式,可以包含證書或者是密鑰;其內(nèi)容通常是以類似 “—–BEGIN …—–” 開(kāi)頭 “—–END …—–” 為結(jié)尾的這樣的格式進(jìn)行描述的。
因?yàn)镈ER是純二進(jìn)制格式,對(duì)人不友好,所以一般都用PEM進(jìn)行存儲(chǔ)。這里主要介紹PEM格式的公私鑰。
公鑰PEM
PKCS #1
PKCS #1 標(biāo)準(zhǔn)是專門為 RSA 密鑰
進(jìn)行定義的,其對(duì)應(yīng)的 PEM 文件格式如下,
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----
上面的內(nèi)容 BASE64 ENCODED DATA 指的就是 ANS.1 的 DER 的 Base64 編碼。其ASN.1的格式為:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
各個(gè)filed的含義:
- modulus 是RSA的合數(shù)模n。
- publicExponent 是RSA公開(kāi)冪e。
PKCS #8
PKCS#8 標(biāo)準(zhǔn)定義了一個(gè)密鑰格式的通用方案,它不僅僅為 RSA 所使用,同樣也可以被其它密鑰所使用。其對(duì)應(yīng)的PEM文件格式如下:
-----BEGIN PUBLIC KEY-----
BASE64 ENCODED DATA
-----END PUBLIC KEY-----
注意,這里就沒(méi)有 RSA 字樣了,因?yàn)?PKCS#8 是一個(gè)通用型的秘鑰格式方案;其中的 BASE64 ENCODED DATA 所標(biāo)注的內(nèi)容為 PEM 格式中對(duì) DER 原始二進(jìn)制進(jìn)行的 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,但是實(shí)際上,可以看到,它同樣可以用作 Public Key 的格式定義;而 PKCS#8 是站在 PKCS#7 CMS 的基礎(chǔ)之上進(jìn)行編碼格式定義的。
私鑰PEM
PKCS #1
PKCS#1 是專門為 RSA 所涉及的,其對(duì)應(yīng)的 PEM 格式如下:
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----
原始的 DER 格式結(jié)構(gòu),既是 ASN.1 的數(shù)據(jù)結(jié)構(gòu):
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
}
其中各個(gè)字段的含義為:
- version 是版本號(hào),兩個(gè)素?cái)?shù)為0,如果使用了多素?cái)?shù),則版本號(hào)應(yīng)該是1。
Version ::= INTEGER { two-prime(0), multi(1) }
(CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --}) - modulus 是RSA合數(shù)模n。
- publicExponent 是RSA的公開(kāi)冪e。
- privateExponent 是RSA的私有冪d。
- prime1 是n的素?cái)?shù)因子p。
- prime2 是n的素?cái)?shù)因子q。
- exponent1 等于d mod (p ? 1)。
- exponent2 等于d mod (q ? 1)。
- coefficient 是CRT系數(shù) q–1 mod p。
- otherPrimeInfos 按順序包含了其它素?cái)?shù)r3, …, ru的信息。如果version是0 ,它應(yīng)該被忽略;而如果version是1,它應(yīng)該至少包含OtherPrimeInfo的一個(gè)實(shí)例。
PKCS #8
未加密
PEM格式:
-----BEGIN PRIVATE KEY-----
BASE64 ENCODED DATA
-----END PRIVATE KEY-----
對(duì)應(yīng)的DER:
PrivateKeyInfo ::= SEQUENCE {
version Version,
algorithm AlgorithmIdentifier,
PrivateKey BIT STRING
}
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
加密
針對(duì)私鑰的內(nèi)容進(jìn)行加密。
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證書請(qǐng)求時(shí)同時(shí)產(chǎn)生密鑰。
- genpkey除了可以生成RSA密鑰外,還可以生成DSA、DH密鑰。
使用genpkey命令生成RSA私鑰文件,選擇DES-EDE3-CBC算法進(jìn)行加密,口令是1234:
openssl genpkey -algorithm RSA -out privatekey.pem -pass pass:1234 -des-ede3-cbc
生成私鑰文件,其密鑰長(zhǎng)度為1024。
這里還可以直接生成Openssl ASN格式的key:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info:DES-EDE3-CBC,4D5D1AF13367D726
BASE64私鑰內(nèi)容
-----END RSA PRIVATE KEY-----
還有很多格式,比如微軟的PVK等。Openssl ASN格式在加密私鑰數(shù)據(jù)時(shí)只能用MD5算法生成key,而且只迭代計(jì)算了1次。所以從1.0.0開(kāi)始Openssl把PKCS#8格式作為默認(rèn)格式,可以為私鑰文件提供更好的安全性和擴(kuò)展性。
genrsa
生成ASN格式的key;
rsa
生成或者轉(zhuǎn)化為PVK格式:openssl rsa -in privatekey.pem -out privatekey.pvk -outform PVK
。
解析私鑰
使用asn1parse
命令讀取私鑰SAN.1結(jié)構(gòu),其中-i表示使用縮進(jìn)格式。
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 表示節(jié)點(diǎn)在整個(gè)文件中的偏移長(zhǎng)度
- d=0 表示節(jié)點(diǎn)深度
- hl=3 表示節(jié)點(diǎn)頭字節(jié)長(zhǎng)度
- l=135 表示節(jié)點(diǎn)數(shù)據(jù)字節(jié)長(zhǎng)度
- cons 表示該節(jié)點(diǎn)為結(jié)構(gòu)節(jié)點(diǎn),表示包含子節(jié)點(diǎn)或者子結(jié)構(gòu)數(shù)據(jù)
- prim 表示該節(jié)點(diǎn)為原始節(jié)點(diǎn),包含數(shù)據(jù)
- SEQUENCE、OCTET STRING等都是ASN.1中定義的數(shù)據(jù)類型,具體可以參考ASN.1格式說(shuō)明。
最后一個(gè)節(jié)點(diǎn)OCTET STRING [HEX DUMP],就是加密后的私鑰數(shù)據(jù)。