259 lines
7.8 KiB
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))
|
|
}
|
|
})
|
|
}
|