package crypto
import (
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"testing"
"time"
)
const certificateLifetime = 365 * 2
func TestCrypto(t *testing.T) {
roots := x509.NewCertPool()
intermediates := x509.NewCertPool()
// Test CA
fmt.Println("Building CA...")
caKey, caCrt := buildCA(t)
roots.AddCert(caCrt)
// Test intermediate
fmt.Println("Building intermediate 1...")
intKey, intCrt := buildIntermediate(t, caKey, caCrt)
verify(t, intCrt, x509.VerifyOptions{
Roots: roots,
Intermediates: intermediates,
}, true, 2)
intermediates.AddCert(intCrt)
// Test intermediate 2
fmt.Println("Building intermediate 2...")
intKey2, intCrt2 := buildIntermediate(t, intKey, intCrt)
verify(t, intCrt2, x509.VerifyOptions{
Roots: roots,
Intermediates: intermediates,
}, true, 3)
intermediates.AddCert(intCrt2)
// Test server cert
fmt.Println("Building server...")
_, serverCrt := buildServer(t, intKey2, intCrt2)
verify(t, serverCrt, x509.VerifyOptions{
DNSName: "localhost",
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}, true, 4)
verify(t, serverCrt, x509.VerifyOptions{
DNSName: "www.example.com",
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}, true, 4)
verify(t, serverCrt, x509.VerifyOptions{
DNSName: "127.0.0.1",
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}, true, 4)
verify(t, serverCrt, x509.VerifyOptions{
DNSName: "www.foo.com",
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}, false, 4)
// Test client cert
fmt.Println("Building client...")
_, clientCrt := buildClient(t, intKey2, intCrt2)
verify(t, clientCrt, x509.VerifyOptions{
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}, true, 4)
}
func buildCA(t *testing.T) (crypto.PrivateKey, *x509.Certificate) {
caPublicKey, caPrivateKey, err := NewKeyPair()
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
caTemplate := newSigningCertificateTemplate(pkix.Name{CommonName: "CA"}, certificateLifetime, time.Now)
caCrt, err := signCertificate(caTemplate, caPublicKey, caTemplate, caPrivateKey)
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
return caPrivateKey, caCrt
}
func buildIntermediate(t *testing.T, signingKey crypto.PrivateKey, signingCrt *x509.Certificate) (crypto.PrivateKey, *x509.Certificate) {
intermediatePublicKey, intermediatePrivateKey, err := NewKeyPair()
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
intermediateTemplate := newSigningCertificateTemplate(pkix.Name{CommonName: "Intermediate"}, certificateLifetime, time.Now)
intermediateCrt, err := signCertificate(intermediateTemplate, intermediatePublicKey, signingCrt, signingKey)
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
if err := intermediateCrt.CheckSignatureFrom(signingCrt); err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
return intermediatePrivateKey, intermediateCrt
}
func buildServer(t *testing.T, signingKey crypto.PrivateKey, signingCrt *x509.Certificate) (crypto.PrivateKey, *x509.Certificate) {
serverPublicKey, serverPrivateKey, err := NewKeyPair()
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
hosts := []string{"127.0.0.1", "localhost", "www.example.com"}
serverTemplate := newServerCertificateTemplate(pkix.Name{CommonName: "Server"}, hosts, certificateLifetime, time.Now)
serverCrt, err := signCertificate(serverTemplate, serverPublicKey, signingCrt, signingKey)
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
if err := serverCrt.CheckSignatureFrom(signingCrt); err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
return serverPrivateKey, serverCrt
}
func buildClient(t *testing.T, signingKey crypto.PrivateKey, signingCrt *x509.Certificate) (crypto.PrivateKey, *x509.Certificate) {
clientPublicKey, clientPrivateKey, err := NewKeyPair()
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
clientTemplate := newClientCertificateTemplate(pkix.Name{CommonName: "Client"}, certificateLifetime, time.Now)
clientCrt, err := signCertificate(clientTemplate, clientPublicKey, signingCrt, signingKey)
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
if err := clientCrt.CheckSignatureFrom(signingCrt); err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
return clientPrivateKey, clientCrt
}
func verify(t *testing.T, cert *x509.Certificate, opts x509.VerifyOptions, success bool, chainLength int) {
validChains, err := cert.Verify(opts)
if success {
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
if len(validChains) != 1 {
t.Fatalf("Expected a valid chain")
}
if len(validChains[0]) != chainLength {
t.Fatalf("Expected a valid chain of length %d, got %d", chainLength, len(validChains[0]))
}
} else if err == nil && len(validChains) > 0 {
t.Fatalf("Expected failure, got success")
}
}
func TestRandomSerialGenerator(t *testing.T) {
generator := &RandomSerialGenerator{}
hostnames := []string{"foo", "bar"}
template := newServerCertificateTemplate(pkix.Name{CommonName: hostnames[0]}, hostnames, certificateLifetime, time.Now)
if _, err := generator.Next(template); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestValidityPeriodOfClientCertificate(t *testing.T) {
currentTime := time.Now()
currentFakeTime := func() time.Time {
return currentTime
}
tests := []struct {
passedExpireDays int
realExpireDays int
}{
{
passedExpireDays: 100,
realExpireDays: 100,
},
{
passedExpireDays: 0,
realExpireDays: DefaultCertificateLifetimeInDays,
},
{
passedExpireDays: -1,
realExpireDays: DefaultCertificateLifetimeInDays,
},
}
for _, test := range tests {
cert := newClientCertificateTemplate(pkix.Name{CommonName: "client"}, test.passedExpireDays, currentFakeTime)
expirationDate := cert.NotAfter
expectedExpirationDate := currentTime.Add(time.Duration(test.realExpireDays) * 24 * time.Hour)
if expectedExpirationDate != expirationDate {
t.Errorf("expected that client certificate will expire at %v but found %v", expectedExpirationDate, expirationDate)
}
}
}
func TestValidityPeriodOfServerCertificate(t *testing.T) {
currentTime := time.Now()
currentFakeTime := func() time.Time {
return currentTime
}
tests := []struct {
passedExpireDays int
realExpireDays int
}{
{
passedExpireDays: 100,
realExpireDays: 100,
},
{
passedExpireDays: 0,
realExpireDays: DefaultCertificateLifetimeInDays,
},
{
passedExpireDays: -1,
realExpireDays: DefaultCertificateLifetimeInDays,
},
}
for _, test := range tests {
cert := newServerCertificateTemplate(
pkix.Name{CommonName: "server"},
[]string{"www.example.com"},
test.passedExpireDays,
currentFakeTime,
)
expirationDate := cert.NotAfter
expectedExpirationDate := currentTime.Add(time.Duration(test.realExpireDays) * 24 * time.Hour)
if expectedExpirationDate != expirationDate {
t.Errorf("expected that server certificate will expire at %v but found %v", expectedExpirationDate, expirationDate)
}
}
}
func TestValidityPeriodOfSigningCertificate(t *testing.T) {
currentTime := time.Now()
currentFakeTime := func() time.Time {
return currentTime
}
tests := []struct {
passedExpireDays int
realExpireDays int
}{
{
passedExpireDays: 100,
realExpireDays: 100,
},
{
passedExpireDays: 0,
realExpireDays: DefaultCACertificateLifetimeInDays,
},
{
passedExpireDays: -1,
realExpireDays: DefaultCACertificateLifetimeInDays,
},
}
for _, test := range tests {
cert := newSigningCertificateTemplate(pkix.Name{CommonName: "CA"}, test.passedExpireDays, currentFakeTime)
expirationDate := cert.NotAfter
expectedExpirationDate := currentTime.Add(time.Duration(test.realExpireDays) * 24 * time.Hour)
if expectedExpirationDate != expirationDate {
t.Errorf("expected that CA certificate will expire at %v but found %v", expectedExpirationDate, expirationDate)
}
}
}