各种密钥格式简介
两种编码方式:
-
.der:用ASN.1语法编码的der格式;
-
.pem:用BASE64编码的密钥;
# ASN.1 ------(序列化)------ DER ------(Base64编码)------ PEM
主流密钥文件后缀:
-
.cer,.cert:Windows证书,存放公钥,没有私钥;
-
.crt:Linux证书,,存放公钥,没有私钥;
-
.key, 一般是私钥
其它格式
- .pfx, p12:der格式,存放公钥和加密私钥,主要用于windows平台,浏览器可以使用,
openssl pkcs12 -info -nocerts -in keystore.p12
- .X509 证书
openssl x509 -help
- .csr,Certificate Signing Request,即证书签名请求文件,把CSR文件提交给证书颁发机构后,证书颁发机构使用其根证书私钥签名就生成了证书公钥文件,也就是颁发给用户的证书。
- .jks,java 密钥库. 同时包含证书和私钥,一般有密码保护。可以由p12转换而来。
keytool -v -list -keystore file.jks
可以将多级证书导入同一个证书文件中,形成一个包含完整证书链的证书
PyCryptodome支持的非对称密钥格式如下
RSA public key:
- X.509 certificate (binary or PEM format)
- X.509
subjectPublicKeyInfo
DER SEQUENCE (binary or PEM encoding) - PKCS#1
RSAPublicKey
DER SEQUENCE (binary or PEM encoding) - An OpenSSH line (e.g. the content of
~/.ssh/id_ecdsa
, ASCII)
RSA private key:
- PKCS#1
RSAPrivateKey
DER SEQUENCE (binary or PEM encoding) - PKCS#8
PrivateKeyInfo
orEncryptedPrivateKeyInfo
DER SEQUENCE (binary or PEM encoding) - OpenSSH (text format, introduced in OpenSSH 6.5)
DER
ASN.1 defines the following rule sets that govern how data structures that are being sent between computers are encoded and decoded.
- Basic Encoding Rules (BER)
- Canonical Encoding Rules (CER), subsets of BER
- Distinguished Encoding Rules (DER), subsets of BER
- Packed Encoding Rules (PER)
CER and DER were developed later as specialized subsets of BER.
PER was developed in response to criticisms about the amount of bandwidth required to transmit data using BER or its variants. PER provides a significant savings.
DER was created to satisfy the requirements of the X.509 specification for secure data transfer.
DER文件后缀通常为 “.der” 和 “.cer”
各个公钥算法文档都会给出密钥的DER格式,如RFC 8017: PKCS #1附录Appendix A部分给出的私钥格式:
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
}
PyCryptodome源码参考
\Crypto\PublicKey\RSA.py
from Crypto.Util.asn1 import DerSequence
#: `Object ID`_ for the RSA encryption algorithm. This OID often indicates
#: a generic RSA key, even when such key will be actually used for digital
#: signatures.
#:
#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html
oid = "1.2.840.113549.1.1.1"
def export_key(self, format='PEM', passphrase=None, pkcs=1,
protection=None, randfunc=None):
# ...
# DER format is always used, even in case of PEM, which simply
# encodes it into BASE64.
if self.has_private():
binary_key = DerSequence([0,
self.n,
self.e,
self.d,
self.p,
self.q,
self.d % (self.p-1),
self.d % (self.q-1),
Integer(self.q).inverse(self.p)
]).encode()
if pkcs == 1:
key_type = 'RSA PRIVATE KEY'
if format == 'DER' and passphrase:
raise ValueError("PKCS#1 private key cannot be encrypted")
else: # PKCS#8
from Crypto.IO import PKCS8
if format == 'PEM' and protection is None:
key_type = 'PRIVATE KEY'
binary_key = PKCS8.wrap(binary_key, oid, None)
else:
key_type = 'ENCRYPTED PRIVATE KEY'
if not protection:
protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
binary_key = PKCS8.wrap(binary_key, oid,
passphrase, protection)
passphrase = None
else: # if self.has_private():
key_type = "PUBLIC KEY"
binary_key = _create_subject_public_key_info(oid,
DerSequence([self.n,
self.e])
)
if format == 'DER':
return binary_key
if format == 'PEM':
from Crypto.IO import PEM
pem_str = PEM.encode(binary_key, key_type, passphrase, randfunc)
return tobytes(pem_str)
以上封装支持pkcs1和pkcs8两种,pkcs8封装源码如下:
if key_params is None:
key_params = DerNull()
#
# PrivateKeyInfo ::= SEQUENCE {
# version Version,
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
# privateKey PrivateKey,
# attributes [0] IMPLICIT Attributes OPTIONAL
# }
#
pk_info = DerSequence([
0,
DerSequence([
DerObjectId(key_oid),
key_params
]),
DerOctetString(private_key)
])
pk_info_der = pk_info.encode()
if passphrase is None:
return pk_info_der
if not passphrase:
raise ValueError("Empty passphrase")
# Encryption with PBES2
passphrase = tobytes(passphrase)
if protection is None:
protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
return PBES2.encrypt(pk_info_der, passphrase,
protection, prot_params, randfunc)
PEM
PEM,Privacy Enhanced Mail
以下为OpenSSL接口文档对私钥pem的介绍。
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,3F17F5316E2BAC89
...base64 encoded data...
-----END RSA PRIVATE KEY-----
The line beginning with Proc-Type contains the version and the protection on the encapsulated data. The line beginning DEK-Info contains two comma separated values: the encryption algorithm name as used by EVP_get_cipherbyname() and an initialization vector used by the cipher encoded as a set of hexadecimal digits. After those two lines is the base64-encoded encrypted data.
文件后缀通常为".pem"、“.cer”、“.crt”、“.key”.
OpenSSL命令操作
环境:ubuntu 20.04
Windows下会有乱码
# 获取baidu证书
openssl s_client -connect baidu.com:443 < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > baidu.crt
head .\baidu.crt -n 20
openssl x509 -in baidu.crt -text -noout
# 转der
openssl x509 -outform der -in baidu.crt -out baidu.der
# 提取公钥
openssl x509 -in baidu.crt -pubkey -noout > baidu.crt
cat baidu.key
#-----BEGIN PUBLIC KEY-----
#MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn67f1NLdKwi96kdzB+/W
#...
参考资料
RSA — PyCryptodome 3.17.0 documentation
Distinguished Encoding Rules - Win32 apps | Microsoft Learn
PEM: