如何在Golang中为证书签名请求(CSR)添加keyUsage扩展
问题解答
1. 你当前的实现存在错误,不符合RFC5280规范
核心问题是位序不匹配:
- Go标准库
x509.KeyUsage系列常量按小端位序定义,KeyUsageCertSign的值为1 << 5 = 0x20(二进制00100000) - RFC5280定义的KeyUsage BIT STRING按大端位序定义,
keyCertSign对应第5位(从0开始计数),对应的二进制值应为00000100(值为0x04)
直接序列化x509.KeyUsageCertSign的话,最终CSR会被解析为携带keyEncipherment用法,和你的预期不符。
2. 更简便的实现方案
目前标准库没有公开提供直接给CSR添加KeyUsage扩展的工具方法,你可以选择以下两种方案实现:
方案一:复用标准库能力,无需自行处理位序列化
你可以借助标准库生成证书的逻辑间接得到正确的KeyUsage扩展,完全避免自己处理位序和ASN.1序列化:
// 临时证书模板,仅需设置需要的KeyUsage tempCertTemplate := &x509.Certificate{ KeyUsage: x509.KeyUsageCertSign, } // 生成临时自签名证书,仅用于提取正确序列化的扩展 tempDER, err := x509.CreateCertificate(rand.Reader, tempCertTemplate, tempCertTemplate, privateKey.Public(), privateKey) if err != nil { // 处理错误 } // 解析临时证书 tempCert, err := x509.ParseCertificate(tempDER) if err != nil { // 处理错误 } // 提取KeyUsage扩展 var keyUsageExt pkix.Extension for _, ext := range tempCert.Extensions { if ext.Id.Equal([]int{2, 5, 29, 15}) { keyUsageExt = ext break } } // 直接添加到CSR模板即可 csrTemplate := x509.CertificateRequest{ SignatureAlgorithm: x509.SHA256WithRSA, // 替换为你的实际算法 PublicKeyAlgorithm: x509.RSA, // 替换为你的实际公钥算法 PublicKey: publicKey, // 替换为你的实际公钥 Subject: pkix.Name{CommonName: "你的证书主体名称"}, ExtraExtensions: []pkix.Extension{keyUsageExt}, }
方案二:自行实现位转换,性能更好
如果对性能有要求,可以自行实现符合规范的转换逻辑:
// keyUsageToASN1BitString 将x509.KeyUsage转换为符合RFC5280规范的ASN.1 BitString func keyUsageToASN1BitString(ku x509.KeyUsage) asn1.BitString { var ret asn1.BitString ret.BitLength = 9 ret.Bytes = make([]byte, 2) if ku&x509.KeyUsageDigitalSignature != 0 { ret.Bytes[0] |= 0x80 } if ku&x509.KeyUsageContentCommitment != 0 { ret.Bytes[0] |= 0x40 } if ku&x509.KeyUsageKeyEncipherment != 0 { ret.Bytes[0] |= 0x20 } if ku&x509.KeyUsageDataEncipherment != 0 { ret.Bytes[0] |= 0x10 } if ku&x509.KeyUsageKeyAgreement != 0 { ret.Bytes[0] |= 0x08 } if ku&x509.KeyUsageCertSign != 0 { ret.Bytes[0] |= 0x04 } if ku&x509.KeyUsageCRLSign != 0 { ret.Bytes[0] |= 0x02 } if ku&x509.KeyUsageEncipherOnly != 0 { ret.Bytes[0] |= 0x01 } if ku&x509.KeyUsageDecipherOnly != 0 { ret.Bytes[1] |= 0x80 } // 优化字节长度 for ret.BitLength > 0 && ret.Bytes[(ret.BitLength-1)/8]&(1<<(7-(ret.BitLength-1)%8)) == 0 { ret.BitLength-- } if ret.BitLength == 0 { ret.Bytes = nil return ret } ret.Bytes = ret.Bytes[:(ret.BitLength+7)/8] return ret }
使用方式如下:
asn1KeyUsage, err := asn1.Marshal(keyUsageToASN1BitString(x509.KeyUsageCertSign)) if err != nil { // 处理错误 } csrTemplate.ExtraExtensions = []pkix.Extension{ { Id: []int{2, 5, 29, 15}, Critical: true, Value: asn1KeyUsage, }, }
3. 规范符合验证与测试方法
生成CSR后,直接用openssl命令即可验证扩展是否符合预期:
openssl req -in 你的CSR文件路径.pem -text -noout
在输出的Requested Extensions部分,看到如下内容就说明配置正确:
X509v3 Key Usage: critical Certificate Sign
内容的提问来源于stack exchange,提问作者mipnw
相关产品推荐
相关产品推荐

