使用spacemonkeygo/openssl签名证书时SKID与AKID异常问题排查
问题分析与解决:spacemonkeygo/openssl签名证书SKID异常导致自签发判定
问题根源
- SKID被错误复用:spacemonkeygo/openssl的
SetIssuer方法存在默认逻辑漏洞——直接将颁发者证书的Subject Key Identifier(SKID)复用为新证书的SKID,导致新证书的SKID与颁发者KeyID完全一致,触发自签发判定。 - 缺失颁发者标识扩展:如果不调用
SetIssuer,新证书的SKID虽能正常生成,但会缺少必要的Authority Key Identifier(AKID)扩展,导致证书链验证不通过,最终显示无效。
解决方法
核心思路是手动控制SKID和AKID扩展的生成,绕过库的默认错误逻辑,确保两个扩展的取值符合X.509规范:
1. 手动生成并设置SKID扩展
从新证书的公钥计算SKID(标准逻辑是取公钥DER编码的SHA-1哈希前20字节),通过AddExtension手动添加,覆盖库的默认错误值。
2. 正确配置AKID扩展
从颁发者证书中提取其SKID作为AKID的keyIdentifier字段,同时补充颁发者的DN和序列号,确保验证逻辑能正确识别证书链。
示例代码
package main import ( "crypto/sha1" "fmt" "strings" "github.com/spacemonkeygo/openssl" ) func SignCSR(csr *openssl.CertificateRequest, issuerCert *openssl.Certificate, issuerKey *openssl.PrivateKey, serial []byte, notBefore, notAfter openssl.Asn1Time) (*openssl.Certificate, error) { cert, err := openssl.NewCertificateFromCSR(csr) if err != nil { return nil, err } // 设置基础证书字段 cert.SetIssuer(issuerCert.SubjectName()) cert.SetSubject(csr.Subject()) cert.SetNotBefore(notBefore) cert.SetNotAfter(notAfter) cert.SetSerialNumber(serial) // 手动生成SKID:从新证书公钥计算 pubKey, err := cert.PublicKey() if err != nil { return nil, err } pubKeyDER, err := pubKey.MarshalPKIXPublicKey() if err != nil { return nil, err } skidHash := sha1.Sum(pubKeyDER) skidExt, err := openssl.NewExtension("subjectKeyIdentifier", fmt.Sprintf("hash:%x", skidHash[:])) if err != nil { return nil, err } cert.AddExtension(skidExt) // 手动生成AKID:从颁发者证书提取SKID,补充颁发者信息 issuerSkidExt, err := issuerCert.GetExtension("subjectKeyIdentifier") if err != nil { return nil, err } issuerSkid := strings.TrimPrefix(issuerSkidExt.Value(), "hash:") akidValue := fmt.Sprintf("keyid:%s,issuer:dirName:%s,serial:%x", issuerSkid, issuerCert.SubjectName().String(), issuerCert.SerialNumber().Bytes()) akidExt, err := openssl.NewExtension("authorityKeyIdentifier", akidValue) if err != nil { return nil, err } cert.AddExtension(akidExt) // 用颁发者私钥签名证书 if err := cert.Sign(issuerKey, openssl.EVP_SHA256); err != nil { return nil, err } return cert, nil }
验证方法
签名完成后,用OpenSSL命令检查证书字段:
openssl x509 -in signed_cert.pem -text -noout
确认:
Subject Key Identifier与颁发者证书的Subject Key Identifier不同Authority Key Identifier中的keyid与颁发者证书的SKID一致,且包含正确的颁发者DN和序列号
内容的提问来源于stack exchange,提问作者Sergey Kolesnik
相关产品推荐
相关产品推荐

