CA: Choose issuer cert based on CSR's PublicKeyAlgorithm (#5042)
The ca's configuration already has support for containing multiple issuers. However, when it comes time to actually sign a (pre)cert, it always uses the defaultIssuer. This change has the ca instead choose which issuer to use based on the PublicKeyAlgorithm requested in the CSR (or, for final cert issuances, based on the PublicKeyAlgorithm in the precert). This will allow us to use our RSA issuers to sign certificates for users who aren't ready to switch to ECDSA, while immediately switching to our new ECDSA chain for subscribers who want to use it. Fixed #5027
This commit is contained in:
parent
050a60f810
commit
00133dc6c3
176
ca/ca.go
176
ca/ca.go
|
@ -111,17 +111,25 @@ const (
|
|||
certType = certificateType("certificate")
|
||||
)
|
||||
|
||||
// Three maps of keys to internalIssuers. Lookup by PublicKeyAlgorithm is
|
||||
// useful for determining which issuer to use to sign a given (pre)cert, based
|
||||
// on its PublicKeyAlgorithm. Lookup by CommonName is useful for determining
|
||||
// which issuer to use to sign an OCSP response, based on the cert's
|
||||
// Issuer CN. Lookup by ID is useful for the same functionality, in cases
|
||||
// where features.StoreIssuerInfo is true and the OCSP request is identified
|
||||
// by Serial and IssuerID rather than by the full cert.
|
||||
type issuerMaps struct {
|
||||
byAlg map[x509.PublicKeyAlgorithm]*internalIssuer
|
||||
byName map[string]*internalIssuer
|
||||
byID map[int64]*internalIssuer
|
||||
}
|
||||
|
||||
// CertificateAuthorityImpl represents a CA that signs certificates, CRLs, and
|
||||
// OCSP responses.
|
||||
type CertificateAuthorityImpl struct {
|
||||
rsaProfile string
|
||||
ecdsaProfile string
|
||||
// A map from issuer cert common name to an internalIssuer struct
|
||||
issuers map[string]*internalIssuer
|
||||
// A map from issuer ID to internalIssuer
|
||||
idToIssuer map[int64]*internalIssuer
|
||||
// The common name of the default issuer cert
|
||||
defaultIssuer *internalIssuer
|
||||
rsaProfile string
|
||||
ecdsaProfile string
|
||||
issuers issuerMaps
|
||||
sa certificateStorage
|
||||
pa core.PolicyAuthority
|
||||
keyPolicy goodkey.KeyPolicy
|
||||
|
@ -164,54 +172,82 @@ type internalIssuer struct {
|
|||
boulderSigner *bsigner.Signer
|
||||
}
|
||||
|
||||
func makeInternalIssuers(issuers []bsigner.Config, lifespanOCSP time.Duration) (map[string]*internalIssuer, error) {
|
||||
internalIssuers := make(map[string]*internalIssuer, len(issuers))
|
||||
func makeInternalIssuers(issuers []bsigner.Config, lifespanOCSP time.Duration) (issuerMaps, error) {
|
||||
issuersByAlg := make(map[x509.PublicKeyAlgorithm]*internalIssuer, 2)
|
||||
issuersByName := make(map[string]*internalIssuer, len(issuers))
|
||||
issuersByID := make(map[int64]*internalIssuer, len(issuers))
|
||||
for _, issuer := range issuers {
|
||||
signer, err := bsigner.NewSigner(issuer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return issuerMaps{}, err
|
||||
}
|
||||
if internalIssuers[issuer.Issuer.Subject.CommonName] != nil {
|
||||
return nil, errors.New("Multiple issuer certs with the same CommonName are not supported")
|
||||
}
|
||||
internalIssuers[issuer.Issuer.Subject.CommonName] = &internalIssuer{
|
||||
ii := &internalIssuer{
|
||||
cert: issuer.Issuer,
|
||||
ocspSigner: issuer.Signer,
|
||||
boulderSigner: signer,
|
||||
}
|
||||
if issuer.Profile.UseForRSALeaves {
|
||||
if issuersByAlg[x509.RSA] != nil {
|
||||
return issuerMaps{}, errors.New("Multiple issuer certs for RSA are not allowed")
|
||||
}
|
||||
issuersByAlg[x509.RSA] = ii
|
||||
}
|
||||
if issuer.Profile.UseForECDSALeaves {
|
||||
if issuersByAlg[x509.ECDSA] != nil {
|
||||
return issuerMaps{}, errors.New("Multiple issuer certs for ECDSA are not allowed")
|
||||
}
|
||||
issuersByAlg[x509.ECDSA] = ii
|
||||
}
|
||||
if issuersByName[issuer.Issuer.Subject.CommonName] != nil {
|
||||
return issuerMaps{}, errors.New("Multiple issuer certs with the same CommonName are not supported")
|
||||
}
|
||||
issuersByName[issuer.Issuer.Subject.CommonName] = ii
|
||||
issuersByID[idForIssuer(issuer.Issuer)] = ii
|
||||
}
|
||||
return internalIssuers, nil
|
||||
return issuerMaps{issuersByAlg, issuersByName, issuersByID}, nil
|
||||
}
|
||||
|
||||
func makeCFSSLInternalIssuers(
|
||||
issuers []Issuer,
|
||||
policy *cfsslConfig.Signing,
|
||||
lifespanOCSP time.Duration,
|
||||
) (map[string]*internalIssuer, error) {
|
||||
func makeCFSSLInternalIssuers(issuers []Issuer, policy *cfsslConfig.Signing, lifespanOCSP time.Duration) (issuerMaps, error) {
|
||||
if len(issuers) == 0 {
|
||||
return nil, errors.New("No issuers specified.")
|
||||
return issuerMaps{}, errors.New("No issuers specified.")
|
||||
}
|
||||
internalIssuers := make(map[string]*internalIssuer)
|
||||
for _, iss := range issuers {
|
||||
issuersByAlg := make(map[x509.PublicKeyAlgorithm]*internalIssuer, len(issuers))
|
||||
issuersByName := make(map[string]*internalIssuer, len(issuers))
|
||||
issuersByID := make(map[int64]*internalIssuer, len(issuers))
|
||||
for idx, iss := range issuers {
|
||||
if iss.Cert == nil || iss.Signer == nil {
|
||||
return nil, errors.New("Issuer with nil cert or signer specified.")
|
||||
return issuerMaps{}, errors.New("Issuer with nil cert or signer specified.")
|
||||
}
|
||||
cfsslSigner, err := local.NewSigner(iss.Signer, iss.Cert, x509.SHA256WithRSA, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return issuerMaps{}, err
|
||||
}
|
||||
cn := iss.Cert.Subject.CommonName
|
||||
if issuersByName[cn] != nil {
|
||||
return issuerMaps{}, errors.New("Multiple issuer certs with the same CommonName are not supported")
|
||||
}
|
||||
|
||||
cn := iss.Cert.Subject.CommonName
|
||||
if internalIssuers[cn] != nil {
|
||||
return nil, errors.New("Multiple issuer certs with the same CommonName are not supported")
|
||||
}
|
||||
internalIssuers[cn] = &internalIssuer{
|
||||
ii := &internalIssuer{
|
||||
cert: iss.Cert,
|
||||
cfsslSigner: cfsslSigner,
|
||||
ocspSigner: iss.Signer,
|
||||
}
|
||||
|
||||
// Rather than reading a config to pick which issuer to use for each alg,
|
||||
// just fall back to our old behavior of "the first issuer is used by default
|
||||
// for everything". Ensure that the first issuer is an RSA key so that signing
|
||||
// with x509.SHA256WithRSA doesn't break.
|
||||
if idx == 0 {
|
||||
if iss.Cert.PublicKeyAlgorithm != x509.RSA {
|
||||
return issuerMaps{}, errors.New("Default (first) issuer must be RSA when using CFSSL")
|
||||
}
|
||||
issuersByAlg[x509.RSA] = ii
|
||||
issuersByAlg[x509.ECDSA] = ii
|
||||
}
|
||||
issuersByName[cn] = ii
|
||||
issuersByID[idForIssuer(iss.Cert)] = ii
|
||||
}
|
||||
return internalIssuers, nil
|
||||
return issuerMaps{issuersByAlg, issuersByName, issuersByID}, nil
|
||||
}
|
||||
|
||||
// idForIssuer generates a stable ID for an issuer certificate. This
|
||||
|
@ -245,17 +281,15 @@ func NewCertificateAuthorityImpl(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var internalIssuers map[string]*internalIssuer
|
||||
var defaultIssuer *internalIssuer
|
||||
var issuers issuerMaps
|
||||
// rsaProfile and ecdsaProfile are unused when using the boulder signer
|
||||
// instead of the CFSSL signer
|
||||
var rsaProfile, ecdsaProfile string
|
||||
if features.Enabled(features.NonCFSSLSigner) {
|
||||
internalIssuers, err = makeInternalIssuers(boulderIssuers, config.LifespanOCSP.Duration)
|
||||
issuers, err = makeInternalIssuers(boulderIssuers, config.LifespanOCSP.Duration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultIssuer = internalIssuers[boulderIssuers[0].Issuer.Subject.CommonName]
|
||||
} else {
|
||||
// CFSSL requires processing JSON configs through its own LoadConfig, so we
|
||||
// serialize and then deserialize.
|
||||
|
@ -278,7 +312,7 @@ func NewCertificateAuthorityImpl(
|
|||
}
|
||||
}
|
||||
|
||||
internalIssuers, err = makeCFSSLInternalIssuers(
|
||||
issuers, err = makeCFSSLInternalIssuers(
|
||||
cfsslIssuers,
|
||||
cfsslConfigObj.Signing,
|
||||
config.LifespanOCSP.Duration)
|
||||
|
@ -291,7 +325,6 @@ func NewCertificateAuthorityImpl(
|
|||
if rsaProfile == "" || ecdsaProfile == "" {
|
||||
return nil, errors.New("must specify rsaProfile and ecdsaProfile")
|
||||
}
|
||||
defaultIssuer = internalIssuers[cfsslIssuers[0].Cert.Subject.CommonName]
|
||||
}
|
||||
|
||||
csrExtensionCount := prometheus.NewCounterVec(
|
||||
|
@ -335,8 +368,7 @@ func NewCertificateAuthorityImpl(
|
|||
ca = &CertificateAuthorityImpl{
|
||||
sa: sa,
|
||||
pa: pa,
|
||||
issuers: internalIssuers,
|
||||
defaultIssuer: defaultIssuer,
|
||||
issuers: issuers,
|
||||
rsaProfile: rsaProfile,
|
||||
ecdsaProfile: ecdsaProfile,
|
||||
prefix: config.SerialPrefix,
|
||||
|
@ -352,12 +384,6 @@ func NewCertificateAuthorityImpl(
|
|||
signErrorCounter: signErrorCounter,
|
||||
}
|
||||
|
||||
ca.idToIssuer = make(map[int64]*internalIssuer)
|
||||
for _, ii := range ca.issuers {
|
||||
id := idForIssuer(ii.cert)
|
||||
ca.idToIssuer[id] = ii
|
||||
}
|
||||
|
||||
if config.Expiry == "" {
|
||||
return nil, errors.New("Config must specify an expiry period.")
|
||||
}
|
||||
|
@ -484,7 +510,7 @@ func (ca *CertificateAuthorityImpl) GenerateOCSP(ctx context.Context, req *capb.
|
|||
}
|
||||
serial = serialInt
|
||||
var ok bool
|
||||
issuer, ok = ca.idToIssuer[req.IssuerID]
|
||||
issuer, ok = ca.issuers.byID[req.IssuerID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("This CA doesn't have an issuer cert with ID %d", req.IssuerID)
|
||||
}
|
||||
|
@ -497,7 +523,7 @@ func (ca *CertificateAuthorityImpl) GenerateOCSP(ctx context.Context, req *capb.
|
|||
|
||||
serial = cert.SerialNumber
|
||||
cn := cert.Issuer.CommonName
|
||||
issuer = ca.issuers[cn]
|
||||
issuer = ca.issuers.byName[cn]
|
||||
if issuer == nil {
|
||||
return nil, fmt.Errorf("This CA doesn't have an issuer cert with CommonName %q", cn)
|
||||
}
|
||||
|
@ -554,7 +580,7 @@ func (ca *CertificateAuthorityImpl) IssuePrecertificate(ctx context.Context, iss
|
|||
return nil, err
|
||||
}
|
||||
|
||||
precertDER, err := ca.issuePrecertificateInner(ctx, issueReq, serialBigInt, validity)
|
||||
precertDER, issuer, err := ca.issuePrecertificateInner(ctx, issueReq, serialBigInt, validity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -570,17 +596,13 @@ func (ca *CertificateAuthorityImpl) IssuePrecertificate(ctx context.Context, iss
|
|||
}
|
||||
|
||||
req := &sapb.AddCertificateRequest{
|
||||
Der: precertDER,
|
||||
RegID: regID,
|
||||
Ocsp: ocspResp.Response,
|
||||
Issued: nowNanos,
|
||||
Der: precertDER,
|
||||
RegID: regID,
|
||||
Ocsp: ocspResp.Response,
|
||||
Issued: nowNanos,
|
||||
IssuerID: idForIssuer(issuer.cert),
|
||||
}
|
||||
|
||||
// we currently only use one issuer, in the future when we support multiple
|
||||
// the issuer will need to be derived from issueReq
|
||||
issuerID := idForIssuer(ca.defaultIssuer.cert)
|
||||
req.IssuerID = issuerID
|
||||
|
||||
_, err = ca.sa.AddPrecertificate(ctx, req)
|
||||
if err != nil {
|
||||
ca.orphanCount.With(prometheus.Labels{"type": "precert"}).Inc()
|
||||
|
@ -656,18 +678,23 @@ func (ca *CertificateAuthorityImpl) IssueCertificateForPrecertificate(ctx contex
|
|||
scts = append(scts, sct)
|
||||
}
|
||||
|
||||
issuer, ok := ca.issuers.byAlg[precert.PublicKeyAlgorithm]
|
||||
if !ok {
|
||||
return nil, berrors.InternalServerError("no issuer found for public key algorithm %s", precert.PublicKeyAlgorithm)
|
||||
}
|
||||
|
||||
var certDER []byte
|
||||
if features.Enabled(features.NonCFSSLSigner) {
|
||||
issuanceReq, err := bsigner.RequestFromPrecert(precert, scts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certDER, err = ca.defaultIssuer.boulderSigner.Issue(issuanceReq)
|
||||
certDER, err = issuer.boulderSigner.Issue(issuanceReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
certPEM, err := ca.defaultIssuer.cfsslSigner.SignFromPrecert(precert, scts)
|
||||
certPEM, err := issuer.cfsslSigner.SignFromPrecert(precert, scts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -729,10 +756,10 @@ func (ca *CertificateAuthorityImpl) generateSerialNumberAndValidity() (*big.Int,
|
|||
return serialBigInt, validity, nil
|
||||
}
|
||||
|
||||
func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context, issueReq *capb.IssueCertificateRequest, serialBigInt *big.Int, validity validity) ([]byte, error) {
|
||||
func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context, issueReq *capb.IssueCertificateRequest, serialBigInt *big.Int, validity validity) ([]byte, *internalIssuer, error) {
|
||||
csr, err := x509.ParseCertificateRequest(issueReq.Csr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := csrlib.VerifyCSR(
|
||||
|
@ -746,20 +773,23 @@ func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
|
|||
ca.log.AuditErr(err.Error())
|
||||
// VerifyCSR returns berror instances that can be passed through as-is
|
||||
// without wrapping.
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
extensions, err := ca.extensionsFromCSR(csr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
issuer := ca.defaultIssuer
|
||||
issuer, ok := ca.issuers.byAlg[csr.PublicKeyAlgorithm]
|
||||
if !ok {
|
||||
return nil, nil, berrors.InternalServerError("no issuer found for public key algorithm %s", csr.PublicKeyAlgorithm)
|
||||
}
|
||||
|
||||
if issuer.cert.NotAfter.Before(validity.NotAfter) {
|
||||
err = berrors.InternalServerError("cannot issue a certificate that expires after the issuer certificate")
|
||||
ca.log.AuditErr(err.Error())
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
serialHex := core.SerialToString(serialBigInt)
|
||||
|
@ -782,7 +812,7 @@ func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
|
|||
if err != nil {
|
||||
err = berrors.InternalServerError("failed to sign certificate: %s", err)
|
||||
ca.log.AuditErrf("Signing failed: serial=[%s] err=[%v]", serialHex, err)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
// Convert the CSR to PEM
|
||||
|
@ -800,7 +830,7 @@ func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
|
|||
default:
|
||||
err = berrors.InternalServerError("unsupported key type %T", csr.PublicKey)
|
||||
ca.log.AuditErr(err.Error())
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Send the cert off for signing
|
||||
|
@ -833,25 +863,25 @@ func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
|
|||
lintErrsJSON, _ := json.Marshal(lErr.ErrorResults)
|
||||
ca.log.AuditErrf("Signing failed: serial=[%s] err=[%v] lintErrors=%s",
|
||||
serialHex, err, string(lintErrsJSON))
|
||||
return nil, berrors.InternalServerError("failed to sign certificate: %s", err)
|
||||
return nil, nil, berrors.InternalServerError("failed to sign certificate: %s", err)
|
||||
}
|
||||
|
||||
err = berrors.InternalServerError("failed to sign certificate: %s", err)
|
||||
ca.log.AuditErrf("Signing failed: serial=[%s] err=[%v]", serialHex, err)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(certPEM) == 0 {
|
||||
err = berrors.InternalServerError("no certificate returned by server")
|
||||
ca.log.AuditErrf("PEM empty from Signer: serial=[%s] err=[%v]", serialHex, err)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil || block.Type != "CERTIFICATE" {
|
||||
err = berrors.InternalServerError("invalid certificate value returned")
|
||||
ca.log.AuditErrf("PEM decode error, aborting: serial=[%s] pem=[%s] err=[%v]", serialHex, certPEM, err)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
certDER = block.Bytes
|
||||
}
|
||||
|
@ -861,7 +891,7 @@ func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
|
|||
serialHex, strings.Join(csr.DNSNames, ", "), hex.EncodeToString(csr.Raw),
|
||||
hex.EncodeToString(certDER))
|
||||
|
||||
return certDER, nil
|
||||
return certDER, issuer, nil
|
||||
}
|
||||
|
||||
func (ca *CertificateAuthorityImpl) storeCertificate(
|
||||
|
|
|
@ -267,15 +267,15 @@ func setup(t *testing.T) *testCtx {
|
|||
Signer: caKey,
|
||||
Clk: fc,
|
||||
Profile: bsigner.ProfileConfig{
|
||||
AllowECDSAKeys: true,
|
||||
AllowRSAKeys: true,
|
||||
AllowMustStaple: true,
|
||||
AllowCTPoison: true,
|
||||
AllowSCTList: true,
|
||||
AllowCommonName: true,
|
||||
IssuerURL: "http://not-example.com/issuer-url",
|
||||
OCSPURL: "http://not-example.com/ocsp",
|
||||
CRLURL: "http://not-example.com/crl",
|
||||
UseForECDSALeaves: true,
|
||||
UseForRSALeaves: true,
|
||||
AllowMustStaple: true,
|
||||
AllowCTPoison: true,
|
||||
AllowSCTList: true,
|
||||
AllowCommonName: true,
|
||||
IssuerURL: "http://not-example.com/issuer-url",
|
||||
OCSPURL: "http://not-example.com/ocsp",
|
||||
CRLURL: "http://not-example.com/crl",
|
||||
Policies: []bsigner.PolicyInformation{
|
||||
{OID: "2.23.140.1.2.1"},
|
||||
},
|
||||
|
@ -1223,7 +1223,8 @@ func TestIssuePrecertificateLinting(t *testing.T) {
|
|||
|
||||
// Reconfigure the CA's cfsslSigner to be a linttrapSigner that always returns
|
||||
// two LintResults.
|
||||
ca.defaultIssuer.cfsslSigner = &linttrapSigner{
|
||||
rsaIssuer := ca.issuers.byAlg[x509.RSA]
|
||||
rsaIssuer.cfsslSigner = &linttrapSigner{
|
||||
lintErr: &local.LintError{
|
||||
ErrorResults: map[string]lint.LintResult{
|
||||
"foobar": {
|
||||
|
@ -1286,8 +1287,9 @@ func TestGenerateOCSPWithIssuerID(t *testing.T) {
|
|||
test.AssertError(t, err, "GenerateOCSP didn't fail with invalid IssuerID")
|
||||
|
||||
// GenerateOCSP with feature enabled + req contains good IssuerID
|
||||
rsaIssuer := ca.issuers.byAlg[x509.RSA]
|
||||
_, err = ca.GenerateOCSP(context.Background(), &capb.GenerateOCSPRequest{
|
||||
IssuerID: idForIssuer(ca.defaultIssuer.cert),
|
||||
IssuerID: idForIssuer(rsaIssuer.cert),
|
||||
Serial: "DEADDEADDEADDEADDEADDEADDEADDEADDEAD",
|
||||
Status: string(core.OCSPStatusGood),
|
||||
})
|
||||
|
|
|
@ -239,7 +239,6 @@ func TestRootConfigValidate(t *testing.T) {
|
|||
SkipLints: []string{
|
||||
"e_ext_authority_key_identifier_missing",
|
||||
"e_ext_authority_key_identifier_no_key_identifier",
|
||||
"e_sub_ca_aia_does_not_contain_ocsp_url",
|
||||
"e_sub_ca_aia_missing",
|
||||
"e_sub_ca_certificate_policies_missing",
|
||||
"e_sub_ca_crl_distribution_points_missing",
|
||||
|
|
|
@ -46,8 +46,8 @@ type IssuanceRequest struct {
|
|||
}
|
||||
|
||||
type signingProfile struct {
|
||||
allowRSAKeys bool
|
||||
allowECDSAKeys bool
|
||||
useForRSALeaves bool
|
||||
useForECDSALeaves bool
|
||||
|
||||
allowMustStaple bool
|
||||
allowCTPoison bool
|
||||
|
@ -78,8 +78,9 @@ type PolicyInformation struct {
|
|||
|
||||
// ProfileConfig describes the certificate issuance constraints
|
||||
type ProfileConfig struct {
|
||||
AllowRSAKeys bool
|
||||
AllowECDSAKeys bool
|
||||
UseForRSALeaves bool
|
||||
UseForECDSALeaves bool
|
||||
|
||||
AllowMustStaple bool
|
||||
AllowCTPoison bool
|
||||
AllowSCTList bool
|
||||
|
@ -114,17 +115,17 @@ var stringToQualifierType = map[string]asn1.ObjectIdentifier{
|
|||
|
||||
func newProfile(config ProfileConfig) (*signingProfile, error) {
|
||||
sp := &signingProfile{
|
||||
allowRSAKeys: config.AllowRSAKeys,
|
||||
allowECDSAKeys: config.AllowECDSAKeys,
|
||||
allowMustStaple: config.AllowMustStaple,
|
||||
allowCTPoison: config.AllowCTPoison,
|
||||
allowSCTList: config.AllowSCTList,
|
||||
allowCommonName: config.AllowCommonName,
|
||||
issuerURL: config.IssuerURL,
|
||||
crlURL: config.CRLURL,
|
||||
ocspURL: config.OCSPURL,
|
||||
maxBackdate: config.MaxValidityBackdate.Duration,
|
||||
maxValidity: config.MaxValidityPeriod.Duration,
|
||||
useForRSALeaves: config.UseForRSALeaves,
|
||||
useForECDSALeaves: config.UseForECDSALeaves,
|
||||
allowMustStaple: config.AllowMustStaple,
|
||||
allowCTPoison: config.AllowCTPoison,
|
||||
allowSCTList: config.AllowSCTList,
|
||||
allowCommonName: config.AllowCommonName,
|
||||
issuerURL: config.IssuerURL,
|
||||
crlURL: config.CRLURL,
|
||||
ocspURL: config.OCSPURL,
|
||||
maxBackdate: config.MaxValidityBackdate.Duration,
|
||||
maxValidity: config.MaxValidityPeriod.Duration,
|
||||
}
|
||||
if config.IssuerURL == "" {
|
||||
return nil, errors.New("Issuer URL is required")
|
||||
|
@ -170,12 +171,12 @@ func newProfile(config ProfileConfig) (*signingProfile, error) {
|
|||
func (p *signingProfile) requestValid(clk clock.Clock, req *IssuanceRequest) error {
|
||||
switch req.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if !p.allowRSAKeys {
|
||||
return errors.New("RSA keys not allowed")
|
||||
if !p.useForRSALeaves {
|
||||
return errors.New("cannot sign RSA public keys")
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
if !p.allowECDSAKeys {
|
||||
return errors.New("ECDSA keys not allowed")
|
||||
if !p.useForECDSALeaves {
|
||||
return errors.New("cannot sign ECDSA public keys")
|
||||
}
|
||||
default:
|
||||
return errors.New("unsupported public key type")
|
||||
|
|
|
@ -25,14 +25,14 @@ import (
|
|||
|
||||
func defaultProfileConfig() ProfileConfig {
|
||||
return ProfileConfig{
|
||||
AllowECDSAKeys: true,
|
||||
AllowRSAKeys: true,
|
||||
AllowCommonName: true,
|
||||
AllowCTPoison: true,
|
||||
AllowSCTList: true,
|
||||
AllowMustStaple: true,
|
||||
IssuerURL: "http://issuer-url",
|
||||
OCSPURL: "http://ocsp-url",
|
||||
UseForECDSALeaves: true,
|
||||
UseForRSALeaves: true,
|
||||
AllowCommonName: true,
|
||||
AllowCTPoison: true,
|
||||
AllowSCTList: true,
|
||||
AllowMustStaple: true,
|
||||
IssuerURL: "http://issuer-url",
|
||||
OCSPURL: "http://ocsp-url",
|
||||
Policies: []PolicyInformation{
|
||||
{OID: "1.2.3"},
|
||||
},
|
||||
|
@ -55,14 +55,14 @@ func TestNewProfilePolicies(t *testing.T) {
|
|||
profile, err := newProfile(config)
|
||||
test.AssertNotError(t, err, "newProfile failed")
|
||||
test.AssertDeepEquals(t, *profile, signingProfile{
|
||||
allowRSAKeys: true,
|
||||
allowECDSAKeys: true,
|
||||
allowMustStaple: true,
|
||||
allowCTPoison: true,
|
||||
allowSCTList: true,
|
||||
allowCommonName: true,
|
||||
issuerURL: "http://issuer-url",
|
||||
ocspURL: "http://ocsp-url",
|
||||
useForRSALeaves: true,
|
||||
useForECDSALeaves: true,
|
||||
allowMustStaple: true,
|
||||
allowCTPoison: true,
|
||||
allowSCTList: true,
|
||||
allowCommonName: true,
|
||||
issuerURL: "http://issuer-url",
|
||||
ocspURL: "http://ocsp-url",
|
||||
policies: &pkix.Extension{
|
||||
Id: asn1.ObjectIdentifier{2, 5, 29, 32},
|
||||
Value: []byte{48, 36, 48, 4, 6, 2, 42, 3, 48, 28, 6, 3, 42, 3, 4, 48, 21, 48, 19, 6, 8, 43, 6, 1, 5, 5, 7, 2, 1, 22, 7, 99, 112, 115, 45, 117, 114, 108},
|
||||
|
@ -142,21 +142,21 @@ func TestRequestValid(t *testing.T) {
|
|||
expectedError: "unsupported public key type",
|
||||
},
|
||||
{
|
||||
name: "rsa keys not allowed",
|
||||
name: "cannot sign rsa",
|
||||
profile: &signingProfile{},
|
||||
request: &IssuanceRequest{PublicKey: &rsa.PublicKey{}},
|
||||
expectedError: "RSA keys not allowed",
|
||||
expectedError: "cannot sign RSA public keys",
|
||||
},
|
||||
{
|
||||
name: "ecdsa keys not allowed",
|
||||
name: "cannot sign ecdsa",
|
||||
profile: &signingProfile{},
|
||||
request: &IssuanceRequest{PublicKey: &ecdsa.PublicKey{}},
|
||||
expectedError: "ECDSA keys not allowed",
|
||||
expectedError: "cannot sign ECDSA public keys",
|
||||
},
|
||||
{
|
||||
name: "must staple not allowed",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
useForECDSALeaves: true,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -167,7 +167,7 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "ct poison not allowed",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
useForECDSALeaves: true,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -178,7 +178,7 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "sct list not allowed",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
useForECDSALeaves: true,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -189,9 +189,9 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "sct list and ct poison not allowed",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
allowCTPoison: true,
|
||||
allowSCTList: true,
|
||||
useForECDSALeaves: true,
|
||||
allowCTPoison: true,
|
||||
allowSCTList: true,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -203,7 +203,7 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "common name not allowed",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
useForECDSALeaves: true,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -214,7 +214,7 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "negative validity",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
useForECDSALeaves: true,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -226,8 +226,8 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "validity larger than max",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
maxValidity: time.Minute,
|
||||
useForECDSALeaves: true,
|
||||
maxValidity: time.Minute,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -239,9 +239,9 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "validity backdated more than max",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
maxBackdate: time.Hour,
|
||||
useForECDSALeaves: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
maxBackdate: time.Hour,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -253,9 +253,9 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "validity is forward dated",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
maxBackdate: time.Hour,
|
||||
useForECDSALeaves: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
maxBackdate: time.Hour,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -267,8 +267,8 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "serial too short",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
useForECDSALeaves: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -280,8 +280,8 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "serial too long",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
useForECDSALeaves: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
@ -294,8 +294,8 @@ func TestRequestValid(t *testing.T) {
|
|||
{
|
||||
name: "good",
|
||||
profile: &signingProfile{
|
||||
allowECDSAKeys: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
useForECDSALeaves: true,
|
||||
maxValidity: time.Hour * 2,
|
||||
},
|
||||
request: &IssuanceRequest{
|
||||
PublicKey: &ecdsa.PublicKey{},
|
||||
|
|
|
@ -23,7 +23,6 @@ certificate-profile:
|
|||
skip-lints:
|
||||
- e_ext_authority_key_identifier_missing
|
||||
- e_ext_authority_key_identifier_no_key_identifier
|
||||
- e_sub_ca_aia_does_not_contain_ocsp_url
|
||||
- e_sub_ca_aia_missing
|
||||
- e_sub_ca_certificate_policies_missing
|
||||
- e_sub_ca_crl_distribution_points_missing
|
||||
|
|
Loading…
Reference in New Issue