Skip to content

Commit

Permalink
Support ignoring certificate expiry for re-enrolls
Browse files Browse the repository at this point in the history
- add a new option to the config yaml file
- there several cerficate checks, and these all do a cert expiry check
- Make the expiry aspect of these null and void in the case of the flag in the config
  being set, and the action being a re-enroll

Signed-off-by: Matthew B White <whitemat@uk.ibm.com>
  • Loading branch information
mbwhite authored and denyeart committed Jul 22, 2021
1 parent 3852738 commit ac256a9
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 13 deletions.
1 change: 1 addition & 0 deletions docs/source/servercli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Fabric-CA Server's CLI
-b, --boot string The user:pass for bootstrap admin which is required to build default config file
--ca.certfile string PEM-encoded CA certificate file (default "ca-cert.pem")
--ca.chainfile string PEM-encoded CA chain file (default "ca-chain.pem")
--ca.ignorecertexpiry Ignore Certificate Expirty for re-enroll
--ca.keyfile string PEM-encoded CA key file
-n, --ca.name string Certificate Authority name
--cacount int Number of non-default CA instances
Expand Down
12 changes: 11 additions & 1 deletion lib/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,11 +471,21 @@ func (ca *CA) initConfig() (err error) {

// VerifyCertificate verifies that 'cert' was issued by this CA
// Return nil if successful; otherwise, return an error.
func (ca *CA) VerifyCertificate(cert *x509.Certificate) error {
func (ca *CA) VerifyCertificate(cert *x509.Certificate, forceTime bool) error {

log.Debugf("Certicate Dates: NotAfter = %s\n Cert NotBefore = %s \n", cert.NotAfter.String(), cert.NotBefore.String())

opts, err := ca.getVerifyOptions()
if err != nil {
return errors.WithMessage(err, "Failed to get verify options")
}

// force time to be 30seconds after start to ensure expiry doesn't get flaged
// this is one of the checks that made on the certificate
if forceTime {
opts.CurrentTime = cert.NotBefore.Add(time.Duration(time.Second * 30))
}

_, err = cert.Verify(*opts)
if err != nil {
return errors.WithMessage(err, "Failed to verify certificate")
Expand Down
10 changes: 5 additions & 5 deletions lib/ca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,14 +591,14 @@ func TestCAVerifyCertificate(t *testing.T) {
ca.Config.CA.Keyfile = caKey
ca.Config.CA.Certfile = caCert
ca.Config.CA.Chainfile = "../testdata/empty.json"
err = ca.VerifyCertificate(cert)
err = ca.VerifyCertificate(cert, false)
t.Log("ca.VerifyCertificate error: ", err)
if err == nil {
t.Error("VerifyCertificate should have failed")
}

ca.Config.CA.Chainfile = "../testdata/crl.pem"
err = ca.VerifyCertificate(cert)
err = ca.VerifyCertificate(cert, false)
t.Log("ca.VerifyCertificate error: ", err)
if err == nil {
t.Error("VerifyCertificate should have failed")
Expand All @@ -612,7 +612,7 @@ func TestCAVerifyCertificate(t *testing.T) {
err = ioutil.WriteFile(filepath.Join(os.TempDir(), "ca-chainfile.pem"), caCert2, 0644)
assert.NoError(t, err, "failed to write ca-chainfile.pem")
ca.Config.CA.Chainfile = filepath.Join(os.TempDir(), "ca-chainfile.pem")
err = ca.VerifyCertificate(cert)
err = ca.VerifyCertificate(cert, false)
t.Log("ca.VerifyCertificate error: ", err)
if err == nil {
t.Error("VerifyCertificate should have failed")
Expand All @@ -625,13 +625,13 @@ func TestCAVerifyCertificate(t *testing.T) {
ca.Config.CA.Chainfile = "doesNotExist"
ca.Config.CA.Certfile = "doesNotExist"
ca.Config.Intermediate.ParentServer.URL = "http://127.0.0.1:" + caPort
err = ca.VerifyCertificate(cert)
err = ca.VerifyCertificate(cert, false)
t.Log("ca.VerifyCertificate error: ", err)
if err == nil {
t.Error("VerifyCertificate should have failed")
}
ca.Config.CA.Chainfile = noUsageCert
err = ca.VerifyCertificate(cert)
err = ca.VerifyCertificate(cert, false)
t.Log("ca.VerifyCertificate error: ", err)
if err == nil {
t.Error("VerifyCertificate should have failed")
Expand Down
11 changes: 7 additions & 4 deletions lib/caconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ ca:
certfile: ca-cert.pem
# The CA key file
keyfile: ca-key.pem
# Ignore Certificate Expiration in the case of re-enroll
ignoreCertExpiry: false
#############################################################################
# Database section
Expand Down Expand Up @@ -116,10 +118,11 @@ type affiliationsOptions struct {

// CAInfo is the CA information on a fabric-ca-server
type CAInfo struct {
Name string `opt:"n" help:"Certificate Authority name"`
Keyfile string `help:"PEM-encoded CA key file"`
Certfile string `def:"ca-cert.pem" help:"PEM-encoded CA certificate file"`
Chainfile string `def:"ca-chain.pem" help:"PEM-encoded CA chain file"`
Name string `opt:"n" help:"Certificate Authority name"`
Keyfile string `help:"PEM-encoded CA key file"`
Certfile string `def:"ca-cert.pem" help:"PEM-encoded CA certificate file"`
Chainfile string `def:"ca-chain.pem" help:"PEM-encoded CA chain file"`
IgnoreCertExpiry bool `def:"false" help:"Ignore Certificate Expirty for re-enroll"`
}

// CAConfigDB is the database part of the server's config
Expand Down
16 changes: 13 additions & 3 deletions lib/serverrequestcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,13 @@ func (ctx *serverRequestContextImpl) verifyX509Token(ca *CA, authHdr, method, ur
if err2 != nil {
return "", caerrors.NewAuthenticationErr(caerrors.ErrInvalidToken, "Invalid token in authorization header: %s", err2)
}

// Make sure the caller's cert was issued by this CA
err2 = ca.VerifyCertificate(cert)
err2 = ca.VerifyCertificate(cert, (ctx.endpoint.Path == "reenroll" && ctx.ca.Config.CA.IgnoreCertExpiry))
if err2 != nil {
return "", caerrors.NewAuthenticationErr(caerrors.ErrUntrustedCertificate, "Untrusted certificate: %s", err2)
}

id := util.GetEnrollmentIDFromX509Certificate(cert)
log.Debugf("Checking for revocation/expiration of certificate owned by '%s'", id)

Expand All @@ -202,10 +204,17 @@ func (ctx *serverRequestContextImpl) verifyX509Token(ca *CA, authHdr, method, ur
if !checked {
return "", caerrors.NewHTTPErr(401, caerrors.ErrCertRevokeCheckFailure, "Failed while checking for revocation")
}

if expired {
return "", caerrors.NewAuthenticationErr(caerrors.ErrCertExpired,
"The certificate in the authorization header is a revoked or expired certificate")
log.Debugf("Expired Certificate")
if ctx.endpoint.Path == "reenroll" && ctx.ca.Config.CA.IgnoreCertExpiry {
log.Infof("Ignoring expired certificate for re-endroll operation")
} else {
return "", caerrors.NewAuthenticationErr(caerrors.ErrCertExpired,
"The certificate in the authorization header is a revoked or expired certificate")
}
}

aki := hex.EncodeToString(cert.AuthorityKeyId)
serial := util.GetSerialAsHex(cert.SerialNumber)
aki = strings.ToLower(strings.TrimLeft(aki, "0"))
Expand All @@ -215,6 +224,7 @@ func (ctx *serverRequestContextImpl) verifyX509Token(ca *CA, authHdr, method, ur
if err != nil {
return "", err
}

if certificate.Status == "revoked" {
return "", caerrors.NewAuthenticationErr(caerrors.ErrCertRevoked, "The certificate in the authorization header is a revoked certificate")
}
Expand Down

0 comments on commit ac256a9

Please sign in to comment.