github.com/tw-bc-group/fabric-ca-gm@v0.0.0-20201218004200-3b690512bd5a/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 sm2 "github.com/Hyperledger-TWGC/tjfoc-gm/x509" 16 "github.com/cloudflare/cfssl/config" 17 "github.com/cloudflare/cfssl/csr" 18 cferr "github.com/cloudflare/cfssl/errors" 19 "github.com/cloudflare/cfssl/log" 20 "github.com/cloudflare/cfssl/signer" 21 "github.com/pkg/errors" 22 "github.com/tw-bc-group/fabric-ca-gm/api" 23 "github.com/tw-bc-group/fabric-ca-gm/lib/caerrors" 24 "github.com/tw-bc-group/fabric-ca-gm/lib/common" 25 "github.com/tw-bc-group/fabric-ca-gm/lib/server/user" 26 "github.com/tw-bc-group/fabric-ca-gm/util" 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 var csrReq *x509.CertificateRequest 186 var err error 187 if IsGMConfig() { 188 sm2csrReq, err := sm2.ParseCertificateRequest(block.Bytes) 189 if err == nil { 190 csrReq = ParseSm2CertificateRequest2X509(sm2csrReq) 191 } 192 } else { 193 csrReq, err = x509.ParseCertificateRequest(block.Bytes) 194 } 195 if err != nil { 196 return err 197 } 198 log.Debugf("Processing sign request: id=%s, CommonName=%s, Subject=%+v", id, csrReq.Subject.CommonName, req.Subject) 199 if (req.Subject != nil && req.Subject.CN != id) || csrReq.Subject.CommonName != id { 200 return errors.New("The CSR subject common name must equal the enrollment ID") 201 } 202 isForCACert, err := isRequestForCASigningCert(csrReq, ca, req.Profile) 203 if err != nil { 204 return err 205 } 206 if isForCACert { 207 // This is a request for a CA certificate, so make sure the caller 208 // has the 'hf.IntermediateCA' attribute 209 err := ca.attributeIsTrue(id, "hf.IntermediateCA") 210 if err != nil { 211 return err 212 } 213 } 214 // Check the CSR input length 215 err = csrInputLengthCheck(csrReq) 216 if err != nil { 217 return err 218 } 219 caller, err := ctx.GetCaller() 220 if err != nil { 221 return err 222 } 223 // Set the OUs in the request appropriately. 224 setRequestOUs(req, caller) 225 log.Debug("Finished processing sign request") 226 return nil 227 } 228 229 // Check to see if this is a request for a CA signing certificate. 230 // This can occur if the profile or the CSR has the IsCA bit set. 231 // See the X.509 BasicConstraints extension (RFC 5280, 4.2.1.9). 232 func isRequestForCASigningCert(csrReq *x509.CertificateRequest, ca *CA, profile string) (bool, error) { 233 // Check the profile to see if the IsCA bit is set 234 sp := getSigningProfile(ca, profile) 235 if sp == nil { 236 return false, errors.Errorf("Invalid profile: '%s'", profile) 237 } 238 if sp.CAConstraint.IsCA { 239 log.Debugf("Request is for a CA signing certificate as set in profile '%s'", profile) 240 return true, nil 241 } 242 // Check the CSR to see if the IsCA bit is set 243 for _, val := range csrReq.Extensions { 244 if val.Id.Equal(basicConstraintsOID) { 245 var constraints csr.BasicConstraints 246 var rest []byte 247 var err error 248 if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil { 249 return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Failed parsing CSR constraints: %s", err) 250 } else if len(rest) != 0 { 251 return false, caerrors.NewHTTPErr(400, caerrors.ErrBadCSR, "Trailing data after X.509 BasicConstraints") 252 } 253 if constraints.IsCA { 254 log.Debug("Request is for a CA signing certificate as indicated in the CSR") 255 return true, nil 256 } 257 } 258 } 259 // The IsCA bit was not set 260 log.Debug("Request is not for a CA signing certificate") 261 return false, nil 262 } 263 264 func getSigningProfile(ca *CA, profile string) *config.SigningProfile { 265 if profile == "" { 266 return ca.Config.Signing.Default 267 } 268 return ca.Config.Signing.Profiles[profile] 269 } 270 271 // Checks to make sure that character limits are not exceeded for CSR fields 272 func csrInputLengthCheck(req *x509.CertificateRequest) error { 273 log.Debug("Checking CSR fields to make sure that they do not exceed maximum character limits") 274 275 for _, n := range req.Subject.Names { 276 value := n.Value.(string) 277 switch { 278 case n.Type.Equal(commonNameOID): 279 if len(value) > commonNameLength { 280 return errors.Errorf("The CN '%s' exceeds the maximum character limit of %d", value, commonNameLength) 281 } 282 case n.Type.Equal(serialNumberOID): 283 if len(value) > serialNumberLength { 284 return errors.Errorf("The serial number '%s' exceeds the maximum character limit of %d", value, serialNumberLength) 285 } 286 case n.Type.Equal(organizationalUnitOID): 287 if len(value) > organizationalUnitNameLength { 288 return errors.Errorf("The organizational unit name '%s' exceeds the maximum character limit of %d", value, organizationalUnitNameLength) 289 } 290 case n.Type.Equal(organizationOID): 291 if len(value) > organizationNameLength { 292 return errors.Errorf("The organization name '%s' exceeds the maximum character limit of %d", value, organizationNameLength) 293 } 294 case n.Type.Equal(countryOID): 295 if len(value) > countryNameLength { 296 return errors.Errorf("The country name '%s' exceeds the maximum character limit of %d", value, countryNameLength) 297 } 298 case n.Type.Equal(localityOID): 299 if len(value) > localityNameLength { 300 return errors.Errorf("The locality name '%s' exceeds the maximum character limit of %d", value, localityNameLength) 301 } 302 case n.Type.Equal(stateOID): 303 if len(value) > stateOrProvinceNameLength { 304 return errors.Errorf("The state name '%s' exceeds the maximum character limit of %d", value, stateOrProvinceNameLength) 305 } 306 } 307 } 308 309 return nil 310 } 311 312 // Set the OU fields of the sign request based on the identity's type and affilation. 313 // For example, if the type is 'peer' and the affiliation is 'a.b.c', the 314 // OUs become 'OU=c,OU=b,OU=a,OU=peer'. 315 // This is necessary because authorization decisions are made based on the OU fields, 316 // so we ignore any OU values specified in the enroll request and set them according 317 // to the type and affiliation. 318 func setRequestOUs(req *signer.SignRequest, caller user.User) { 319 s := req.Subject 320 if s == nil { 321 s = &signer.Subject{} 322 } 323 names := []csr.Name{} 324 // Add non-OU fields from request 325 for _, name := range s.Names { 326 if name.C != "" || name.L != "" || name.O != "" || name.ST != "" || name.SerialNumber != "" { 327 name.OU = "" 328 names = append(names, name) 329 } 330 } 331 // Add an OU field with the type 332 names = append(names, csr.Name{OU: caller.GetType()}) 333 for _, aff := range caller.GetAffiliationPath() { 334 names = append(names, csr.Name{OU: aff}) 335 } 336 // Replace with new names 337 s.Names = names 338 req.Subject = s 339 }