cleanup CSR & CertificateTemplate util code
Signed-off-by: Tim Ramlot <42113979+inteon@users.noreply.github.com>
This commit is contained in:
parent
308c1472aa
commit
1c2662af82
@ -19,6 +19,7 @@ package pki
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Copied from x509.go
|
||||
@ -28,14 +29,40 @@ var (
|
||||
|
||||
// Copied from x509.go
|
||||
type basicConstraints struct {
|
||||
IsCA bool
|
||||
IsCA bool `asn1:"optional"`
|
||||
MaxPathLen int `asn1:"optional,default:-1"`
|
||||
}
|
||||
|
||||
// Adapted from x509.go
|
||||
func MarshalBasicConstraints(isCA bool) (pkix.Extension, error) {
|
||||
func MarshalBasicConstraints(isCA bool, maxPathLen *int) (pkix.Extension, error) {
|
||||
ext := pkix.Extension{Id: OIDExtensionBasicConstraints}
|
||||
|
||||
// A value of -1 causes encoding/asn1 to omit the value as desired.
|
||||
maxPathLenValue := -1
|
||||
if maxPathLen != nil {
|
||||
maxPathLenValue = *maxPathLen
|
||||
}
|
||||
|
||||
var err error
|
||||
ext.Value, err = asn1.Marshal(basicConstraints{isCA})
|
||||
ext.Value, err = asn1.Marshal(basicConstraints{isCA, maxPathLenValue})
|
||||
return ext, err
|
||||
}
|
||||
|
||||
// Adapted from x509.go
|
||||
func UnmarshalBasicConstraints(value []byte) (isCA bool, maxPathLen *int, err error) {
|
||||
var constraints basicConstraints
|
||||
var rest []byte
|
||||
|
||||
if rest, err = asn1.Unmarshal(value, &constraints); err != nil {
|
||||
return isCA, maxPathLen, err
|
||||
} else if len(rest) != 0 {
|
||||
return isCA, maxPathLen, errors.New("x509: trailing data after X.509 BasicConstraints")
|
||||
}
|
||||
|
||||
isCA = constraints.IsCA
|
||||
if constraints.MaxPathLen >= 0 {
|
||||
maxPathLen = new(int)
|
||||
*maxPathLen = constraints.MaxPathLen
|
||||
}
|
||||
return isCA, maxPathLen, nil
|
||||
}
|
||||
|
||||
287
pkg/util/pki/certificatetemplate.go
Normal file
287
pkg/util/pki/certificatetemplate.go
Normal file
@ -0,0 +1,287 @@
|
||||
/*
|
||||
Copyright 2020 The cert-manager 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/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
|
||||
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
|
||||
experimentalapi "github.com/cert-manager/cert-manager/pkg/apis/experimental/v1alpha1"
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
)
|
||||
|
||||
type CertificateTemplateMutator func(*x509.Certificate)
|
||||
|
||||
// CertificateTemplateOverrideDuration returns a CertificateTemplateMutator that overrides the
|
||||
// certificate duration.
|
||||
func CertificateTemplateOverrideDuration(duration time.Duration) CertificateTemplateMutator {
|
||||
return func(cert *x509.Certificate) {
|
||||
cert.NotBefore = time.Now()
|
||||
cert.NotAfter = cert.NotBefore.Add(duration)
|
||||
}
|
||||
}
|
||||
|
||||
// CertificateTemplateOverrideBasicConstraints returns a CertificateTemplateMutator that overrides
|
||||
// the certificate basic constraints.
|
||||
func CertificateTemplateOverrideBasicConstraints(isCA bool, maxPathLen *int) CertificateTemplateMutator {
|
||||
return func(cert *x509.Certificate) {
|
||||
cert.BasicConstraintsValid = true
|
||||
cert.IsCA = isCA
|
||||
if maxPathLen != nil {
|
||||
cert.MaxPathLen = *maxPathLen
|
||||
cert.MaxPathLenZero = *maxPathLen == 0
|
||||
} else {
|
||||
cert.MaxPathLen = 0
|
||||
cert.MaxPathLenZero = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OverrideTemplateKeyUsages returns a CertificateTemplateMutator that overrides the
|
||||
// certificate key usages.
|
||||
func CertificateTemplateOverrideKeyUsages(keyUsage x509.KeyUsage, extKeyUsage []x509.ExtKeyUsage) CertificateTemplateMutator {
|
||||
return func(cert *x509.Certificate) {
|
||||
cert.KeyUsage = keyUsage
|
||||
cert.ExtKeyUsage = extKeyUsage
|
||||
}
|
||||
}
|
||||
|
||||
// CertificateTemplateAddKeyUsages returns a CertificateTemplateMutator that adds the given key usages
|
||||
// to the certificate key usages.
|
||||
func CertificateTemplateAddKeyUsages(keyUsage x509.KeyUsage, extKeyUsage []x509.ExtKeyUsage) CertificateTemplateMutator {
|
||||
return func(cert *x509.Certificate) {
|
||||
cert.KeyUsage |= keyUsage
|
||||
|
||||
OuterLoop:
|
||||
for _, usage := range extKeyUsage {
|
||||
for _, existingUsage := range cert.ExtKeyUsage {
|
||||
if existingUsage == usage {
|
||||
continue OuterLoop
|
||||
}
|
||||
}
|
||||
|
||||
cert.ExtKeyUsage = append(cert.ExtKeyUsage, usage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CertificateTemplateFromCSR will create a x509.Certificate for the
|
||||
// given *x509.CertificateRequest.
|
||||
// Call OverrideTemplateFromOptions to override the duration, isCA, maxPathLen, keyUsage, and extKeyUsage.
|
||||
func CertificateTemplateFromCSR(csr *x509.CertificateRequest, mutators ...CertificateTemplateMutator) (*x509.Certificate, error) {
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
|
||||
}
|
||||
|
||||
cert := &x509.Certificate{
|
||||
// Version must be 2 according to RFC5280.
|
||||
// A version value of 2 confusingly means version 3.
|
||||
// This value isn't used by Go at the time of writing.
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.1
|
||||
Version: 2,
|
||||
SerialNumber: serialNumber,
|
||||
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
|
||||
PublicKey: csr.PublicKey,
|
||||
Subject: csr.Subject,
|
||||
RawSubject: csr.RawSubject,
|
||||
DNSNames: csr.DNSNames,
|
||||
IPAddresses: csr.IPAddresses,
|
||||
EmailAddresses: csr.EmailAddresses,
|
||||
URIs: csr.URIs,
|
||||
}
|
||||
|
||||
// Start by copying all extensions from the CSR
|
||||
extractExtensions := func(template *x509.Certificate, val pkix.Extension) error {
|
||||
// Check the CSR for the X.509 BasicConstraints (RFC 5280, 4.2.1.9)
|
||||
// extension and append to template if necessary
|
||||
if val.Id.Equal(OIDExtensionBasicConstraints) {
|
||||
unmarshalIsCA, unmarshalMaxPathLen, err := UnmarshalBasicConstraints(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template.BasicConstraintsValid = true
|
||||
template.IsCA = unmarshalIsCA
|
||||
if unmarshalMaxPathLen != nil {
|
||||
template.MaxPathLen = *unmarshalMaxPathLen
|
||||
template.MaxPathLenZero = *unmarshalMaxPathLen == 0
|
||||
} else {
|
||||
template.MaxPathLen = 0
|
||||
template.MaxPathLenZero = false
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 5280, 4.2.1.3
|
||||
if val.Id.Equal(OIDExtensionKeyUsage) {
|
||||
usage, err := UnmarshalKeyUsage(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template.KeyUsage = usage
|
||||
}
|
||||
|
||||
if val.Id.Equal(OIDExtensionExtendedKeyUsage) {
|
||||
extUsages, unknownUsages, err := UnmarshalExtKeyUsage(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template.ExtKeyUsage = extUsages
|
||||
template.UnknownExtKeyUsage = unknownUsages
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, val := range csr.Extensions {
|
||||
if err := extractExtensions(cert, val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, val := range csr.ExtraExtensions {
|
||||
if err := extractExtensions(cert, val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, mutator := range mutators {
|
||||
mutator(cert)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// CertificateTemplateFromCSRPEM will create a x509.Certificate for the
|
||||
// given csrPEM.
|
||||
// Call OverrideTemplateFromOptions to override the duration, isCA, maxPathLen, keyUsage, and extKeyUsage.
|
||||
func CertificateTemplateFromCSRPEM(csrPEM []byte, mutators ...CertificateTemplateMutator) (*x509.Certificate, error) {
|
||||
csr, err := DecodeX509CertificateRequestBytes(csrPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := csr.CheckSignature(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CertificateTemplateFromCSR(csr, mutators...)
|
||||
}
|
||||
|
||||
// CertificateTemplateFromCertificate will create a x509.Certificate for the given
|
||||
// Certificate resource
|
||||
func CertificateTemplateFromCertificate(crt *v1.Certificate) (*x509.Certificate, error) {
|
||||
csr, err := GenerateCSR(crt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certDuration := apiutil.DefaultCertDuration(crt.Spec.Duration)
|
||||
keyUsage, extKeyUsage, err := KeyUsagesForCertificateOrCertificateRequest(crt.Spec.Usages, crt.Spec.IsCA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CertificateTemplateFromCSR(
|
||||
csr,
|
||||
CertificateTemplateOverrideDuration(certDuration),
|
||||
CertificateTemplateOverrideBasicConstraints(crt.Spec.IsCA, nil),
|
||||
CertificateTemplateOverrideKeyUsages(keyUsage, extKeyUsage),
|
||||
)
|
||||
}
|
||||
|
||||
// CertificateTemplateFromCertificateRequest will create a x509.Certificate for the given
|
||||
// CertificateRequest resource
|
||||
func CertificateTemplateFromCertificateRequest(cr *v1.CertificateRequest) (*x509.Certificate, error) {
|
||||
certDuration := apiutil.DefaultCertDuration(cr.Spec.Duration)
|
||||
keyUsage, extKeyUsage, err := KeyUsagesForCertificateOrCertificateRequest(cr.Spec.Usages, cr.Spec.IsCA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CertificateTemplateFromCSRPEM(
|
||||
cr.Spec.Request,
|
||||
CertificateTemplateOverrideDuration(certDuration),
|
||||
CertificateTemplateOverrideBasicConstraints(cr.Spec.IsCA, nil),
|
||||
CertificateTemplateOverrideKeyUsages(keyUsage, extKeyUsage),
|
||||
)
|
||||
}
|
||||
|
||||
// CertificateTemplateFromCertificateRequest will create a x509.Certificate for the given
|
||||
// CertificateSigningRequest resource
|
||||
func CertificateTemplateFromCertificateSigningRequest(csr *certificatesv1.CertificateSigningRequest) (*x509.Certificate, error) {
|
||||
duration, err := DurationFromCertificateSigningRequest(csr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ku, eku, err := BuildKeyUsagesKube(csr.Spec.Usages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isCA := csr.Annotations[experimentalapi.CertificateSigningRequestIsCAAnnotationKey] == "true"
|
||||
|
||||
return CertificateTemplateFromCSRPEM(
|
||||
csr.Spec.Request,
|
||||
CertificateTemplateOverrideDuration(duration),
|
||||
CertificateTemplateOverrideBasicConstraints(isCA, nil),
|
||||
CertificateTemplateOverrideKeyUsages(ku, eku),
|
||||
)
|
||||
}
|
||||
|
||||
// Deprecated: use CertificateTemplateFromCertificate instead.
|
||||
func GenerateTemplate(crt *v1.Certificate) (*x509.Certificate, error) {
|
||||
return CertificateTemplateFromCertificate(crt)
|
||||
}
|
||||
|
||||
// Deprecated: use CertificateTemplateFromCertificateRequest instead.
|
||||
func GenerateTemplateFromCertificateRequest(cr *v1.CertificateRequest) (*x509.Certificate, error) {
|
||||
return CertificateTemplateFromCertificateRequest(cr)
|
||||
}
|
||||
|
||||
// Deprecated: use CertificateTemplateFromCertificateSigningRequest instead.
|
||||
func GenerateTemplateFromCertificateSigningRequest(csr *certificatesv1.CertificateSigningRequest) (*x509.Certificate, error) {
|
||||
return CertificateTemplateFromCertificateSigningRequest(csr)
|
||||
}
|
||||
|
||||
// Deprecated: use CertificateTemplateFromCSRPEM instead.
|
||||
func GenerateTemplateFromCSRPEM(csrPEM []byte, duration time.Duration, isCA bool) (*x509.Certificate, error) {
|
||||
return CertificateTemplateFromCSRPEM(
|
||||
csrPEM,
|
||||
CertificateTemplateOverrideDuration(duration),
|
||||
CertificateTemplateOverrideBasicConstraints(isCA, nil),
|
||||
CertificateTemplateOverrideKeyUsages(0, nil),
|
||||
)
|
||||
}
|
||||
|
||||
// Deprecated: use CertificateTemplateFromCSRPEM instead.
|
||||
func GenerateTemplateFromCSRPEMWithUsages(csrPEM []byte, duration time.Duration, isCA bool, keyUsage x509.KeyUsage, extKeyUsage []x509.ExtKeyUsage) (*x509.Certificate, error) {
|
||||
return CertificateTemplateFromCSRPEM(
|
||||
csrPEM,
|
||||
CertificateTemplateOverrideDuration(duration),
|
||||
CertificateTemplateOverrideBasicConstraints(isCA, nil),
|
||||
CertificateTemplateOverrideKeyUsages(keyUsage, extKeyUsage),
|
||||
)
|
||||
}
|
||||
@ -29,7 +29,6 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cert-manager/cert-manager/internal/controller/feature"
|
||||
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
|
||||
@ -164,11 +163,33 @@ func BuildCertManagerKeyUsages(ku x509.KeyUsage, eku []x509.ExtKeyUsage) []v1.Ke
|
||||
return usages
|
||||
}
|
||||
|
||||
type generateCSROptions struct {
|
||||
EncodeBasicConstraintsInRequest bool
|
||||
}
|
||||
|
||||
type GenerateCSROption func(*generateCSROptions)
|
||||
|
||||
// WithEncodeBasicConstraintsInRequest determines whether the BasicConstraints
|
||||
// extension should be encoded in the CSR.
|
||||
// NOTE: this is a temporary option that will be removed in a future release.
|
||||
func WithEncodeBasicConstraintsInRequest(encode bool) GenerateCSROption {
|
||||
return func(o *generateCSROptions) {
|
||||
o.EncodeBasicConstraintsInRequest = encode
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateCSR will generate a new *x509.CertificateRequest template to be used
|
||||
// by issuers that utilise CSRs to obtain Certificates.
|
||||
// The CSR will not be signed, and should be passed to either EncodeCSR or
|
||||
// to the x509.CreateCertificateRequest function.
|
||||
func GenerateCSR(crt *v1.Certificate) (*x509.CertificateRequest, error) {
|
||||
func GenerateCSR(crt *v1.Certificate, optFuncs ...GenerateCSROption) (*x509.CertificateRequest, error) {
|
||||
opts := &generateCSROptions{
|
||||
EncodeBasicConstraintsInRequest: false,
|
||||
}
|
||||
for _, opt := range optFuncs {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
commonName, err := extractCommonName(crt.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -205,8 +226,10 @@ func GenerateCSR(crt *v1.Certificate) (*x509.CertificateRequest, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(feature.UseCertificateRequestBasicConstraints) {
|
||||
extension, err := MarshalBasicConstraints(crt.Spec.IsCA)
|
||||
// NOTE(@inteon): opts.EncodeBasicConstraintsInRequest is a temporary solution and will
|
||||
// be removed/ replaced in a future release.
|
||||
if opts.EncodeBasicConstraintsInRequest {
|
||||
extension, err := MarshalBasicConstraints(crt.Spec.IsCA, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -274,154 +297,6 @@ func buildKeyUsagesExtensionsForCertificate(crt *v1.Certificate) ([]pkix.Extensi
|
||||
return []pkix.Extension{usage, extendedUsages}, nil
|
||||
}
|
||||
|
||||
// GenerateTemplate will create a x509.Certificate for the given Certificate resource.
|
||||
// This should create a Certificate template that is equivalent to the CertificateRequest
|
||||
// generated by GenerateCSR.
|
||||
// The PublicKey field must be populated by the caller.
|
||||
func GenerateTemplate(crt *v1.Certificate) (*x509.Certificate, error) {
|
||||
commonName, err := extractCommonName(crt.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dnsNames := crt.Spec.DNSNames
|
||||
ipAddresses := IPAddressesForCertificate(crt)
|
||||
organization := OrganizationForCertificate(crt)
|
||||
subject := SubjectForCertificate(crt)
|
||||
uris, err := URLsFromStrings(crt.Spec.URIs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyUsages, extKeyUsages, err := KeyUsagesForCertificateOrCertificateRequest(crt.Spec.Usages, crt.Spec.IsCA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(commonName) == 0 && len(dnsNames) == 0 && len(ipAddresses) == 0 && len(uris) == 0 && len(crt.Spec.EmailAddresses) == 0 {
|
||||
return nil, fmt.Errorf("no common name or subject alt names requested on certificate")
|
||||
}
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
|
||||
}
|
||||
|
||||
certDuration := apiutil.DefaultCertDuration(crt.Spec.Duration)
|
||||
|
||||
pubKeyAlgo, _, err := SignatureAlgorithm(crt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert := &x509.Certificate{
|
||||
// Version must be 2 according to RFC5280.
|
||||
// A version value of 2 confusingly means version 3.
|
||||
// This value isn't used by Go at the time of writing.
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.1
|
||||
Version: 2,
|
||||
BasicConstraintsValid: true,
|
||||
SerialNumber: serialNumber,
|
||||
PublicKeyAlgorithm: pubKeyAlgo,
|
||||
IsCA: crt.Spec.IsCA,
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(certDuration),
|
||||
// see http://golang.org/pkg/crypto/x509/#KeyUsage
|
||||
KeyUsage: keyUsages,
|
||||
ExtKeyUsage: extKeyUsages,
|
||||
DNSNames: dnsNames,
|
||||
IPAddresses: ipAddresses,
|
||||
URIs: uris,
|
||||
EmailAddresses: crt.Spec.EmailAddresses,
|
||||
}
|
||||
|
||||
if isLiteralCertificateSubjectEnabled() && len(crt.Spec.LiteralSubject) > 0 {
|
||||
rawSubject, err := ParseSubjectStringToRawDERBytes(crt.Spec.LiteralSubject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert.RawSubject = rawSubject
|
||||
} else {
|
||||
cert.Subject = pkix.Name{
|
||||
Country: subject.Countries,
|
||||
Organization: organization,
|
||||
OrganizationalUnit: subject.OrganizationalUnits,
|
||||
Locality: subject.Localities,
|
||||
Province: subject.Provinces,
|
||||
StreetAddress: subject.StreetAddresses,
|
||||
PostalCode: subject.PostalCodes,
|
||||
SerialNumber: subject.SerialNumber,
|
||||
CommonName: commonName,
|
||||
}
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// GenerateTemplate will create a x509.Certificate for the given
|
||||
// CertificateRequest resource
|
||||
func GenerateTemplateFromCertificateRequest(cr *v1.CertificateRequest) (*x509.Certificate, error) {
|
||||
certDuration := apiutil.DefaultCertDuration(cr.Spec.Duration)
|
||||
keyUsage, extKeyUsage, err := KeyUsagesForCertificateOrCertificateRequest(cr.Spec.Usages, cr.Spec.IsCA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GenerateTemplateFromCSRPEMWithUsages(cr.Spec.Request, certDuration, cr.Spec.IsCA, keyUsage, extKeyUsage)
|
||||
}
|
||||
|
||||
func GenerateTemplateFromCSRPEM(csrPEM []byte, duration time.Duration, isCA bool) (*x509.Certificate, error) {
|
||||
var (
|
||||
ku x509.KeyUsage
|
||||
eku []x509.ExtKeyUsage
|
||||
)
|
||||
return GenerateTemplateFromCSRPEMWithUsages(csrPEM, duration, isCA, ku, eku)
|
||||
}
|
||||
|
||||
func GenerateTemplateFromCSRPEMWithUsages(csrPEM []byte, duration time.Duration, isCA bool, keyUsage x509.KeyUsage, extKeyUsage []x509.ExtKeyUsage) (*x509.Certificate, error) {
|
||||
block, _ := pem.Decode(csrPEM)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode csr")
|
||||
}
|
||||
|
||||
csr, err := x509.ParseCertificateRequest(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := csr.CheckSignature(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
|
||||
}
|
||||
|
||||
return &x509.Certificate{
|
||||
// Version must be 2 according to RFC5280.
|
||||
// A version value of 2 confusingly means version 3.
|
||||
// This value isn't used by Go at the time of writing.
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.1
|
||||
Version: 2,
|
||||
BasicConstraintsValid: true,
|
||||
SerialNumber: serialNumber,
|
||||
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
|
||||
PublicKey: csr.PublicKey,
|
||||
IsCA: isCA,
|
||||
Subject: csr.Subject,
|
||||
RawSubject: csr.RawSubject,
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(duration),
|
||||
// see http://golang.org/pkg/crypto/x509/#KeyUsage
|
||||
KeyUsage: keyUsage,
|
||||
ExtKeyUsage: extKeyUsage,
|
||||
DNSNames: csr.DNSNames,
|
||||
IPAddresses: csr.IPAddresses,
|
||||
EmailAddresses: csr.EmailAddresses,
|
||||
URIs: csr.URIs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SignCertificate returns a signed *x509.Certificate given a template
|
||||
// *x509.Certificate crt and an issuer.
|
||||
// publicKey is the public key of the signee, and signerKey is the private
|
||||
@ -430,7 +305,6 @@ func GenerateTemplateFromCSRPEMWithUsages(csrPEM []byte, duration time.Duration,
|
||||
// which can be used for reading the encoded values.
|
||||
func SignCertificate(template *x509.Certificate, issuerCert *x509.Certificate, publicKey crypto.PublicKey, signerKey interface{}) ([]byte, *x509.Certificate, error) {
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
@ -432,7 +432,7 @@ func TestGenerateCSR(t *testing.T) {
|
||||
|
||||
basicConstraintsGenerator := func(isCA bool) ([]byte, error) {
|
||||
return asn1.Marshal(struct {
|
||||
IsCA bool
|
||||
IsCA bool `asn1:"optional"`
|
||||
}{
|
||||
IsCA: isCA,
|
||||
})
|
||||
@ -615,8 +615,10 @@ func TestGenerateCSR(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, feature.LiteralCertificateSubject, tt.literalCertificateSubjectFeatureEnabled)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, feature.UseCertificateRequestBasicConstraints, tt.basicConstraintsFeatureEnabled)()
|
||||
got, err := GenerateCSR(tt.crt)
|
||||
got, err := GenerateCSR(
|
||||
tt.crt,
|
||||
WithEncodeBasicConstraintsInRequest(tt.basicConstraintsFeatureEnabled),
|
||||
)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GenerateCSR() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
||||
@ -145,6 +145,26 @@ func MarshalKeyUsage(usage x509.KeyUsage) (pkix.Extension, error) {
|
||||
return ext, err
|
||||
}
|
||||
|
||||
func UnmarshalKeyUsage(value []byte) (usage x509.KeyUsage, err error) {
|
||||
var asn1bits asn1.BitString
|
||||
var rest []byte
|
||||
|
||||
if rest, err = asn1.Unmarshal(value, &asn1bits); err != nil {
|
||||
return usage, err
|
||||
} else if len(rest) != 0 {
|
||||
return usage, errors.New("x509: trailing data after X.509 KeyUsage")
|
||||
}
|
||||
|
||||
var usageInt int
|
||||
for i := 0; i < 9; i++ {
|
||||
if asn1bits.At(i) != 0 {
|
||||
usageInt |= 1 << uint(i)
|
||||
}
|
||||
}
|
||||
|
||||
return x509.KeyUsage(usageInt), nil
|
||||
}
|
||||
|
||||
// Adapted from x509.go
|
||||
func MarshalExtKeyUsage(extUsages []x509.ExtKeyUsage, unknownUsages []asn1.ObjectIdentifier) (pkix.Extension, error) {
|
||||
ext := pkix.Extension{Id: OIDExtensionExtendedKeyUsage}
|
||||
@ -164,3 +184,24 @@ func MarshalExtKeyUsage(extUsages []x509.ExtKeyUsage, unknownUsages []asn1.Objec
|
||||
ext.Value, err = asn1.Marshal(oids)
|
||||
return ext, err
|
||||
}
|
||||
|
||||
func UnmarshalExtKeyUsage(value []byte) (extUsages []x509.ExtKeyUsage, unknownUsages []asn1.ObjectIdentifier, err error) {
|
||||
var asn1ExtendedUsages []asn1.ObjectIdentifier
|
||||
var rest []byte
|
||||
|
||||
if rest, err = asn1.Unmarshal(value, &asn1ExtendedUsages); err != nil {
|
||||
return extUsages, unknownUsages, err
|
||||
} else if len(rest) != 0 {
|
||||
return extUsages, unknownUsages, errors.New("x509: trailing data after X.509 ExtendedKeyUsage")
|
||||
}
|
||||
|
||||
for _, asnExtUsage := range asn1ExtendedUsages {
|
||||
if eku, ok := ExtKeyUsageFromOID(asnExtUsage); ok {
|
||||
extUsages = append(extUsages, eku)
|
||||
} else {
|
||||
unknownUsages = append(unknownUsages, asnExtUsage)
|
||||
}
|
||||
}
|
||||
|
||||
return extUsages, unknownUsages, nil
|
||||
}
|
||||
|
||||
@ -28,24 +28,6 @@ import (
|
||||
experimentalapi "github.com/cert-manager/cert-manager/pkg/apis/experimental/v1alpha1"
|
||||
)
|
||||
|
||||
// GenerateTemplateFromCertificateSigningRequest will create an
|
||||
// *x509.Certificate from the given CertificateSigningRequest resource
|
||||
func GenerateTemplateFromCertificateSigningRequest(csr *certificatesv1.CertificateSigningRequest) (*x509.Certificate, error) {
|
||||
duration, err := DurationFromCertificateSigningRequest(csr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ku, eku, err := BuildKeyUsagesKube(csr.Spec.Usages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isCA := csr.Annotations[experimentalapi.CertificateSigningRequestIsCAAnnotationKey] == "true"
|
||||
|
||||
return GenerateTemplateFromCSRPEMWithUsages(csr.Spec.Request, duration, isCA, ku, eku)
|
||||
}
|
||||
|
||||
// DurationFromCertificateSigningRequest returns the duration that the user may
|
||||
// have requested using the annotation
|
||||
// "experimental.cert-manager.io/request-duration" or via the CSR
|
||||
|
||||
Loading…
Reference in New Issue
Block a user