cert-manager/pkg/util/pki/generate_test.go
James Munnelly 0fcc0c666c Update copyright header year
Signed-off-by: James Munnelly <james@munnelly.eu>
2019-01-07 15:07:55 +00:00

338 lines
8.6 KiB
Go

/*
Copyright 2019 The Jetstack cert-manager contributors.
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"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"strings"
"testing"
"time"
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
)
func buildCertificateWithKeyParams(keyAlgo v1alpha1.KeyAlgorithm, keySize int) *v1alpha1.Certificate {
return &v1alpha1.Certificate{
Spec: v1alpha1.CertificateSpec{
CommonName: "test",
DNSNames: []string{"test.test"},
KeyAlgorithm: keyAlgo,
KeySize: keySize,
},
}
}
func ecCurveForKeySize(keySize int) (elliptic.Curve, error) {
switch keySize {
case 0, ECCurve256:
return elliptic.P256(), nil
case ECCurve384:
return elliptic.P384(), nil
case ECCurve521:
return elliptic.P521(), nil
default:
return nil, fmt.Errorf("unknown ecdsa key size specified: %d", keySize)
}
}
func TestGeneratePrivateKeyForCertificate(t *testing.T) {
type testT struct {
name string
keyAlgo v1alpha1.KeyAlgorithm
keySize int
expectErr bool
expectErrStr string
}
tests := []testT{
{
name: "rsa key with weak keysize (< 2048)",
keyAlgo: v1alpha1.RSAKeyAlgorithm,
keySize: 1024,
expectErr: true,
expectErrStr: "weak rsa key size specified",
},
{
name: "rsa key with too big keysize (> 8192)",
keyAlgo: v1alpha1.RSAKeyAlgorithm,
keySize: 8196,
expectErr: true,
expectErrStr: "rsa key size specified too big",
},
{
name: "ecdsa key with unsupported keysize",
keyAlgo: v1alpha1.ECDSAKeyAlgorithm,
keySize: 100,
expectErr: true,
expectErrStr: "unsupported ecdsa key size specified",
},
{
name: "unsupported key algo specified",
keyAlgo: v1alpha1.KeyAlgorithm("blahblah"),
keySize: 256,
expectErr: true,
expectErrStr: "unsupported private key algorithm specified",
},
{
name: "rsa key with keysize 2048",
keyAlgo: v1alpha1.RSAKeyAlgorithm,
keySize: 2048,
expectErr: false,
},
{
name: "rsa key with keysize 4096",
keyAlgo: v1alpha1.RSAKeyAlgorithm,
keySize: 4096,
expectErr: false,
},
{
name: "ecdsa key with keysize 256",
keyAlgo: v1alpha1.ECDSAKeyAlgorithm,
keySize: 256,
expectErr: false,
},
{
name: "ecdsa key with keysize 384",
keyAlgo: v1alpha1.ECDSAKeyAlgorithm,
keySize: 384,
expectErr: false,
},
{
name: "ecdsa key with keysize 521",
keyAlgo: v1alpha1.ECDSAKeyAlgorithm,
keySize: 521,
expectErr: false,
},
{
name: "valid key size with key algorithm not specified",
keyAlgo: v1alpha1.KeyAlgorithm(""),
keySize: 2048,
expectErr: false,
},
{
name: "rsa with keysize not specified",
keyAlgo: v1alpha1.RSAKeyAlgorithm,
expectErr: false,
},
{
name: "ecdsa with keysize not specified",
keyAlgo: v1alpha1.ECDSAKeyAlgorithm,
expectErr: false,
},
}
testFn := func(test testT) func(*testing.T) {
return func(t *testing.T) {
privateKey, err := GeneratePrivateKeyForCertificate(buildCertificateWithKeyParams(test.keyAlgo, test.keySize))
if test.expectErr {
if err == nil {
t.Error("expected err, but got no error")
return
}
if !strings.Contains(err.Error(), test.expectErrStr) {
t.Errorf("expected err string to match: '%s', got: '%s'", test.expectErrStr, err.Error())
return
}
}
if !test.expectErr {
if err != nil {
t.Errorf("expected no err, but got '%q'", err)
return
}
if test.keyAlgo == "rsa" {
// For rsa algorithm, if keysize is not provided, the default of 2048 will be used
expectedRsaKeySize := 2048
if test.keySize != 0 {
expectedRsaKeySize = test.keySize
}
key, ok := privateKey.(*rsa.PrivateKey)
if !ok {
t.Errorf("expected rsa private key, but got %T", privateKey)
return
}
actualKeySize := key.N.BitLen()
if expectedRsaKeySize != actualKeySize {
t.Errorf("expected %d, but got %d", expectedRsaKeySize, actualKeySize)
return
}
}
if test.keyAlgo == "ecdsa" {
// For ecdsa algorithm, if keysize is not provided, the default of 256 will be used
expectedEcdsaKeySize := ECCurve256
if test.keySize != 0 {
expectedEcdsaKeySize = test.keySize
}
key, ok := privateKey.(*ecdsa.PrivateKey)
if !ok {
t.Errorf("expected ecdsa private key, but got %T", privateKey)
return
}
actualKeySize := key.Curve.Params().BitSize
if expectedEcdsaKeySize != actualKeySize {
t.Errorf("expected %d but got %d", expectedEcdsaKeySize, actualKeySize)
return
}
curve, err := ecCurveForKeySize(test.keySize)
if err != nil {
t.Errorf(err.Error())
return
}
if !curve.IsOnCurve(key.PublicKey.X, key.PublicKey.Y) {
t.Error("expected key to be on specified curve")
return
}
}
}
}
}
for _, test := range tests {
t.Run(test.name, testFn(test))
}
}
func signTestCert(key crypto.Signer) *x509.Certificate {
commonName := "testingcert"
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
panic(fmt.Errorf("failed to generate serial number: %s", err.Error()))
}
template := &x509.Certificate{
Version: 3,
BasicConstraintsValid: true,
SerialNumber: serialNumber,
SignatureAlgorithm: x509.SHA256WithRSA,
Subject: pkix.Name{
Organization: []string{defaultOrganization},
CommonName: commonName,
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(v1alpha1.DefaultCertificateDuration),
// see http://golang.org/pkg/crypto/x509/#KeyUsage
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
}
_, crt, err := SignCertificate(template, template, key.Public(), key)
if err != nil {
panic(fmt.Errorf("error signing test cert: %v", err))
}
return crt
}
func TestPublicKeyMatchesCertificate(t *testing.T) {
privKey1, err := GenerateRSAPrivateKey(2048)
if err != nil {
t.Errorf("error generating private key: %v", err)
}
privKey2, err := GenerateRSAPrivateKey(2048)
if err != nil {
t.Errorf("error generating private key: %v", err)
}
testCert1 := signTestCert(privKey1)
testCert2 := signTestCert(privKey2)
matches, err := PublicKeyMatchesCertificate(privKey1.Public(), testCert1)
if err != nil {
t.Errorf("expected no error, but got: %v", err)
}
if !matches {
t.Errorf("expected private key to match certificate, but it did not")
}
matches, err = PublicKeyMatchesCertificate(privKey1.Public(), testCert2)
if err != nil {
t.Errorf("expected no error, but got: %v", err)
}
if matches {
t.Errorf("expected private key to not match certificate, but it did")
}
}
func TestPublicKeyMatchesCertificateRequest(t *testing.T) {
privKey1, err := GenerateRSAPrivateKey(2048)
if err != nil {
t.Errorf("error generating private key: %v", err)
}
privKey2, err := GenerateRSAPrivateKey(2048)
if err != nil {
t.Errorf("error generating private key: %v", err)
}
template := &x509.CertificateRequest{
Version: 3,
// SignatureAlgorithm: sigAlgo,
Subject: pkix.Name{
CommonName: "cn",
},
}
csr1, err := x509.CreateCertificateRequest(rand.Reader, template, privKey1)
if err != nil {
t.Errorf("error generating csr1: %v", err)
}
csr2, err := x509.CreateCertificateRequest(rand.Reader, template, privKey2)
if err != nil {
t.Errorf("error generating csr2: %v", err)
}
parsedCSR1, err := x509.ParseCertificateRequest(csr1)
if err != nil {
t.Errorf("error parsing csr1: %v", err)
}
parsedCSR2, err := x509.ParseCertificateRequest(csr2)
if err != nil {
t.Errorf("error parsing csr2: %v", err)
}
matches, err := PublicKeyMatchesCSR(privKey1.Public(), parsedCSR1)
if err != nil {
t.Errorf("expected no error, but got: %v", err)
}
if !matches {
t.Errorf("expected private key to match certificate, but it did not")
}
matches, err = PublicKeyMatchesCSR(privKey1.Public(), parsedCSR2)
if err != nil {
t.Errorf("expected no error, but got: %v", err)
}
if matches {
t.Errorf("expected private key to not match certificate, but it did")
}
}