密钥格式梳理

本文介绍了密钥的不同格式,包括DER和PEM编码,以及它们在ASN.1、X.509证书、RSA密钥对中的使用。讨论了.cer,.crt,.key等文件后缀的含义,并提到了.PFX和.p12容器。PyCryptodome库在处理RSA密钥时的源码被引用,展示了如何进行密钥的编码和解码。此外,还介绍了OpenSSL命令行工具在处理证书和密钥操作中的用法。
摘要由CSDN通过智能技术生成

各种密钥格式简介

两种编码方式:

  • .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 or EncryptedPrivateKeyInfo 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:

/docs/man3.0/man1/openssl-s_client.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值