github.com/tw-bc-group/fabric-ca-gm@v0.0.0-20201218004200-3b690512bd5a/util/util.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 util 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 "crypto/rsa" 23 "crypto/x509" 24 "encoding/base64" 25 "encoding/json" 26 "encoding/pem" 27 "fmt" 28 "github.com/Hyperledger-TWGC/tjfoc-gm/sm2" 29 x509GM "github.com/Hyperledger-TWGC/tjfoc-gm/x509" 30 "io" 31 "io/ioutil" 32 "math/big" 33 mrand "math/rand" 34 "net/url" 35 "os" 36 "path" 37 "path/filepath" 38 "reflect" 39 "regexp" 40 "strings" 41 "testing" 42 "time" 43 44 "github.com/cloudflare/cfssl/log" 45 "github.com/pkg/errors" 46 "github.com/spf13/viper" 47 "github.com/stretchr/testify/assert" 48 "github.com/tw-bc-group/fabric-gm/bccsp" 49 "github.com/tw-bc-group/net-go-gm/http" 50 "golang.org/x/crypto/ocsp" 51 ) 52 53 var ( 54 rnd = mrand.NewSource(time.Now().UnixNano()) 55 // ErrNotImplemented used to return errors for functions not implemented 56 ErrNotImplemented = errors.New("NOT YET IMPLEMENTED") 57 ) 58 59 const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 60 const ( 61 letterIdxBits = 6 // 6 bits to represent a letter index 62 letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits 63 letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits 64 ) 65 66 // RevocationReasonCodes is a map between string reason codes to integers as defined in RFC 5280 67 var RevocationReasonCodes = map[string]int{ 68 "unspecified": ocsp.Unspecified, 69 "keycompromise": ocsp.KeyCompromise, 70 "cacompromise": ocsp.CACompromise, 71 "affiliationchanged": ocsp.AffiliationChanged, 72 "superseded": ocsp.Superseded, 73 "cessationofoperation": ocsp.CessationOfOperation, 74 "certificatehold": ocsp.CertificateHold, 75 "removefromcrl": ocsp.RemoveFromCRL, 76 "privilegewithdrawn": ocsp.PrivilegeWithdrawn, 77 "aacompromise": ocsp.AACompromise, 78 } 79 80 // SecretTag to tag a field as secret as in password, token 81 const SecretTag = "mask" 82 83 // URLRegex is the regular expression to check if a value is an URL 84 var URLRegex = regexp.MustCompile("(ldap|http)s*://(\\S+):(\\S+)@") 85 86 //ECDSASignature forms the structure for R and S value for ECDSA 87 type ECDSASignature struct { 88 R, S *big.Int 89 } 90 91 // RandomString returns a random string 92 func RandomString(n int) string { 93 b := make([]byte, n) 94 95 for i, cache, remain := n-1, rnd.Int63(), letterIdxMax; i >= 0; { 96 if remain == 0 { 97 cache, remain = rnd.Int63(), letterIdxMax 98 } 99 if idx := int(cache & letterIdxMask); idx < len(letterBytes) { 100 b[i] = letterBytes[idx] 101 i-- 102 } 103 cache >>= letterIdxBits 104 remain-- 105 } 106 107 return string(b) 108 } 109 110 // RemoveQuotes removes outer quotes from a string if necessary 111 func RemoveQuotes(str string) string { 112 if str == "" { 113 return str 114 } 115 if (strings.HasPrefix(str, "'") && strings.HasSuffix(str, "'")) || 116 (strings.HasPrefix(str, "\"") && strings.HasSuffix(str, "\"")) { 117 str = str[1 : len(str)-1] 118 } 119 return str 120 } 121 122 // ReadFile reads a file 123 func ReadFile(file string) ([]byte, error) { 124 return ioutil.ReadFile(file) 125 } 126 127 // WriteFile writes a file 128 func WriteFile(file string, buf []byte, perm os.FileMode) error { 129 dir := path.Dir(file) 130 // Create the directory if it doesn't exist 131 if _, err := os.Stat(dir); os.IsNotExist(err) { 132 err = os.MkdirAll(dir, 0755) 133 if err != nil { 134 return errors.Wrapf(err, "Failed to create directory '%s' for file '%s'", dir, file) 135 } 136 } 137 return ioutil.WriteFile(file, buf, perm) 138 } 139 140 // FileExists checks to see if a file exists 141 func FileExists(name string) bool { 142 if _, err := os.Stat(name); err != nil { 143 if os.IsNotExist(err) { 144 return false 145 } 146 } 147 return true 148 } 149 150 // Marshal to bytes 151 func Marshal(from interface{}, what string) ([]byte, error) { 152 buf, err := json.Marshal(from) 153 if err != nil { 154 return nil, errors.Wrapf(err, "Failed to marshal %s", what) 155 } 156 return buf, nil 157 } 158 159 // Unmarshal from bytes 160 func Unmarshal(from []byte, to interface{}, what string) error { 161 err := json.Unmarshal(from, to) 162 if err != nil { 163 return errors.Wrapf(err, "Failed to unmarshal %s", what) 164 } 165 return nil 166 } 167 168 // CreateToken creates a JWT-like token. 169 // In a normal JWT token, the format of the token created is: 170 // <algorithm,claims,signature> 171 // where each part is base64-encoded string separated by a period. 172 // In this JWT-like token, there are two differences: 173 // 1) the claims section is a certificate, so the format is: 174 // <certificate,signature> 175 // 2) the signature uses the private key associated with the certificate, 176 // and the signature is across both the certificate and the "body" argument, 177 // which is the body of an HTTP request, though could be any arbitrary bytes. 178 // @param cert The pem-encoded certificate 179 // @param key The pem-encoded key 180 // @param method http method of the request 181 // @param uri URI of the request 182 // @param body The body of an HTTP request 183 func CreateToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) { 184 log.Debug("enter util.CreateToken") 185 x509Cert, err := GetX509CertificateFromPEM(cert) 186 if err != nil { 187 return "", err 188 } 189 publicKey := x509Cert.PublicKey 190 191 var token string 192 193 //The RSA Key Gen is commented right now as there is bccsp does 194 switch publicKey.(type) { 195 /* 196 case *rsa.PublicKey: 197 token, err = GenRSAToken(csp, cert, key, body) 198 if err != nil { 199 return "", err 200 } 201 */ 202 case *ecdsa.PublicKey: 203 log.Debug("publicKey.(type)=*ecdsa.PublicKey") 204 token, err = GenECDSAToken(csp, cert, key, method, uri, body) 205 if err != nil { 206 return "", err 207 } 208 case *sm2.PublicKey: 209 log.Debug("publicKey.(type)=*sm2.PublicKey") 210 token, err = GenECDSAToken(csp, cert, key, method, uri, body) 211 if err != nil { 212 return "", err 213 } 214 default: 215 log.Debugf("publicKey=%T", publicKey) 216 } 217 return token, nil 218 } 219 220 //GenRSAToken signs the http body and cert with RSA using RSA private key 221 // @csp : BCCSP instance 222 /* 223 func GenRSAToken(csp bccsp.BCCSP, cert []byte, key []byte, body []byte) (string, error) { 224 privKey, err := GetRSAPrivateKey(key) 225 if err != nil { 226 return "", err 227 } 228 b64body := B64Encode(body) 229 b64cert := B64Encode(cert) 230 bodyAndcert := b64body + "." + b64cert 231 hash := sha512.New384() 232 hash.Write([]byte(bodyAndcert)) 233 h := hash.Sum(nil) 234 RSAsignature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA384, h[:]) 235 if err != nil { 236 return "", errors.Wrap(err, "Failed to rsa.SignPKCS1v15") 237 } 238 b64sig := B64Encode(RSAsignature) 239 token := b64cert + "." + b64sig 240 241 return token, nil 242 } 243 */ 244 245 //GenECDSAToken signs the http body and cert with ECDSA using EC private key 246 func GenECDSAToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) { 247 b64body := B64Encode(body) 248 b64cert := B64Encode(cert) 249 b64uri := B64Encode([]byte(uri)) 250 payload := method + "." + b64uri + "." + b64body + "." + b64cert 251 252 return genECDSAToken(csp, key, b64cert, payload) 253 } 254 255 func genECDSAToken(csp bccsp.BCCSP, key bccsp.Key, b64cert, payload string) (string, error) { 256 digest, digestError := csp.Hash([]byte(payload), &bccsp.SHAOpts{}) 257 if digestError != nil { 258 return "", errors.WithMessage(digestError, fmt.Sprintf("Hash failed on '%s'", payload)) 259 } 260 261 ecSignature, err := csp.Sign(key, digest, nil) 262 if err != nil { 263 return "", errors.WithMessage(err, "BCCSP signature generation failure") 264 } 265 if len(ecSignature) == 0 { 266 return "", errors.New("BCCSP signature creation failed. Signature must be different than nil") 267 } 268 269 b64sig := B64Encode(ecSignature) 270 token := b64cert + "." + b64sig 271 272 return token, nil 273 274 } 275 276 // VerifyToken verifies token signed by either ECDSA or RSA and 277 // returns the associated user ID 278 func VerifyToken(csp bccsp.BCCSP, token string, method, uri string, body []byte, compMode1_3 bool) (*x509.Certificate, error) { 279 280 if csp == nil { 281 return nil, errors.New("BCCSP instance is not present") 282 } 283 x509Cert, b64Cert, b64Sig, err := DecodeToken(token) 284 if err != nil { 285 return nil, err 286 } 287 sig, err := B64Decode(b64Sig) 288 if err != nil { 289 return nil, errors.WithMessage(err, "Invalid base64 encoded signature in token") 290 } 291 b64Body := B64Encode(body) 292 b64uri := B64Encode([]byte(uri)) 293 sigString := method + "." + b64uri + "." + b64Body + "." + b64Cert 294 295 log.Infof("xxx before csp .KeyImport csp : %T b64Body %s", csp, sigString) 296 sm2cert := ParseX509Certificate2Sm2(x509Cert) 297 pk2, err := csp.KeyImport(sm2cert, &bccsp.X509PublicKeyImportOpts{Temporary: true}) 298 log.Infof("xxx end csp .KeyImport pk2 : %T", pk2) 299 if err != nil { 300 return nil, errors.WithMessage(err, "Public Key import into BCCSP failed with error") 301 } 302 if pk2 == nil { 303 return nil, errors.New("Public Key Cannot be imported into BCCSP") 304 } 305 306 //bccsp.X509PublicKeyImportOpts 307 //Using default hash algo 308 digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{}) 309 if digestError != nil { 310 return nil, errors.WithMessage(digestError, "Message digest failed") 311 } 312 log.Debugf("pk2 %T \n sig %T\n digest %s\n", pk2, sig, B64Encode(digest)) 313 valid, validErr := csp.Verify(pk2, sig, digest, nil) 314 if compMode1_3 && !valid { 315 log.Debugf("Failed to verify token based on new authentication header requirements: %s", err) 316 sigString := b64Body + "." + b64Cert 317 digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{}) 318 if digestError != nil { 319 return nil, errors.WithMessage(digestError, "Message digest failed") 320 } 321 valid, validErr = csp.Verify(pk2, sig, digest, nil) 322 } 323 324 if validErr != nil { 325 return nil, errors.WithMessage(validErr, "Token signature validation failure") 326 } 327 if !valid { 328 return nil, errors.New("Token signature validation failed") 329 } 330 331 return x509Cert, nil 332 } 333 334 // DecodeToken extracts an X509 certificate and base64 encoded signature from a token 335 func DecodeToken(token string) (*x509.Certificate, string, string, error) { 336 if token == "" { 337 return nil, "", "", errors.New("Invalid token; it is empty") 338 } 339 parts := strings.Split(token, ".") 340 if len(parts) != 2 { 341 return nil, "", "", errors.New("Invalid token format; expecting 2 parts separated by '.'") 342 } 343 b64cert := parts[0] 344 certDecoded, err := B64Decode(b64cert) 345 if err != nil { 346 return nil, "", "", errors.WithMessage(err, "Failed to decode base64 encoded x509 cert") 347 } 348 x509Cert, err := GetX509CertificateFromPEM(certDecoded) 349 if err != nil { 350 return nil, "", "", errors.WithMessage(err, "Error in parsing x509 certificate given block bytes") 351 } 352 return x509Cert, b64cert, parts[1], nil 353 } 354 355 //GetECPrivateKey get *ecdsa.PrivateKey from key pem 356 func GetECPrivateKey(raw []byte) (*ecdsa.PrivateKey, error) { 357 decoded, _ := pem.Decode(raw) 358 if decoded == nil { 359 return nil, errors.New("Failed to decode the PEM-encoded ECDSA key") 360 } 361 ECprivKey, err := x509.ParseECPrivateKey(decoded.Bytes) 362 if err == nil { 363 return ECprivKey, nil 364 } 365 key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes) 366 if err2 == nil { 367 switch key.(type) { 368 case *ecdsa.PrivateKey: 369 return key.(*ecdsa.PrivateKey), nil 370 case *rsa.PrivateKey: 371 return nil, errors.New("Expecting EC private key but found RSA private key") 372 default: 373 return nil, errors.New("Invalid private key type in PKCS#8 wrapping") 374 } 375 } 376 return nil, errors.Wrap(err2, "Failed parsing EC private key") 377 } 378 379 //GetRSAPrivateKey get *rsa.PrivateKey from key pem 380 func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) { 381 decoded, _ := pem.Decode(raw) 382 if decoded == nil { 383 return nil, errors.New("Failed to decode the PEM-encoded RSA key") 384 } 385 RSAprivKey, err := x509.ParsePKCS1PrivateKey(decoded.Bytes) 386 if err == nil { 387 return RSAprivKey, nil 388 } 389 key, err2 := x509.ParsePKCS8PrivateKey(raw) 390 if err2 == nil { 391 switch key.(type) { 392 case *ecdsa.PrivateKey: 393 return nil, errors.New("Expecting RSA private key but found EC private key") 394 case *rsa.PrivateKey: 395 return key.(*rsa.PrivateKey), nil 396 default: 397 return nil, errors.New("Invalid private key type in PKCS#8 wrapping") 398 } 399 } 400 return nil, errors.Wrap(err, "Failed parsing RSA private key") 401 } 402 403 //GetSM2PrivateKey get *sm2.PrivateKey from key pem 404 func GetSM2PrivateKey(raw []byte) (*sm2.PrivateKey, error) { 405 decoded, _ := pem.Decode(raw) 406 if decoded == nil { 407 return nil, errors.New("Failed to decode the PEM-encoded RSA key") 408 } 409 if key, err := x509GM.ParsePKCS8UnecryptedPrivateKey(decoded.Bytes); err == nil { 410 return key, nil 411 } else { 412 return nil, fmt.Errorf("tls: failed to parse private key %v", err) 413 } 414 } 415 416 // B64Encode base64 encodes bytes 417 func B64Encode(buf []byte) string { 418 return base64.StdEncoding.EncodeToString(buf) 419 } 420 421 // B64Decode base64 decodes a string 422 func B64Decode(str string) (buf []byte, err error) { 423 return base64.StdEncoding.DecodeString(str) 424 } 425 426 // StrContained returns true if 'str' is in 'strs'; otherwise return false 427 func StrContained(str string, strs []string) bool { 428 for _, s := range strs { 429 if strings.ToLower(s) == strings.ToLower(str) { 430 return true 431 } 432 } 433 return false 434 } 435 436 // IsSubsetOf returns an error if there is something in 'small' that 437 // is not in 'big'. Both small and big are assumed to be comma-separated 438 // strings. All string comparisons are case-insensitive. 439 // Examples: 440 // 1) IsSubsetOf('a,B', 'A,B,C') returns nil 441 // 2) IsSubsetOf('A,B,C', 'B,C') returns an error because A is not in the 2nd set. 442 func IsSubsetOf(small, big string) error { 443 bigSet := strings.Split(big, ",") 444 smallSet := strings.Split(small, ",") 445 for _, s := range smallSet { 446 if s != "" && !StrContained(s, bigSet) { 447 return errors.Errorf("'%s' is not a member of '%s'", s, big) 448 } 449 } 450 return nil 451 } 452 453 // HTTPRequestToString returns a string for an HTTP request for debuggging 454 func HTTPRequestToString(req *http.Request) string { 455 body, _ := ioutil.ReadAll(req.Body) 456 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 457 return fmt.Sprintf("%s %s\n%s", 458 req.Method, req.URL, string(body)) 459 } 460 461 // HTTPResponseToString returns a string for an HTTP response for debuggging 462 func HTTPResponseToString(resp *http.Response) string { 463 body, _ := ioutil.ReadAll(resp.Body) 464 resp.Body = ioutil.NopCloser(bytes.NewReader(body)) 465 return fmt.Sprintf("statusCode=%d (%s)\n%s", 466 resp.StatusCode, resp.Status, string(body)) 467 } 468 469 // CreateClientHome will create a home directory if it does not exist 470 func CreateClientHome() (string, error) { 471 log.Debug("CreateHome") 472 home := filepath.Dir(GetDefaultConfigFile("fabric-ca-client")) 473 474 if _, err := os.Stat(home); err != nil { 475 if os.IsNotExist(err) { 476 err := os.MkdirAll(home, 0755) 477 if err != nil { 478 return "", err 479 } 480 } 481 } 482 return home, nil 483 } 484 485 // GetDefaultConfigFile gets the default path for the config file to display in usage message 486 func GetDefaultConfigFile(cmdName string) string { 487 if cmdName == "fabric-ca-server" { 488 var fname = fmt.Sprintf("%s-config.yaml", cmdName) 489 // First check home env variables 490 home := "." 491 envs := []string{"FABRIC_CA_SERVER_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"} 492 for _, env := range envs { 493 envVal := os.Getenv(env) 494 if envVal != "" { 495 home = envVal 496 break 497 } 498 } 499 return path.Join(home, fname) 500 } 501 502 var fname = fmt.Sprintf("%s-config.yaml", cmdName) 503 // First check home env variables 504 var home string 505 envs := []string{"FABRIC_CA_CLIENT_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"} 506 for _, env := range envs { 507 envVal := os.Getenv(env) 508 if envVal != "" { 509 home = envVal 510 return path.Join(home, fname) 511 } 512 } 513 514 return path.Join(os.Getenv("HOME"), ".fabric-ca-client", fname) 515 } 516 517 // GetX509CertificateFromPEMFile gets an X509 certificate from a file 518 func GetX509CertificateFromPEMFile(file string) (*x509.Certificate, error) { 519 pemBytes, err := ReadFile(file) 520 if err != nil { 521 return nil, err 522 } 523 x509Cert, err := GetX509CertificateFromPEM(pemBytes) 524 if err != nil { 525 return nil, errors.Wrapf(err, "Invalid certificate in '%s'", file) 526 } 527 return x509Cert, nil 528 } 529 530 // GetX509CertificateFromPEM get an X509 certificate from bytes in PEM format 531 func GetX509CertificateFromPEM(cert []byte) (*x509.Certificate, error) { 532 block, _ := pem.Decode(cert) 533 if block == nil { 534 return nil, errors.New("Failed to PEM decode certificate") 535 } 536 var x509Cert *x509.Certificate 537 var err error 538 if IsGMConfig() { 539 log.Debugf("IsGMConfig = true") 540 sm2x509Cert, err := x509GM.ParseCertificate(block.Bytes) 541 if err == nil { 542 x509Cert = ParseSm2Certificate2X509(sm2x509Cert) 543 log.Debugf("after parse,x509Cert=%T,x509Cert.PublicKey=%T", x509Cert, x509Cert.PublicKey) 544 } 545 } else { 546 x509Cert, err = x509.ParseCertificate(block.Bytes) 547 } 548 if err != nil { 549 return nil, errors.Wrap(err, "Error parsing certificate") 550 } 551 return x509Cert, nil 552 } 553 554 // GetX509CertificatesFromPEM returns X509 certificates from bytes in PEM format 555 func GetX509CertificatesFromPEM(pemBytes []byte) ([]*x509.Certificate, error) { 556 chain := pemBytes 557 var certs []*x509.Certificate 558 for len(chain) > 0 { 559 var block *pem.Block 560 block, chain = pem.Decode(chain) 561 if block == nil { 562 break 563 } 564 565 var x509Cert *x509.Certificate 566 var err error 567 if IsGMConfig() { 568 sm2x509Cert, err := x509GM.ParseCertificate(block.Bytes) 569 if err == nil { 570 x509Cert = ParseSm2Certificate2X509(sm2x509Cert) 571 } 572 } else { 573 x509Cert, err = x509.ParseCertificate(block.Bytes) 574 } 575 if err != nil { 576 return nil, errors.Wrap(err, "Error parsing certificate") 577 } 578 certs = append(certs, x509Cert) 579 } 580 return certs, nil 581 } 582 583 // GetCertificateDurationFromFile returns the validity duration for a certificate 584 // in a file. 585 func GetCertificateDurationFromFile(file string) (time.Duration, error) { 586 cert, err := GetX509CertificateFromPEMFile(file) 587 if err != nil { 588 return 0, err 589 } 590 return GetCertificateDuration(cert), nil 591 } 592 593 // GetCertificateDuration returns the validity duration for a certificate 594 func GetCertificateDuration(cert *x509.Certificate) time.Duration { 595 return cert.NotAfter.Sub(cert.NotBefore) 596 } 597 598 // GetEnrollmentIDFromPEM returns the EnrollmentID from a PEM buffer 599 func GetEnrollmentIDFromPEM(cert []byte) (string, error) { 600 x509Cert, err := GetX509CertificateFromPEM(cert) 601 if err != nil { 602 return "", err 603 } 604 return GetEnrollmentIDFromX509Certificate(x509Cert), nil 605 } 606 607 // GetEnrollmentIDFromX509Certificate returns the EnrollmentID from the X509 certificate 608 func GetEnrollmentIDFromX509Certificate(cert *x509.Certificate) string { 609 return cert.Subject.CommonName 610 } 611 612 // MakeFileAbs makes 'file' absolute relative to 'dir' if not already absolute 613 func MakeFileAbs(file, dir string) (string, error) { 614 if file == "" { 615 return "", nil 616 } 617 if filepath.IsAbs(file) { 618 return file, nil 619 } 620 path, err := filepath.Abs(filepath.Join(dir, file)) 621 if err != nil { 622 return "", errors.Wrapf(err, "Failed making '%s' absolute based on '%s'", file, dir) 623 } 624 return path, nil 625 } 626 627 // MakeFileNamesAbsolute makes all file names in the list absolute, relative to home 628 func MakeFileNamesAbsolute(files []*string, home string) error { 629 for _, filePtr := range files { 630 abs, err := MakeFileAbs(*filePtr, home) 631 if err != nil { 632 return err 633 } 634 *filePtr = abs 635 } 636 return nil 637 } 638 639 // Fatal logs a fatal message and exits 640 func Fatal(format string, v ...interface{}) { 641 log.Fatalf(format, v...) 642 os.Exit(1) 643 } 644 645 // GetUser returns username and password from CLI input 646 func GetUser(v *viper.Viper) (string, string, error) { 647 var fabricCAServerURL string 648 fabricCAServerURL = v.GetString("url") 649 650 URL, err := url.Parse(fabricCAServerURL) 651 if err != nil { 652 return "", "", err 653 } 654 655 user := URL.User 656 if user == nil { 657 return "", "", errors.New("No username and password provided as part of the Fabric CA server URL") 658 } 659 660 eid := user.Username() 661 if eid == "" { 662 return "", "", errors.New("No username provided as part of URL") 663 } 664 665 pass, _ := user.Password() 666 if pass == "" { 667 return "", "", errors.New("No password provided as part of URL") 668 } 669 670 return eid, pass, nil 671 } 672 673 // GetSerialAsHex returns the serial number from certificate as hex format 674 func GetSerialAsHex(serial *big.Int) string { 675 hex := fmt.Sprintf("%x", serial) 676 return hex 677 } 678 679 // StructToString converts a struct to a string. If a field 680 // has a 'secret' tag, it is masked in the returned string 681 func StructToString(si interface{}) string { 682 rval := reflect.ValueOf(si).Elem() 683 tipe := rval.Type() 684 var buffer bytes.Buffer 685 buffer.WriteString("{ ") 686 for i := 0; i < rval.NumField(); i++ { 687 tf := tipe.Field(i) 688 if !rval.FieldByName(tf.Name).CanSet() { 689 continue // skip unexported fields 690 } 691 var fStr string 692 tagv := tf.Tag.Get(SecretTag) 693 if tagv == "password" || tagv == "username" { 694 fStr = fmt.Sprintf("%s:**** ", tf.Name) 695 } else if tagv == "url" { 696 val, ok := rval.Field(i).Interface().(string) 697 if ok { 698 val = GetMaskedURL(val) 699 fStr = fmt.Sprintf("%s:%v ", tf.Name, val) 700 } else { 701 fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface()) 702 } 703 } else { 704 fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface()) 705 } 706 buffer.WriteString(fStr) 707 } 708 buffer.WriteString(" }") 709 return buffer.String() 710 } 711 712 // GetMaskedURL returns masked URL. It masks username and password from the URL 713 // if present 714 func GetMaskedURL(url string) string { 715 matches := URLRegex.FindStringSubmatch(url) 716 717 // If there is a match, there should be four entries: 1 for 718 // the match and 3 for submatches 719 if len(matches) == 4 { 720 matchIdxs := URLRegex.FindStringSubmatchIndex(url) 721 matchStr := url[matchIdxs[0]:matchIdxs[1]] 722 for idx := 2; idx < len(matches); idx++ { 723 if matches[idx] != "" { 724 matchStr = strings.Replace(matchStr, matches[idx], "****", 1) 725 } 726 } 727 url = url[:matchIdxs[0]] + matchStr + url[matchIdxs[1]:len(url)] 728 } 729 return url 730 } 731 732 // NormalizeStringSlice checks for seperators 733 func NormalizeStringSlice(slice []string) []string { 734 var normalizedSlice []string 735 736 for _, item := range slice { 737 // Remove surrounding brackets "[]" if specified 738 if strings.HasPrefix(item, "[") && strings.HasSuffix(item, "]") { 739 item = item[1 : len(item)-1] 740 } 741 // Split elements based on comma and add to normalized slice 742 elems := strings.Split(item, ",") 743 for _, elem := range elems { 744 normalizedSlice = append(normalizedSlice, strings.TrimSpace(elem)) 745 } 746 } 747 return normalizedSlice 748 } 749 750 // NormalizeFileList provides absolute pathing for the list of files 751 func NormalizeFileList(files []string, homeDir string) ([]string, error) { 752 var err error 753 754 files = NormalizeStringSlice(files) 755 756 for i, file := range files { 757 files[i], err = MakeFileAbs(file, homeDir) 758 if err != nil { 759 return nil, err 760 } 761 } 762 763 return files, nil 764 } 765 766 // CheckHostsInCert checks to see if host correctly inserted into certificate 767 func CheckHostsInCert(certFile string, hosts ...string) error { 768 certBytes, err := ioutil.ReadFile(certFile) 769 if err != nil { 770 return errors.Wrapf(err, "Failed to read certificate file at '%s'", certFile) 771 } 772 773 cert, err := GetX509CertificateFromPEM(certBytes) 774 if err != nil { 775 return errors.Wrap(err, "Failed to get certificate") 776 } 777 778 // combine DNSNames and IPAddresses from cert 779 sans := cert.DNSNames 780 for _, ip := range cert.IPAddresses { 781 sans = append(sans, ip.String()) 782 } 783 for _, host := range hosts { 784 if !containsString(sans, host) { 785 return errors.Errorf("Host '%s' was not found in the certificate in file '%s'", host, certFile) 786 } 787 } 788 return nil 789 } 790 791 func containsString(list []string, item string) bool { 792 for _, elem := range list { 793 if elem == item { 794 return true 795 } 796 } 797 return false 798 } 799 800 // Read reads from Reader into a byte array 801 func Read(r io.Reader, data []byte) ([]byte, error) { 802 j := 0 803 for { 804 n, err := r.Read(data[j:]) 805 j = j + n 806 if err != nil { 807 if err == io.EOF { 808 break 809 } 810 return nil, errors.Wrapf(err, "Read failure") 811 } 812 813 if (n == 0 && j == len(data)) || j > len(data) { 814 return nil, errors.New("Size of requested data is too large") 815 } 816 } 817 818 return data[:j], nil 819 } 820 821 // Hostname name returns the hostname of the machine 822 func Hostname() string { 823 hostname, _ := os.Hostname() 824 if hostname == "" { 825 hostname = "localhost" 826 } 827 return hostname 828 } 829 830 // ValidateAndReturnAbsConf checks to see that there are no conflicts between the 831 // configuration file path and home directory. If no conflicts, returns back the absolute 832 // path for the configuration file and home directory. 833 func ValidateAndReturnAbsConf(configFilePath, homeDir, cmdName string) (string, string, error) { 834 var err error 835 var homeDirSet bool 836 var configFileSet bool 837 838 defaultConfig := GetDefaultConfigFile(cmdName) // Get the default configuration 839 840 if configFilePath == "" { 841 configFilePath = defaultConfig // If no config file path specified, use the default configuration file 842 } else { 843 configFileSet = true 844 } 845 846 if homeDir == "" { 847 homeDir = filepath.Dir(defaultConfig) // If no home directory specified, use the default directory 848 } else { 849 homeDirSet = true 850 } 851 852 // Make the home directory absolute 853 homeDir, err = filepath.Abs(homeDir) 854 if err != nil { 855 return "", "", errors.Wrap(err, "Failed to get full path of config file") 856 } 857 homeDir = strings.TrimRight(homeDir, "/") 858 859 if configFileSet && homeDirSet { 860 log.Warning("Using both --config and --home CLI flags; --config will take precedence") 861 } 862 863 if configFileSet { 864 configFilePath, err = filepath.Abs(configFilePath) 865 if err != nil { 866 return "", "", errors.Wrap(err, "Failed to get full path of configuration file") 867 } 868 return configFilePath, filepath.Dir(configFilePath), nil 869 } 870 871 configFile := filepath.Join(homeDir, filepath.Base(defaultConfig)) // Join specified home directory with default config file name 872 return configFile, homeDir, nil 873 } 874 875 // GetSliceFromList will return a slice from a list 876 func GetSliceFromList(split string, delim string) []string { 877 return strings.Split(strings.Replace(split, " ", "", -1), delim) 878 } 879 880 // ListContains looks through a comma separated list to see if a string exists 881 func ListContains(list, find string) bool { 882 items := strings.Split(list, ",") 883 for _, item := range items { 884 item = strings.TrimPrefix(item, " ") 885 if item == find { 886 return true 887 } 888 } 889 return false 890 } 891 892 //TODO: move these out of production code 893 894 // FatalError will check to see if an error occured if so it will cause the test cases exit 895 func FatalError(t *testing.T, err error, msg string, args ...interface{}) { 896 if len(args) > 0 { 897 msg = fmt.Sprintf(msg, args) 898 } 899 if !assert.NoError(t, err, msg) { 900 t.Fatal(msg) 901 } 902 } 903 904 // ErrorContains will check to see if an error occurred, if so it will check that it contains 905 // the appropriate error message 906 func ErrorContains(t *testing.T, err error, contains, msg string, args ...interface{}) { 907 if len(args) > 0 { 908 msg = fmt.Sprintf(msg, args) 909 } 910 if assert.Error(t, err, msg) { 911 assert.Contains(t, err.Error(), contains) 912 } 913 } 914 915 //sm2 证书转换 x509 证书 916 func ParseSm2Certificate2X509(sm2Cert *x509GM.Certificate) *x509.Certificate { 917 x509cert := &x509.Certificate{ 918 Raw: sm2Cert.Raw, 919 RawTBSCertificate: sm2Cert.RawTBSCertificate, 920 RawSubjectPublicKeyInfo: sm2Cert.RawSubjectPublicKeyInfo, 921 RawSubject: sm2Cert.RawSubject, 922 RawIssuer: sm2Cert.RawIssuer, 923 924 Signature: sm2Cert.Signature, 925 SignatureAlgorithm: x509.SignatureAlgorithm(sm2Cert.SignatureAlgorithm), 926 927 PublicKeyAlgorithm: x509.PublicKeyAlgorithm(sm2Cert.PublicKeyAlgorithm), 928 PublicKey: sm2Cert.PublicKey, 929 930 Version: sm2Cert.Version, 931 SerialNumber: sm2Cert.SerialNumber, 932 Issuer: sm2Cert.Issuer, 933 Subject: sm2Cert.Subject, 934 NotBefore: sm2Cert.NotBefore, 935 NotAfter: sm2Cert.NotAfter, 936 KeyUsage: x509.KeyUsage(sm2Cert.KeyUsage), 937 938 Extensions: sm2Cert.Extensions, 939 940 ExtraExtensions: sm2Cert.ExtraExtensions, 941 942 UnhandledCriticalExtensions: sm2Cert.UnhandledCriticalExtensions, 943 944 //ExtKeyUsage: []x509.ExtKeyUsage(sm2Cert.ExtKeyUsage) , 945 UnknownExtKeyUsage: sm2Cert.UnknownExtKeyUsage, 946 947 BasicConstraintsValid: sm2Cert.BasicConstraintsValid, 948 IsCA: sm2Cert.IsCA, 949 MaxPathLen: sm2Cert.MaxPathLen, 950 // MaxPathLenZero indicates that BasicConstraintsValid==true and 951 // MaxPathLen==0 should be interpreted as an actual maximum path length 952 // of zero. Otherwise, that combination is interpreted as MaxPathLen 953 // not being set. 954 MaxPathLenZero: sm2Cert.MaxPathLenZero, 955 956 SubjectKeyId: sm2Cert.SubjectKeyId, 957 AuthorityKeyId: sm2Cert.AuthorityKeyId, 958 959 // RFC 5280, 4.2.2.1 (Authority Information Access) 960 OCSPServer: sm2Cert.OCSPServer, 961 IssuingCertificateURL: sm2Cert.IssuingCertificateURL, 962 963 // Subject Alternate Name values 964 DNSNames: sm2Cert.DNSNames, 965 EmailAddresses: sm2Cert.EmailAddresses, 966 IPAddresses: sm2Cert.IPAddresses, 967 968 // Name constraints 969 PermittedDNSDomainsCritical: sm2Cert.PermittedDNSDomainsCritical, 970 PermittedDNSDomains: sm2Cert.PermittedDNSDomains, 971 972 // CRL Distribution Points 973 CRLDistributionPoints: sm2Cert.CRLDistributionPoints, 974 975 PolicyIdentifiers: sm2Cert.PolicyIdentifiers, 976 } 977 for _, val := range sm2Cert.ExtKeyUsage { 978 x509cert.ExtKeyUsage = append(x509cert.ExtKeyUsage, x509.ExtKeyUsage(val)) 979 } 980 981 return x509cert 982 } 983 984 // X509证书格式转换为 SM2证书格式 985 func ParseX509Certificate2Sm2(x509Cert *x509.Certificate) *x509GM.Certificate { 986 sm2cert := &x509GM.Certificate{ 987 Raw: x509Cert.Raw, 988 RawTBSCertificate: x509Cert.RawTBSCertificate, 989 RawSubjectPublicKeyInfo: x509Cert.RawSubjectPublicKeyInfo, 990 RawSubject: x509Cert.RawSubject, 991 RawIssuer: x509Cert.RawIssuer, 992 993 Signature: x509Cert.Signature, 994 SignatureAlgorithm: x509GM.SignatureAlgorithm(x509Cert.SignatureAlgorithm), 995 996 PublicKeyAlgorithm: x509GM.PublicKeyAlgorithm(x509Cert.PublicKeyAlgorithm), 997 PublicKey: x509Cert.PublicKey, 998 999 Version: x509Cert.Version, 1000 SerialNumber: x509Cert.SerialNumber, 1001 Issuer: x509Cert.Issuer, 1002 Subject: x509Cert.Subject, 1003 NotBefore: x509Cert.NotBefore, 1004 NotAfter: x509Cert.NotAfter, 1005 KeyUsage: x509GM.KeyUsage(x509Cert.KeyUsage), 1006 1007 Extensions: x509Cert.Extensions, 1008 1009 ExtraExtensions: x509Cert.ExtraExtensions, 1010 1011 UnhandledCriticalExtensions: x509Cert.UnhandledCriticalExtensions, 1012 1013 //ExtKeyUsage: []x509.ExtKeyUsage(x509Cert.ExtKeyUsage) , 1014 UnknownExtKeyUsage: x509Cert.UnknownExtKeyUsage, 1015 1016 BasicConstraintsValid: x509Cert.BasicConstraintsValid, 1017 IsCA: x509Cert.IsCA, 1018 MaxPathLen: x509Cert.MaxPathLen, 1019 // MaxPathLenZero indicates that BasicConstraintsValid==true and 1020 // MaxPathLen==0 should be interpreted as an actual maximum path length 1021 // of zero. Otherwise, that combination is interpreted as MaxPathLen 1022 // not being set. 1023 MaxPathLenZero: x509Cert.MaxPathLenZero, 1024 1025 SubjectKeyId: x509Cert.SubjectKeyId, 1026 AuthorityKeyId: x509Cert.AuthorityKeyId, 1027 1028 // RFC 5280, 4.2.2.1 (Authority Information Access) 1029 OCSPServer: x509Cert.OCSPServer, 1030 IssuingCertificateURL: x509Cert.IssuingCertificateURL, 1031 1032 // Subject Alternate Name values 1033 DNSNames: x509Cert.DNSNames, 1034 EmailAddresses: x509Cert.EmailAddresses, 1035 IPAddresses: x509Cert.IPAddresses, 1036 1037 // Name constraints 1038 PermittedDNSDomainsCritical: x509Cert.PermittedDNSDomainsCritical, 1039 PermittedDNSDomains: x509Cert.PermittedDNSDomains, 1040 1041 // CRL Distribution Points 1042 CRLDistributionPoints: x509Cert.CRLDistributionPoints, 1043 1044 PolicyIdentifiers: x509Cert.PolicyIdentifiers, 1045 } 1046 for _, val := range x509Cert.ExtKeyUsage { 1047 sm2cert.ExtKeyUsage = append(sm2cert.ExtKeyUsage, x509GM.ExtKeyUsage(val)) 1048 } 1049 1050 return sm2cert 1051 } 1052 1053 var providerName string 1054 1055 func IsGMConfig() bool { 1056 if providerName == "" { 1057 return false 1058 } 1059 if strings.ToUpper(providerName) == "GM" { 1060 return true 1061 } 1062 return false 1063 } 1064 1065 func SetProviderName(name string) { 1066 providerName = name 1067 }