You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Golang中无私钥生成X.509证书签名请求(CSR)?

在Golang中使用远程私钥生成CSR的解决方案

问题背景

需要生成X.509证书签名请求(CSR),但私钥存储在远程安全服务中,无法本地持有。仅能通过API获取PEM编码的公钥,以及通过API调用远程私钥对数据进行签名。尝试用虚拟私钥生成CSR后修改x509.CertificateRequest的PublicKey和Signature字段,但原始ASN.1数据(Raw字段)未更新,无法得到正确的CSR。

核心原因

x509.CertificateRequest的Raw字段是解析CSR时缓存的原始ASN.1字节,修改结构体的其他字段不会自动重新生成Raw数据,且Go标准库未提供重新序列化该结构体的方法。因此必须手动构造CSR的ASN.1结构。

解决方案:手动构造CSR的ASN.1结构

CSR遵循ASN.1 DER编码标准,结构分为三部分:待签名数据(TBSCertRequest)、签名算法、签名值。我们可以手动构造各部分并组合成完整CSR,步骤如下:

  1. 解析远程获取的公钥,将其序列化为SubjectPublicKeyInfo格式的ASN.1数据。
  2. 构造待签名数据(TBSCertRequest),包含CSR版本、主题信息、公钥、扩展属性(如邮箱)。
  3. 调用远程API签名TBS数据,获取符合指定算法的签名值。
  4. 组合完整CSR结构,序列化后编码为PEM格式。

完整代码示例

package main

import (
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/pem"
	"fmt"
	"os"
)

var oidEmailAddress = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}

// 定义CSR对应的ASN.1结构体
type certificateRequest struct {
	TBSCertRequest       tbsCertRequest
	SignatureAlgorithm   pkix.AlgorithmIdentifier
	Signature            asn1.BitString
}

type tbsCertRequest struct {
	Version        int `asn1:"optional,explicit,tag:0"`
	Subject        pkix.Name
	SubjectPKInfo  pkix.RawSubjectPublicKeyInfo
	Attributes     []pkix.AttributeTypeAndValueSET `asn1:"optional,explicit,tag:1"`
}

// 替换为实际的远程签名API调用
func remoteSign(tbsData []byte) ([]byte, error) {
	// 注意:需根据签名算法要求处理数据,例如SHA256WithRSA需先计算SHA256哈希再签名
	// 此处为示例,实际应调用远程私钥服务获取签名
	return []byte("replace-with-actual-signature-from-api"), nil
}

func main() {
	// 1. 解析远程获取的公钥PEM
	pubKeyPEM := []byte(`-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+VvZ6hXtGJ2e7QkL+F5aSf8L
... 替换为实际公钥内容 ...
-----END PUBLIC KEY-----`)
	block, _ := pem.Decode(pubKeyPEM)
	if block == nil || block.Type != "PUBLIC KEY" {
		fmt.Println("Failed to parse public key PEM")
		return
	}
	pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		fmt.Printf("Parse public key error: %v\n", err)
		return
	}

	// 2. 将公钥序列化为SubjectPublicKeyInfo格式
	spkiBytes, err := x509.MarshalPKIXPublicKey(pubKey)
	if err != nil {
		fmt.Printf("Marshal public key error: %v\n", err)
		return
	}
	var spki pkix.RawSubjectPublicKeyInfo
	if _, err := asn1.Unmarshal(spkiBytes, &spki); err != nil {
		fmt.Printf("Unmarshal SPKI error: %v\n", err)
		return
	}

	// 3. 构造CSR主题信息
	subj := pkix.Name{
		CommonName:         "example.com",
		Country:            []string{"AU"},
		Province:           []string{"Some-State"},
		Locality:           []string{"MyCity"},
		Organization:       []string{"Company Ltd"},
		OrganizationalUnit: []string{"IT"},
	}

	// 构造扩展属性(如邮箱)
	attributes := []pkix.AttributeTypeAndValueSET{
		{
			Type: oidEmailAddress,
			Value: []pkix.AttributeTypeAndValue{
				{Type: oidEmailAddress, Value: "test@example.com"},
			},
		},
	}

	// 4. 构造待签名的TBSCertRequest
	tbs := tbsCertRequest{
		Version:       0, // CSR v1版本对应0
		Subject:       subj,
		SubjectPKInfo: spki,
		Attributes:    attributes,
	}
	tbsBytes, err := asn1.Marshal(tbs)
	if err != nil {
		fmt.Printf("Marshal TBS error: %v\n", err)
		return
	}

	// 5. 调用远程API获取签名
	signature, err := remoteSign(tbsBytes)
	if err != nil {
		fmt.Printf("Remote sign error: %v\n", err)
		return
	}

	// 6. 构造完整CSR结构
	sigAlg := pkix.AlgorithmIdentifier{
		Algorithm:  asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}, // SHA256WithRSA
		Parameters: asn1.RawValue{Tag: asn1.TagNull, Bytes: []byte{}},
	}
	csr := certificateRequest{
		TBSCertRequest:     tbs,
		SignatureAlgorithm: sigAlg,
		Signature: asn1.BitString{
			Bytes:     signature,
			BitLength: len(signature) * 8,
		},
	}

	// 7. 序列化CSR并编码为PEM
	csrBytes, err := asn1.Marshal(csr)
	if err != nil {
		fmt.Printf("Marshal CSR error: %v\n", err)
		return
	}
	pem.Encode(os.Stdout, &pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: csrBytes,
	})
}

关键注意事项

  • 签名算法一致性:确保远程签名使用的算法与CSR中指定的SignatureAlgorithm一致(示例中为SHA256WithRSA,OID为1.2.840.113549.1.1.11)。
  • TBS数据处理:远程签名时需严格按照算法要求处理TBS数据,例如SHA256WithRSA需先计算TBS数据的SHA256哈希,再用RSA私钥进行PKCS#1 v1.5签名。
  • 属性构造:若不需要扩展属性(如邮箱),可将Attributes字段设为nil,ASN.1序列化时会自动忽略该可选字段。

内容的提问来源于stack exchange,提问作者Eatay Mizrachi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:39:28