Introduce SerialPrefixHex field in CA (#7721)
Add a new SerialPrefixHex field to the CA's config, which takes a two-character hexadecimal string to use as the serial prefix. This matches the way that the OCSP Responder's acceptable serial prefixes are configured, and is easier for human operators to configure than raw integers. At the same time, change the type of the CA's internal serial prefix from `int` to `byte`, using the type system to enforce its 8-bit length. Fixes #7213
This commit is contained in:
parent
a731497958
commit
beddae5970
11
ca/ca.go
11
ca/ca.go
|
@ -130,7 +130,8 @@ type certificateAuthorityImpl struct {
|
|||
issuers issuerMaps
|
||||
certProfiles certProfilesMaps
|
||||
|
||||
prefix int // Prepended to the serial number
|
||||
// The prefix is prepended to the serial number.
|
||||
prefix byte
|
||||
maxNames int
|
||||
keyPolicy goodkey.KeyPolicy
|
||||
clk clock.Clock
|
||||
|
@ -238,7 +239,7 @@ func NewCertificateAuthorityImpl(
|
|||
boulderIssuers []*issuance.Issuer,
|
||||
defaultCertProfileName string,
|
||||
certificateProfiles map[string]*issuance.ProfileConfig,
|
||||
serialPrefix int,
|
||||
serialPrefix byte,
|
||||
maxNames int,
|
||||
keyPolicy goodkey.KeyPolicy,
|
||||
logger blog.Logger,
|
||||
|
@ -248,8 +249,8 @@ func NewCertificateAuthorityImpl(
|
|||
var ca *certificateAuthorityImpl
|
||||
var err error
|
||||
|
||||
if serialPrefix < 1 || serialPrefix > 127 {
|
||||
err = errors.New("serial prefix must be between 1 and 127")
|
||||
if serialPrefix < 0x01 || serialPrefix > 0x7f {
|
||||
err = errors.New("serial prefix must be between 0x01 (1) and 0x7f (127)")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -491,7 +492,7 @@ func (ca *certificateAuthorityImpl) generateSerialNumber() (*big.Int, error) {
|
|||
// We want 136 bits of random number, plus an 8-bit instance id prefix.
|
||||
const randBits = 136
|
||||
serialBytes := make([]byte, randBits/8+1)
|
||||
serialBytes[0] = byte(ca.prefix)
|
||||
serialBytes[0] = ca.prefix
|
||||
_, err := rand.Read(serialBytes[1:])
|
||||
if err != nil {
|
||||
err = berrors.InternalServerError("failed to generate serial: %s", err)
|
||||
|
|
|
@ -103,7 +103,7 @@ type testCtx struct {
|
|||
crl *crlImpl
|
||||
defaultCertProfileName string
|
||||
certProfiles map[string]*issuance.ProfileConfig
|
||||
serialPrefix int
|
||||
serialPrefix byte
|
||||
maxNames int
|
||||
boulderIssuers []*issuance.Issuer
|
||||
keyPolicy goodkey.KeyPolicy
|
||||
|
@ -237,7 +237,7 @@ func setup(t *testing.T) *testCtx {
|
|||
crl: crl,
|
||||
defaultCertProfileName: "legacy",
|
||||
certProfiles: certProfiles,
|
||||
serialPrefix: 17,
|
||||
serialPrefix: 0x11,
|
||||
maxNames: 2,
|
||||
boulderIssuers: boulderIssuers,
|
||||
keyPolicy: keyPolicy,
|
||||
|
@ -257,7 +257,7 @@ func TestSerialPrefix(t *testing.T) {
|
|||
nil,
|
||||
"",
|
||||
nil,
|
||||
0,
|
||||
0x00,
|
||||
testCtx.maxNames,
|
||||
testCtx.keyPolicy,
|
||||
testCtx.logger,
|
||||
|
@ -271,7 +271,7 @@ func TestSerialPrefix(t *testing.T) {
|
|||
nil,
|
||||
"",
|
||||
nil,
|
||||
128,
|
||||
0x80,
|
||||
testCtx.maxNames,
|
||||
testCtx.keyPolicy,
|
||||
testCtx.logger,
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"flag"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/ca"
|
||||
|
@ -61,15 +62,26 @@ type Config struct {
|
|||
}
|
||||
|
||||
// How long issued certificates are valid for.
|
||||
// Deprecated: Use Issuace.CertProfiles.MaxValidityPeriod instead.
|
||||
// Deprecated: Use Issuance.CertProfiles.MaxValidityPeriod instead.
|
||||
Expiry config.Duration
|
||||
|
||||
// How far back certificates should be backdated.
|
||||
// Deprecated: Use Issuace.CertProfiles.MaxValidityBackdate instead.
|
||||
// Deprecated: Use Issuance.CertProfiles.MaxValidityBackdate instead.
|
||||
Backdate config.Duration
|
||||
|
||||
// What digits we should prepend to serials after randomly generating them.
|
||||
SerialPrefix int `validate:"required,min=1,max=127"`
|
||||
// Deprecated: Use SerialPrefixHex instead.
|
||||
SerialPrefix int `validate:"required_without=SerialPrefixHex,omitempty,min=1,max=127"`
|
||||
|
||||
// SerialPrefixHex is the hex string to prepend to serials after randomly
|
||||
// generating them. The minimum value is "01" to ensure that at least
|
||||
// one bit in the prefix byte is set. The maximum value is "7f" to
|
||||
// ensure that the first bit in the prefix byte is not set. The validate
|
||||
// library cannot enforce mix/max values on strings, so that is done in
|
||||
// NewCertificateAuthorityImpl.
|
||||
//
|
||||
// TODO(#7213): Replace `required_without` with `required` when SerialPrefix is removed.
|
||||
SerialPrefixHex string `validate:"required_without=SerialPrefix,omitempty,hexadecimal,len=2"`
|
||||
|
||||
// MaxNames is the maximum number of subjectAltNames in a single cert.
|
||||
// The value supplied MUST be greater than 0 and no more than 100. These
|
||||
|
@ -152,6 +164,13 @@ func main() {
|
|||
c.CA.DebugAddr = *debugAddr
|
||||
}
|
||||
|
||||
serialPrefix := byte(c.CA.SerialPrefix)
|
||||
if c.CA.SerialPrefixHex != "" {
|
||||
parsedSerialPrefix, err := strconv.ParseUint(c.CA.SerialPrefixHex, 16, 8)
|
||||
cmd.FailOnError(err, "Couldn't convert SerialPrefixHex to int")
|
||||
serialPrefix = byte(parsedSerialPrefix)
|
||||
}
|
||||
|
||||
if c.CA.MaxNames == 0 {
|
||||
cmd.Fail("Error in CA config: MaxNames must not be 0")
|
||||
}
|
||||
|
@ -280,7 +299,7 @@ func main() {
|
|||
issuers,
|
||||
c.CA.Issuance.DefaultCertificateProfileName,
|
||||
c.CA.Issuance.CertProfiles,
|
||||
c.CA.SerialPrefix,
|
||||
serialPrefix,
|
||||
c.CA.MaxNames,
|
||||
kp,
|
||||
logger,
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"serialPrefix": 127,
|
||||
"serialPrefixHex": "7f",
|
||||
"maxNames": 100,
|
||||
"lifespanOCSP": "96h",
|
||||
"goodkey": {
|
||||
|
|
Loading…
Reference in New Issue