From 6619c79a5bc0a4984f0c43b0651552e77a1c6361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=94=E7=BB=B4=E5=9B=BD?= Date: Mon, 26 Jun 2023 13:35:56 +0800 Subject: [PATCH] Add DER support for gernerating and parsing CSR --- csr/csr.go | 15 +++++++++++-- csr/csr_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++ helpers/helpers.go | 17 +++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/csr/csr.go b/csr/csr.go index c88a16c07..52039326b 100644 --- a/csr/csr.go +++ b/csr/csr.go @@ -408,9 +408,9 @@ func Regenerate(priv crypto.Signer, csr []byte) ([]byte, error) { return x509.CreateCertificateRequest(rand.Reader, req, priv) } -// Generate creates a new CSR from a CertificateRequest structure and +// GenerateDER creates a new CSR(ASN1 DER encoded) from a CertificateRequest structure and // an existing key. The KeyRequest field is ignored. -func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) { +func GenerateDER(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) { sigAlgo := helpers.SignerAlgo(priv) if sigAlgo == x509.UnknownSignatureAlgorithm { return nil, cferr.New(cferr.PrivateKeyError, cferr.Unavailable) @@ -466,6 +466,17 @@ func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err erro err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err) return } + return +} + +// Generate creates a new CSR(PEM encoded) from a CertificateRequest structure and +// an existing key. The KeyRequest field is ignored. +func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) { + + csr, err = GenerateDER(priv, req) + if err != nil { + return + } block := pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csr, diff --git a/csr/csr_test.go b/csr/csr_test.go index e125ac0a0..ebe2552a9 100644 --- a/csr/csr_test.go +++ b/csr/csr_test.go @@ -705,6 +705,59 @@ func TestGenerate(t *testing.T) { } } +func TestGenerateASN1(t *testing.T) { + var req = &CertificateRequest{ + Names: []Name{ + { + C: "US", + ST: "California", + L: "San Francisco", + O: "CloudFlare", + OU: "Systems Engineering", + }, + }, + CN: "cloudflare.com", + Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1", "jdoe@example.com", "https://www.cloudflare.com"}, + KeyRequest: &KeyRequest{"ecdsa", 256}, + } + + key, err := req.KeyRequest.Generate() + if err != nil { + t.Fatalf("%v", err) + } + + priv, ok := key.(crypto.Signer) + if !ok { + t.Fatal("Private key is not a signer.") + } + + csrDER, err := GenerateDER(priv, req) + if err != nil { + t.Fatalf("%v", err) + } + + csr, err := helpers.ParseCSRDER(csrDER) + if err != nil { + t.Fatalf("%v", err) + } + + if len(csr.DNSNames) != 2 { + t.Fatal("SAN parsing error") + } + + if len(csr.IPAddresses) != 1 { + t.Fatal("SAN parsing error") + } + + if len(csr.EmailAddresses) != 1 { + t.Fatal("SAN parsing error") + } + + if len(csr.URIs) != 1 { + t.Fatal("SAN parsing error") + } +} + // TestReGenerate ensures Regenerate() is abel to use the provided CSR as a template for signing a new // CSR using priv. func TestReGenerate(t *testing.T) { diff --git a/helpers/helpers.go b/helpers/helpers.go index 3b4dfe724..e721e352c 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -457,6 +457,23 @@ func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) { return csrObject, nil } +// ParseCSRDER parses a PEM-encoded certificate signing request. +// It does not check the signature. This is useful for dumping data from a CSR +// locally. +func ParseCSRDER(csrDER []byte) (*x509.CertificateRequest, error) { + csrObject, err := x509.ParseCertificateRequest(csrDER) + if err != nil { + return nil, err + } + + err = csrObject.CheckSignature() + if err != nil { + return nil, err + } + + return csrObject, nil +} + // SignerAlgo returns an X.509 signature algorithm from a crypto.Signer. func SignerAlgo(priv crypto.Signer) x509.SignatureAlgorithm { switch pub := priv.Public().(type) {