certs代码
处理 certs 的相关逻辑
certs 相关的逻辑实现在文件 pkg/sentry/certs/certs.go
中。
准备工作
常量定义
const (
BlockTypeCertificate = "CERTIFICATE"
BlockTypeECPrivateKey = "EC PRIVATE KEY"
BlockTypePKCS1PrivateKey = "RSA PRIVATE KEY"
BlockTypePKCS8PrivateKey = "PRIVATE KEY"
)
备注:这里的常量定义和 csr.go 中的有部分重复。
结构体定义
Credentials 结构体包含一个证书和一个 私钥:
// Credentials holds a certificate and private key.
type Credentials struct {
PrivateKey crypto.PrivateKey
Certificate *x509.Certificate
}
实现
解码 PEM key
DecodePEMKey() 接收一个 PEM key 字节数组并返回一个代表 RSA 或 EC 私钥 的对象:
func DecodePEMKey(key []byte) (crypto.PrivateKey, error) {
// 解码 pem key
block, _ := pem.Decode(key)
if block == nil {
return nil, errors.New("key is not PEM encoded")
}
// 按照类型进行后续解析处理
switch block.Type {
case BlockTypeECPrivateKey:
// EC Private Key
return x509.ParseECPrivateKey(block.Bytes)
case BlockTypePKCS1PrivateKey:
// PKCS1 Private Key
return x509.ParsePKCS1PrivateKey(block.Bytes)
case BlockTypePKCS8PrivateKey:
// PKCS8 Private Key
return x509.ParsePKCS8PrivateKey(block.Bytes)
default:
return nil, fmt.Errorf("unsupported block type %s", block.Type)
}
}
解码 PEM 证书
DecodePEMCertificates() 方法接收一个 PEM 编码的 x509 证书字节数组,并以 x509.Certificate 对象片断的方式返回所有证书:
func DecodePEMCertificates(crtb []byte) ([]*x509.Certificate, error) {
certs := []*x509.Certificate{}
// crtb 数组可能包含多个证书
for len(crtb) > 0 {
var err error
var cert *x509.Certificate
// 解码单个 pem 证书
cert, crtb, err = decodeCertificatePEM(crtb)
if err != nil {
return nil, err
}
if cert != nil {
// it's a cert, add to pool
certs = append(certs, cert)
}
}
return certs, nil
}
decodeCertificatePEM() 方法解码单个 pem 证书:
func decodeCertificatePEM(crtb []byte) (*x509.Certificate, []byte, error) {
// 执行pem 解码
// pem.Decode() 方法将在输入中找到下一个 PEM 格式的块(证书,私钥 等)的输入。
// 它返回该块和输入的其余部分。
// 注意是返回剩余部分,当没有更多部分时,返回的长度为0
// 如果没有找到PEM数据,则返回 block 为nil,其余部分返回整个输入。
block, crtb := pem.Decode(crtb)
if block == nil {
return nil, crtb, errors.New("invalid PEM certificate")
}
if block.Type != BlockTypeCertificate {
return nil, nil, nil
}
// 解码 x509 证书
c, err := x509.ParseCertificate(block.Bytes)
return c, crtb, err
}
生成基础证书的第一步就是生成其他证书。
从文件中获取 PEM 凭证
PEMCredentialsFromFiles() 方法接收一个密钥/证书对的路径,并返回一个经过验证的Credentials包装器:
func PEMCredentialsFromFiles(certPem, keyPem []byte) (*Credentials, error) {
// 解码 PEM key
pk, err := DecodePEMKey(keyPem)
if err != nil {
return nil, err
}
// 解码 PEM 证书
// 如果有多个证书,实际后续只使用多个证书中的第一个
crts, err := DecodePEMCertificates(certPem)
if err != nil {
return nil, err
}
if len(crts) == 0 {
return nil, errors.New("no certificates found")
}
// 检查私钥和证书的 PublicKey 是否匹配
match := matchCertificateAndKey(pk, crts[0])
if !match {
return nil, errors.New("error validating credentials: public and private key pair do not match")
}
// 构建 Credentials 结构体并返回
creds := &Credentials{
PrivateKey: pk,
Certificate: crts[0],
}
return creds, nil
}
matchCertificateAndKey() 方法检查私钥和证书的 PublicKey 是否匹配 :
func matchCertificateAndKey(pk any, cert *x509.Certificate) bool {
// 根据私钥的类型进行匹配
// 实际是根据私钥类型的不同,获取到 cert 相应的 PublicKey,然后和私钥的 PublicKey 对比看是否相同
switch key := pk.(type) {
case *ecdsa.PrivateKey:
// ecdsa PrivateKey
if cert.PublicKeyAlgorithm != x509.ECDSA {
return false
}
pub, ok := cert.PublicKey.(*ecdsa.PublicKey)
return ok && pub.Equal(key.Public())
case *rsa.PrivateKey:
// rsa PrivateKey
if cert.PublicKeyAlgorithm != x509.RSA {
return false
}
pub, ok := cert.PublicKey.(*rsa.PublicKey)
return ok && pub.Equal(key.Public())
case ed25519.PrivateKey:
// ed25519 Private Key
if cert.PublicKeyAlgorithm != x509.Ed25519 {
return false
}
pub, ok := cert.PublicKey.(ed25519.PublicKey)
return ok && pub.Equal(key.Public())
default:
return false
}
}
从 PEM 创建 cert pool
CertPoolFromPEM() 方法从一个 PEM 编码的证书字符串返回一个 CertPool
func CertPoolFromPEM(certPem []byte) (*x509.CertPool, error) {
// 解码 PEM 证书
certs, err := DecodePEMCertificates(certPem)
if err != nil {
return nil, err
}
if len(certs) == 0 {
return nil, errors.New("no certificates found")
}
// 从多个证书中创建 cert pool
return certPoolFromCertificates(certs), nil
}
certPoolFromCertificates() 方法的实现很简单:
func certPoolFromCertificates(certs []*x509.Certificate) *x509.CertPool {
// 创建 cert pool
pool := x509.NewCertPool()
for _, c := range certs {
// 将每个证书添加到 pool
pool.AddCert(c)
}
return pool
}
解析 PRM CSR
ParsePemCSR() 使用给定的 PEM 编码的证书签名请求构建一个 x509 证书请求:
func ParsePemCSR(csrPem []byte) (*x509.CertificateRequest, error) {
// pem 解码
block, _ := pem.Decode(csrPem)
if block == nil {
return nil, errors.New("certificate signing request is not properly encoded")
}
// 尝试 x509 解码证书请求
csr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse X.509 certificate signing request: %w", err)
}
return csr, nil
}
生成 ECP 私钥
GenerateECPrivateKey() 方法返回一个新的 ECP 私钥:
func GenerateECPrivateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}
这里涉及很多 x509 相关的领域知识。