Merge pull request #8988 from johngmyers/private-key-size

Use KOPS_RSA_PRIVATE_KEY_SIZE for more keys
This commit is contained in:
Kubernetes Prow Robot 2020-04-29 20:00:18 -07:00 committed by GitHub
commit 936d656b4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 168 additions and 196 deletions

View File

@ -112,7 +112,6 @@ k8s.io/kops/pkg/nodeidentity/gce
k8s.io/kops/pkg/nodeidentity/openstack
k8s.io/kops/pkg/nodelabels
k8s.io/kops/pkg/pki
k8s.io/kops/pkg/pkiutil
k8s.io/kops/pkg/pretty
k8s.io/kops/pkg/rbac
k8s.io/kops/pkg/resources

View File

@ -55,7 +55,6 @@ go_library(
"//pkg/kubemanifest:go_default_library",
"//pkg/nodelabels:go_default_library",
"//pkg/pki:go_default_library",
"//pkg/pkiutil:go_default_library",
"//pkg/rbac:go_default_library",
"//pkg/systemd:go_default_library",
"//pkg/tokens:go_default_library",
@ -78,7 +77,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//vendor/k8s.io/client-go/util/cert:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/mount:go_default_library",

View File

@ -17,17 +17,16 @@ limitations under the License.
package model
import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"golang.org/x/sys/unix"
certutil "k8s.io/client-go/util/cert"
"k8s.io/klog"
"k8s.io/kops/pkg/pkiutil"
"k8s.io/kops/pkg/pki"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
)
@ -147,8 +146,7 @@ func (b *CiliumBuilder) buildCiliumEtcdSecrets(c *fi.ModelBuilderContext) error
{
p := filepath.Join(dir, "etcd-ca.crt")
certBytes := pkiutil.EncodeCertPEM(etcdClientsCACertificate.Certificate)
if err := ioutil.WriteFile(p, certBytes, 0644); err != nil {
if err := etcdClientsCACertificate.WriteToFile(p, 0644); err != nil {
return fmt.Errorf("error writing certificate key file %q: %v", p, err)
}
}
@ -156,39 +154,34 @@ func (b *CiliumBuilder) buildCiliumEtcdSecrets(c *fi.ModelBuilderContext) error
name := "etcd-client"
humanName := dir + "/" + name
privateKey, err := pkiutil.NewPrivateKey()
privateKey, err := pki.GeneratePrivateKey()
if err != nil {
return fmt.Errorf("unable to create private key %q: %v", humanName, err)
}
privateKeyBytes := pkiutil.EncodePrivateKeyPEM(privateKey)
certConfig := &certutil.Config{
CommonName: "cilium",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
signingKey, ok := etcdClientsCAPrivateKey.Key.(*rsa.PrivateKey)
if !ok {
return fmt.Errorf("etcd-clients-ca private key had unexpected type %T", etcdClientsCAPrivateKey.Key)
certTmpl := &x509.Certificate{
Subject: pkix.Name{
CommonName: "cilium",
},
NotAfter: time.Now().Add(time.Hour * 24 * 365).UTC(),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
klog.Infof("signing certificate for %q", humanName)
cert, err := pkiutil.NewSignedCert(certConfig, privateKey, etcdClientsCACertificate.Certificate, signingKey)
cert, err := pki.SignNewCertificate(privateKey, certTmpl, etcdClientsCACertificate.Certificate, etcdClientsCAPrivateKey)
if err != nil {
return fmt.Errorf("error signing certificate for %q: %v", humanName, err)
}
certBytes := pkiutil.EncodeCertPEM(cert)
p := filepath.Join(dir, name)
{
if err := ioutil.WriteFile(p+".crt", certBytes, 0644); err != nil {
if err := cert.WriteToFile(p+".crt", 0644); err != nil {
return fmt.Errorf("error writing certificate key file %q: %v", p+".crt", err)
}
}
{
if err := ioutil.WriteFile(p+".key", privateKeyBytes, 0600); err != nil {
if err := privateKey.WriteToFile(p+".key", 0600); err != nil {
return fmt.Errorf("error writing private key file %q: %v", p+".key", err)
}
}

View File

@ -17,16 +17,15 @@ limitations under the License.
package model
import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
certutil "k8s.io/client-go/util/cert"
"k8s.io/klog"
"k8s.io/kops/pkg/pkiutil"
"k8s.io/kops/pkg/pki"
"k8s.io/kops/upup/pkg/fi"
)
@ -106,8 +105,7 @@ func (b *EtcdManagerTLSBuilder) buildKubeAPIServerKeypair() error {
{
p := filepath.Join(dir, "etcd-ca.crt")
certBytes := pkiutil.EncodeCertPEM(etcdClientsCACertificate.Certificate)
if err := ioutil.WriteFile(p, certBytes, 0644); err != nil {
if err := etcdClientsCACertificate.WriteToFile(p, 0644); err != nil {
return fmt.Errorf("error writing certificate key file %q: %v", p, err)
}
}
@ -115,39 +113,34 @@ func (b *EtcdManagerTLSBuilder) buildKubeAPIServerKeypair() error {
name := "etcd-client"
humanName := dir + "/" + name
privateKey, err := pkiutil.NewPrivateKey()
privateKey, err := pki.GeneratePrivateKey()
if err != nil {
return fmt.Errorf("unable to create private key %q: %v", humanName, err)
}
privateKeyBytes := pkiutil.EncodePrivateKeyPEM(privateKey)
certConfig := &certutil.Config{
CommonName: "kube-apiserver",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
signingKey, ok := etcdClientsCAPrivateKey.Key.(*rsa.PrivateKey)
if !ok {
return fmt.Errorf("etcd-clients-ca private key had unexpected type %T", etcdClientsCAPrivateKey.Key)
certTmpl := &x509.Certificate{
Subject: pkix.Name{
CommonName: "kube-apiserver",
},
NotAfter: time.Now().Add(time.Hour * 24 * 365).UTC(),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
klog.Infof("signing certificate for %q", humanName)
cert, err := pkiutil.NewSignedCert(certConfig, privateKey, etcdClientsCACertificate.Certificate, signingKey)
cert, err := pki.SignNewCertificate(privateKey, certTmpl, etcdClientsCACertificate.Certificate, etcdClientsCAPrivateKey)
if err != nil {
return fmt.Errorf("error signing certificate for %q: %v", humanName, err)
}
certBytes := pkiutil.EncodeCertPEM(cert)
p := filepath.Join(dir, name)
{
if err := ioutil.WriteFile(p+".crt", certBytes, 0644); err != nil {
if err := cert.WriteToFile(p+".crt", 0644); err != nil {
return fmt.Errorf("error writing certificate key file %q: %v", p+".crt", err)
}
}
{
if err := ioutil.WriteFile(p+".key", privateKeyBytes, 0600); err != nil {
if err := privateKey.WriteToFile(p+".key", 0600); err != nil {
return fmt.Errorf("error writing private key file %q: %v", p+".key", err)
}
}

View File

@ -24,4 +24,8 @@ go_test(
"sshkey_test.go",
],
embed = [":go_default_library"],
deps = [
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
],
)

View File

@ -26,6 +26,7 @@ import (
"encoding/pem"
"fmt"
"io"
"os"
"k8s.io/klog"
)
@ -150,3 +151,15 @@ func (c *Certificate) WriteTo(w io.Writer) (int64, error) {
}
return b.WriteTo(w)
}
func (c *Certificate) WriteToFile(filename string, perm os.FileMode) error {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
_, err = c.WriteTo(f)
if err1 := f.Close(); err == nil {
err = err1
}
return err
}

View File

@ -23,62 +23,128 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGenerateCertificate(t *testing.T) {
data := "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA4JwpEprZ5n8RIEt6jT2lAh+UDgRgx/4px21gjgywQivYHVxH\nAZexVb/E9pBa9Q2G9B1Q7TCO7YsUVRQy4JMDZVt+McFnWVwexnqBYFNcVjkEmDgA\ngvCYGE0P9d/RwRL4KuLHo+u6fv7P0jXMN+CpOxyLhYZZNa0ZOZDHsSiJSQSj9WGF\nGHrbCf0KVDpKieR1uBqHrRO+mLR5zkX2L58m74kjK4dsBhmjeq/7OAoTmiG2QgJ/\nP2IjyhiA2mRqY+hl55lwEUV/0yHYEkJC8LdGkwwZz2eF77aSPGmi/A2CSKgMwDTx\n9m+P7jcpWreYw6NG9BueGoDIve/tgFKwvVFF6QIDAQABAoIBAA0ktjaTfyrAxsTI\nBezb7Zr5NBW55dvuII299cd6MJo+rI/TRYhvUv48kY8IFXp/hyUjzgeDLunxmIf9\n/Zgsoic9Ol44/g45mMduhcGYPzAAeCdcJ5OB9rR9VfDCXyjYLlN8H8iU0734tTqM\n0V13tQ9zdSqkGPZOIcq/kR/pylbOZaQMe97BTlsAnOMSMKDgnftY4122Lq3GYy+t\nvpr+bKVaQZwvkLoSU3rECCaKaghgwCyX7jft9aEkhdJv+KlwbsGY6WErvxOaLWHd\ncuMQjGapY1Fa/4UD00mvrA260NyKfzrp6+P46RrVMwEYRJMIQ8YBAk6N6Hh7dc0G\n8Z6i1m0CgYEA9HeCJR0TSwbIQ1bDXUrzpftHuidG5BnSBtax/ND9qIPhR/FBW5nj\n22nwLc48KkyirlfIULd0ae4qVXJn7wfYcuX/cJMLDmSVtlM5Dzmi/91xRiFgIzx1\nAsbBzaFjISP2HpSgL+e9FtSXaaqeZVrflitVhYKUpI/AKV31qGHf04sCgYEA6zTV\n99Sb49Wdlns5IgsfnXl6ToRttB18lfEKcVfjAM4frnkk06JpFAZeR+9GGKUXZHqs\nz2qcplw4d/moCC6p3rYPBMLXsrGNEUFZqBlgz72QA6BBq3X0Cg1Bc2ZbK5VIzwkg\nST2SSux6ccROfgULmN5ZiLOtdUKNEZpFF3i3qtsCgYADT/s7dYFlatobz3kmMnXK\nsfTu2MllHdRys0YGHu7Q8biDuQkhrJwhxPW0KS83g4JQym+0aEfzh36bWcl+u6R7\nKhKj+9oSf9pndgk345gJz35RbPJYh+EuAHNvzdgCAvK6x1jETWeKf6btj5pF1U1i\nQ4QNIw/QiwIXjWZeubTGsQKBgQCbduLu2rLnlyyAaJZM8DlHZyH2gAXbBZpxqU8T\nt9mtkJDUS/KRiEoYGFV9CqS0aXrayVMsDfXY6B/S/UuZjO5u7LtklDzqOf1aKG3Q\ndGXPKibknqqJYH+bnUNjuYYNerETV57lijMGHuSYCf8vwLn3oxBfERRX61M/DU8Z\nworz/QKBgQDCTJI2+jdXg26XuYUmM4XXfnocfzAXhXBULt1nENcogNf1fcptAVtu\nBAiz4/HipQKqoWVUYmxfgbbLRKKLK0s0lOWKbYdVjhEm/m2ZU8wtXTagNwkIGoyq\nY/C1Lox4f1ROJnCjc/hfcOjcxX5M8A8peecHWlVtUPKTJgxQ7oMKcw==\n-----END RSA PRIVATE KEY-----\n"
publicKeyData := "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4JwpEprZ5n8RIEt6jT2l\nAh+UDgRgx/4px21gjgywQivYHVxHAZexVb/E9pBa9Q2G9B1Q7TCO7YsUVRQy4JMD\nZVt+McFnWVwexnqBYFNcVjkEmDgAgvCYGE0P9d/RwRL4KuLHo+u6fv7P0jXMN+Cp\nOxyLhYZZNa0ZOZDHsSiJSQSj9WGFGHrbCf0KVDpKieR1uBqHrRO+mLR5zkX2L58m\n74kjK4dsBhmjeq/7OAoTmiG2QgJ/P2IjyhiA2mRqY+hl55lwEUV/0yHYEkJC8LdG\nkwwZz2eF77aSPGmi/A2CSKgMwDTx9m+P7jcpWreYw6NG9BueGoDIve/tgFKwvVFF\n6QIDAQAB\n-----END RSA PUBLIC KEY-----\n"
signerCertData := "-----BEGIN CERTIFICATE-----\nMIIBTDCB96ADAgECAhBjHcUz56MCdYqSYy7TYNe3MA0GCSqGSIb3DQEBCwUAMBUx\nEzARBgNVBAMTCnNlbGZzaWduZWQwHhcNMjAwNDI0MjMzNDM5WhcNMzAwNDI0MjMz\nNDM5WjAVMRMwEQYDVQQDEwpzZWxmc2lnbmVkMFwwDQYJKoZIhvcNAQEBBQADSwAw\nSAJBAL5zWUObMH5dBestQgDIa4B/rT7Cc21AK+B7gPvMcEfIWow5u6QE+EyhRTPv\n727oY+2MU9e4vq5RXBG7hneuBoECAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgEGMA8G\nA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADQQBLUFz7gDKRRyjEwgRZnZzP\nOma9WIgOjX36OFllyGkspu1ZcW/EtGEGNXqtMsm1QmG38Lh7Nkehb5xoAmm6hkFA\n-----END CERTIFICATE-----"
signerKeyData := "-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBAL5zWUObMH5dBestQgDIa4B/rT7Cc21AK+B7gPvMcEfIWow5u6QE\n+EyhRTPv727oY+2MU9e4vq5RXBG7hneuBoECAwEAAQJAZ9ZUUPwIEJ1/YJ4oYmzj\n0AfM2W8DqAlY4ufzh1YL0daGUkiuQg0p6CeqFqgnQluZ3bcXPG8iBQp1EeekULFL\nAQIhAOGbozbIEI+26Ehv41aCMWkKO1R05AVzmoNp1T2Ke6npAiEA2BtEPRSdhLek\nZR7vhk7KNTJ2XExJ+T/l2849EsojANkCIAWYD1b3ZPm7Rk0tgQyPE9yP5WK1t0Wv\nVSB3ClOJUIGpAiAfUBQbJZmNWW6gmFLsiw4RlzY/OW6ehvuvVbrTtiZMQQIgD2zY\nU2EjvR0zY5PsJYbcLHa9ieCA5ni/VW70WKn9K5s=\n-----END RSA PRIVATE KEY-----"
key, err := ParsePEMPrivateKey([]byte(data))
if err != nil {
t.Fatalf("error from ParsePEMPrivateKey: %v", err)
}
require.NoError(t, err, "ParsePEMPrivateKey")
signerKey, err := ParsePEMPrivateKey([]byte(signerKeyData))
require.NoError(t, err, "ParsePEMPrivateKey")
{
var b bytes.Buffer
pkData, err := x509.MarshalPKIXPublicKey(key.Key.(*rsa.PrivateKey).Public())
if err != nil {
t.Fatalf("error from MarshalPKIXPublicKey: %v", err)
}
if err := pem.Encode(&b, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pkData}); err != nil {
t.Fatalf("error serializing public key")
}
require.NoError(t, err, "MarshalPKIXPublicKey")
if b.String() != publicKeyData {
t.Fatalf("unexpected output from pem Encode: %q", b.String())
}
err = pem.Encode(&b, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pkData})
require.NoError(t, err, "serializing public key")
require.Equal(t, b.String(), publicKeyData)
}
subject := &pkix.Name{
CommonName: "kubernetes",
}
signer, err := ParsePEMCertificate([]byte(signerCertData))
require.NoError(t, err, "ParsePEMCertificate")
template := &x509.Certificate{
Subject: *subject,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
ExtKeyUsage: []x509.ExtKeyUsage{},
BasicConstraintsValid: true,
IsCA: true,
}
for _, tc := range []struct {
name string
template x509.Certificate
signer *x509.Certificate
signerKey *PrivateKey
expectedExtKeyUsage []x509.ExtKeyUsage
}{
{
name: "selfsigned",
template: x509.Certificate{
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
ExtKeyUsage: []x509.ExtKeyUsage{},
BasicConstraintsValid: true,
IsCA: true,
},
expectedExtKeyUsage: nil,
},
{
name: "client",
template: x509.Certificate{
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
NotAfter: time.Now().Add(time.Hour * 24 * 365).UTC(),
},
signerKey: signerKey,
signer: signer.Certificate,
expectedExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
{
name: "server",
template: x509.Certificate{
DNSNames: []string{"a.invalid", "b.invalid"},
NotAfter: time.Now().Add(time.Hour * 24 * 365).UTC(),
},
signerKey: signerKey,
signer: signer.Certificate,
expectedExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
} {
t.Run(tc.name, func(t *testing.T) {
tc.template.Subject = pkix.Name{
CommonName: tc.name,
}
cert, err := SignNewCertificate(key, template, nil, nil)
if err != nil {
t.Fatalf("error from SignNewCertificate: %v", err)
}
cert, err := SignNewCertificate(key, &tc.template, tc.signer, tc.signerKey)
require.NoError(t, err, "SignNewCertificate")
{
var b bytes.Buffer
pkData, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
if err != nil {
t.Fatalf("error from MarshalPKIXPublicKey: %v", err)
}
if err := pem.Encode(&b, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pkData}); err != nil {
t.Fatalf("error serializing public key")
}
{
subject := cert.Certificate.Subject
subject.Names = nil
assert.Equal(t, subject, tc.template.Subject)
}
assert.Equal(t, cert.Subject, cert.Certificate.Subject)
if b.String() != publicKeyData {
t.Fatalf("unexpected output from pem Encode: %q", b.String())
}
assert.Equal(t, cert.Certificate.DNSNames, tc.template.DNSNames)
assert.Equal(t, cert.IsCA, tc.template.IsCA)
assert.Equal(t, cert.Certificate.IsCA, tc.template.IsCA)
{
var b bytes.Buffer
pkData, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
require.NoError(t, err, "MarshalPKIXPublicKey")
err = pem.Encode(&b, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pkData})
require.NoError(t, err, "serializing public key")
assert.Equal(t, b.String(), publicKeyData)
}
assert.Equal(t, cert.PublicKey, cert.Certificate.PublicKey)
if tc.template.KeyUsage == 0 {
tc.template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
}
assert.Equal(t, cert.Certificate.KeyUsage, tc.template.KeyUsage, "KeyUsage")
assert.Equal(t, cert.Certificate.ExtKeyUsage, tc.expectedExtKeyUsage, "ExtKeyUsage")
if tc.signer == nil {
tc.signer = cert.Certificate
}
assert.Equal(t, cert.Certificate.Issuer, signer.Certificate.Subject, "Issuer")
pool := x509.NewCertPool()
pool.AddCert(tc.signer)
_, err = cert.Certificate.Verify(x509.VerifyOptions{
Roots: pool,
KeyUsages: tc.expectedExtKeyUsage,
})
assert.NoError(t, err, "verify certificate")
// notbefore, notafter, serialnumber, basiccvalid
})
}
}

View File

@ -96,14 +96,17 @@ func SignNewCertificate(privateKey *PrivateKey, template *x509.Certificate, sign
return nil, fmt.Errorf("error creating certificate: %v", err)
}
c := &Certificate{}
c.PublicKey = template.PublicKey
cert, err := x509.ParseCertificate(certificateData)
if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err)
}
c.Certificate = cert
c := &Certificate{
Subject: cert.Subject,
IsCA: cert.IsCA,
Certificate: cert,
PublicKey: cert.PublicKey,
}
return c, nil
}

View File

@ -165,6 +165,18 @@ func (k *PrivateKey) WriteTo(w io.Writer) (int64, error) {
return data.WriteTo(w)
}
func (k *PrivateKey) WriteToFile(filename string, perm os.FileMode) error {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
_, err = k.WriteTo(f)
if err1 := f.Close(); err == nil {
err = err1
}
return err
}
func parsePEMPrivateKey(pemData []byte) (crypto.PrivateKey, error) {
for {
block, rest := pem.Decode(pemData)

View File

@ -1,9 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["pki_helpers.go"],
importpath = "k8s.io/kops/pkg/pkiutil",
visibility = ["//visibility:public"],
deps = ["//vendor/k8s.io/client-go/util/cert:go_default_library"],
)

View File

@ -1,100 +0,0 @@
/*
Copyright 2019 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 pkiutil
import (
"crypto"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math"
"math/big"
"time"
certutil "k8s.io/client-go/util/cert"
)
const (
// CertificateBlockType is a possible value for pem.Block.Type.
CertificateBlockType = "CERTIFICATE"
// RSAPrivateKeyBlockType is a possible value for pem.Block.Type.
RSAPrivateKeyBlockType = "RSA PRIVATE KEY"
rsaKeySize = 2048
duration365d = time.Hour * 24 * 365
)
// EncodeCertPEM returns PEM-endcoded certificate data
func EncodeCertPEM(cert *x509.Certificate) []byte {
block := pem.Block{
Type: CertificateBlockType,
Bytes: cert.Raw,
}
return pem.EncodeToMemory(&block)
}
// EncodePrivateKeyPEM returns PEM-encoded private key data
func EncodePrivateKeyPEM(key *rsa.PrivateKey) []byte {
block := pem.Block{
Type: RSAPrivateKeyBlockType,
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
return pem.EncodeToMemory(&block)
}
// NewPrivateKey creates an RSA private key
func NewPrivateKey() (*rsa.PrivateKey, error) {
return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
}
// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg *certutil.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, err
}
if len(cfg.CommonName) == 0 {
return nil, fmt.Errorf("must specify a CommonName")
}
if len(cfg.Usages) == 0 {
return nil, fmt.Errorf("must specify at least one ExtKeyUsage")
}
certTmpl := x509.Certificate{
Subject: pkix.Name{
CommonName: cfg.CommonName,
Organization: cfg.Organization,
},
DNSNames: cfg.AltNames.DNSNames,
IPAddresses: cfg.AltNames.IPs,
SerialNumber: serial,
NotBefore: caCert.NotBefore,
NotAfter: time.Now().Add(duration365d).UTC(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: cfg.Usages,
}
certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
if err != nil {
return nil, err
}
return x509.ParseCertificate(certDERBytes)
}