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 }