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