Skip to content

Commit

Permalink
Review comments and added an integration test
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew B White <whitemat@uk.ibm.com>
  • Loading branch information
mbwhite authored and denyeart committed Jul 22, 2021
1 parent ac256a9 commit cb74047
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 11 deletions.
2 changes: 2 additions & 0 deletions cmd/fabric-ca-server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ ca:
certfile:
# Chain file
chainfile:
# Ignore Certificate Expiration in the case of re-enroll
reenrollIgnoreCertExpiry: false
#############################################################################
# The gencrl REST endpoint is used to generate a CRL that contains revoked
Expand Down
2 changes: 1 addition & 1 deletion lib/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ func (ca *CA) initConfig() (err error) {
// Return nil if successful; otherwise, return an 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())
log.Debugf("Certicate Dates: NotAfter = %s NotBefore = %s \n", cert.NotAfter.String(), cert.NotBefore.String())

opts, err := ca.getVerifyOptions()
if err != nil {
Expand Down
12 changes: 5 additions & 7 deletions lib/caconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ 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 @@ -118,11 +116,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"`
IgnoreCertExpiry bool `def:"false" help:"Ignore Certificate Expirty for re-enroll"`
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"`
ReenrollIgnoreCertExpiry bool `def:"false" help:"Ignore Certificate Expirty for re-enroll"`
}

// CAConfigDB is the database part of the server's config
Expand Down
7 changes: 4 additions & 3 deletions lib/serverrequestcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ func (ctx *serverRequestContextImpl) verifyX509Token(ca *CA, authHdr, method, ur
}

// Make sure the caller's cert was issued by this CA
err2 = ca.VerifyCertificate(cert, (ctx.endpoint.Path == "reenroll" && ctx.ca.Config.CA.IgnoreCertExpiry))
reenrollIgnoreCertExpiry := ctx.endpoint.Path == "reenroll" && ctx.ca.Config.CA.ReenrollIgnoreCertExpiry
err2 = ca.VerifyCertificate(cert, reenrollIgnoreCertExpiry)
if err2 != nil {
return "", caerrors.NewAuthenticationErr(caerrors.ErrUntrustedCertificate, "Untrusted certificate: %s", err2)
}
Expand All @@ -207,8 +208,8 @@ func (ctx *serverRequestContextImpl) verifyX509Token(ca *CA, authHdr, method, ur

if expired {
log.Debugf("Expired Certificate")
if ctx.endpoint.Path == "reenroll" && ctx.ca.Config.CA.IgnoreCertExpiry {
log.Infof("Ignoring expired certificate for re-endroll operation")
if reenrollIgnoreCertExpiry {
log.Infof("Ignoring expired certificate for re-enroll operation")
} else {
return "", caerrors.NewAuthenticationErr(caerrors.ErrCertExpired,
"The certificate in the authorization header is a revoked or expired certificate")
Expand Down
165 changes: 165 additions & 0 deletions test/integration/certexpiry/certexpiry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package defserver

import (
"fmt"
"os"
"testing"
"time"

"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/log"
"github.com/hyperledger/fabric-ca/cmd/fabric-ca-client/command"
"github.com/hyperledger/fabric-ca/internal/pkg/util"
"github.com/hyperledger/fabric-ca/lib"
"github.com/hyperledger/fabric-ca/lib/metadata"
)

const (
cmdName = "fabric-ca-client"
)

var (
defaultServer *lib.Server
defaultServerPort = 7055
defaultServerEnrollURL = fmt.Sprintf("http://admin:adminpw@localhost:%d", defaultServerPort)
defaultServerHomeDir = "certExpiryServerDir"
storeCertsDir = "/tmp/testCertsCertExpiry"
clientCAHome = "/tmp/certExpiryCaHome"
)

func TestMain(m *testing.M) {
var err error

metadata.Version = "1.1.0"
os.Setenv("FABRIC_CA_SERVER_SIGNING_DEFAULT_EXPIRY", "1m")
os.Setenv("FABRIC_CA_CLIENT_HOME", clientCAHome)

os.RemoveAll(defaultServerHomeDir)
os.RemoveAll(storeCertsDir)
os.RemoveAll(clientCAHome)
defaultServer, err = getDefaultServer()
if err != nil {
log.Errorf("Failed to get instance of server: %s", err)
os.Exit(1)
}

err = defaultServer.Start()
if err != nil {
log.Errorf("Failed to start server: %s", err)
os.Exit(1)
}

rc := m.Run()

err = defaultServer.Stop()
if err != nil {
log.Errorf("Failed to stop server: %s, integration test results: %d", err, rc)
os.Exit(1)
}

os.RemoveAll(defaultServerHomeDir)
os.RemoveAll(storeCertsDir)
os.RemoveAll(clientCAHome)
os.Exit(rc)
}

func TestReenrollExpiredCert(t *testing.T) {
var err error

// Enroll a user that will be used for subsequent certificate commands
err = command.RunMain([]string{cmdName, "enroll", "-u", defaultServerEnrollURL, "-d"})
util.FatalError(t, err, "Failed to enroll user")

// Register a new user
err = command.RunMain([]string{cmdName, "register", "-u", defaultServerEnrollURL, "-d", "--csr.keyrequest.reusekey", "--id.name", "user1", "--id.secret", "user1pw", "--id.type", "client"})
util.FatalError(t, err, "Failed to register new user1")

userServiceEnrollURL := fmt.Sprintf("http://user1:user1pw@localhost:%d", defaultServerPort)

// Enroll and then reenroll to check
err = command.RunMain([]string{cmdName, "enroll", "-u", userServiceEnrollURL, "-d"})
util.FatalError(t, err, "Failed to enroll user1")

err = command.RunMain([]string{cmdName, "reenroll", "-u", userServiceEnrollURL, "-d", "--csr.keyrequest.reusekey"})
util.FatalError(t, err, "Failed to reenroll user1")

log.Infof("Tested re-enroll of id, waiting for cert to expiry before testing re-enroll\n")
time.Sleep(2 * time.Minute)

// within the setting in the CA config reenrollIgnoreCertExpiry this call would normally fail
err = command.RunMain([]string{cmdName, "reenroll", "-u", userServiceEnrollURL, "-d", "--csr.keyrequest.reusekey"})
util.FatalError(t, err, "Failed to reenroll user1 %s", time.Now())
}

func getDefaultServer() (*lib.Server, error) {
affiliations := map[string]interface{}{
"hyperledger": map[string]interface{}{
"fabric": []string{"ledger", "orderer", "security"},
"fabric-ca": nil,
"sdk": nil,
},
"org2": []string{"dept1"},
"org1": nil,
"org2dept1": nil,
}
profiles := map[string]*config.SigningProfile{
"tls": &config.SigningProfile{
Usage: []string{"signing", "key encipherment", "server auth", "client auth", "key agreement"},
ExpiryString: "1m",
},
"ca": &config.SigningProfile{
Usage: []string{"cert sign", "crl sign"},
ExpiryString: "1m",
CAConstraint: config.CAConstraint{
IsCA: true,
MaxPathLen: 0,
},
},
}
defaultProfile := &config.SigningProfile{
Usage: []string{"cert sign"},
ExpiryString: "1m",
Expiry: time.Minute * 1, // set to force certs to expiry quickly
}
srv := &lib.Server{
Config: &lib.ServerConfig{
Port: defaultServerPort,
Debug: true,
},
CA: lib.CA{
Config: &lib.CAConfig{
Intermediate: lib.IntermediateCA{
ParentServer: lib.ParentServer{
URL: "",
},
},
CA: lib.CAInfo{
ReenrollIgnoreCertExpiry: true,
},
Affiliations: affiliations,
Registry: lib.CAConfigRegistry{
MaxEnrollments: -1,
},
Signing: &config.Signing{
Profiles: profiles,
Default: defaultProfile,
},
Version: "1.1.0", // The default test server/ca should use the latest version
},
},
HomeDir: defaultServerHomeDir,
}
// The bootstrap user's affiliation is the empty string, which
// means the user is at the affiliation root
err := srv.RegisterBootstrapUser("admin", "adminpw", "")
if err != nil {
return nil, err
}
return srv, nil
}

0 comments on commit cb74047

Please sign in to comment.