mirror of
https://github.com/caddyserver/caddy.git
synced 2025-12-08 06:09:53 +00:00
caddypki: Add support for multiple intermediates in signing chain
This commit is contained in:
parent
786d537877
commit
c2a2626f86
9 changed files with 570 additions and 64 deletions
258
modules/caddytls/internalissuer_test.go
Normal file
258
modules/caddytls/internalissuer_test.go
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddypki"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"go.step.sm/crypto/keyutil"
|
||||
"go.step.sm/crypto/pemutil"
|
||||
)
|
||||
|
||||
func TestInternalIssuer_Issue(t *testing.T) {
|
||||
rootSigner, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatalf("Creating root signer failed: %v", err)
|
||||
}
|
||||
|
||||
tmpl := &x509.Certificate{
|
||||
Subject: pkix.Name{CommonName: "test-root"},
|
||||
IsCA: true,
|
||||
MaxPathLen: 3,
|
||||
}
|
||||
rootBytes, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, rootSigner.Public(), rootSigner)
|
||||
if err != nil {
|
||||
t.Fatalf("Creating root certificate failed: %v", err)
|
||||
}
|
||||
|
||||
root, err := x509.ParseCertificate(rootBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Parsing root certificate failed: %v", err)
|
||||
}
|
||||
|
||||
firstIntermediateSigner, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatalf("Creating intermedaite signer failed: %v", err)
|
||||
}
|
||||
|
||||
firstIntermediateBytes, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{
|
||||
Subject: pkix.Name{CommonName: "test-first-intermediate"},
|
||||
IsCA: true,
|
||||
MaxPathLen: 2,
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
}, root, firstIntermediateSigner.Public(), rootSigner)
|
||||
if err != nil {
|
||||
t.Fatalf("Creating intermediate certificate failed: %v", err)
|
||||
}
|
||||
|
||||
firstIntermediate, err := x509.ParseCertificate(firstIntermediateBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Parsing intermediate certificate failed: %v", err)
|
||||
}
|
||||
|
||||
secondIntermediateSigner, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatalf("Creating second intermedaite signer failed: %v", err)
|
||||
}
|
||||
|
||||
secondIntermediateBytes, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{
|
||||
Subject: pkix.Name{CommonName: "test-second-intermediate"},
|
||||
IsCA: true,
|
||||
MaxPathLen: 2,
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
}, firstIntermediate, secondIntermediateSigner.Public(), firstIntermediateSigner)
|
||||
if err != nil {
|
||||
t.Fatalf("Creating second intermediate certificate failed: %v", err)
|
||||
}
|
||||
|
||||
secondIntermediate, err := x509.ParseCertificate(secondIntermediateBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Parsing second intermediate certificate failed: %v", err)
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
storageDir := filepath.Join(dir, "certmagic")
|
||||
rootCertFile := filepath.Join(dir, "root.pem")
|
||||
if _, err = pemutil.Serialize(root, pemutil.WithFilename(rootCertFile)); err != nil {
|
||||
t.Fatalf("Failed serializing root certificate: %v", err)
|
||||
}
|
||||
intermediateCertFile := filepath.Join(dir, "intermediate.pem")
|
||||
if _, err = pemutil.Serialize(firstIntermediate, pemutil.WithFilename(intermediateCertFile)); err != nil {
|
||||
t.Fatalf("Failed serializing intermediate certificate: %v", err)
|
||||
}
|
||||
intermediateKeyFile := filepath.Join(dir, "intermediate.key")
|
||||
if _, err = pemutil.Serialize(firstIntermediateSigner, pemutil.WithFilename(intermediateKeyFile)); err != nil {
|
||||
t.Fatalf("Failed serializing intermediate key: %v", err)
|
||||
}
|
||||
|
||||
var intermediateChainContents []byte
|
||||
intermediateChain := []*x509.Certificate{secondIntermediate, firstIntermediate}
|
||||
for _, cert := range intermediateChain {
|
||||
b, err := pemutil.Serialize(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed serializing intermediate certificate: %v", err)
|
||||
}
|
||||
intermediateChainContents = append(intermediateChainContents, pem.EncodeToMemory(b)...)
|
||||
}
|
||||
intermediateChainFile := filepath.Join(dir, "intermediates.pem")
|
||||
if err := os.WriteFile(intermediateChainFile, intermediateChainContents, 0644); err != nil {
|
||||
t.Fatalf("Failed writing intermediate chain: %v", err)
|
||||
}
|
||||
intermediateChainKeyFile := filepath.Join(dir, "intermediates.key")
|
||||
if _, err = pemutil.Serialize(secondIntermediateSigner, pemutil.WithFilename(intermediateChainKeyFile)); err != nil {
|
||||
t.Fatalf("Failed serializing intermediate key: %v", err)
|
||||
}
|
||||
|
||||
signer, err := keyutil.GenerateDefaultSigner()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating signer: %v", err)
|
||||
}
|
||||
|
||||
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
|
||||
Subject: pkix.Name{CommonName: "test"},
|
||||
}, signer)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating CSR: %v", err)
|
||||
}
|
||||
|
||||
csr, err := x509.ParseCertificateRequest(csrBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed parsing CSR: %v", err)
|
||||
}
|
||||
|
||||
t.Run("generated-with-defaults", func(t *testing.T) {
|
||||
caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: t.Context()})
|
||||
t.Cleanup(cancel)
|
||||
logger := zap.NewNop()
|
||||
|
||||
ca := &caddypki.CA{
|
||||
StorageRaw: []byte(fmt.Sprintf(`{"module": "file_system", "root": %q}`, storageDir)),
|
||||
}
|
||||
if err := ca.Provision(caddyCtx, "local-test-generated", logger); err != nil {
|
||||
t.Fatalf("Failed provisioning CA: %v", err)
|
||||
}
|
||||
|
||||
iss := InternalIssuer{
|
||||
SignWithRoot: false,
|
||||
ca: ca,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
c, err := iss.Issue(t.Context(), csr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed issuing certificate: %v", err)
|
||||
}
|
||||
|
||||
chain, err := pemutil.ParseCertificateBundle(c.Certificate)
|
||||
if err != nil {
|
||||
t.Errorf("Failed issuing certificate: %v", err)
|
||||
}
|
||||
if len(chain) != 2 {
|
||||
t.Errorf("Expected 2 certificates in chain; got %d", len(chain))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("single-intermediate-from-disk", func(t *testing.T) {
|
||||
caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: t.Context()})
|
||||
t.Cleanup(cancel)
|
||||
logger := zap.NewNop()
|
||||
|
||||
ca := &caddypki.CA{
|
||||
Root: &caddypki.KeyPair{
|
||||
Certificate: rootCertFile,
|
||||
},
|
||||
Intermediate: &caddypki.KeyPair{
|
||||
Certificate: intermediateCertFile,
|
||||
PrivateKey: intermediateKeyFile,
|
||||
},
|
||||
StorageRaw: []byte(fmt.Sprintf(`{"module": "file_system", "root": %q}`, storageDir)),
|
||||
}
|
||||
|
||||
if err := ca.Provision(caddyCtx, "local-test-single-intermediate", logger); err != nil {
|
||||
t.Fatalf("Failed provisioning CA: %v", err)
|
||||
}
|
||||
|
||||
iss := InternalIssuer{
|
||||
ca: ca,
|
||||
SignWithRoot: false,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
c, err := iss.Issue(t.Context(), csr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed issuing certificate: %v", err)
|
||||
}
|
||||
|
||||
chain, err := pemutil.ParseCertificateBundle(c.Certificate)
|
||||
if err != nil {
|
||||
t.Errorf("Failed issuing certificate: %v", err)
|
||||
}
|
||||
if len(chain) != 2 {
|
||||
t.Errorf("Expected 2 certificates in chain; got %d", len(chain))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("multiple-intermediates-from-disk", func(t *testing.T) {
|
||||
caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: t.Context()})
|
||||
t.Cleanup(cancel)
|
||||
logger := zap.NewNop()
|
||||
|
||||
ca := &caddypki.CA{
|
||||
Root: &caddypki.KeyPair{
|
||||
Certificate: rootCertFile,
|
||||
},
|
||||
Intermediate: &caddypki.KeyPair{
|
||||
Certificate: intermediateChainFile,
|
||||
PrivateKey: intermediateChainKeyFile,
|
||||
},
|
||||
StorageRaw: []byte(fmt.Sprintf(`{"module": "file_system", "root": %q}`, storageDir)),
|
||||
}
|
||||
|
||||
if err := ca.Provision(caddyCtx, "local-test", zap.NewNop()); err != nil {
|
||||
t.Fatalf("Failed provisioning CA: %v", err)
|
||||
}
|
||||
|
||||
iss := InternalIssuer{
|
||||
ca: ca,
|
||||
SignWithRoot: false,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
c, err := iss.Issue(t.Context(), csr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed issuing certificate: %v", err)
|
||||
}
|
||||
|
||||
chain, err := pemutil.ParseCertificateBundle(c.Certificate)
|
||||
if err != nil {
|
||||
t.Errorf("Failed issuing certificate: %v", err)
|
||||
}
|
||||
if len(chain) != 3 {
|
||||
t.Errorf("Expected 3 certificates in chain; got %d", len(chain))
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue