github.com/blockchain-gm/fabric-ca@v0.0.0-20200423072702-b2c40c7ac69c/lib/serverenroll.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  	"encoding/asn1"
    12  	"encoding/pem"
    13  	"time"
    14  
    15  	"github.com/cloudflare/cfssl/config"
    16  	"github.com/cloudflare/cfssl/csr"
    17  	cferr "github.com/cloudflare/cfssl/errors"
    18  	"github.com/cloudflare/cfssl/log"
    19  	"github.com/cloudflare/cfssl/signer"
    20  	"github.com/hyperledger/fabric-ca/api"
    21  	"github.com/hyperledger/fabric-ca/lib/caerrors"
    22  	"github.com/hyperledger/fabric-ca/lib/common"
    23  	"github.com/hyperledger/fabric-ca/lib/server/user"
    24  	"github.com/hyperledger/fabric-ca/util"
    25  	"github.com/pkg/errors"
    26  	"github.com/tjfoc/gmsm/sm2"
    27  )
    28  
    29  const (
    30  	commonNameLength             = 64
    31  	serialNumberLength           = 64
    32  	countryNameLength            = 2
    33  	localityNameLength           = 128
    34  	stateOrProvinceNameLength    = 128
    35  	organizationNameLength       = 64
    36  	organizationalUnitNameLength = 64
    37  )
    38  
    39  var (
    40  	// The X.509 BasicConstraints object identifier (RFC 5280, 4.2.1.9)
    41  	basicConstraintsOID   = asn1.ObjectIdentifier{2, 5, 29, 19}
    42  	commonNameOID         = asn1.ObjectIdentifier{2, 5, 4, 3}
    43  	serialNumberOID       = asn1.ObjectIdentifier{2, 5, 4, 5}
    44  	countryOID            = asn1.ObjectIdentifier{2, 5, 4, 6}
    45  	localityOID           = asn1.ObjectIdentifier{2, 5, 4, 7}
    46  	stateOID              = asn1.ObjectIdentifier{2, 5, 4, 8}
    47  	organizationOID       = asn1.ObjectIdentifier{2, 5, 4, 10}
    48  	organizationalUnitOID = asn1.ObjectIdentifier{2, 5, 4, 11}
    49  )
    50  
    51  func newEnrollEndpoint(s *Server) *serverEndpoint {
    52  	return &serverEndpoint{
    53  		Path:      "enroll",
    54  		Methods:   []string{"POST"},
    55  		Handler:   enrollHandler,
    56  		Server:    s,
    57  		successRC: 201,
    58  	}
    59  }
    60  
    61  func newReenrollEndpoint(s *Server) *serverEndpoint {
    62  	return &serverEndpoint{
    63  		Path:      "reenroll",
    64  		Methods:   []string{"POST"},
    65  		Handler:   reenrollHandler,
    66  		Server:    s,
    67  		successRC: 201,
    68  	}
    69  }
    70  
    71  // Handle an enroll request, guarded by basic authentication
    72  func enrollHandler(ctx *serverRequestContextImpl) (interface{}, error) {
    73  	id, err := ctx.BasicAuthentication()
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	resp, err := handleEnroll(ctx, id)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	err = ctx.ui.LoginComplete()
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	return resp, nil
    86  }
    87  
    88  // Handle a reenroll request, guarded by token authentication
    89  func reenrollHandler(ctx *serverRequestContextImpl) (interface{}, error) {
    90  	// Authenticate the caller
    91  	id, err := ctx.TokenAuthentication()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	return handleEnroll(ctx, id)
    96  }
    97  
    98  // Handle the common processing for enroll and reenroll
    99  func handleEnroll(ctx *serverRequestContextImpl, id string) (interface{}, error) {
   100  	var req api.EnrollmentRequestNet
   101  	err := ctx.ReadBody(&req)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	// Get the targeted CA
   106  	ca, err := ctx.GetCA()
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	// If NotAfter is not set in the request, then set it to the expiry in the
   111  	// specified profile
   112  	if req.NotAfter.IsZero() {
   113  		profile := ca.Config.Signing.Default
   114  		if req.Profile != "" && ca.Config.Signing != nil &&
   115  			ca.Config.Signing.Profiles != nil && ca.Config.Signing.Profiles[req.Profile] != nil {
   116  			profile = ca.Config.Signing.Profiles[req.Profile]
   117  		}
   118  		req.NotAfter = time.Now().Round(time.Minute).Add(profile.Expiry).UTC()
   119  	}
   120  	caexpiry, err := ca.getCACertExpiry()
   121  	if err != nil {
   122  		return nil, errors.New("Failed to get CA certificate information")
   123  	}
   124  
   125  	// Make sure requested expiration for enrollment certificate is not after CA certificate
   126  	// expiration
   127  	if !caexpiry.IsZero() && req.NotAfter.After(caexpiry) {
   128  		log.Debugf("Requested expiry '%s' is after the CA certificate expiry '%s'. Will use CA cert expiry",
   129  			req.NotAfter, caexpiry)
   130  		req.NotAfter = caexpiry
   131  	}
   132  
   133  	// Process the sign request from the caller.
   134  	// Make sure it is authorized and do any swizzling appropriate to the request.
   135  	err = processSignRequest(id, &req.SignRequest, ca, ctx)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	// Get an attribute extension if one is being requested
   140  	ext, err := ctx.GetAttrExtension(req.AttrReqs, req.Profile)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	// If there is an extension requested, add it to the request
   145  	if ext != nil {
   146  		log.Debugf("Adding attribute extension to CSR: %+v", ext)
   147  		req.Extensions = append(req.Extensions, *ext)
   148  	}
   149  	// Sign the certificate
   150  	// cert, err := ca.enrollSigner.Sign(req.SignRequest)
   151  	cert, err := signCert(req.SignRequest, ca)
   152  	if err != nil {
   153  		return nil, errors.WithMessage(err, "Certificate signing failure")
   154  	}
   155  	// Add server info to the response
   156  	resp := &common.EnrollmentResponseNet{
   157  		Cert: util.B64Encode(cert),
   158  	}
   159  	err = ca.fillCAInfo(&resp.ServerInfo)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	// Success
   164  	return resp, nil
   165  }
   166  
   167  // Process the sign request.
   168  // Make any authorization checks needed, depending on the contents
   169  // of the CSR (Certificate Signing Request).
   170  // In particular, if the request is for an intermediate CA certificate,
   171  // the caller must have the "hf.IntermediateCA" attribute.
   172  // Check to see that CSR values do not exceed the character limit
   173  // as specified in RFC 3280, page 103.
   174  // Set the OU fields of the request.
   175  func processSignRequest(id string, req *signer.SignRequest, ca *CA, ctx *serverRequestContextImpl) error {
   176  	// Decode and parse the request into a CSR so we can make checks
   177  	block, _ := pem.Decode([]byte(req.Request))
   178  	if block == nil {
   179  		return cferr.New(cferr.CSRError, cferr.DecodeFailed)
   180  	}
   181  	if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" {
   182  		return cferr.Wrap(cferr.CSRError,
   183  			cferr.BadRequest, errors.New("not a certificate or csr"))
   184  	}
   185  	// csrReq, err := x509.ParseCertificateRequest(block.Bytes)
   186  	// if err != nil {
   187  	// 	return err
   188  	// }
   189  	var csrReq *x509.CertificateRequest
   190  	var err error
   191  	if IsGMConfig() {
   192  		sm2csrReq, err := sm2.ParseCertificateRequest(block.Bytes)
   193  		if err == nil {
   194  			csrReq = ParseSm2CertificateRequest2X509(sm2csrReq)
   195  		}
   196  	} else {
   197  		csrReq, err = x509.ParseCertificateRequest(block.Bytes)
   198  	}
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	log.Debugf("Processing sign request: id=%s, CommonName=%s, Subject=%+v", id, csrReq.Subject.CommonName, req.Subject)
   204  	if (req.Subject != nil && req.Subject.CN != id) || csrReq.Subject.CommonName != id {
   205  		return errors.New("The CSR subject common name must equal the enrollment ID")
   206  	}
   207  	isForCACert, err := isRequestForCASigningCert(csrReq, ca, req.Profile)
   208  	if err != nil {
   209  		return err
   210  	}
   211  	if isForCACert {
   212  		// This is a request for a CA certificate, so make sure the caller
   213  		// has the 'hf.IntermediateCA' attribute
   214  		err := ca.attributeIsTrue(id, "hf.IntermediateCA")
   215  		if err != nil {
   216  			return err
   217  		}
   218  	}
   219  	// Check the CSR input length
   220  	err = csrInputLengthCheck(csrReq)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	caller, err := ctx.GetCaller()
   225  	if err != nil {
   226  		return err
   227  	}
   228  	// Set the OUs in the request appropriately.
   229  	setRequestOUs(req, caller)
   230  	log.Debug("Finished processing sign request")
   231  	return nil
   232  }
   233  
   234  // Check to see if this is a request for a CA signing certificate.
   235  // This can occur if the profile or the CSR has the IsCA bit set.
   236  // See the X.509 BasicConstraints extension (RFC 5280, 4.2.1.9).
   237  func isRequestForCASigningCert(csrReq *x509.CertificateRequest, ca *CA, profile string) (bool, error) {
   238  	// Check the profile to see if the IsCA bit is set
   239  	sp := getSigningProfile(ca, profile)
   240  	if sp == nil {
   241  		return false, errors.Errorf("Invalid profile: '%s'", profile)
   242  	}
   243  	if sp.CAConstraint.IsCA {
   244  		log.Debugf("Request is for a CA signing certificate as set in profile '%s'", profile)
   245  		return true, nil
   246  	}
   247  	// Check the CSR to see if the IsCA bit is set
   248  	for _, val := range csrReq.Extensions {
   249  		if val.Id.Equal(basicConstraintsOID) {
   250  			var constraints csr.BasicConstraints
   251  			var rest []byte
   252  			var err error
   253  			if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil {
   254  				return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Failed parsing CSR constraints: %s", err)
   255  			} else if len(rest) != 0 {
   256  				return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Trailing data after X.509 BasicConstraints")
   257  			}
   258  			if constraints.IsCA {
   259  				log.Debug("Request is for a CA signing certificate as indicated in the CSR")
   260  				return true, nil
   261  			}
   262  		}
   263  	}
   264  	// The IsCA bit was not set
   265  	log.Debug("Request is not for a CA signing certificate")
   266  	return false, nil
   267  }
   268  
   269  func getSigningProfile(ca *CA, profile string) *config.SigningProfile {
   270  	if profile == "" {
   271  		return ca.Config.Signing.Default
   272  	}
   273  	return ca.Config.Signing.Profiles[profile]
   274  }
   275  
   276  // Checks to make sure that character limits are not exceeded for CSR fields
   277  func csrInputLengthCheck(req *x509.CertificateRequest) error {
   278  	log.Debug("Checking CSR fields to make sure that they do not exceed maximum character limits")
   279  
   280  	for _, n := range req.Subject.Names {
   281  		value := n.Value.(string)
   282  		switch {
   283  		case n.Type.Equal(commonNameOID):
   284  			if len(value) > commonNameLength {
   285  				return errors.Errorf("The CN '%s' exceeds the maximum character limit of %d", value, commonNameLength)
   286  			}
   287  		case n.Type.Equal(serialNumberOID):
   288  			if len(value) > serialNumberLength {
   289  				return errors.Errorf("The serial number '%s' exceeds the maximum character limit of %d", value, serialNumberLength)
   290  			}
   291  		case n.Type.Equal(organizationalUnitOID):
   292  			if len(value) > organizationalUnitNameLength {
   293  				return errors.Errorf("The organizational unit name '%s' exceeds the maximum character limit of %d", value, organizationalUnitNameLength)
   294  			}
   295  		case n.Type.Equal(organizationOID):
   296  			if len(value) > organizationNameLength {
   297  				return errors.Errorf("The organization name '%s' exceeds the maximum character limit of %d", value, organizationNameLength)
   298  			}
   299  		case n.Type.Equal(countryOID):
   300  			if len(value) > countryNameLength {
   301  				return errors.Errorf("The country name '%s' exceeds the maximum character limit of %d", value, countryNameLength)
   302  			}
   303  		case n.Type.Equal(localityOID):
   304  			if len(value) > localityNameLength {
   305  				return errors.Errorf("The locality name '%s' exceeds the maximum character limit of %d", value, localityNameLength)
   306  			}
   307  		case n.Type.Equal(stateOID):
   308  			if len(value) > stateOrProvinceNameLength {
   309  				return errors.Errorf("The state name '%s' exceeds the maximum character limit of %d", value, stateOrProvinceNameLength)
   310  			}
   311  		}
   312  	}
   313  
   314  	return nil
   315  }
   316  
   317  // Set the OU fields of the sign request based on the identity's type and affilation.
   318  // For example, if the type is 'peer' and the affiliation is 'a.b.c', the
   319  // OUs become 'OU=c,OU=b,OU=a,OU=peer'.
   320  // This is necessary because authorization decisions are made based on the OU fields,
   321  // so we ignore any OU values specified in the enroll request and set them according
   322  // to the type and affiliation.
   323  func setRequestOUs(req *signer.SignRequest, caller user.User) {
   324  	s := req.Subject
   325  	if s == nil {
   326  		s = &signer.Subject{}
   327  	}
   328  	names := []csr.Name{}
   329  	// Add non-OU fields from request
   330  	for _, name := range s.Names {
   331  		if name.C != "" || name.L != "" || name.O != "" || name.ST != "" || name.SerialNumber != "" {
   332  			name.OU = ""
   333  			names = append(names, name)
   334  		}
   335  	}
   336  	// Add an OU field with the type
   337  	names = append(names, csr.Name{OU: caller.GetType()})
   338  	for _, aff := range caller.GetAffiliationPath() {
   339  		names = append(names, csr.Name{OU: aff})
   340  	}
   341  	// Replace with new names
   342  	s.Names = names
   343  	req.Subject = s
   344  }