caddy/modules/caddytls/internalissuer_test.go

259 lines
7.8 KiB
Go

// 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))
}
})
}