CA: Add GenerateCRL gRPC method (#6187)
Add a new CA gRPC method named `GenerateCRL`. In the style of the existing `GenerateOCSP` method, this new endpoint is implemented as a separate service, for which the CA binary spins up an additional gRPC service. This method uses gRPC streaming for both its input and output. For input, the stream must contain exactly one metadata message identifying the crl number, issuer, and timestamp, and then any number of messages identifying a single certificate which should be included in the CRL. For output, it simply streams chunks of bytes. Fixes #6161
This commit is contained in:
parent
c65329202e
commit
e13918b50e
10
ca/ca.go
10
ca/ca.go
|
|
@ -55,6 +55,7 @@ type certificateAuthorityImpl struct {
|
|||
sa sapb.StorageAuthorityCertificateClient
|
||||
pa core.PolicyAuthority
|
||||
ocsp *ocspImpl
|
||||
crl *crlImpl
|
||||
issuers issuerMaps
|
||||
|
||||
// This is temporary, and will be used for testing and slow roll-out
|
||||
|
|
@ -101,6 +102,7 @@ func NewCertificateAuthorityImpl(
|
|||
sa sapb.StorageAuthorityCertificateClient,
|
||||
pa core.PolicyAuthority,
|
||||
ocsp *ocspImpl,
|
||||
crl *crlImpl,
|
||||
boulderIssuers []*issuance.Issuer,
|
||||
ecdsaAllowList *ECDSAAllowList,
|
||||
certExpiry time.Duration,
|
||||
|
|
@ -154,6 +156,7 @@ func NewCertificateAuthorityImpl(
|
|||
sa: sa,
|
||||
pa: pa,
|
||||
ocsp: ocsp,
|
||||
crl: crl,
|
||||
issuers: issuers,
|
||||
validityPeriod: certExpiry,
|
||||
backdate: certBackdate,
|
||||
|
|
@ -582,3 +585,10 @@ func (ca *certificateAuthorityImpl) integrateOrphan() error {
|
|||
func (ca *certificateAuthorityImpl) GenerateOCSP(ctx context.Context, req *capb.GenerateOCSPRequest) (*capb.OCSPResponse, error) {
|
||||
return ca.ocsp.GenerateOCSP(ctx, req)
|
||||
}
|
||||
|
||||
// GenerateCRL is simply a passthrough to crlImpl.GenerateCRL so that other
|
||||
// services which need to talk to the CA anyway can do so without configuring
|
||||
// two separate gRPC service backends.
|
||||
func (ca *certificateAuthorityImpl) GenerateCRL(stream capb.CertificateAuthority_GenerateCRLServer) error {
|
||||
return ca.crl.GenerateCRL(stream)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ func mustRead(path string) []byte {
|
|||
type testCtx struct {
|
||||
pa core.PolicyAuthority
|
||||
ocsp *ocspImpl
|
||||
crl *crlImpl
|
||||
certExpiry time.Duration
|
||||
certBackdate time.Duration
|
||||
serialPrefix int
|
||||
|
|
@ -259,9 +260,18 @@ func setup(t *testing.T) *testCtx {
|
|||
)
|
||||
test.AssertNotError(t, err, "Failed to create ocsp impl")
|
||||
|
||||
crl, err := NewCRLImpl(
|
||||
boulderIssuers,
|
||||
time.Hour,
|
||||
100,
|
||||
blog.NewMock(),
|
||||
)
|
||||
test.AssertNotError(t, err, "Failed to create crl impl")
|
||||
|
||||
return &testCtx{
|
||||
pa: pa,
|
||||
ocsp: ocsp,
|
||||
crl: crl,
|
||||
certExpiry: 8760 * time.Hour,
|
||||
certBackdate: time.Hour,
|
||||
serialPrefix: 17,
|
||||
|
|
@ -285,6 +295,7 @@ func TestFailNoSerialPrefix(t *testing.T) {
|
|||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
testCtx.certBackdate,
|
||||
0,
|
||||
|
|
@ -382,6 +393,7 @@ func issueCertificateSubTestSetup(t *testing.T) (*certificateAuthorityImpl, *moc
|
|||
sa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
&ECDSAAllowList{},
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -429,6 +441,7 @@ func TestMultipleIssuers(t *testing.T) {
|
|||
sa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -565,6 +578,7 @@ func TestInvalidCSRs(t *testing.T) {
|
|||
sa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -603,6 +617,7 @@ func TestRejectValidityTooLong(t *testing.T) {
|
|||
sa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -705,6 +720,7 @@ func TestIssueCertificateForPrecertificate(t *testing.T) {
|
|||
sa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -812,6 +828,7 @@ func TestIssueCertificateForPrecertificateDuplicateSerial(t *testing.T) {
|
|||
sa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -855,6 +872,7 @@ func TestIssueCertificateForPrecertificateDuplicateSerial(t *testing.T) {
|
|||
errorsa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -932,6 +950,7 @@ func TestPrecertOrphanQueue(t *testing.T) {
|
|||
qsa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
@ -999,6 +1018,7 @@ func TestOrphanQueue(t *testing.T) {
|
|||
qsa,
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,221 @@
|
|||
package ca
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
capb "github.com/letsencrypt/boulder/ca/proto"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
corepb "github.com/letsencrypt/boulder/core/proto"
|
||||
"github.com/letsencrypt/boulder/issuance"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
)
|
||||
|
||||
type crlImpl struct {
|
||||
capb.UnimplementedCRLGeneratorServer
|
||||
issuers map[issuance.IssuerNameID]*issuance.Issuer
|
||||
lifetime time.Duration
|
||||
maxLogLen int
|
||||
log blog.Logger
|
||||
}
|
||||
|
||||
func NewCRLImpl(issuers []*issuance.Issuer, lifetime time.Duration, maxLogLen int, logger blog.Logger) (*crlImpl, error) {
|
||||
issuersByNameID := make(map[issuance.IssuerNameID]*issuance.Issuer, len(issuers))
|
||||
for _, issuer := range issuers {
|
||||
issuersByNameID[issuer.Cert.NameID()] = issuer
|
||||
}
|
||||
|
||||
if lifetime == 0 {
|
||||
logger.Warningf("got zero for crl lifetime; setting to default 9 days")
|
||||
lifetime = 9 * 24 * time.Hour
|
||||
} else if lifetime >= 10*24*time.Hour {
|
||||
return nil, fmt.Errorf("crl lifetime cannot be more than 10 days, got %q", lifetime)
|
||||
} else if lifetime <= 0*time.Hour {
|
||||
return nil, fmt.Errorf("crl lifetime must be positive, got %q", lifetime)
|
||||
}
|
||||
|
||||
return &crlImpl{
|
||||
issuers: issuersByNameID,
|
||||
lifetime: lifetime,
|
||||
maxLogLen: maxLogLen,
|
||||
log: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ci *crlImpl) GenerateCRL(stream capb.CRLGenerator_GenerateCRLServer) error {
|
||||
var issuer *issuance.Issuer
|
||||
var template *x509.RevocationList
|
||||
var shard int64
|
||||
rcs := make([]pkix.RevokedCertificate, 0)
|
||||
|
||||
for {
|
||||
in, err := stream.Recv()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
switch payload := in.Payload.(type) {
|
||||
case *capb.GenerateCRLRequest_Metadata:
|
||||
if template != nil {
|
||||
return errors.New("got more than one metadata message")
|
||||
}
|
||||
|
||||
template, err = ci.metadataToTemplate(payload.Metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
issuer, ok = ci.issuers[issuance.IssuerNameID(payload.Metadata.IssuerNameID)]
|
||||
if !ok {
|
||||
return fmt.Errorf("got unrecognized IssuerNameID: %d", payload.Metadata.IssuerNameID)
|
||||
}
|
||||
|
||||
shard = payload.Metadata.Shard
|
||||
|
||||
case *capb.GenerateCRLRequest_Entry:
|
||||
rc, err := ci.entryToRevokedCertificate(payload.Entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rcs = append(rcs, *rc)
|
||||
|
||||
default:
|
||||
return errors.New("got empty or malformed message in input stream")
|
||||
}
|
||||
}
|
||||
|
||||
if template == nil {
|
||||
return errors.New("no crl metadata received")
|
||||
}
|
||||
|
||||
// Compute a unique ID for this issuer-number-shard combo, to tie together all
|
||||
// the audit log lines related to its issuance.
|
||||
logID := blog.LogLineChecksum(fmt.Sprintf("%d", issuer.Cert.NameID()) + template.Number.String() + fmt.Sprintf("%d", shard))
|
||||
ci.log.AuditInfof(
|
||||
"Signing CRL: logID=[%s] issuer=[%s] number=[%s] shard=[%d] thisUpdate=[%s] nextUpdate=[%s] numEntries=[%d]",
|
||||
logID, issuer.Cert.Subject.CommonName, template.Number.String(), template.ThisUpdate, template.NextUpdate, len(rcs),
|
||||
)
|
||||
|
||||
builder := strings.Builder{}
|
||||
for i := 0; i < len(rcs); i += 1 {
|
||||
if builder.Len() == 0 {
|
||||
fmt.Fprintf(&builder, "Signing CRL: logID=[%s] entries=[", logID)
|
||||
}
|
||||
|
||||
// TODO: Figure out how best to include the reason code here, since it's
|
||||
// slow/difficult to extract it from the already-encoded entry extension.
|
||||
fmt.Fprintf(&builder, "%x,", rcs[i].SerialNumber.Bytes())
|
||||
|
||||
if builder.Len() != ci.maxLogLen {
|
||||
ci.log.AuditInfof("%s", builder)
|
||||
builder = strings.Builder{}
|
||||
}
|
||||
}
|
||||
|
||||
template.RevokedCertificates = rcs
|
||||
crlBytes, err := x509.CreateRevocationList(
|
||||
rand.Reader,
|
||||
template,
|
||||
issuer.Cert.Certificate,
|
||||
issuer.Signer,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("signing crl: %w", err)
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(crlBytes)
|
||||
ci.log.AuditInfof(
|
||||
"Signing CRL success: logID=[%s] size=[%d] hash=[%d]",
|
||||
logID, len(crlBytes), hash[:],
|
||||
)
|
||||
|
||||
for i := 0; i < len(crlBytes); i += 1000 {
|
||||
j := i + 1000
|
||||
if j > len(crlBytes) {
|
||||
j = len(crlBytes)
|
||||
}
|
||||
err = stream.Send(&capb.GenerateCRLResponse{
|
||||
Chunk: crlBytes[i:j],
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i%1000 == 0 {
|
||||
ci.log.Debugf("Wrote %d bytes to output stream", i*1000)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *crlImpl) metadataToTemplate(meta *capb.CRLMetadata) (*x509.RevocationList, error) {
|
||||
if meta.IssuerNameID == 0 || meta.ThisUpdate == 0 {
|
||||
return nil, errors.New("got incomplete metadata message")
|
||||
}
|
||||
|
||||
// The CRL Number MUST be at most 20 octets, per RFC 5280 Section 5.2.3.
|
||||
// A 64-bit (8-byte) integer will never exceed that requirement, but lets
|
||||
// us guarantee that the CRL Number is always increasing without having to
|
||||
// store or look up additional state.
|
||||
number := big.NewInt(meta.ThisUpdate)
|
||||
thisUpdate := time.Unix(0, meta.ThisUpdate)
|
||||
|
||||
return &x509.RevocationList{
|
||||
Number: number,
|
||||
ThisUpdate: thisUpdate,
|
||||
NextUpdate: thisUpdate.Add(-time.Second).Add(ci.lifetime),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (ci *crlImpl) entryToRevokedCertificate(entry *corepb.CRLEntry) (*pkix.RevokedCertificate, error) {
|
||||
serial, err := core.StringToSerial(entry.Serial)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.RevokedAt == 0 {
|
||||
return nil, errors.New("got empty or zero revocation timestamp")
|
||||
}
|
||||
revokedAt := time.Unix(0, entry.RevokedAt)
|
||||
|
||||
// RFC 5280 Section 5.3.1 says "the reason code CRL entry extension SHOULD be
|
||||
// absent instead of using the unspecified (0) reasonCode value.", so we make
|
||||
// sure we only add this extension if we have a non-zero revocation reason.
|
||||
var extensions []pkix.Extension
|
||||
if entry.Reason != 0 {
|
||||
reasonBytes, err := asn1.Marshal(asn1.Enumerated(entry.Reason))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extensions = []pkix.Extension{
|
||||
// The Reason Code extension, as defined in RFC 5280 Section 5.3.1:
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1
|
||||
{
|
||||
Id: asn1.ObjectIdentifier{2, 5, 29, 21}, // id-ce-reasonCode
|
||||
Value: reasonBytes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &pkix.RevokedCertificate{
|
||||
SerialNumber: serial,
|
||||
RevocationTime: revokedAt,
|
||||
Extensions: extensions,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
package ca
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
capb "github.com/letsencrypt/boulder/ca/proto"
|
||||
corepb "github.com/letsencrypt/boulder/core/proto"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
||||
type mockGenerateCRLBidiStream struct {
|
||||
grpc.ServerStream
|
||||
input <-chan *capb.GenerateCRLRequest
|
||||
output chan<- *capb.GenerateCRLResponse
|
||||
}
|
||||
|
||||
func (s mockGenerateCRLBidiStream) Recv() (*capb.GenerateCRLRequest, error) {
|
||||
next, ok := <-s.input
|
||||
if !ok {
|
||||
return nil, io.EOF
|
||||
}
|
||||
return next, nil
|
||||
}
|
||||
|
||||
func (s mockGenerateCRLBidiStream) Send(entry *capb.GenerateCRLResponse) error {
|
||||
s.output <- entry
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestGenerateCRL(t *testing.T) {
|
||||
testCtx := setup(t)
|
||||
crli := testCtx.crl
|
||||
errs := make(chan error, 1)
|
||||
|
||||
// Test that we get an error when no metadata is sent.
|
||||
ins := make(chan *capb.GenerateCRLRequest)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: nil})
|
||||
}()
|
||||
close(ins)
|
||||
err := <-errs
|
||||
test.AssertError(t, err, "can't generate CRL with no metadata")
|
||||
test.AssertContains(t, err.Error(), "no crl metadata received")
|
||||
|
||||
// Test that we get an error when incomplete metadata is sent.
|
||||
ins = make(chan *capb.GenerateCRLRequest)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: nil})
|
||||
}()
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Metadata{
|
||||
Metadata: &capb.CRLMetadata{},
|
||||
},
|
||||
}
|
||||
close(ins)
|
||||
err = <-errs
|
||||
test.AssertError(t, err, "can't generate CRL with incomplete metadata")
|
||||
test.AssertContains(t, err.Error(), "got incomplete metadata message")
|
||||
|
||||
// Test that we get an error when unrecognized metadata is sent.
|
||||
ins = make(chan *capb.GenerateCRLRequest)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: nil})
|
||||
}()
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Metadata{
|
||||
Metadata: &capb.CRLMetadata{
|
||||
IssuerNameID: 1,
|
||||
ThisUpdate: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
close(ins)
|
||||
err = <-errs
|
||||
test.AssertError(t, err, "can't generate CRL with bad metadata")
|
||||
test.AssertContains(t, err.Error(), "got unrecognized IssuerNameID")
|
||||
|
||||
// Test that we get an error when two metadata are sent.
|
||||
ins = make(chan *capb.GenerateCRLRequest)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: nil})
|
||||
}()
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Metadata{
|
||||
Metadata: &capb.CRLMetadata{
|
||||
IssuerNameID: int64(testCtx.boulderIssuers[0].Cert.NameID()),
|
||||
ThisUpdate: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Metadata{
|
||||
Metadata: &capb.CRLMetadata{
|
||||
IssuerNameID: int64(testCtx.boulderIssuers[0].Cert.NameID()),
|
||||
ThisUpdate: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
close(ins)
|
||||
err = <-errs
|
||||
test.AssertError(t, err, "can't generate CRL with duplicate metadata")
|
||||
test.AssertContains(t, err.Error(), "got more than one metadata message")
|
||||
|
||||
// Test that we get an error when an entry has a bad serial.
|
||||
ins = make(chan *capb.GenerateCRLRequest)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: nil})
|
||||
}()
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "123",
|
||||
Reason: 1,
|
||||
RevokedAt: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
close(ins)
|
||||
err = <-errs
|
||||
test.AssertError(t, err, "can't generate CRL with bad serials")
|
||||
test.AssertContains(t, err.Error(), "Invalid serial number")
|
||||
|
||||
// Test that we get an error when an entry has a bad revocation time.
|
||||
ins = make(chan *capb.GenerateCRLRequest)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: nil})
|
||||
}()
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "deadbeefdeadbeefdeadbeefdeadbeefdead",
|
||||
Reason: 1,
|
||||
RevokedAt: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
close(ins)
|
||||
err = <-errs
|
||||
test.AssertError(t, err, "can't generate CRL with bad serials")
|
||||
test.AssertContains(t, err.Error(), "got empty or zero revocation timestamp")
|
||||
|
||||
// Test that generating an empty CRL works.
|
||||
ins = make(chan *capb.GenerateCRLRequest)
|
||||
outs := make(chan *capb.GenerateCRLResponse)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: outs})
|
||||
close(outs)
|
||||
}()
|
||||
crlBytes := make([]byte, 0)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for resp := range outs {
|
||||
crlBytes = append(crlBytes, resp.Chunk...)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Metadata{
|
||||
Metadata: &capb.CRLMetadata{
|
||||
IssuerNameID: int64(testCtx.boulderIssuers[0].Cert.NameID()),
|
||||
ThisUpdate: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
close(ins)
|
||||
err = <-errs
|
||||
<-done
|
||||
test.AssertNotError(t, err, "generating empty CRL should work")
|
||||
test.Assert(t, len(crlBytes) > 0, "should have gotten some CRL bytes")
|
||||
crl, err := x509.ParseCRL(crlBytes)
|
||||
test.AssertNotError(t, err, "should be able to parse empty CRL")
|
||||
test.AssertEquals(t, len(crl.TBSCertList.RevokedCertificates), 0)
|
||||
err = testCtx.boulderIssuers[0].Cert.CheckCRLSignature(crl)
|
||||
test.AssertNotError(t, err, "CRL signature should validate")
|
||||
|
||||
// Test that generating a CRL with some entries works.
|
||||
ins = make(chan *capb.GenerateCRLRequest)
|
||||
outs = make(chan *capb.GenerateCRLResponse)
|
||||
go func() {
|
||||
errs <- crli.GenerateCRL(mockGenerateCRLBidiStream{input: ins, output: outs})
|
||||
close(outs)
|
||||
}()
|
||||
crlBytes = make([]byte, 0)
|
||||
done = make(chan struct{})
|
||||
go func() {
|
||||
for resp := range outs {
|
||||
crlBytes = append(crlBytes, resp.Chunk...)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Metadata{
|
||||
Metadata: &capb.CRLMetadata{
|
||||
IssuerNameID: int64(testCtx.boulderIssuers[0].Cert.NameID()),
|
||||
ThisUpdate: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "000000000000000000000000000000000000",
|
||||
RevokedAt: time.Now().UnixNano(),
|
||||
// Reason 0, Unspecified, is omitted.
|
||||
},
|
||||
},
|
||||
}
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "111111111111111111111111111111111111",
|
||||
Reason: 1, // keyCompromise
|
||||
RevokedAt: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "333333333333333333333333333333333333",
|
||||
Reason: 3, // affiliationChanged
|
||||
RevokedAt: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "444444444444444444444444444444444444",
|
||||
Reason: 4, // superseded
|
||||
RevokedAt: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "555555555555555555555555555555555555",
|
||||
Reason: 5, // cessationOfOperation
|
||||
RevokedAt: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
ins <- &capb.GenerateCRLRequest{
|
||||
Payload: &capb.GenerateCRLRequest_Entry{
|
||||
Entry: &corepb.CRLEntry{
|
||||
Serial: "999999999999999999999999999999999999",
|
||||
Reason: 9, // privilegeWithdrawn
|
||||
RevokedAt: time.Now().UnixNano(),
|
||||
},
|
||||
},
|
||||
}
|
||||
close(ins)
|
||||
err = <-errs
|
||||
<-done
|
||||
test.AssertNotError(t, err, "generating empty CRL should work")
|
||||
test.Assert(t, len(crlBytes) > 0, "should have gotten some CRL bytes")
|
||||
crl, err = x509.ParseCRL(crlBytes)
|
||||
test.AssertNotError(t, err, "should be able to parse empty CRL")
|
||||
test.AssertEquals(t, len(crl.TBSCertList.RevokedCertificates), 6)
|
||||
err = testCtx.boulderIssuers[0].Cert.CheckCRLSignature(crl)
|
||||
test.AssertNotError(t, err, "CRL signature should validate")
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ func TestOCSP(t *testing.T) {
|
|||
&mockSA{},
|
||||
testCtx.pa,
|
||||
testCtx.ocsp,
|
||||
testCtx.crl,
|
||||
testCtx.boulderIssuers,
|
||||
nil,
|
||||
testCtx.certExpiry,
|
||||
|
|
|
|||
|
|
@ -337,6 +337,196 @@ func (x *OCSPResponse) GetResponse() []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
type GenerateCRLRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Payload:
|
||||
// *GenerateCRLRequest_Metadata
|
||||
// *GenerateCRLRequest_Entry
|
||||
Payload isGenerateCRLRequest_Payload `protobuf_oneof:"payload"`
|
||||
}
|
||||
|
||||
func (x *GenerateCRLRequest) Reset() {
|
||||
*x = GenerateCRLRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ca_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GenerateCRLRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GenerateCRLRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GenerateCRLRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ca_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GenerateCRLRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GenerateCRLRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ca_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (m *GenerateCRLRequest) GetPayload() isGenerateCRLRequest_Payload {
|
||||
if m != nil {
|
||||
return m.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GenerateCRLRequest) GetMetadata() *CRLMetadata {
|
||||
if x, ok := x.GetPayload().(*GenerateCRLRequest_Metadata); ok {
|
||||
return x.Metadata
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GenerateCRLRequest) GetEntry() *proto.CRLEntry {
|
||||
if x, ok := x.GetPayload().(*GenerateCRLRequest_Entry); ok {
|
||||
return x.Entry
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isGenerateCRLRequest_Payload interface {
|
||||
isGenerateCRLRequest_Payload()
|
||||
}
|
||||
|
||||
type GenerateCRLRequest_Metadata struct {
|
||||
Metadata *CRLMetadata `protobuf:"bytes,1,opt,name=metadata,proto3,oneof"`
|
||||
}
|
||||
|
||||
type GenerateCRLRequest_Entry struct {
|
||||
Entry *proto.CRLEntry `protobuf:"bytes,2,opt,name=entry,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*GenerateCRLRequest_Metadata) isGenerateCRLRequest_Payload() {}
|
||||
|
||||
func (*GenerateCRLRequest_Entry) isGenerateCRLRequest_Payload() {}
|
||||
|
||||
type CRLMetadata struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
IssuerNameID int64 `protobuf:"varint,1,opt,name=issuerNameID,proto3" json:"issuerNameID,omitempty"`
|
||||
ThisUpdate int64 `protobuf:"varint,2,opt,name=thisUpdate,proto3" json:"thisUpdate,omitempty"` // Unix timestamp (nanoseconds), also used for CRLNumber.
|
||||
Shard int64 `protobuf:"varint,3,opt,name=shard,proto3" json:"shard,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CRLMetadata) Reset() {
|
||||
*x = CRLMetadata{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ca_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CRLMetadata) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CRLMetadata) ProtoMessage() {}
|
||||
|
||||
func (x *CRLMetadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ca_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CRLMetadata.ProtoReflect.Descriptor instead.
|
||||
func (*CRLMetadata) Descriptor() ([]byte, []int) {
|
||||
return file_ca_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *CRLMetadata) GetIssuerNameID() int64 {
|
||||
if x != nil {
|
||||
return x.IssuerNameID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *CRLMetadata) GetThisUpdate() int64 {
|
||||
if x != nil {
|
||||
return x.ThisUpdate
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *CRLMetadata) GetShard() int64 {
|
||||
if x != nil {
|
||||
return x.Shard
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GenerateCRLResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Chunk []byte `protobuf:"bytes,1,opt,name=chunk,proto3" json:"chunk,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GenerateCRLResponse) Reset() {
|
||||
*x = GenerateCRLResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ca_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GenerateCRLResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GenerateCRLResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GenerateCRLResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ca_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GenerateCRLResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GenerateCRLResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ca_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *GenerateCRLResponse) GetChunk() []byte {
|
||||
if x != nil {
|
||||
return x.Chunk
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_ca_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_ca_proto_rawDesc = []byte{
|
||||
|
|
@ -376,32 +566,59 @@ var file_ca_proto_rawDesc = []byte{
|
|||
0x08, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x49, 0x44, 0x22, 0x2a, 0x0a, 0x0c, 0x4f, 0x43, 0x53,
|
||||
0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x92, 0x02, 0x0a, 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x55,
|
||||
0x0a, 0x13, 0x49, 0x73, 0x73, 0x75, 0x65, 0x50, 0x72, 0x65, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x63, 0x61, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65,
|
||||
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x63, 0x61, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x50, 0x72, 0x65,
|
||||
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x21, 0x49, 0x73, 0x73, 0x75, 0x65, 0x43, 0x65,
|
||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x50, 0x72, 0x65, 0x63,
|
||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x2e, 0x63, 0x61, 0x2e,
|
||||
0x49, 0x73, 0x73, 0x75, 0x65, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
|
||||
0x46, 0x6f, 0x72, 0x50, 0x72, 0x65, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a,
|
||||
0x0c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4f, 0x43, 0x53, 0x50, 0x12, 0x17, 0x2e,
|
||||
0x63, 0x61, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4f, 0x43, 0x53, 0x50, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x63, 0x61, 0x2e, 0x4f, 0x43, 0x53, 0x50,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x4c, 0x0a, 0x0d, 0x4f, 0x43,
|
||||
0x53, 0x50, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3b, 0x0a, 0x0c, 0x47,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, 0x0a, 0x12, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
|
||||
0x65, 0x43, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6d,
|
||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e,
|
||||
0x63, 0x61, 0x2e, 0x43, 0x52, 0x4c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00,
|
||||
0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2e, 0x43, 0x52, 0x4c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x67, 0x0a,
|
||||
0x0b, 0x43, 0x52, 0x4c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0c,
|
||||
0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x0c, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x49, 0x44,
|
||||
0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x68, 0x69, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x68, 0x69, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||
0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x2b, 0x0a, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61,
|
||||
0x74, 0x65, 0x43, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x63, 0x68,
|
||||
0x75, 0x6e, 0x6b, 0x32, 0xd8, 0x02, 0x0a, 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x55, 0x0a, 0x13,
|
||||
0x49, 0x73, 0x73, 0x75, 0x65, 0x50, 0x72, 0x65, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x63, 0x61, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x43, 0x65,
|
||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1f, 0x2e, 0x63, 0x61, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x50, 0x72, 0x65, 0x63, 0x65,
|
||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x21, 0x49, 0x73, 0x73, 0x75, 0x65, 0x43, 0x65, 0x72, 0x74,
|
||||
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x50, 0x72, 0x65, 0x63, 0x65, 0x72,
|
||||
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x2e, 0x63, 0x61, 0x2e, 0x49, 0x73,
|
||||
0x73, 0x75, 0x65, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x46, 0x6f,
|
||||
0x72, 0x50, 0x72, 0x65, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x65,
|
||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0c, 0x47,
|
||||
0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4f, 0x43, 0x53, 0x50, 0x12, 0x17, 0x2e, 0x63, 0x61,
|
||||
0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4f, 0x43, 0x53, 0x50, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x63, 0x61, 0x2e, 0x4f, 0x43, 0x53, 0x50, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x65, 0x74, 0x73, 0x65, 0x6e, 0x63, 0x72, 0x79,
|
||||
0x70, 0x74, 0x2f, 0x62, 0x6f, 0x75, 0x6c, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x61, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0b, 0x47, 0x65, 0x6e, 0x65,
|
||||
0x72, 0x61, 0x74, 0x65, 0x43, 0x52, 0x4c, 0x12, 0x16, 0x2e, 0x63, 0x61, 0x2e, 0x47, 0x65, 0x6e,
|
||||
0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x17, 0x2e, 0x63, 0x61, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x52, 0x4c,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x32, 0x4c,
|
||||
0x0a, 0x0d, 0x4f, 0x43, 0x53, 0x50, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12,
|
||||
0x3b, 0x0a, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4f, 0x43, 0x53, 0x50, 0x12,
|
||||
0x17, 0x2e, 0x63, 0x61, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4f, 0x43, 0x53,
|
||||
0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x63, 0x61, 0x2e, 0x4f, 0x43,
|
||||
0x53, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x54, 0x0a, 0x0c,
|
||||
0x43, 0x52, 0x4c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x44, 0x0a, 0x0b,
|
||||
0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x52, 0x4c, 0x12, 0x16, 0x2e, 0x63, 0x61,
|
||||
0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x61, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
|
||||
0x65, 0x43, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01,
|
||||
0x30, 0x01, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x6c, 0x65, 0x74, 0x73, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2f, 0x62, 0x6f, 0x75,
|
||||
0x6c, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -416,29 +633,39 @@ func file_ca_proto_rawDescGZIP() []byte {
|
|||
return file_ca_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ca_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_ca_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_ca_proto_goTypes = []interface{}{
|
||||
(*IssueCertificateRequest)(nil), // 0: ca.IssueCertificateRequest
|
||||
(*IssuePrecertificateResponse)(nil), // 1: ca.IssuePrecertificateResponse
|
||||
(*IssueCertificateForPrecertificateRequest)(nil), // 2: ca.IssueCertificateForPrecertificateRequest
|
||||
(*GenerateOCSPRequest)(nil), // 3: ca.GenerateOCSPRequest
|
||||
(*OCSPResponse)(nil), // 4: ca.OCSPResponse
|
||||
(*proto.Certificate)(nil), // 5: core.Certificate
|
||||
(*GenerateCRLRequest)(nil), // 5: ca.GenerateCRLRequest
|
||||
(*CRLMetadata)(nil), // 6: ca.CRLMetadata
|
||||
(*GenerateCRLResponse)(nil), // 7: ca.GenerateCRLResponse
|
||||
(*proto.CRLEntry)(nil), // 8: core.CRLEntry
|
||||
(*proto.Certificate)(nil), // 9: core.Certificate
|
||||
}
|
||||
var file_ca_proto_depIdxs = []int32{
|
||||
0, // 0: ca.CertificateAuthority.IssuePrecertificate:input_type -> ca.IssueCertificateRequest
|
||||
2, // 1: ca.CertificateAuthority.IssueCertificateForPrecertificate:input_type -> ca.IssueCertificateForPrecertificateRequest
|
||||
3, // 2: ca.CertificateAuthority.GenerateOCSP:input_type -> ca.GenerateOCSPRequest
|
||||
3, // 3: ca.OCSPGenerator.GenerateOCSP:input_type -> ca.GenerateOCSPRequest
|
||||
1, // 4: ca.CertificateAuthority.IssuePrecertificate:output_type -> ca.IssuePrecertificateResponse
|
||||
5, // 5: ca.CertificateAuthority.IssueCertificateForPrecertificate:output_type -> core.Certificate
|
||||
4, // 6: ca.CertificateAuthority.GenerateOCSP:output_type -> ca.OCSPResponse
|
||||
4, // 7: ca.OCSPGenerator.GenerateOCSP:output_type -> ca.OCSPResponse
|
||||
4, // [4:8] is the sub-list for method output_type
|
||||
0, // [0:4] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
6, // 0: ca.GenerateCRLRequest.metadata:type_name -> ca.CRLMetadata
|
||||
8, // 1: ca.GenerateCRLRequest.entry:type_name -> core.CRLEntry
|
||||
0, // 2: ca.CertificateAuthority.IssuePrecertificate:input_type -> ca.IssueCertificateRequest
|
||||
2, // 3: ca.CertificateAuthority.IssueCertificateForPrecertificate:input_type -> ca.IssueCertificateForPrecertificateRequest
|
||||
3, // 4: ca.CertificateAuthority.GenerateOCSP:input_type -> ca.GenerateOCSPRequest
|
||||
5, // 5: ca.CertificateAuthority.GenerateCRL:input_type -> ca.GenerateCRLRequest
|
||||
3, // 6: ca.OCSPGenerator.GenerateOCSP:input_type -> ca.GenerateOCSPRequest
|
||||
5, // 7: ca.CRLGenerator.GenerateCRL:input_type -> ca.GenerateCRLRequest
|
||||
1, // 8: ca.CertificateAuthority.IssuePrecertificate:output_type -> ca.IssuePrecertificateResponse
|
||||
9, // 9: ca.CertificateAuthority.IssueCertificateForPrecertificate:output_type -> core.Certificate
|
||||
4, // 10: ca.CertificateAuthority.GenerateOCSP:output_type -> ca.OCSPResponse
|
||||
7, // 11: ca.CertificateAuthority.GenerateCRL:output_type -> ca.GenerateCRLResponse
|
||||
4, // 12: ca.OCSPGenerator.GenerateOCSP:output_type -> ca.OCSPResponse
|
||||
7, // 13: ca.CRLGenerator.GenerateCRL:output_type -> ca.GenerateCRLResponse
|
||||
8, // [8:14] is the sub-list for method output_type
|
||||
2, // [2:8] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ca_proto_init() }
|
||||
|
|
@ -507,6 +734,46 @@ func file_ca_proto_init() {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
file_ca_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GenerateCRLRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ca_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CRLMetadata); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ca_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GenerateCRLResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_ca_proto_msgTypes[5].OneofWrappers = []interface{}{
|
||||
(*GenerateCRLRequest_Metadata)(nil),
|
||||
(*GenerateCRLRequest_Entry)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
|
|
@ -514,9 +781,9 @@ func file_ca_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_ca_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 2,
|
||||
NumServices: 3,
|
||||
},
|
||||
GoTypes: file_ca_proto_goTypes,
|
||||
DependencyIndexes: file_ca_proto_depIdxs,
|
||||
|
|
|
|||
|
|
@ -10,14 +10,7 @@ service CertificateAuthority {
|
|||
rpc IssuePrecertificate(IssueCertificateRequest) returns (IssuePrecertificateResponse) {}
|
||||
rpc IssueCertificateForPrecertificate(IssueCertificateForPrecertificateRequest) returns (core.Certificate) {}
|
||||
rpc GenerateOCSP(GenerateOCSPRequest) returns (OCSPResponse) {}
|
||||
}
|
||||
|
||||
// OCSPGenerator generates OCSP. We separate this out from
|
||||
// CertificateAuthority so that we can restrict access to a different subset of
|
||||
// hosts, so the hosts that need to request OCSP generation don't need to be
|
||||
// able to request certificate issuance.
|
||||
service OCSPGenerator {
|
||||
rpc GenerateOCSP(GenerateOCSPRequest) returns (OCSPResponse) {}
|
||||
rpc GenerateCRL(stream GenerateCRLRequest) returns (stream GenerateCRLResponse) {}
|
||||
}
|
||||
|
||||
message IssueCertificateRequest {
|
||||
|
|
@ -38,6 +31,14 @@ message IssueCertificateForPrecertificateRequest {
|
|||
int64 orderID = 4;
|
||||
}
|
||||
|
||||
// OCSPGenerator generates OCSP. We separate this out from
|
||||
// CertificateAuthority so that we can restrict access to a different subset of
|
||||
// hosts, so the hosts that need to request OCSP generation don't need to be
|
||||
// able to request certificate issuance.
|
||||
service OCSPGenerator {
|
||||
rpc GenerateOCSP(GenerateOCSPRequest) returns (OCSPResponse) {}
|
||||
}
|
||||
|
||||
// Exactly one of certDER or [serial and issuerID] must be set.
|
||||
message GenerateOCSPRequest {
|
||||
string status = 2;
|
||||
|
|
@ -50,3 +51,25 @@ message GenerateOCSPRequest {
|
|||
message OCSPResponse {
|
||||
bytes response = 1;
|
||||
}
|
||||
|
||||
// CRLGenerator signs CRLs. It is separated for the same reason as OCSPGenerator.
|
||||
service CRLGenerator {
|
||||
rpc GenerateCRL(stream GenerateCRLRequest) returns (stream GenerateCRLResponse) {}
|
||||
}
|
||||
|
||||
message GenerateCRLRequest {
|
||||
oneof payload {
|
||||
CRLMetadata metadata = 1;
|
||||
core.CRLEntry entry = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message CRLMetadata {
|
||||
int64 issuerNameID = 1;
|
||||
int64 thisUpdate = 2; // Unix timestamp (nanoseconds), also used for CRLNumber.
|
||||
int64 shard = 3;
|
||||
}
|
||||
|
||||
message GenerateCRLResponse {
|
||||
bytes chunk = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ type CertificateAuthorityClient interface {
|
|||
IssuePrecertificate(ctx context.Context, in *IssueCertificateRequest, opts ...grpc.CallOption) (*IssuePrecertificateResponse, error)
|
||||
IssueCertificateForPrecertificate(ctx context.Context, in *IssueCertificateForPrecertificateRequest, opts ...grpc.CallOption) (*proto.Certificate, error)
|
||||
GenerateOCSP(ctx context.Context, in *GenerateOCSPRequest, opts ...grpc.CallOption) (*OCSPResponse, error)
|
||||
GenerateCRL(ctx context.Context, opts ...grpc.CallOption) (CertificateAuthority_GenerateCRLClient, error)
|
||||
}
|
||||
|
||||
type certificateAuthorityClient struct {
|
||||
|
|
@ -63,6 +64,37 @@ func (c *certificateAuthorityClient) GenerateOCSP(ctx context.Context, in *Gener
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *certificateAuthorityClient) GenerateCRL(ctx context.Context, opts ...grpc.CallOption) (CertificateAuthority_GenerateCRLClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &CertificateAuthority_ServiceDesc.Streams[0], "/ca.CertificateAuthority/GenerateCRL", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &certificateAuthorityGenerateCRLClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type CertificateAuthority_GenerateCRLClient interface {
|
||||
Send(*GenerateCRLRequest) error
|
||||
Recv() (*GenerateCRLResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type certificateAuthorityGenerateCRLClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *certificateAuthorityGenerateCRLClient) Send(m *GenerateCRLRequest) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *certificateAuthorityGenerateCRLClient) Recv() (*GenerateCRLResponse, error) {
|
||||
m := new(GenerateCRLResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// CertificateAuthorityServer is the server API for CertificateAuthority service.
|
||||
// All implementations must embed UnimplementedCertificateAuthorityServer
|
||||
// for forward compatibility
|
||||
|
|
@ -70,6 +102,7 @@ type CertificateAuthorityServer interface {
|
|||
IssuePrecertificate(context.Context, *IssueCertificateRequest) (*IssuePrecertificateResponse, error)
|
||||
IssueCertificateForPrecertificate(context.Context, *IssueCertificateForPrecertificateRequest) (*proto.Certificate, error)
|
||||
GenerateOCSP(context.Context, *GenerateOCSPRequest) (*OCSPResponse, error)
|
||||
GenerateCRL(CertificateAuthority_GenerateCRLServer) error
|
||||
mustEmbedUnimplementedCertificateAuthorityServer()
|
||||
}
|
||||
|
||||
|
|
@ -86,6 +119,9 @@ func (UnimplementedCertificateAuthorityServer) IssueCertificateForPrecertificate
|
|||
func (UnimplementedCertificateAuthorityServer) GenerateOCSP(context.Context, *GenerateOCSPRequest) (*OCSPResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GenerateOCSP not implemented")
|
||||
}
|
||||
func (UnimplementedCertificateAuthorityServer) GenerateCRL(CertificateAuthority_GenerateCRLServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method GenerateCRL not implemented")
|
||||
}
|
||||
func (UnimplementedCertificateAuthorityServer) mustEmbedUnimplementedCertificateAuthorityServer() {}
|
||||
|
||||
// UnsafeCertificateAuthorityServer may be embedded to opt out of forward compatibility for this service.
|
||||
|
|
@ -153,6 +189,32 @@ func _CertificateAuthority_GenerateOCSP_Handler(srv interface{}, ctx context.Con
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CertificateAuthority_GenerateCRL_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(CertificateAuthorityServer).GenerateCRL(&certificateAuthorityGenerateCRLServer{stream})
|
||||
}
|
||||
|
||||
type CertificateAuthority_GenerateCRLServer interface {
|
||||
Send(*GenerateCRLResponse) error
|
||||
Recv() (*GenerateCRLRequest, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type certificateAuthorityGenerateCRLServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *certificateAuthorityGenerateCRLServer) Send(m *GenerateCRLResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *certificateAuthorityGenerateCRLServer) Recv() (*GenerateCRLRequest, error) {
|
||||
m := new(GenerateCRLRequest)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// CertificateAuthority_ServiceDesc is the grpc.ServiceDesc for CertificateAuthority service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
|
|
@ -173,7 +235,14 @@ var CertificateAuthority_ServiceDesc = grpc.ServiceDesc{
|
|||
Handler: _CertificateAuthority_GenerateOCSP_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "GenerateCRL",
|
||||
Handler: _CertificateAuthority_GenerateCRL_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "ca.proto",
|
||||
}
|
||||
|
||||
|
|
@ -262,3 +331,121 @@ var OCSPGenerator_ServiceDesc = grpc.ServiceDesc{
|
|||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "ca.proto",
|
||||
}
|
||||
|
||||
// CRLGeneratorClient is the client API for CRLGenerator service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type CRLGeneratorClient interface {
|
||||
GenerateCRL(ctx context.Context, opts ...grpc.CallOption) (CRLGenerator_GenerateCRLClient, error)
|
||||
}
|
||||
|
||||
type cRLGeneratorClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewCRLGeneratorClient(cc grpc.ClientConnInterface) CRLGeneratorClient {
|
||||
return &cRLGeneratorClient{cc}
|
||||
}
|
||||
|
||||
func (c *cRLGeneratorClient) GenerateCRL(ctx context.Context, opts ...grpc.CallOption) (CRLGenerator_GenerateCRLClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &CRLGenerator_ServiceDesc.Streams[0], "/ca.CRLGenerator/GenerateCRL", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &cRLGeneratorGenerateCRLClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type CRLGenerator_GenerateCRLClient interface {
|
||||
Send(*GenerateCRLRequest) error
|
||||
Recv() (*GenerateCRLResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type cRLGeneratorGenerateCRLClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *cRLGeneratorGenerateCRLClient) Send(m *GenerateCRLRequest) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *cRLGeneratorGenerateCRLClient) Recv() (*GenerateCRLResponse, error) {
|
||||
m := new(GenerateCRLResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// CRLGeneratorServer is the server API for CRLGenerator service.
|
||||
// All implementations must embed UnimplementedCRLGeneratorServer
|
||||
// for forward compatibility
|
||||
type CRLGeneratorServer interface {
|
||||
GenerateCRL(CRLGenerator_GenerateCRLServer) error
|
||||
mustEmbedUnimplementedCRLGeneratorServer()
|
||||
}
|
||||
|
||||
// UnimplementedCRLGeneratorServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedCRLGeneratorServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedCRLGeneratorServer) GenerateCRL(CRLGenerator_GenerateCRLServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method GenerateCRL not implemented")
|
||||
}
|
||||
func (UnimplementedCRLGeneratorServer) mustEmbedUnimplementedCRLGeneratorServer() {}
|
||||
|
||||
// UnsafeCRLGeneratorServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to CRLGeneratorServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeCRLGeneratorServer interface {
|
||||
mustEmbedUnimplementedCRLGeneratorServer()
|
||||
}
|
||||
|
||||
func RegisterCRLGeneratorServer(s grpc.ServiceRegistrar, srv CRLGeneratorServer) {
|
||||
s.RegisterService(&CRLGenerator_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _CRLGenerator_GenerateCRL_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(CRLGeneratorServer).GenerateCRL(&cRLGeneratorGenerateCRLServer{stream})
|
||||
}
|
||||
|
||||
type CRLGenerator_GenerateCRLServer interface {
|
||||
Send(*GenerateCRLResponse) error
|
||||
Recv() (*GenerateCRLRequest, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type cRLGeneratorGenerateCRLServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *cRLGeneratorGenerateCRLServer) Send(m *GenerateCRLResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *cRLGeneratorGenerateCRLServer) Recv() (*GenerateCRLRequest, error) {
|
||||
m := new(GenerateCRLRequest)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// CRLGenerator_ServiceDesc is the grpc.ServiceDesc for CRLGenerator service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var CRLGenerator_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ca.CRLGenerator",
|
||||
HandlerType: (*CRLGeneratorServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "GenerateCRL",
|
||||
Handler: _CRLGenerator_GenerateCRL_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "ca.proto",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ package notmain
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/beeker1121/goque"
|
||||
"github.com/honeycombio/beeline-go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/health"
|
||||
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
||||
|
||||
|
|
@ -34,6 +36,7 @@ type Config struct {
|
|||
|
||||
GRPCCA *cmd.GRPCServerConfig
|
||||
GRPCOCSPGenerator *cmd.GRPCServerConfig
|
||||
GRPCCRLGenerator *cmd.GRPCServerConfig
|
||||
|
||||
SAService *cmd.GRPCClientConfig
|
||||
|
||||
|
|
@ -56,10 +59,16 @@ type Config struct {
|
|||
// The maximum number of subjectAltNames in a single certificate
|
||||
MaxNames int
|
||||
|
||||
// LifespanOCSP is how long OCSP responses are valid for; It should be longer
|
||||
// than the minTimeToExpiry field for the OCSP Updater.
|
||||
// LifespanOCSP is how long OCSP responses are valid for. It should be
|
||||
// longer than the minTimeToExpiry field for the OCSP Updater. Per the BRs,
|
||||
// Section 4.9.10, it MUST NOT be more than 10 days.
|
||||
LifespanOCSP cmd.ConfigDuration
|
||||
|
||||
// LifespanCRL is how long CRLs are valid for. It should be longer than the
|
||||
// `period` field of the CRL Updater. Per the BRs, Section 4.9.7, it MUST
|
||||
// NOT be more than 10 days.
|
||||
LifespanCRL cmd.ConfigDuration
|
||||
|
||||
// GoodKey is an embedded config stanza for the goodkey library.
|
||||
GoodKey goodkey.Config
|
||||
|
||||
|
|
@ -130,6 +139,7 @@ func loadBoulderIssuers(profileConfig issuance.ProfileConfig, issuerConfigs []is
|
|||
func main() {
|
||||
caAddr := flag.String("ca-addr", "", "CA gRPC listen address override")
|
||||
ocspAddr := flag.String("ocsp-addr", "", "OCSP gRPC listen address override")
|
||||
crlAddr := flag.String("crl-addr", "", "CRL gRPC listen address override")
|
||||
debugAddr := flag.String("debug-addr", "", "Debug server address override")
|
||||
configFile := flag.String("config", "", "File path to the configuration file for this service")
|
||||
flag.Parse()
|
||||
|
|
@ -151,6 +161,10 @@ func main() {
|
|||
if *ocspAddr != "" {
|
||||
c.CA.GRPCOCSPGenerator.Address = *ocspAddr
|
||||
}
|
||||
// TODO(#6161): Remove second conditional when we know it always exists.
|
||||
if *crlAddr != "" && c.CA.GRPCCRLGenerator != nil {
|
||||
c.CA.GRPCCRLGenerator.Address = *crlAddr
|
||||
}
|
||||
if *debugAddr != "" {
|
||||
c.CA.DebugAddr = *debugAddr
|
||||
}
|
||||
|
|
@ -261,7 +275,7 @@ func main() {
|
|||
go ocspi.LogOCSPLoop()
|
||||
|
||||
ocspSrv, ocspListener, err := bgrpc.NewServer(c.CA.GRPCOCSPGenerator, tlsConfig, serverMetrics, clk)
|
||||
cmd.FailOnError(err, "Unable to setup CA gRPC server")
|
||||
cmd.FailOnError(err, "Unable to setup CA OCSP gRPC server")
|
||||
capb.RegisterOCSPGeneratorServer(ocspSrv, ocspi)
|
||||
ocspHealth := health.NewServer()
|
||||
healthpb.RegisterHealthServer(ocspSrv, ocspHealth)
|
||||
|
|
@ -272,10 +286,40 @@ func main() {
|
|||
wg.Done()
|
||||
}()
|
||||
|
||||
crli, err := ca.NewCRLImpl(
|
||||
boulderIssuers,
|
||||
c.CA.LifespanCRL.Duration,
|
||||
c.CA.OCSPLogMaxLength,
|
||||
logger,
|
||||
)
|
||||
cmd.FailOnError(err, "Failed to create CRL impl")
|
||||
|
||||
// TODO(#6161): Make this unconditional after this config is deployed. We have
|
||||
// to pre-declare crlListener even though it is only used inside the if block
|
||||
// so that the other assignments inside the block don't shadow crlSrv and
|
||||
// crlHealth, leaving them nil.
|
||||
var crlSrv *grpc.Server
|
||||
var crlHealth *health.Server
|
||||
var crlListener net.Listener
|
||||
if c.CA.GRPCCRLGenerator != nil {
|
||||
crlSrv, crlListener, err = bgrpc.NewServer(c.CA.GRPCCRLGenerator, tlsConfig, serverMetrics, clk)
|
||||
cmd.FailOnError(err, "Unable to setup CA CRL gRPC server")
|
||||
capb.RegisterCRLGeneratorServer(crlSrv, crli)
|
||||
crlHealth = health.NewServer()
|
||||
healthpb.RegisterHealthServer(crlSrv, crlHealth)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
cmd.FailOnError(cmd.FilterShutdownErrors(crlSrv.Serve(crlListener)),
|
||||
"CRLGenerator gRPC service failed")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
cai, err := ca.NewCertificateAuthorityImpl(
|
||||
sa,
|
||||
pa,
|
||||
ocspi,
|
||||
crli,
|
||||
boulderIssuers,
|
||||
ecdsaAllowList,
|
||||
c.CA.Expiry.Duration,
|
||||
|
|
@ -309,9 +353,17 @@ func main() {
|
|||
go cmd.CatchSignals(logger, func() {
|
||||
caHealth.Shutdown()
|
||||
ocspHealth.Shutdown()
|
||||
// TODO(#6161): Make this unconditional.
|
||||
if crlHealth != nil {
|
||||
crlHealth.Shutdown()
|
||||
}
|
||||
ecdsaAllowList.Stop()
|
||||
caSrv.GracefulStop()
|
||||
ocspSrv.GracefulStop()
|
||||
// TODO(#6161): Make this unconditional.
|
||||
if crlSrv != nil {
|
||||
crlSrv.GracefulStop()
|
||||
}
|
||||
wg.Wait()
|
||||
ocspi.Stop()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -83,5 +83,6 @@ func generateKey(session *pkcs11helpers.Session, label string, outputPath string
|
|||
return nil, fmt.Errorf("Failed to write public key to %q: %s", outputPath, err)
|
||||
}
|
||||
log.Printf("Public key written to %q\n", outputPath)
|
||||
|
||||
return &keyInfo{key: pubKey, der: der, id: keyID}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,7 +240,8 @@ type keyConfig struct {
|
|||
PKCS11 PKCS11KeyGenConfig `yaml:"pkcs11"`
|
||||
Key keyGenConfig `yaml:"key"`
|
||||
Outputs struct {
|
||||
PublicKeyPath string `yaml:"public-key-path"`
|
||||
PublicKeyPath string `yaml:"public-key-path"`
|
||||
PKCS11ConfigPath string `yaml:"pkcs11-config-path"`
|
||||
} `yaml:"outputs"`
|
||||
}
|
||||
|
||||
|
|
@ -621,6 +622,17 @@ func keyCeremony(configBytes []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if config.Outputs.PKCS11ConfigPath != "" {
|
||||
contents := fmt.Sprintf(
|
||||
`{"module": %q, "tokenLabel": %q, "pin": %q}`,
|
||||
config.PKCS11.Module, config.PKCS11.StoreLabel, config.PKCS11.PIN,
|
||||
)
|
||||
err = writeFile(config.Outputs.PKCS11ConfigPath, []byte(contents))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -544,9 +544,11 @@ func TestKeyConfigValidate(t *testing.T) {
|
|||
RSAModLength: 2048,
|
||||
},
|
||||
Outputs: struct {
|
||||
PublicKeyPath string `yaml:"public-key-path"`
|
||||
PublicKeyPath string `yaml:"public-key-path"`
|
||||
PKCS11ConfigPath string `yaml:"pkcs11-config-path"`
|
||||
}{
|
||||
PublicKeyPath: "path",
|
||||
PublicKeyPath: "path",
|
||||
PKCS11ConfigPath: "path.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -48,3 +48,8 @@ func (ca *MockCA) IssueCertificateForPrecertificate(ctx context.Context, req *ca
|
|||
func (ca *MockCA) GenerateOCSP(ctx context.Context, req *capb.GenerateOCSPRequest, _ ...grpc.CallOption) (*capb.OCSPResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GenerateCRL is a mock
|
||||
func (ca *MockCA) GenerateCRL(ctx context.Context, opts ...grpc.CallOption) (capb.CertificateAuthority_GenerateCRLClient, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,3 +9,4 @@ key:
|
|||
ecdsa-curve: P-384
|
||||
outputs:
|
||||
public-key-path: /hierarchy/intermediate-signing-pub-ecdsa.pem
|
||||
pkcs11-config-path: /hierarchy/intermediate-signing-key-ecdsa.pkcs11.json
|
||||
|
|
|
|||
|
|
@ -9,3 +9,4 @@ key:
|
|||
rsa-mod-length: 2048
|
||||
outputs:
|
||||
public-key-path: /hierarchy/intermediate-signing-pub-rsa.pem
|
||||
pkcs11-config-path: /hierarchy/intermediate-signing-key-rsa.pkcs11.json
|
||||
|
|
|
|||
|
|
@ -24,6 +24,14 @@
|
|||
"orphan-finder.boulder"
|
||||
]
|
||||
},
|
||||
"grpcCRLGenerator": {
|
||||
"maxConnectionAge": "30s",
|
||||
"address": ":9106",
|
||||
"clientNames": [
|
||||
"health-checker.boulder",
|
||||
"crl-updater.boulder"
|
||||
]
|
||||
},
|
||||
"saService": {
|
||||
"serverAddress": "sa.boulder:9095",
|
||||
"timeout": "15s"
|
||||
|
|
@ -59,11 +67,22 @@
|
|||
"ocspURL": "http://127.0.0.1:4002/",
|
||||
"crlURL": "http://example.com/crl",
|
||||
"location": {
|
||||
"configFile": "test/test-ca.key-pkcs11.json",
|
||||
"configFile": "/hierarchy/intermediate-signing-key-rsa.pkcs11.json",
|
||||
"certFile": "/hierarchy/intermediate-cert-rsa-a.pem",
|
||||
"numSessions": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"useForRSALeaves": false,
|
||||
"useForECDSALeaves": true,
|
||||
"issuerURL": "http://127.0.0.1:4001/aia/issuer/5214744660557630",
|
||||
"ocspURL": "http://127.0.0.1:4002/",
|
||||
"location": {
|
||||
"configFile": "/hierarchy/intermediate-signing-key-ecdsa.pkcs11.json",
|
||||
"certFile": "/hierarchy/intermediate-cert-ecdsa-a.pem",
|
||||
"numSessions": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"useForRSALeaves": false,
|
||||
"useForECDSALeaves": false,
|
||||
|
|
@ -71,7 +90,7 @@
|
|||
"ocspURL": "http://127.0.0.1:4002/",
|
||||
"crlURL": "http://example.com/crl",
|
||||
"location": {
|
||||
"configFile": "test/test-ca.key-pkcs11.json",
|
||||
"configFile": "/hierarchy/intermediate-signing-key-rsa.pkcs11.json",
|
||||
"certFile": "/hierarchy/intermediate-cert-rsa-b.pem",
|
||||
"numSessions": 2
|
||||
}
|
||||
|
|
@ -84,6 +103,7 @@
|
|||
"serialPrefix": 255,
|
||||
"maxNames": 100,
|
||||
"lifespanOCSP": "96h",
|
||||
"lifespanCRL": "216h",
|
||||
"goodkey": {
|
||||
"weakKeyFile": "test/example-weak-keys.json",
|
||||
"blockedKeyFile": "test/example-blocked-keys.yaml",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,14 @@
|
|||
"orphan-finder.boulder"
|
||||
]
|
||||
},
|
||||
"grpcCRLGenerator": {
|
||||
"maxConnectionAge": "30s",
|
||||
"address": ":9106",
|
||||
"clientNames": [
|
||||
"health-checker.boulder",
|
||||
"crl-updater.boulder"
|
||||
]
|
||||
},
|
||||
"saService": {
|
||||
"serverAddress": "sa.boulder:9095",
|
||||
"timeout": "15s"
|
||||
|
|
@ -59,11 +67,22 @@
|
|||
"ocspURL": "http://127.0.0.1:4002/",
|
||||
"crlURL": "http://example.com/crl",
|
||||
"location": {
|
||||
"configFile": "test/test-ca.key-pkcs11.json",
|
||||
"configFile": "/hierarchy/intermediate-signing-key-rsa.pkcs11.json",
|
||||
"certFile": "/hierarchy/intermediate-cert-rsa-a.pem",
|
||||
"numSessions": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"useForRSALeaves": false,
|
||||
"useForECDSALeaves": true,
|
||||
"issuerURL": "http://127.0.0.1:4001/aia/issuer/5214744660557630",
|
||||
"ocspURL": "http://127.0.0.1:4002/",
|
||||
"location": {
|
||||
"configFile": "/hierarchy/intermediate-signing-key-ecdsa.pkcs11.json",
|
||||
"certFile": "/hierarchy/intermediate-cert-ecdsa-a.pem",
|
||||
"numSessions": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"useForRSALeaves": false,
|
||||
"useForECDSALeaves": false,
|
||||
|
|
@ -71,7 +90,7 @@
|
|||
"ocspURL": "http://127.0.0.1:4002/",
|
||||
"crlURL": "http://example.com/crl",
|
||||
"location": {
|
||||
"configFile": "test/test-ca.key-pkcs11.json",
|
||||
"configFile": "/hierarchy/intermediate-signing-key-rsa.pkcs11.json",
|
||||
"certFile": "/hierarchy/intermediate-cert-rsa-b.pem",
|
||||
"numSessions": 2
|
||||
}
|
||||
|
|
@ -84,6 +103,7 @@
|
|||
"serialPrefix": 255,
|
||||
"maxNames": 100,
|
||||
"lifespanOCSP": "96h",
|
||||
"lifespanCRL": "216h",
|
||||
"goodkey": {
|
||||
"weakKeyFile": "test/example-weak-keys.json",
|
||||
"blockedKeyFile": "test/example-blocked-keys.yaml",
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ SERVICES = (
|
|||
('sd-test-srv', 'boulder-remoteva-a', 'boulder-remoteva-b')),
|
||||
Service('boulder-ca-a',
|
||||
8001, 'ca1.boulder:9093',
|
||||
('./bin/boulder-ca', '--config', os.path.join(config_dir, 'ca-a.json'), '--ca-addr', 'ca1.boulder:9093', '--ocsp-addr', 'ca1.boulder:9096', '--debug-addr', ':8001'),
|
||||
('./bin/boulder-ca', '--config', os.path.join(config_dir, 'ca-a.json'), '--ca-addr', 'ca1.boulder:9093', '--ocsp-addr', 'ca1.boulder:9096', '--crl-addr', 'ca1.boulder:9196', '--debug-addr', ':8001'),
|
||||
('sd-test-srv', 'boulder-sa-1', 'boulder-sa-2')),
|
||||
Service('boulder-ca-b',
|
||||
8101, 'ca2.boulder:9093',
|
||||
('./bin/boulder-ca', '--config', os.path.join(config_dir, 'ca-b.json'), '--ca-addr', 'ca2.boulder:9093', '--ocsp-addr', 'ca2.boulder:9096', '--debug-addr', ':8101'),
|
||||
('./bin/boulder-ca', '--config', os.path.join(config_dir, 'ca-b.json'), '--ca-addr', 'ca2.boulder:9093', '--ocsp-addr', 'ca2.boulder:9096', '--crl-addr', 'ca2.boulder:9196', '--debug-addr', ':8101'),
|
||||
('sd-test-srv', 'boulder-sa-1', 'boulder-sa-2')),
|
||||
Service('akamai-test-srv',
|
||||
6789, None,
|
||||
|
|
|
|||
Loading…
Reference in New Issue