cleanup CSR & CertificateTemplate util code

Signed-off-by: Tim Ramlot <42113979+inteon@users.noreply.github.com>
This commit is contained in:
Tim Ramlot 2023-05-09 15:09:37 +02:00
parent 308c1472aa
commit 1c2662af82
No known key found for this signature in database
GPG Key ID: 47428728E0C2878D
6 changed files with 390 additions and 177 deletions

View File

@ -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
}

View 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),
)
}

View File

@ -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())
}

View File

@ -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

View File

@ -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
}

View File

@ -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