Add test for pki.IssueCert()

This commit is contained in:
John Gardiner Myers 2020-05-16 21:42:02 -07:00
parent c142483cfa
commit 08cdee1de2
2 changed files with 191 additions and 0 deletions

View File

@ -22,6 +22,7 @@ go_test(
name = "go_default_test",
srcs = [
"certificate_test.go",
"issue_test.go",
"privatekey_test.go",
"sshkey_test.go",
],

190
pkg/pki/issue_test.go Normal file
View File

@ -0,0 +1,190 @@
/*
Copyright 2020 The Kubernetes 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 pki
import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"net"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type mockKeystore struct {
t *testing.T
signer string
cert *Certificate
key *PrivateKey
invoked bool
}
func (m *mockKeystore) FindKeypair(name string) (*Certificate, *PrivateKey, bool, error) {
assert.False(m.t, m.invoked, "invoked already")
m.invoked = true
assert.Equal(m.t, m.signer, name, "name argument")
return m.cert, m.key, false, nil
}
func TestIssueCert(t *testing.T) {
caCertificate, err := ParsePEMCertificate([]byte("-----BEGIN CERTIFICATE-----\nMIIBRjCB8aADAgECAhAzhRMOcwfggPtgZNIOFU19MA0GCSqGSIb3DQEBCwUAMBIx\nEDAOBgNVBAMTB1Rlc3QgQ0EwHhcNMjAwNTE1MDIzNjI0WhcNMzAwNTE1MDIzNjI0\nWjASMRAwDgYDVQQDEwdUZXN0IENBMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM/S\ncagGaiDA3jJWBXUr8rM19TWLA65jK/iA05FCsmQbyvETs5gbJdBfnhQp8wkKFlkt\nKxZ34k3wQUzoB1lv8/kCAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADQQCDOxvs58AVAWgWLtD3Obvy7XXsKx6d\nMzg9epbiQchLE4G/jlbgVu7vwh8l5XFNfQooG6stCU7pmLFXkXzkJQxr\n-----END CERTIFICATE-----\n"))
require.NoError(t, err)
caPrivateKey, err := ParsePEMPrivateKey([]byte("-----BEGIN RSA PRIVATE KEY-----\nMIIBPAIBAAJBAM/ScagGaiDA3jJWBXUr8rM19TWLA65jK/iA05FCsmQbyvETs5gb\nJdBfnhQp8wkKFlktKxZ34k3wQUzoB1lv8/kCAwEAAQJBAJzXQZeBX87gP9DVQsEv\nLbc6XZjPFTQi/ChLcWALaf5J7drFJHUcWbKIHzOmM3fm3lQlb/1IcwOBU5cTY0e9\nBVECIQD73kxOWWAIzKqMOvFZ9s79Et7G1HUMnVAVKJ1NS1uvYwIhANM7LULdi0YD\nbcHvDl3+Msj4cPH7CXAJFyPWaQZPlXPzAiEAhDg6jpbUl0n57guzT6sFFk2lrXMy\nzyB2PeVITp9UzkkCIEpcF7flQ+U2ycmuvVELbpdfFmupIw5ktNex4DEPjR5PAiEA\n68vR1L1Kaja/GzU76qAQaYA/V1Ag4sPmOQdEaVZKu78=\n-----END RSA PRIVATE KEY-----\n"))
require.NoError(t, err)
privateKey, err := ParsePEMPrivateKey([]byte("-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBANgL5cR2cLOB7oZZTiuiUmMwQRBaia8yLULt+XtBtDHf0lPOrn78\nvLPh7P7zRBgHczbTddcsg68g9vAfb9TC5M8CAwEAAQJAJytxCv+WS1VhU4ZZf9u8\nKDOVeEuR7uuf/SR8OPaenvPqONpYbZSVjnWnRBRHvg3HaHchQqH32UljZUojs9z4\nEQIhAO/yoqCFckfqswOGwWyYX1oNOtU8w9ulXlZqAtZieavVAiEA5n/tKHoZyx3U\nbZcks/wns1WqhAoSmDJpMyVXOVrUlBMCIDGnalQBiYasYOMn7bsFRSYjertJ2dYI\nQJ9tTK0Er90JAiAmpVQx8SbZ80pmhWzV8HUHkFligf3UHr+cn6ocJ6p0mQIgB728\npdvrS5zRPoUN8BHfWOZcPrElKTuJjP2kH6eNPvI=\n-----END RSA PRIVATE KEY-----"))
require.NoError(t, err)
for _, tc := range []struct {
name string
req IssueCertRequest
expectedKeyUsage x509.KeyUsage
expectedExtKeyUsage []x509.ExtKeyUsage
expectedSubject pkix.Name
expectedDNSNames []string
expectedIPAddresses []net.IP
}{
{
name: "ca",
req: IssueCertRequest{
Type: "ca",
Subject: pkix.Name{
CommonName: "Test CA",
},
},
expectedKeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
expectedSubject: pkix.Name{CommonName: "Test CA"},
},
{
name: "client",
req: IssueCertRequest{
Type: "client",
Subject: pkix.Name{
CommonName: "Test client",
Organization: []string{"system:masters"},
},
Serial: BuildPKISerial(123456),
},
expectedKeyUsage: x509.KeyUsageDigitalSignature,
expectedExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
expectedSubject: pkix.Name{CommonName: "Test client", Organization: []string{"system:masters"}},
},
{
name: "clientServer",
req: IssueCertRequest{
Type: "clientServer",
Subject: pkix.Name{
CommonName: "Test client/server",
},
AlternateNames: []string{"*.internal.test.cluster.local", "localhost", "127.0.0.1"},
PrivateKey: privateKey,
},
expectedKeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
expectedExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
expectedSubject: pkix.Name{CommonName: "Test client/server"},
expectedDNSNames: []string{"*.internal.test.cluster.local", "localhost"},
expectedIPAddresses: []net.IP{net.ParseIP("127.0.0.1").To4()},
},
{
name: "server",
req: IssueCertRequest{
Type: "server",
Subject: pkix.Name{
CommonName: "Test server",
},
AlternateNames: []string{"*.internal.test.cluster.local", "localhost", "127.0.0.1"},
PrivateKey: privateKey,
},
expectedKeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
expectedExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
expectedSubject: pkix.Name{CommonName: "Test server"},
expectedDNSNames: []string{"*.internal.test.cluster.local", "localhost"},
expectedIPAddresses: []net.IP{net.ParseIP("127.0.0.1").To4()},
},
} {
t.Run(tc.name, func(t *testing.T) {
minExpectedValidity := time.Now().Add(time.Hour * 10 * 365 * 24).Unix()
var keystore Keystore
if tc.req.Type != "ca" {
tc.req.Signer = tc.name + "-signer"
keystore = &mockKeystore{
t: t,
signer: tc.req.Signer,
cert: caCertificate,
key: caPrivateKey,
}
}
certificate, key, err := IssueCert(&tc.req, keystore)
require.NoError(t, err)
cert := certificate.Certificate
if tc.req.Signer == "" {
assert.Equal(t, cert.Issuer, cert.Subject, "self-signed")
assert.NoError(t, cert.CheckSignatureFrom(cert), "check signature")
} else {
assert.Equal(t, cert.Issuer, caCertificate.Certificate.Subject, "cert signer")
assert.NoError(t, cert.CheckSignatureFrom(caCertificate.Certificate), "check signature")
}
// type
assert.Equal(t, certificate.IsCA, cert.IsCA, "IsCA matches")
assert.Equal(t, tc.req.Type == "ca", cert.IsCA, "IsCA")
assert.True(t, cert.BasicConstraintsValid, "BasicConstraintsValid")
assert.Equal(t, tc.expectedKeyUsage, cert.KeyUsage, "KeyUsage")
assert.ElementsMatch(t, tc.expectedExtKeyUsage, cert.ExtKeyUsage)
assert.Nil(t, cert.ExtraExtensions, "ExtraExtensions")
// subject
assert.Equal(t, certificate.Subject, cert.Subject, "Subject matches")
actualName := cert.Subject
actualName.Names = nil
assert.Equal(t, tc.expectedSubject, actualName, "Subject")
// alternateNames
assert.Equal(t, tc.expectedDNSNames, cert.DNSNames, "DNSNames")
assert.Equal(t, tc.expectedIPAddresses, cert.IPAddresses, "IPAddresses")
assert.Empty(t, cert.EmailAddresses, "EmailAddresses")
// privateKey
rsaPrivateKey, ok := key.Key.(*rsa.PrivateKey)
require.True(t, ok, "private key is RSA")
if tc.req.PrivateKey == nil {
assert.Equal(t, 2048, rsaPrivateKey.N.BitLen(), "Private key length")
} else {
assert.Equal(t, tc.req.PrivateKey, key, "Private key")
}
assert.Equal(t, &rsaPrivateKey.PublicKey, cert.PublicKey, "certificate public key matches private key")
assert.Equal(t, certificate.PublicKey, cert.PublicKey, "PublicKey")
// serial
if tc.req.Serial != nil {
assert.Equal(t, cert.SerialNumber, tc.req.Serial, "SerialNumber")
} else {
assert.Greater(t, cert.SerialNumber.BitLen(), 110, "SerialNumber bit length")
}
// validity
assert.Less(t, cert.NotBefore.Unix(), time.Now().Add(time.Hour*-47).Unix(), "NotBefore")
assert.GreaterOrEqual(t, cert.NotAfter.Unix(), minExpectedValidity, "NotAfter")
})
}
}