Skip to content

Instantly share code, notes, and snippets.

@lidaobing
Created August 14, 2026 21:11
Show Gist options
  • Select an option

  • Save lidaobing/143884eab3393cfa6bdf0e9d56f161d8 to your computer and use it in GitHub Desktop.

Select an option

Save lidaobing/143884eab3393cfa6bdf0e9d56f161d8 to your computer and use it in GitHub Desktop.
package main
import (
"context"
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"io"
"log"
"os"
kms "cloud.google.com/go/kms/apiv1"
"github.com/pborman/getopt/v2"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)
// BasicConstraintsExtension 对应 ASN.1 BasicConstraints 结构
type BasicConstraintsExtension struct {
IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional,default:-1"`
}
// KMSSigner 实现了 crypto.Signer 接口
type KMSSigner struct {
client *kms.KeyManagementClient
keyVersion string
publicKey crypto.PublicKey
}
func NewKMSSigner(ctx context.Context, client *kms.KeyManagementClient, keyVersion string) (*KMSSigner, error) {
req := &kmspb.GetPublicKeyRequest{Name: keyVersion}
response, err := client.GetPublicKey(ctx, req)
if err != nil {
return nil, fmt.Errorf("failed to get public key from KMS: %w", err)
}
fmt.Println(response.Pem)
block, _ := pem.Decode([]byte(response.Pem))
if block == nil {
return nil, fmt.Errorf("failed to parse PEM block")
}
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse PKIX public key: %w", err)
}
return &KMSSigner{
client: client,
keyVersion: keyVersion,
publicKey: pubKey,
}, nil
}
func (s *KMSSigner) Public() crypto.PublicKey {
return s.publicKey
}
// Sign 实现 crypto.Signer 接口
func (s *KMSSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
ctx := context.Background()
// Ed25519 直接传入原始数据进行签名
req := &kmspb.AsymmetricSignRequest{
Name: s.keyVersion,
Data: digest,
}
result, err := s.client.AsymmetricSign(ctx, req)
if err != nil {
return nil, fmt.Errorf("KMS AsymmetricSign call failed: %w", err)
}
return result.Signature, nil
}
func main() {
var (
help bool
key string
output string = "1.csr" // 默认值
cn string = "CN1"
)
// 注册参数:变量指针, 短选项(char), 长选项(string), 描述
getopt.FlagLong(&help, "help", 'h', "Show help message")
getopt.FlagLong(&key, "key", 'k', "Key URL, sample projects/YOUR_PROJECT/locations/global/keyRings/YOUR_KEYRING/cryptoKeys/YOUR_KEY/cryptoKeyVersions/1")
getopt.FlagLong(&output, "output", 'o', "output CSR file path")
getopt.FlagLong(&cn, "cn", 0, "Cert Subject CN")
// 解析命令行参数
getopt.Parse()
// 处理帮助请求
if help {
getopt.Usage()
os.Exit(0)
}
if key == "" {
log.Printf("key is required")
getopt.Usage()
os.Exit(1)
}
ctx := context.Background()
kmsClient, err := kms.NewKeyManagementClient(ctx)
if err != nil {
log.Fatalf("Failed to create KMS client: %v", err)
}
defer kmsClient.Close()
signer, err := NewKMSSigner(ctx, kmsClient, key)
if err != nil {
log.Fatalf("Failed to create KMS Signer: %v", err)
}
// 1. 手动序列化 Basic Constraints (CA: TRUE)
// OID 2.5.29.19 即是 id-ce-basicConstraints
basicConstraintsVal, err := asn1.Marshal(BasicConstraintsExtension{
IsCA: true,
MaxPathLen: 0, // 限制该 Sub-CA 不能再签发下一级 Intermediate CA(根据需求调整,不限制可设为 -1)
})
if err != nil {
log.Fatalf("Failed to marshal basic constraints: %v", err)
}
// 2. 定义包含 CA 扩展属性的 CSR Template
csrTemplate := x509.CertificateRequest{
Subject: pkix.Name{
CommonName: cn,
},
ExtraExtensions: []pkix.Extension{
{
Id: asn1.ObjectIdentifier{2, 5, 29, 19}, // OID for BasicConstraints
Critical: true, // CA 扩展必须设为 Critical
Value: basicConstraintsVal,
},
{
Id: asn1.ObjectIdentifier{2, 5, 29, 15}, // OID for KeyUsage
Critical: true,
// KeyCertSign (4) | CRLSign (2) => 0x06
Value: []byte{0x03, 0x02, 0x01, 0x06},
},
},
}
// 3. 调用 x509 库发起请求并通过 KMS 签名生成 CSR
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, signer)
if err != nil {
log.Fatalf("Failed to create CSR: %v", err)
}
// 4. 导出 PEM
csrPem := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csrBytes,
})
fmt.Println("Successfully generated SubCA CSR with CA:TRUE using Cloud KMS Ed25519 Key:")
fmt.Println(string(csrPem))
_ = os.WriteFile(output, csrPem, 0644)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment