mirror of https://github.com/kubernetes/kops.git
Move cert issuance code to pki module
This commit is contained in:
parent
2bfdf8a9c3
commit
c142483cfa
|
|
@ -35,7 +35,6 @@ go_library(
|
||||||
"import.go",
|
"import.go",
|
||||||
"import_cluster.go",
|
"import_cluster.go",
|
||||||
"main.go",
|
"main.go",
|
||||||
"pkix.go",
|
|
||||||
"replace.go",
|
"replace.go",
|
||||||
"rollingupdate.go",
|
"rollingupdate.go",
|
||||||
"rollingupdatecluster.go",
|
"rollingupdatecluster.go",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
|
"k8s.io/kops/pkg/pki"
|
||||||
"k8s.io/kops/upup/pkg/fi"
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
"k8s.io/kubectl/pkg/util/i18n"
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
|
|
@ -176,8 +177,8 @@ func describeKeypair(keyStore fi.CAStore, item *fi.KeystoreItem, w *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
if cert != nil {
|
if cert != nil {
|
||||||
fmt.Fprintf(w, "Subject:\t%s\n", pkixNameToString(&cert.Certificate.Subject))
|
fmt.Fprintf(w, "Subject:\t%s\n", pki.PkixNameToString(&cert.Certificate.Subject))
|
||||||
fmt.Fprintf(w, "Issuer:\t%s\n", pkixNameToString(&cert.Certificate.Issuer))
|
fmt.Fprintf(w, "Issuer:\t%s\n", pki.PkixNameToString(&cert.Certificate.Issuer))
|
||||||
fmt.Fprintf(w, "AlternateNames:\t%s\n", strings.Join(alternateNames, ", "))
|
fmt.Fprintf(w, "AlternateNames:\t%s\n", strings.Join(alternateNames, ", "))
|
||||||
fmt.Fprintf(w, "CA:\t%v\n", cert.IsCA)
|
fmt.Fprintf(w, "CA:\t%v\n", cert.IsCA)
|
||||||
fmt.Fprintf(w, "NotAfter:\t%s\n", cert.Certificate.NotAfter)
|
fmt.Fprintf(w, "NotAfter:\t%s\n", cert.Certificate.NotAfter)
|
||||||
|
|
|
||||||
|
|
@ -1,58 +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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func pkixNameToString(name *pkix.Name) string {
|
|
||||||
seq := name.ToRDNSequence()
|
|
||||||
var s bytes.Buffer
|
|
||||||
for _, rdnSet := range seq {
|
|
||||||
for _, rdn := range rdnSet {
|
|
||||||
if s.Len() != 0 {
|
|
||||||
s.WriteString(",")
|
|
||||||
}
|
|
||||||
key := ""
|
|
||||||
t := rdn.Type
|
|
||||||
if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
|
|
||||||
switch t[3] {
|
|
||||||
case 3:
|
|
||||||
key = "cn"
|
|
||||||
case 5:
|
|
||||||
key = "serial"
|
|
||||||
case 6:
|
|
||||||
key = "c"
|
|
||||||
case 7:
|
|
||||||
key = "l"
|
|
||||||
case 10:
|
|
||||||
key = "o"
|
|
||||||
case 11:
|
|
||||||
key = "ou"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if key == "" {
|
|
||||||
key = t.String()
|
|
||||||
}
|
|
||||||
s.WriteString(fmt.Sprintf("%v=%v", key, rdn.Value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.String()
|
|
||||||
}
|
|
||||||
|
|
@ -3,8 +3,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"cert_utils.go",
|
||||||
"certificate.go",
|
"certificate.go",
|
||||||
"csr.go",
|
"csr.go",
|
||||||
|
"issue.go",
|
||||||
"privatekey.go",
|
"privatekey.go",
|
||||||
"sshkey.go",
|
"sshkey.go",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,20 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package fitasks
|
package pki
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pkixNameToString(name *pkix.Name) string {
|
func PkixNameToString(name *pkix.Name) string {
|
||||||
seq := name.ToRDNSequence()
|
seq := name.ToRDNSequence()
|
||||||
var s bytes.Buffer
|
var s bytes.Buffer
|
||||||
for _, rdnSet := range seq {
|
for _, rdnSet := range seq {
|
||||||
|
|
@ -61,32 +62,6 @@ func pkixNameToString(name *pkix.Name) string {
|
||||||
return s.String()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePkixName(s string) (*pkix.Name, error) {
|
|
||||||
name := new(pkix.Name)
|
|
||||||
|
|
||||||
tokens := strings.Split(s, ",")
|
|
||||||
for _, token := range tokens {
|
|
||||||
token = strings.TrimSpace(token)
|
|
||||||
kv := strings.SplitN(token, "=", 2)
|
|
||||||
if len(kv) != 2 {
|
|
||||||
return nil, fmt.Errorf("unrecognized token (expected k=v): %q", token)
|
|
||||||
}
|
|
||||||
k := strings.ToLower(kv[0])
|
|
||||||
v := kv[1]
|
|
||||||
|
|
||||||
switch k {
|
|
||||||
case "cn":
|
|
||||||
name.CommonName = v
|
|
||||||
case "o":
|
|
||||||
name.Organization = append(name.Organization, v)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unrecognized key %q in token %q", k, token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyUsageStrings = map[x509.KeyUsage]string{
|
var keyUsageStrings = map[x509.KeyUsage]string{
|
||||||
x509.KeyUsageDigitalSignature: "KeyUsageDigitalSignature",
|
x509.KeyUsageDigitalSignature: "KeyUsageDigitalSignature",
|
||||||
x509.KeyUsageContentCommitment: "KeyUsageContentCommitment",
|
x509.KeyUsageContentCommitment: "KeyUsageContentCommitment",
|
||||||
|
|
@ -153,3 +128,29 @@ func parseExtKeyUsage(s string) (x509.ExtKeyUsage, bool) {
|
||||||
}
|
}
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildTypeDescription extracts the type based on the certificate extensions
|
||||||
|
func BuildTypeDescription(cert *x509.Certificate) string {
|
||||||
|
var options []string
|
||||||
|
|
||||||
|
if cert.IsCA {
|
||||||
|
options = append(options, "CA")
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, keyUsageToString(cert.KeyUsage)...)
|
||||||
|
|
||||||
|
for _, extKeyUsage := range cert.ExtKeyUsage {
|
||||||
|
options = append(options, extKeyUsageToString(extKeyUsage))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(options)
|
||||||
|
s := strings.Join(options, ",")
|
||||||
|
|
||||||
|
for k, v := range wellKnownCertificateTypes {
|
||||||
|
if v == s {
|
||||||
|
s = k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
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/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wellKnownCertificateTypes = map[string]string{
|
||||||
|
"ca": "CA,KeyUsageCRLSign,KeyUsageCertSign",
|
||||||
|
"client": "ExtKeyUsageClientAuth,KeyUsageDigitalSignature",
|
||||||
|
"clientServer": "ExtKeyUsageClientAuth,ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
|
||||||
|
"server": "ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
|
||||||
|
}
|
||||||
|
|
||||||
|
type IssueCertRequest struct {
|
||||||
|
// Signer is the keypair to use to sign.
|
||||||
|
Signer string
|
||||||
|
// Type is the type of certificate i.e. CA, server, client etc.
|
||||||
|
Type string
|
||||||
|
// Subject is the certificate subject.
|
||||||
|
Subject pkix.Name
|
||||||
|
// AlternateNames is a list of alternative names for this certificate.
|
||||||
|
AlternateNames []string
|
||||||
|
|
||||||
|
// PrivateKey is the private key for this certificate. If nil, a new private key will be generated.
|
||||||
|
PrivateKey *PrivateKey
|
||||||
|
|
||||||
|
// Serial is the certificate serial number. If nil, a random number will be generated.
|
||||||
|
Serial *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Keystore interface {
|
||||||
|
// FindKeypair finds a cert & private key, returning nil where either is not found
|
||||||
|
// (if the certificate is found but not keypair, that is not an error: only the cert will be returned).
|
||||||
|
// This func returns a cert, private key and a bool. The bool value is whether the keypair is stored
|
||||||
|
// in a legacy format. This bool is used by a keypair
|
||||||
|
// task to convert a Legacy Keypair to the new Keypair API format.
|
||||||
|
FindKeypair(name string) (*Certificate, *PrivateKey, bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IssueCert(request *IssueCertRequest, keystore Keystore) (*Certificate, *PrivateKey, error) {
|
||||||
|
certificateType := request.Type
|
||||||
|
if expanded, found := wellKnownCertificateTypes[certificateType]; found {
|
||||||
|
certificateType = expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
template := &x509.Certificate{
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: false,
|
||||||
|
SerialNumber: request.Serial,
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := strings.Split(certificateType, ",")
|
||||||
|
for _, t := range tokens {
|
||||||
|
if strings.HasPrefix(t, "KeyUsage") {
|
||||||
|
ku, found := parseKeyUsage(t)
|
||||||
|
if !found {
|
||||||
|
return nil, nil, fmt.Errorf("unrecognized certificate option: %v", t)
|
||||||
|
}
|
||||||
|
template.KeyUsage |= ku
|
||||||
|
} else if strings.HasPrefix(t, "ExtKeyUsage") {
|
||||||
|
ku, found := parseExtKeyUsage(t)
|
||||||
|
if !found {
|
||||||
|
return nil, nil, fmt.Errorf("unrecognized certificate option: %v", t)
|
||||||
|
}
|
||||||
|
template.ExtKeyUsage = append(template.ExtKeyUsage, ku)
|
||||||
|
} else if t == "CA" {
|
||||||
|
template.IsCA = true
|
||||||
|
} else {
|
||||||
|
return nil, nil, fmt.Errorf("unrecognized certificate option: %q", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template.Subject = request.Subject
|
||||||
|
|
||||||
|
var alternateNames []string
|
||||||
|
alternateNames = append(alternateNames, request.AlternateNames...)
|
||||||
|
|
||||||
|
for _, san := range alternateNames {
|
||||||
|
san = strings.TrimSpace(san)
|
||||||
|
if san == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ip := net.ParseIP(san); ip != nil {
|
||||||
|
template.IPAddresses = append(template.IPAddresses, ip)
|
||||||
|
} else {
|
||||||
|
template.DNSNames = append(template.DNSNames, san)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var caCertificate *x509.Certificate
|
||||||
|
var caPrivateKey *PrivateKey
|
||||||
|
if !template.IsCA {
|
||||||
|
var err error
|
||||||
|
var caCert *Certificate
|
||||||
|
caCert, caPrivateKey, _, err = keystore.FindKeypair(request.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if caPrivateKey == nil {
|
||||||
|
return nil, nil, fmt.Errorf("ca key for %q was not found; cannot issue certificates", request.Signer)
|
||||||
|
}
|
||||||
|
if caCert == nil {
|
||||||
|
return nil, nil, fmt.Errorf("ca certificate for %q was not found; cannot issue certificates", request.Signer)
|
||||||
|
}
|
||||||
|
caCertificate = caCert.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey := request.PrivateKey
|
||||||
|
if privateKey == nil {
|
||||||
|
var err error
|
||||||
|
privateKey, err = GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate, err := SignNewCertificate(privateKey, template, caCertificate, caPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificate, privateKey, err
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"cert_utils.go",
|
|
||||||
"keypair.go",
|
"keypair.go",
|
||||||
"keypair_fitask.go",
|
"keypair_fitask.go",
|
||||||
"managedfile.go",
|
"managedfile.go",
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,8 @@ limitations under the License.
|
||||||
package fitasks
|
package fitasks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"net"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -31,13 +28,6 @@ import (
|
||||||
"k8s.io/kops/upup/pkg/fi"
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
)
|
)
|
||||||
|
|
||||||
var wellKnownCertificateTypes = map[string]string{
|
|
||||||
"ca": "CA,KeyUsageCRLSign,KeyUsageCertSign",
|
|
||||||
"client": "ExtKeyUsageClientAuth,KeyUsageDigitalSignature",
|
|
||||||
"clientServer": "ExtKeyUsageClientAuth,ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
|
|
||||||
"server": "ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:generate fitask -type=Keypair
|
//go:generate fitask -type=Keypair
|
||||||
type Keypair struct {
|
type Keypair struct {
|
||||||
// Name is the name of the keypair
|
// Name is the name of the keypair
|
||||||
|
|
@ -100,12 +90,12 @@ func (e *Keypair) Find(c *fi.Context) (*Keypair, error) {
|
||||||
actual := &Keypair{
|
actual := &Keypair{
|
||||||
Name: &name,
|
Name: &name,
|
||||||
AlternateNames: alternateNames,
|
AlternateNames: alternateNames,
|
||||||
Subject: pkixNameToString(&cert.Subject),
|
Subject: pki.PkixNameToString(&cert.Subject),
|
||||||
Type: buildTypeDescription(cert.Certificate),
|
Type: pki.BuildTypeDescription(cert.Certificate),
|
||||||
LegacyFormat: legacyFormat,
|
LegacyFormat: legacyFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
actual.Signer = &Keypair{Subject: pkixNameToString(&cert.Certificate.Issuer)}
|
actual.Signer = &Keypair{Subject: pki.PkixNameToString(&cert.Certificate.Issuer)}
|
||||||
|
|
||||||
// Avoid spurious changes
|
// Avoid spurious changes
|
||||||
actual.Lifecycle = e.Lifecycle
|
actual.Lifecycle = e.Lifecycle
|
||||||
|
|
@ -227,7 +217,7 @@ func (_ *Keypair) Render(c *fi.Context, a, e, changes *Keypair) error {
|
||||||
return fmt.Errorf("subject name was empty for SSL keypair %q", *e.Name)
|
return fmt.Errorf("subject name was empty for SSL keypair %q", *e.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
req := issueCertRequest{
|
req := pki.IssueCertRequest{
|
||||||
Signer: signer,
|
Signer: signer,
|
||||||
Type: e.Type,
|
Type: e.Type,
|
||||||
Subject: *subjectPkix,
|
Subject: *subjectPkix,
|
||||||
|
|
@ -235,7 +225,7 @@ func (_ *Keypair) Render(c *fi.Context, a, e, changes *Keypair) error {
|
||||||
PrivateKey: privateKey,
|
PrivateKey: privateKey,
|
||||||
Serial: serial,
|
Serial: serial,
|
||||||
}
|
}
|
||||||
cert, privateKey, err := issueCert(&req, c.Keystore)
|
cert, privateKey, err := pki.IssueCert(&req, c.Keystore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -273,130 +263,28 @@ func (_ *Keypair) Render(c *fi.Context, a, e, changes *Keypair) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type issueCertRequest struct {
|
func parsePkixName(s string) (*pkix.Name, error) {
|
||||||
// Signer is the keypair to use to sign.
|
name := new(pkix.Name)
|
||||||
Signer string
|
|
||||||
// Type is the type of certificate i.e. CA, server, client etc.
|
|
||||||
Type string
|
|
||||||
// Subject is the certificate subject.
|
|
||||||
Subject pkix.Name
|
|
||||||
// AlternateNames is a list of alternative names for this certificate.
|
|
||||||
AlternateNames []string
|
|
||||||
|
|
||||||
// PrivateKey is the private key for this certificate. If nil, a new private key will be generated.
|
tokens := strings.Split(s, ",")
|
||||||
PrivateKey *pki.PrivateKey
|
for _, token := range tokens {
|
||||||
|
token = strings.TrimSpace(token)
|
||||||
|
kv := strings.SplitN(token, "=", 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return nil, fmt.Errorf("unrecognized token (expected k=v): %q", token)
|
||||||
|
}
|
||||||
|
k := strings.ToLower(kv[0])
|
||||||
|
v := kv[1]
|
||||||
|
|
||||||
// Serial is the certificate serial number. If nil, a random number will be generated.
|
switch k {
|
||||||
Serial *big.Int
|
case "cn":
|
||||||
}
|
name.CommonName = v
|
||||||
|
case "o":
|
||||||
func issueCert(request *issueCertRequest, keystore fi.Keystore) (*pki.Certificate, *pki.PrivateKey, error) {
|
name.Organization = append(name.Organization, v)
|
||||||
certificateType := request.Type
|
default:
|
||||||
if expanded, found := wellKnownCertificateTypes[certificateType]; found {
|
return nil, fmt.Errorf("unrecognized key %q in token %q", k, token)
|
||||||
certificateType = expanded
|
|
||||||
}
|
|
||||||
|
|
||||||
template := &x509.Certificate{
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
IsCA: false,
|
|
||||||
SerialNumber: request.Serial,
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens := strings.Split(certificateType, ",")
|
|
||||||
for _, t := range tokens {
|
|
||||||
if strings.HasPrefix(t, "KeyUsage") {
|
|
||||||
ku, found := parseKeyUsage(t)
|
|
||||||
if !found {
|
|
||||||
return nil, nil, fmt.Errorf("unrecognized certificate option: %v", t)
|
|
||||||
}
|
|
||||||
template.KeyUsage |= ku
|
|
||||||
} else if strings.HasPrefix(t, "ExtKeyUsage") {
|
|
||||||
ku, found := parseExtKeyUsage(t)
|
|
||||||
if !found {
|
|
||||||
return nil, nil, fmt.Errorf("unrecognized certificate option: %v", t)
|
|
||||||
}
|
|
||||||
template.ExtKeyUsage = append(template.ExtKeyUsage, ku)
|
|
||||||
} else if t == "CA" {
|
|
||||||
template.IsCA = true
|
|
||||||
} else {
|
|
||||||
return nil, nil, fmt.Errorf("unrecognized certificate option: %q", t)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template.Subject = request.Subject
|
return name, nil
|
||||||
|
|
||||||
var alternateNames []string
|
|
||||||
alternateNames = append(alternateNames, request.AlternateNames...)
|
|
||||||
|
|
||||||
for _, san := range alternateNames {
|
|
||||||
san = strings.TrimSpace(san)
|
|
||||||
if san == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ip := net.ParseIP(san); ip != nil {
|
|
||||||
template.IPAddresses = append(template.IPAddresses, ip)
|
|
||||||
} else {
|
|
||||||
template.DNSNames = append(template.DNSNames, san)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var caCertificate *x509.Certificate
|
|
||||||
var caPrivateKey *pki.PrivateKey
|
|
||||||
if !template.IsCA {
|
|
||||||
var err error
|
|
||||||
var caCert *pki.Certificate
|
|
||||||
caCert, caPrivateKey, _, err = keystore.FindKeypair(request.Signer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if caPrivateKey == nil {
|
|
||||||
return nil, nil, fmt.Errorf("ca key for %q was not found; cannot issue certificates", request.Signer)
|
|
||||||
}
|
|
||||||
if caCert == nil {
|
|
||||||
return nil, nil, fmt.Errorf("ca certificate for %q was not found; cannot issue certificates", request.Signer)
|
|
||||||
}
|
|
||||||
caCertificate = caCert.Certificate
|
|
||||||
}
|
|
||||||
|
|
||||||
privateKey := request.PrivateKey
|
|
||||||
if privateKey == nil {
|
|
||||||
var err error
|
|
||||||
privateKey, err = pki.GeneratePrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
certificate, err := pki.SignNewCertificate(privateKey, template, caCertificate, caPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return certificate, privateKey, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildTypeDescription extracts the type based on the certificate extensions
|
|
||||||
func buildTypeDescription(cert *x509.Certificate) string {
|
|
||||||
var options []string
|
|
||||||
|
|
||||||
if cert.IsCA {
|
|
||||||
options = append(options, "CA")
|
|
||||||
}
|
|
||||||
|
|
||||||
options = append(options, keyUsageToString(cert.KeyUsage)...)
|
|
||||||
|
|
||||||
for _, extKeyUsage := range cert.ExtKeyUsage {
|
|
||||||
options = append(options, extKeyUsageToString(extKeyUsage))
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(options)
|
|
||||||
s := strings.Join(options, ",")
|
|
||||||
|
|
||||||
for k, v := range wellKnownCertificateTypes {
|
|
||||||
if v == s {
|
|
||||||
s = k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue