parent
23e14f1149
commit
585319f247
55
ca/ca.go
55
ca/ca.go
|
@ -60,7 +60,6 @@ type issuanceEvent struct {
|
|||
Issuer string
|
||||
OrderID int64
|
||||
Profile string
|
||||
ProfileHash string
|
||||
Requester int64
|
||||
Result struct {
|
||||
Precertificate string `json:",omitempty"`
|
||||
|
@ -79,20 +78,10 @@ type issuerMaps struct {
|
|||
|
||||
type certProfileWithID struct {
|
||||
// name is a human readable name used to refer to the certificate profile.
|
||||
name string
|
||||
// hash is SHA256 sum over every exported field of an issuance.ProfileConfig
|
||||
// used to generate the embedded *issuance.Profile.
|
||||
hash [32]byte
|
||||
name string
|
||||
profile *issuance.Profile
|
||||
}
|
||||
|
||||
// certProfilesMaps allows looking up the human-readable name of a certificate
|
||||
// profile to retrieve the actual profile.
|
||||
type certProfilesMaps struct {
|
||||
profileByHash map[[32]byte]*certProfileWithID
|
||||
profileByName map[string]*certProfileWithID
|
||||
}
|
||||
|
||||
// caMetrics holds various metrics which are shared between caImpl, ocspImpl,
|
||||
// and crlImpl.
|
||||
type caMetrics struct {
|
||||
|
@ -150,7 +139,7 @@ type certificateAuthorityImpl struct {
|
|||
sctClient rapb.SCTProviderClient
|
||||
pa core.PolicyAuthority
|
||||
issuers issuerMaps
|
||||
certProfiles certProfilesMaps
|
||||
certProfiles map[string]*certProfileWithID
|
||||
|
||||
// The prefix is prepended to the serial number.
|
||||
prefix byte
|
||||
|
@ -190,46 +179,27 @@ func makeIssuerMaps(issuers []*issuance.Issuer) (issuerMaps, error) {
|
|||
}
|
||||
|
||||
// makeCertificateProfilesMap processes a set of named certificate issuance
|
||||
// profile configs into a two pre-computed maps: 1) a human-readable name to the
|
||||
// profile and 2) a unique hash over contents of the profile to the profile
|
||||
// itself. It returns the maps or an error if a duplicate name or hash is found.
|
||||
//
|
||||
// The unique hash is used in the case of
|
||||
// - RA instructs CA1 to issue a precertificate
|
||||
// - CA1 returns the precertificate DER bytes and profile hash to the RA
|
||||
// - RA instructs CA2 to issue a final certificate, but CA2 does not contain a
|
||||
// profile corresponding to that hash and an issuance is prevented.
|
||||
func makeCertificateProfilesMap(profiles map[string]*issuance.ProfileConfig) (certProfilesMaps, error) {
|
||||
// profile configs into a map from name to profile.
|
||||
func makeCertificateProfilesMap(profiles map[string]*issuance.ProfileConfig) (map[string]*certProfileWithID, error) {
|
||||
if len(profiles) <= 0 {
|
||||
return certProfilesMaps{}, fmt.Errorf("must pass at least one certificate profile")
|
||||
return nil, fmt.Errorf("must pass at least one certificate profile")
|
||||
}
|
||||
|
||||
profilesByName := make(map[string]*certProfileWithID, len(profiles))
|
||||
profilesByHash := make(map[[32]byte]*certProfileWithID, len(profiles))
|
||||
|
||||
for name, profileConfig := range profiles {
|
||||
profile, err := issuance.NewProfile(profileConfig)
|
||||
if err != nil {
|
||||
return certProfilesMaps{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash := profile.Hash()
|
||||
|
||||
withID := certProfileWithID{
|
||||
profilesByName[name] = &certProfileWithID{
|
||||
name: name,
|
||||
hash: hash,
|
||||
profile: profile,
|
||||
}
|
||||
|
||||
profilesByName[name] = &withID
|
||||
_, found := profilesByHash[hash]
|
||||
if found {
|
||||
return certProfilesMaps{}, fmt.Errorf("duplicate certificate profile hash %d", hash)
|
||||
}
|
||||
profilesByHash[hash] = &withID
|
||||
}
|
||||
|
||||
return certProfilesMaps{profilesByHash, profilesByName}, nil
|
||||
return profilesByName, nil
|
||||
}
|
||||
|
||||
// NewCertificateAuthorityImpl creates a CA instance that can sign certificates
|
||||
|
@ -300,8 +270,7 @@ var ocspStatusToCode = map[string]int{
|
|||
// precertificate.
|
||||
//
|
||||
// Subsequent final issuance based on this precertificate must happen at most once, and must use the same
|
||||
// certificate profile. The certificate profile is identified by a hash to ensure an exact match even if
|
||||
// the configuration for a specific profile _name_ changes.
|
||||
// certificate profile.
|
||||
//
|
||||
// Returns precertificate DER.
|
||||
//
|
||||
|
@ -349,7 +318,7 @@ func (ca *certificateAuthorityImpl) IssueCertificate(ctx context.Context, issueR
|
|||
}
|
||||
|
||||
// All issuance requests must come with a profile name, and the RA handles selecting the default.
|
||||
certProfile, ok := ca.certProfiles.profileByName[issueReq.CertProfileName]
|
||||
certProfile, ok := ca.certProfiles[issueReq.CertProfileName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("the CA is incapable of using a profile named %s", issueReq.CertProfileName)
|
||||
}
|
||||
|
@ -444,7 +413,6 @@ func (ca *certificateAuthorityImpl) issueCertificateForPrecertificate(ctx contex
|
|||
Issuer: issuer.Name(),
|
||||
OrderID: orderID,
|
||||
Profile: certProfile.name,
|
||||
ProfileHash: hex.EncodeToString(certProfile.hash[:]),
|
||||
Requester: regID,
|
||||
}
|
||||
ca.log.AuditObject("Signing cert", logEvent)
|
||||
|
@ -611,7 +579,6 @@ func (ca *certificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
|
|||
IssuanceRequest: req,
|
||||
Issuer: issuer.Name(),
|
||||
Profile: certProfile.name,
|
||||
ProfileHash: hex.EncodeToString(certProfile.hash[:]),
|
||||
Requester: issueReq.RegistrationID,
|
||||
OrderID: issueReq.OrderID,
|
||||
}
|
||||
|
@ -645,7 +612,7 @@ func (ca *certificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
|
|||
logEvent.CSR = ""
|
||||
ca.log.AuditObject("Signing precert success", logEvent)
|
||||
|
||||
return certDER, &certProfileWithID{certProfile.name, certProfile.hash, nil}, nil
|
||||
return certDER, &certProfileWithID{certProfile.name, nil}, nil
|
||||
}
|
||||
|
||||
// verifyTBSCertIsDeterministic verifies that x509.CreateCertificate signing
|
||||
|
|
|
@ -333,7 +333,7 @@ func TestIssuePrecertificate(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Certificate request failed to parse")
|
||||
issueReq := &capb.IssueCertificateRequest{Csr: testCase.csr, RegistrationID: mrand.Int63(), OrderID: mrand.Int63()}
|
||||
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
certDER, err := ca.issuePrecertificate(ctx, profile, issueReq)
|
||||
test.AssertNotError(t, err, "Failed to issue precertificate")
|
||||
|
||||
|
@ -444,7 +444,7 @@ func TestMultipleIssuers(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to remake CA")
|
||||
|
||||
// Test that an RSA CSR gets issuance from an RSA issuer.
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
issuedCertDER, err := ca.issuePrecertificate(ctx, profile, &capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: mrand.Int63(), OrderID: mrand.Int63()})
|
||||
test.AssertNotError(t, err, "Failed to issue certificate")
|
||||
cert, err := x509.ParseCertificate(issuedCertDER)
|
||||
|
@ -529,7 +529,7 @@ func TestUnpredictableIssuance(t *testing.T) {
|
|||
req := &capb.IssueCertificateRequest{Csr: ECDSACSR, RegistrationID: mrand.Int63(), OrderID: mrand.Int63()}
|
||||
seenE2 := false
|
||||
seenR3 := false
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
for i := 0; i < 20; i++ {
|
||||
precertDER, err := ca.issuePrecertificate(ctx, profile, req)
|
||||
test.AssertNotError(t, err, "Failed to issue test certificate")
|
||||
|
@ -552,22 +552,11 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
|||
testCtx := setup(t)
|
||||
test.AssertEquals(t, len(testCtx.certProfiles), 2)
|
||||
|
||||
testProfile := issuance.ProfileConfig{
|
||||
AllowMustStaple: false,
|
||||
MaxValidityPeriod: config.Duration{Duration: time.Hour * 24 * 90},
|
||||
MaxValidityBackdate: config.Duration{Duration: time.Hour},
|
||||
}
|
||||
|
||||
type nameToHash struct {
|
||||
name string
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
profileConfigs map[string]*issuance.ProfileConfig
|
||||
expectedErrSubstr string
|
||||
expectedProfiles []nameToHash
|
||||
expectedProfiles []string
|
||||
}{
|
||||
{
|
||||
name: "nil profile map",
|
||||
|
@ -579,39 +568,17 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
|||
profileConfigs: map[string]*issuance.ProfileConfig{},
|
||||
expectedErrSubstr: "at least one certificate profile",
|
||||
},
|
||||
{
|
||||
name: "duplicate hash",
|
||||
profileConfigs: map[string]*issuance.ProfileConfig{
|
||||
"default": &testProfile,
|
||||
"default2": &testProfile,
|
||||
},
|
||||
expectedErrSubstr: "duplicate certificate profile hash",
|
||||
},
|
||||
{
|
||||
name: "empty profile config",
|
||||
profileConfigs: map[string]*issuance.ProfileConfig{
|
||||
"empty": {},
|
||||
},
|
||||
expectedProfiles: []nameToHash{
|
||||
{
|
||||
name: "empty",
|
||||
hash: [32]byte{0xe4, 0xf6, 0xd, 0xa, 0xa6, 0xd7, 0xf3, 0xd3, 0xb6, 0xa6, 0x49, 0x4b, 0x1c, 0x86, 0x1b, 0x99, 0xf6, 0x49, 0xc6, 0xf9, 0xec, 0x51, 0xab, 0xaf, 0x20, 0x1b, 0x20, 0xf2, 0x97, 0x32, 0x7c, 0x95},
|
||||
},
|
||||
},
|
||||
expectedProfiles: []string{"empty"},
|
||||
},
|
||||
{
|
||||
name: "default profiles from setup func",
|
||||
profileConfigs: testCtx.certProfiles,
|
||||
expectedProfiles: []nameToHash{
|
||||
{
|
||||
name: "legacy",
|
||||
hash: [32]byte{0xb7, 0xd9, 0x7e, 0xfc, 0x5a, 0xdd, 0xc7, 0xfe, 0xc, 0xea, 0xed, 0x7b, 0x8c, 0xf5, 0x4, 0x57, 0x71, 0x97, 0x42, 0x80, 0xbe, 0x4d, 0x14, 0xa, 0x35, 0x9a, 0x89, 0xc3, 0x7a, 0x57, 0x41, 0xb7},
|
||||
},
|
||||
{
|
||||
name: "modern",
|
||||
hash: [32]byte{0x2e, 0x82, 0x9b, 0xe4, 0x4d, 0xac, 0xfc, 0x2d, 0x83, 0xbf, 0x62, 0xe5, 0xe1, 0x50, 0xe8, 0xba, 0xd2, 0x66, 0x1a, 0xb3, 0xf2, 0xe7, 0xb5, 0xf2, 0x24, 0x94, 0x1f, 0x83, 0xc6, 0x57, 0xe, 0x58},
|
||||
},
|
||||
},
|
||||
name: "default profiles from setup func",
|
||||
profileConfigs: testCtx.certProfiles,
|
||||
expectedProfiles: []string{"legacy", "modern"},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -628,17 +595,14 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
|||
}
|
||||
|
||||
if tc.expectedProfiles != nil {
|
||||
test.AssertEquals(t, len(profiles.profileByName), len(tc.expectedProfiles))
|
||||
test.AssertEquals(t, len(profiles), len(tc.expectedProfiles))
|
||||
}
|
||||
|
||||
for _, expected := range tc.expectedProfiles {
|
||||
cpwid, ok := profiles.profileByName[expected.name]
|
||||
test.Assert(t, ok, fmt.Sprintf("expected profile %q not found", expected.name))
|
||||
test.AssertEquals(t, cpwid.hash, expected.hash)
|
||||
cpwid, ok := profiles[expected]
|
||||
test.Assert(t, ok, fmt.Sprintf("expected profile %q not found", expected))
|
||||
|
||||
cpwid, ok = profiles.profileByHash[expected.hash]
|
||||
test.Assert(t, ok, fmt.Sprintf("expected profile %q not found", expected.hash))
|
||||
test.AssertEquals(t, cpwid.name, expected.name)
|
||||
test.AssertEquals(t, cpwid.name, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -711,7 +675,7 @@ func TestInvalidCSRs(t *testing.T) {
|
|||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
serializedCSR := mustRead(testCase.csrPath)
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
issueReq := &capb.IssueCertificateRequest{Csr: serializedCSR, RegistrationID: mrand.Int63(), OrderID: mrand.Int63(), CertProfileName: "legacy"}
|
||||
_, err = ca.issuePrecertificate(ctx, profile, issueReq)
|
||||
|
||||
|
@ -749,7 +713,7 @@ func TestRejectValidityTooLong(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to create CA")
|
||||
|
||||
// Test that the CA rejects CSRs that would expire after the intermediate cert
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
_, err = ca.issuePrecertificate(ctx, profile, &capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: mrand.Int63(), OrderID: mrand.Int63(), CertProfileName: "legacy"})
|
||||
test.AssertError(t, err, "Cannot issue a certificate that expires after the intermediate certificate")
|
||||
test.AssertErrorIs(t, err, berrors.InternalServer)
|
||||
|
@ -842,7 +806,7 @@ func TestIssueCertificateForPrecertificate(t *testing.T) {
|
|||
testCtx.fc)
|
||||
test.AssertNotError(t, err, "Failed to create CA")
|
||||
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
issueReq := capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: mrand.Int63(), OrderID: mrand.Int63(), CertProfileName: "legacy"}
|
||||
precertDER, err := ca.issuePrecertificate(ctx, profile, &issueReq)
|
||||
test.AssertNotError(t, err, "Failed to issue precert")
|
||||
|
@ -905,7 +869,7 @@ func TestIssueCertificateForPrecertificateWithSpecificCertificateProfile(t *test
|
|||
test.AssertNotError(t, err, "Failed to create CA")
|
||||
|
||||
selectedProfile := "modern"
|
||||
certProfile, ok := ca.certProfiles.profileByName[selectedProfile]
|
||||
certProfile, ok := ca.certProfiles[selectedProfile]
|
||||
test.Assert(t, ok, "Certificate profile was expected to exist")
|
||||
|
||||
issueReq := capb.IssueCertificateRequest{
|
||||
|
@ -1024,7 +988,7 @@ func TestIssueCertificateForPrecertificateDuplicateSerial(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
issueReq := capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: mrand.Int63(), OrderID: mrand.Int63(), CertProfileName: "legacy"}
|
||||
precertDER, err := ca.issuePrecertificate(ctx, profile, &issueReq)
|
||||
test.AssertNotError(t, err, "Failed to issue precert")
|
||||
|
|
|
@ -45,7 +45,7 @@ func TestOCSP(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to create CA")
|
||||
ocspi := testCtx.ocsp
|
||||
|
||||
profile := ca.certProfiles.profileByName["legacy"]
|
||||
profile := ca.certProfiles["legacy"]
|
||||
// Issue a certificate from an RSA issuer, request OCSP from the same issuer,
|
||||
// and make sure it works.
|
||||
rsaCertDER, err := ca.issuePrecertificate(ctx, profile, &capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: mrand.Int63(), OrderID: mrand.Int63(), CertProfileName: "legacy"})
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
@ -30,58 +29,40 @@ import (
|
|||
)
|
||||
|
||||
// ProfileConfig describes the certificate issuance constraints for all issuers.
|
||||
//
|
||||
// This struct gets hashed in the CA to allow matching up precert and final cert
|
||||
// issuance by the exact profile config. We compute the hash over an ASN.1 encoding
|
||||
// because ASN.1 encoding has a canonical form and can omit optional fields (which
|
||||
// allows for gracefully adding new fields without changing the hash of existing
|
||||
// profile configs). This struct does not get embedded into any certs, CRLs, or
|
||||
// other objects, and does not get signed; it's only used internally.
|
||||
//
|
||||
// Note: even though these fields have encoding instructions (tag:N), they will
|
||||
// be encoded in the order they appear in the struct, so do not reorder them.
|
||||
type ProfileConfig struct {
|
||||
// AllowMustStaple, when false, causes all IssuanceRequests which specify the
|
||||
// OCSP Must Staple extension to be rejected.
|
||||
AllowMustStaple bool `asn1:"tag:1,optional"`
|
||||
AllowMustStaple bool
|
||||
|
||||
// OmitCommonName causes the CN field to be excluded from the resulting
|
||||
// certificate, regardless of its inclusion in the IssuanceRequest.
|
||||
OmitCommonName bool `asn1:"tag:2,optional"`
|
||||
OmitCommonName bool
|
||||
// OmitKeyEncipherment causes the keyEncipherment bit to be omitted from the
|
||||
// Key Usage field of all certificates (instead of only from ECDSA certs).
|
||||
OmitKeyEncipherment bool `asn1:"tag:3,optional"`
|
||||
OmitKeyEncipherment bool
|
||||
// OmitClientAuth causes the id-kp-clientAuth OID (TLS Client Authentication)
|
||||
// to be omitted from the EKU extension.
|
||||
OmitClientAuth bool `asn1:"tag:4,optional"`
|
||||
OmitClientAuth bool
|
||||
// OmitSKID causes the Subject Key Identifier extension to be omitted.
|
||||
OmitSKID bool `asn1:"tag:5,optional"`
|
||||
OmitSKID bool
|
||||
// OmitOCSP causes the OCSP URI field to be omitted from the Authority
|
||||
// Information Access extension. This cannot be true unless
|
||||
// IncludeCRLDistributionPoints is also true, to ensure that every
|
||||
// certificate has at least one revocation mechanism included.
|
||||
OmitOCSP bool `asn1:"tag:11,optional"`
|
||||
OmitOCSP bool
|
||||
// IncludeCRLDistributionPoints causes the CRLDistributionPoints extension to
|
||||
// be added to all certificates issued by this profile.
|
||||
IncludeCRLDistributionPoints bool `asn1:"tag:6,optional"`
|
||||
IncludeCRLDistributionPoints bool
|
||||
|
||||
MaxValidityPeriod config.Duration `asn1:"tag:7,optional"`
|
||||
MaxValidityBackdate config.Duration `asn1:"tag:8,optional"`
|
||||
MaxValidityPeriod config.Duration
|
||||
MaxValidityBackdate config.Duration
|
||||
|
||||
// LintConfig is a path to a zlint config file, which can be used to control
|
||||
// the behavior of zlint's "customizable lints".
|
||||
LintConfig string `asn1:"tag:9,optional"`
|
||||
LintConfig string
|
||||
// IgnoredLints is a list of lint names that we know will fail for this
|
||||
// profile, and which we know it is safe to ignore.
|
||||
IgnoredLints []string `asn1:"tag:10,optional"`
|
||||
}
|
||||
|
||||
func (pcn ProfileConfig) hash() ([32]byte, error) {
|
||||
encodedBytes, err := asn1.Marshal(pcn)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
return sha256.Sum256(encodedBytes), nil
|
||||
IgnoredLints []string
|
||||
}
|
||||
|
||||
// PolicyConfig describes a policy
|
||||
|
@ -104,8 +85,6 @@ type Profile struct {
|
|||
maxValidity time.Duration
|
||||
|
||||
lints lint.Registry
|
||||
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
// NewProfile converts the profile config into a usable profile.
|
||||
|
@ -137,11 +116,6 @@ func NewProfile(profileConfig *ProfileConfig) (*Profile, error) {
|
|||
lints.SetConfiguration(lintconfig)
|
||||
}
|
||||
|
||||
hash, err := profileConfig.hash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sp := &Profile{
|
||||
allowMustStaple: profileConfig.AllowMustStaple,
|
||||
omitCommonName: profileConfig.OmitCommonName,
|
||||
|
@ -153,16 +127,11 @@ func NewProfile(profileConfig *ProfileConfig) (*Profile, error) {
|
|||
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
|
||||
maxValidity: profileConfig.MaxValidityPeriod.Duration,
|
||||
lints: lints,
|
||||
hash: hash,
|
||||
}
|
||||
|
||||
return sp, nil
|
||||
}
|
||||
|
||||
func (p *Profile) Hash() [32]byte {
|
||||
return p.hash
|
||||
}
|
||||
|
||||
// GenerateValidity returns a notBefore/notAfter pair bracketing the input time,
|
||||
// based on the profile's configured backdate and validity.
|
||||
func (p *Profile) GenerateValidity(now time.Time) (time.Time, time.Time) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -937,28 +936,3 @@ func TestNewProfile(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileHash(t *testing.T) {
|
||||
// A profile _with_ IncludeCRLDistributionPoints.
|
||||
// Hash calculated over the ASN.1 encoding of the `ProfileConfigNew`.
|
||||
profile := ProfileConfig{
|
||||
IncludeCRLDistributionPoints: true,
|
||||
AllowMustStaple: true,
|
||||
OmitCommonName: true,
|
||||
OmitKeyEncipherment: false,
|
||||
OmitClientAuth: false,
|
||||
OmitSKID: true,
|
||||
MaxValidityPeriod: config.Duration{Duration: time.Hour},
|
||||
MaxValidityBackdate: config.Duration{Duration: time.Second},
|
||||
LintConfig: "example/config.toml",
|
||||
IgnoredLints: []string{"one", "two"},
|
||||
}
|
||||
hash, err := profile.hash()
|
||||
if err != nil {
|
||||
t.Fatalf("hashing %+v: %s", profile, err)
|
||||
}
|
||||
expectedHash := "d2a6c9f0aa37d2ac0b15476cb6e0ae9b98ba59b1321d8d6da26efc620581c53d"
|
||||
if expectedHash != fmt.Sprintf("%x", hash) {
|
||||
t.Errorf("%+v.Hash()=%x, want %s", profile, hash, expectedHash)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue