diff --git a/modules/caddypki/ca.go b/modules/caddypki/ca.go index c778e0364..69804675e 100644 --- a/modules/caddypki/ca.go +++ b/modules/caddypki/ca.go @@ -286,7 +286,7 @@ func (ca CA) loadOrGenRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, } if rootCert == nil { - rootCert, err = pemDecodeSingleCert(rootCertPEM) + rootCert, err = pemDecodeCertificate(rootCertPEM) if err != nil { return nil, nil, fmt.Errorf("parsing root certificate PEM: %v", err) } diff --git a/modules/caddypki/crypto.go b/modules/caddypki/crypto.go index 8328aa7cc..bfc02c77f 100644 --- a/modules/caddypki/crypto.go +++ b/modules/caddypki/crypto.go @@ -30,7 +30,7 @@ import ( "go.step.sm/crypto/pemutil" ) -func pemDecodeSingleCert(pemDER []byte) (*x509.Certificate, error) { +func pemDecodeCertificate(pemDER []byte) (*x509.Certificate, error) { pemBlock, remaining := pem.Decode(pemDER) if pemBlock == nil { return nil, fmt.Errorf("no PEM block found") diff --git a/modules/caddypki/crypto_test.go b/modules/caddypki/crypto_test.go index 0e3bc4a72..a07763d14 100644 --- a/modules/caddypki/crypto_test.go +++ b/modules/caddypki/crypto_test.go @@ -144,7 +144,7 @@ func TestKeyPair_Load(t *testing.T) { t.Fatalf("Failed loading KeyPair: %v", err) } if len(chain) != 2 { - t.Errorf("Expected 1 certificate in chain; got %d", len(chain)) + t.Errorf("Expected 2 certificates in chain; got %d", len(chain)) } if signer == nil { t.Error("Expected signer to be returned") @@ -168,3 +168,147 @@ func TestKeyPair_Load(t *testing.T) { } }) } + +func Test_pemDecodeCertificate(t *testing.T) { + signer, err := keyutil.GenerateDefaultSigner() + if err != nil { + t.Fatalf("Failed creating signer: %v", err) + } + + tmpl := &x509.Certificate{ + Subject: pkix.Name{CommonName: "test-cert"}, + IsCA: true, + MaxPathLen: 3, + } + derBytes, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, signer.Public(), signer) + if err != nil { + t.Fatalf("Creating root certificate failed: %v", err) + } + cert, err := x509.ParseCertificate(derBytes) + if err != nil { + t.Fatalf("Parsing root certificate failed: %v", err) + } + + pemBlock, err := pemutil.Serialize(cert) + if err != nil { + t.Fatalf("Failed serializing certificate: %v", err) + } + pemData := pem.EncodeToMemory(pemBlock) + + t.Run("ok", func(t *testing.T) { + cert, err := pemDecodeCertificate(pemData) + if err != nil { + t.Fatalf("Failed decoding PEM data: %v", err) + } + if cert == nil { + t.Errorf("Expected a certificate in PEM data") + } + }) + + t.Run("fail/no-pem-data", func(t *testing.T) { + cert, err := pemDecodeCertificate(nil) + if err == nil { + t.Fatalf("Expected pemDecodeCertificate to return an error") + } + if cert != nil { + t.Errorf("Expected pemDecodeCertificate to return nil") + } + }) + + t.Run("fail/multiple", func(t *testing.T) { + multiplePEMData := append(pemData, pemData...) + cert, err := pemDecodeCertificate(multiplePEMData) + if err == nil { + t.Fatalf("Expected pemDecodeCertificate to return an error") + } + if cert != nil { + t.Errorf("Expected pemDecodeCertificate to return nil") + } + }) + + t.Run("fail/no-pem-certificate", func(t *testing.T) { + pkData := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: []byte("some-bogus-private-key"), + }) + cert, err := pemDecodeCertificate(pkData) + if err == nil { + t.Fatalf("Expected pemDecodeCertificate to return an error") + } + if cert != nil { + t.Errorf("Expected pemDecodeCertificate to return nil") + } + }) +} + +func Test_pemDecodeCertificateChain(t *testing.T) { + signer, err := keyutil.GenerateDefaultSigner() + if err != nil { + t.Fatalf("Failed creating signer: %v", err) + } + + tmpl := &x509.Certificate{ + Subject: pkix.Name{CommonName: "test-cert"}, + IsCA: true, + MaxPathLen: 3, + } + derBytes, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, signer.Public(), signer) + if err != nil { + t.Fatalf("Creating root certificate failed: %v", err) + } + cert, err := x509.ParseCertificate(derBytes) + if err != nil { + t.Fatalf("Parsing root certificate failed: %v", err) + } + + pemBlock, err := pemutil.Serialize(cert) + if err != nil { + t.Fatalf("Failed serializing certificate: %v", err) + } + pemData := pem.EncodeToMemory(pemBlock) + + t.Run("ok/single", func(t *testing.T) { + certs, err := pemDecodeCertificateChain(pemData) + if err != nil { + t.Fatalf("Failed decoding PEM data: %v", err) + } + if len(certs) != 1 { + t.Errorf("Expected 1 certificate in PEM data; got %d", len(certs)) + } + }) + + t.Run("ok/multiple", func(t *testing.T) { + multiplePEMData := append(pemData, pemData...) + certs, err := pemDecodeCertificateChain(multiplePEMData) + if err != nil { + t.Fatalf("Failed decoding PEM data: %v", err) + } + if len(certs) != 2 { + t.Errorf("Expected 2 certificates in PEM data; got %d", len(certs)) + } + }) + + t.Run("fail/no-pem-certificate", func(t *testing.T) { + pkData := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: []byte("some-bogus-private-key"), + }) + certs, err := pemDecodeCertificateChain(pkData) + if err == nil { + t.Fatalf("Expected pemDecodeCertificateChain to return an error") + } + if len(certs) != 0 { + t.Errorf("Expected 0 certificates in PEM data; got %d", len(certs)) + } + }) + + t.Run("fail/no-der-certificate", func(t *testing.T) { + certs, err := pemDecodeCertificateChain([]byte("invalid-der-data")) + if err == nil { + t.Fatalf("Expected pemDecodeCertificateChain to return an error") + } + if len(certs) != 0 { + t.Errorf("Expected 0 certificates in PEM data; got %d", len(certs)) + } + }) +}