github.com/silveraid/fabric-ca@v1.1.0-preview.0.20180127000700-71974f53ab08/lib/servergencrl.go (about)

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