github.com/bestbeforetoday/fabric-ca@v2.0.0-alpha+incompatible/lib/servergencrl.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package lib
     8  
     9  import (
    10  	"crypto/x509"
    11  	"crypto/x509/pkix"
    12  	"encoding/pem"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"math/big"
    16  	"time"
    17  
    18  	"github.com/cloudflare/cfssl/crl"
    19  	"github.com/cloudflare/cfssl/log"
    20  	"github.com/hyperledger/fabric-ca/api"
    21  	"github.com/hyperledger/fabric-ca/lib/caerrors"
    22  	"github.com/hyperledger/fabric-ca/util"
    23  	"github.com/pkg/errors"
    24  )
    25  
    26  const (
    27  	crlPemType = "X509 CRL"
    28  )
    29  
    30  // The response to the POST /gencrl request
    31  type genCRLResponseNet struct {
    32  	// Base64 encoding of PEM-encoded CRL
    33  	CRL string
    34  }
    35  
    36  func newGenCRLEndpoint(s *Server) *serverEndpoint {
    37  	return &serverEndpoint{
    38  		Path:    "gencrl",
    39  		Methods: []string{"POST"},
    40  		Handler: genCRLHandler,
    41  		Server:  s,
    42  	}
    43  }
    44  
    45  // Handle an generate CRL request
    46  func genCRLHandler(ctx *serverRequestContextImpl) (interface{}, error) {
    47  	var req api.GenCRLRequest
    48  	err := ctx.ReadBody(&req)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	// Authenticate the invoker
    54  	id, err := ctx.TokenAuthentication()
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	log.Debugf("Received gencrl request from %s: %+v", id, util.StructToString(&req))
    59  
    60  	// Get targeted CA
    61  	ca, err := ctx.GetCA()
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	// Make sure that the user has the "hf.GenCRL" attribute in order to be authorized
    67  	// to generate CRL. This attribute comes from the user registry, which
    68  	// is either in the DB if LDAP is not configured, or comes from LDAP if LDAP is
    69  	// configured.
    70  	err = ca.attributeIsTrue(id, "hf.GenCRL")
    71  	if err != nil {
    72  		return nil, caerrors.NewAuthorizationErr(caerrors.ErrNoGenCRLAuth, "The identity '%s' does not have authority to generate a CRL", id)
    73  	}
    74  
    75  	crl, err := genCRL(ca, req)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	log.Debugf("Successfully generated CRL")
    80  
    81  	resp := &genCRLResponseNet{CRL: util.B64Encode(crl)}
    82  	return resp, nil
    83  }
    84  
    85  // GenCRL will generate CRL
    86  func genCRL(ca *CA, req api.GenCRLRequest) ([]byte, error) {
    87  	var err error
    88  	if !req.RevokedBefore.IsZero() && req.RevokedAfter.After(req.RevokedBefore) {
    89  		return nil, caerrors.NewHTTPErr(400, caerrors.ErrInvalidRevokedAfter,
    90  			"Invalid 'revokedafter' value. It must not be a timestamp greater than 'revokedbefore'")
    91  	}
    92  
    93  	if !req.ExpireBefore.IsZero() && req.ExpireAfter.After(req.ExpireBefore) {
    94  		return nil, caerrors.NewHTTPErr(400, caerrors.ErrInvalidExpiredAfter,
    95  			"Invalid 'expireafter' value. It must not be a timestamp greater than 'expirebefore'")
    96  	}
    97  
    98  	// Get revoked certificates from the database
    99  	certs, err := ca.certDBAccessor.GetRevokedCertificates(req.ExpireAfter, req.ExpireBefore, req.RevokedAfter, req.RevokedBefore)
   100  	if err != nil {
   101  		log.Errorf("Failed to get revoked certificates from the database: %s", err)
   102  		return nil, caerrors.NewHTTPErr(500, caerrors.ErrRevokedCertsFromDB, "Failed to get revoked certificates")
   103  	}
   104  
   105  	caCert, err := getCACert(ca)
   106  	if err != nil {
   107  		log.Errorf("Failed to get certficate for CA '%s': %s", ca.HomeDir, err)
   108  		return nil, caerrors.NewHTTPErr(500, caerrors.ErrGetCACert, "Failed to get certficate for CA '%s'", ca.HomeDir)
   109  	}
   110  
   111  	if !canSignCRL(caCert) {
   112  		return nil, caerrors.NewHTTPErr(500, caerrors.ErrNoCrlSignAuth,
   113  			"The CA does not have authority to generate a CRL. Its certificate does not have 'crl sign' key usage")
   114  	}
   115  
   116  	// Get the signer for the CA
   117  	_, signer, err := util.GetSignerFromCert(caCert, ca.csp)
   118  	if err != nil {
   119  		log.Errorf("Failed to get signer for CA '%s': %s", ca.HomeDir, err)
   120  		return nil, caerrors.NewHTTPErr(500, caerrors.ErrGetCASigner, "Failed to get signer for CA '%s'", ca.HomeDir)
   121  	}
   122  
   123  	expiry := time.Now().UTC().Add(ca.Config.CRL.Expiry)
   124  	var revokedCerts []pkix.RevokedCertificate
   125  
   126  	// For every record, create a new revokedCertificate and add it to slice
   127  	for _, certRecord := range certs {
   128  		serialInt := new(big.Int)
   129  		serialInt.SetString(certRecord.Serial, 16)
   130  		revokedCert := pkix.RevokedCertificate{
   131  			SerialNumber:   serialInt,
   132  			RevocationTime: certRecord.RevokedAt,
   133  		}
   134  		revokedCerts = append(revokedCerts, revokedCert)
   135  	}
   136  
   137  	crl, err := crl.CreateGenericCRL(revokedCerts, signer, caCert, expiry)
   138  	if err != nil {
   139  		log.Errorf("Failed to generate CRL for CA '%s': %s", ca.HomeDir, err)
   140  		return nil, caerrors.NewHTTPErr(500, caerrors.ErrGenCRL, "Failed to generate CRL for CA '%s'", ca.HomeDir)
   141  	}
   142  	blk := &pem.Block{Bytes: crl, Type: crlPemType}
   143  	return pem.EncodeToMemory(blk), nil
   144  }
   145  
   146  func getCACert(ca *CA) (*x509.Certificate, error) {
   147  	// Get CA certificate
   148  	caCertBytes, err := ioutil.ReadFile(ca.Config.CA.Certfile)
   149  	if err != nil {
   150  		return nil, errors.WithMessage(err, fmt.Sprintf("Failed to read certificate for the CA '%s'", ca.HomeDir))
   151  	}
   152  	caCert, err := BytesToX509Cert(caCertBytes)
   153  	if err != nil {
   154  		return nil, errors.WithMessage(err, fmt.Sprintf("Failed to get certificate for the CA '%s'", ca.HomeDir))
   155  	}
   156  	return caCert, nil
   157  }