gitee.com/lh-her-team/common@v1.5.1/cert/cert.go (about) 1 package cert 2 3 import ( 4 "crypto/rand" 5 "crypto/rsa" 6 "crypto/x509" 7 "crypto/x509/pkix" 8 "encoding/asn1" 9 "encoding/json" 10 "encoding/pem" 11 "errors" 12 "fmt" 13 "io/ioutil" 14 "math/big" 15 "net" 16 "os" 17 "path/filepath" 18 "time" 19 20 bcx509 "gitee.com/lh-her-team/common/crypto/x509" 21 "github.com/tjfoc/gmsm/sm2" 22 23 "gitee.com/lh-her-team/common/crypto" 24 "gitee.com/lh-her-team/common/crypto/asym" 25 "gitee.com/lh-her-team/common/crypto/hash" 26 ) 27 28 const ( 29 defaultCountry = "CN" 30 defaultLocality = "Shaanxi" 31 defaultProvince = "Shaanxi" 32 defaultOrganizationalUnit = "Herbt" 33 defaultOrganization = "Herbt" 34 defaultCommonName = "hercules-bt.com" 35 createFileFailedErrorTemplate = "create file failed, %s" 36 parseCertificateFailedErrorTemplate = "ParseCertificateRequest failed, %s" 37 ) 38 39 const ( 40 defaultExpireYear = 10 41 ) 42 43 // CACertificateConfig contains necessary parameters for creating private key. 44 type CACertificateConfig struct { 45 PrivKey crypto.PrivateKey 46 HashType crypto.HashType 47 CertPath string 48 CertFileName string 49 Country string 50 Locality string 51 Province string 52 OrganizationalUnit string 53 Organization string 54 CommonName string 55 ExpireYear int32 56 Sans []string 57 } 58 59 // CreatePrivKey - create private key file 60 func CreatePrivKey(keyType crypto.KeyType, keyPath, keyFile string, isTLS bool) (key crypto.PrivateKey, err error) { 61 algoName, ok := crypto.KeyType2NameMap[keyType] 62 if !ok { 63 return nil, fmt.Errorf("unknown key algo type [%d]", keyType) 64 } 65 var privKeyPEM string 66 if P11Context != nil && P11Context.enable && !isTLS { 67 var keySpecBytes []byte 68 keySpecBytes, key, err = CreateP11Key(P11Context.handle, P11Context.keyType, P11Context.keyId, P11Context.keyPwd) 69 if err != nil { 70 return nil, fmt.Errorf("generate pkcs11 key pair [%s] failed, %s", algoName, err.Error()) 71 } 72 privKeyPEM = string(keySpecBytes) 73 } else { 74 key, err = asym.GenerateKeyPair(keyType) 75 if err != nil { 76 return nil, fmt.Errorf("generate key pair [%s] failed, %s", algoName, err.Error()) 77 } 78 privKeyPEM, err = key.String() 79 if err != nil { 80 return nil, fmt.Errorf("key to pem failed, %s", err.Error()) 81 } 82 } 83 if keyPath != "" { 84 if err = os.MkdirAll(keyPath, os.ModePerm); err != nil { 85 return nil, fmt.Errorf("mk key dir failed, %s", err.Error()) 86 } 87 if err = ioutil.WriteFile(filepath.Join(keyPath, keyFile), 88 []byte(privKeyPEM), 0600); err != nil { 89 return nil, fmt.Errorf("save key to file [%s] failed, %s", keyPath, err.Error()) 90 } 91 } 92 return key, nil 93 } 94 95 // CreateCACertificate - create ca cert file 96 func CreateCACertificate(cfg *CACertificateConfig) error { 97 template, err := GenerateCertTemplate(&GenerateCertTemplateConfig{ 98 PrivKey: cfg.PrivKey, 99 IsCA: true, 100 Country: cfg.Country, 101 Locality: cfg.Locality, 102 Province: cfg.Province, 103 OrganizationalUnit: cfg.OrganizationalUnit, 104 Organization: cfg.Organization, 105 CommonName: cfg.CommonName, 106 ExpireYear: cfg.ExpireYear, 107 Sans: cfg.Sans, 108 KeyUsages: []x509.KeyUsage{x509.KeyUsageCertSign, x509.KeyUsageCRLSign}, 109 ExtKeyUsages: []x509.ExtKeyUsage{}, 110 }) 111 if err != nil { 112 return fmt.Errorf("generateCertTemplate failed, %s", err.Error()) 113 } 114 template.SubjectKeyId, err = ComputeSKI(cfg.HashType, cfg.PrivKey.PublicKey().ToStandardKey()) 115 if err != nil { 116 return fmt.Errorf("create CA cert compute SKI failed, %s", err.Error()) 117 } 118 err = createCertificate(cfg.PrivKey, template, template, cfg.CertPath, cfg.CertFileName) 119 if err != nil { 120 return fmt.Errorf("createCertificate failed, %s", err.Error()) 121 } 122 return nil 123 } 124 125 // CSRConfig contains necessary parameters for creating csr. 126 type CSRConfig struct { 127 PrivKey crypto.PrivateKey 128 CsrPath string 129 CsrFileName string 130 Country string 131 Locality string 132 Province string 133 OrganizationalUnit string 134 Organization string 135 CommonName string 136 } 137 138 func CreateCSR(cfg *CSRConfig) error { 139 templateX509, err := GenerateCSRTemplate( 140 cfg.PrivKey, 141 cfg.Country, 142 cfg.Locality, 143 cfg.Province, 144 cfg.OrganizationalUnit, 145 cfg.Organization, 146 cfg.CommonName, 147 ) 148 if err != nil { 149 return fmt.Errorf("generate csr template failed, %s", err.Error()) 150 } 151 template, err := bcx509.X509CertCsrToHerbtCertCsr(templateX509) 152 if err != nil { 153 return fmt.Errorf("generate csr failed, %s", err.Error()) 154 } 155 data, err := bcx509.CreateCertificateRequest(rand.Reader, template, cfg.PrivKey.ToStandardKey()) 156 if err != nil { 157 return fmt.Errorf("CreateCertificateRequest failed, %s", err.Error()) 158 } 159 if err = os.MkdirAll(cfg.CsrPath, os.ModePerm); err != nil { 160 return fmt.Errorf("mk csr dir failed, %s", err.Error()) 161 } 162 path := filepath.Join(cfg.CsrPath, cfg.CsrFileName) 163 f, err := os.Create(path) 164 if err != nil { 165 return fmt.Errorf(createFileFailedErrorTemplate, err.Error()) 166 } 167 defer f.Close() 168 return pem.Encode(f, &pem.Block{Type: "CSR", Bytes: data}) 169 } 170 171 // IssueCertificateConfig contains necessary parameters for issuing cert. 172 type IssueCertificateConfig struct { 173 HashType crypto.HashType 174 IsCA bool 175 IssuerPrivKeyFilePath string 176 IssuerCertFilePath string 177 IssuerPrivKeyPwd []byte 178 CsrFilePath string 179 CertPath string 180 CertFileName string 181 ExpireYear int32 182 Sans []string 183 //Uuid string 184 KeyUsages []x509.KeyUsage 185 ExtKeyUsages []x509.ExtKeyUsage 186 } 187 188 // IssueCertificate - issue certification 189 func IssueCertificate(cfg *IssueCertificateConfig) error { 190 privKey, issuerCert, csr, sn, err := issueCertificatePrepare(cfg) 191 if err != nil { 192 return err 193 } 194 basicConstraintsValid := false 195 if cfg.IsCA { 196 basicConstraintsValid = true 197 } 198 expireYear := cfg.ExpireYear 199 if expireYear <= 0 { 200 expireYear = defaultExpireYear 201 } 202 dnsName, ipAddrs := dealSANS(cfg.Sans) 203 var keyUsages x509.KeyUsage 204 if len(cfg.KeyUsages) != 0 { 205 for _, keyUsage := range cfg.KeyUsages { 206 keyUsages |= keyUsage 207 } 208 } 209 notBefore := time.Now().Add(-10 * time.Minute).UTC() 210 template := &x509.Certificate{ 211 Signature: csr.Signature, 212 SignatureAlgorithm: x509.SignatureAlgorithm(csr.SignatureAlgorithm), 213 PublicKey: csr.PublicKey, 214 PublicKeyAlgorithm: x509.PublicKeyAlgorithm(csr.PublicKeyAlgorithm), 215 SerialNumber: sn, 216 NotBefore: notBefore, 217 NotAfter: notBefore.Add(time.Duration(expireYear) * 365 * 24 * time.Hour).UTC(), 218 BasicConstraintsValid: basicConstraintsValid, 219 IsCA: cfg.IsCA, 220 Issuer: issuerCert.Subject, 221 KeyUsage: keyUsages, 222 ExtKeyUsage: cfg.ExtKeyUsages, 223 IPAddresses: ipAddrs, 224 DNSNames: dnsName, 225 //ExtraExtensions: extraExtensions, 226 Subject: csr.Subject, 227 } 228 if issuerCert.SubjectKeyId != nil { 229 template.AuthorityKeyId = issuerCert.SubjectKeyId 230 } else { 231 template.AuthorityKeyId, err = ComputeSKI(cfg.HashType, issuerCert.PublicKey) 232 if err != nil { 233 return fmt.Errorf("issue cert compute issuer cert SKI failed, %s", err.Error()) 234 } 235 } 236 template.SubjectKeyId, err = ComputeSKI(cfg.HashType, csr.PublicKey.ToStandardKey()) 237 if err != nil { 238 return fmt.Errorf("issue cert compute csr SKI failed, %s", err.Error()) 239 } 240 x509certEncode, err := bcx509.CreateCertificate(rand.Reader, template, issuerCert, 241 csr.PublicKey.ToStandardKey(), privKey.ToStandardKey()) 242 if err != nil { 243 return fmt.Errorf("issue certificate failed, %s", err) 244 } 245 if err = os.MkdirAll(cfg.CertPath, os.ModePerm); err != nil { 246 return fmt.Errorf("mk cert dir failed, %s", err.Error()) 247 } 248 f, err := os.Create(filepath.Join(cfg.CertPath, cfg.CertFileName)) 249 if err != nil { 250 return fmt.Errorf(createFileFailedErrorTemplate, err.Error()) 251 } 252 defer f.Close() 253 return pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: x509certEncode}) 254 } 255 256 func issueCertificatePrepare(cfg *IssueCertificateConfig) (privKey crypto.PrivateKey, issuerCert *x509.Certificate, 257 csr *bcx509.CertificateRequest, sn *big.Int, err error) { 258 privKeyRaw, err := ioutil.ReadFile(cfg.IssuerPrivKeyFilePath) 259 if err != nil { 260 err = fmt.Errorf("read private key file [%s] failed, %s", cfg.IssuerPrivKeyFilePath, err) 261 return 262 } 263 if P11Context != nil && P11Context.enable { 264 privKey, err = ParseP11PrivKey(P11Context.handle, privKeyRaw) 265 if err != nil { 266 err = fmt.Errorf("parse pkcs11 privakey failed, %s", err) 267 return 268 } 269 } else { 270 privKey, err = asym.PrivateKeyFromPEM(privKeyRaw, cfg.IssuerPrivKeyPwd) 271 if err != nil { 272 err = fmt.Errorf("PrivateKeyFromPEM failed, %s", err) 273 return 274 } 275 } 276 issuerCert, err = ParseCertificate(cfg.IssuerCertFilePath) 277 if err != nil { 278 err = fmt.Errorf("ParseCertificate cert failed, %s", err) 279 return 280 } 281 csrOriginal, err := ParseCertificateRequest(cfg.CsrFilePath) 282 if err != nil { 283 err = fmt.Errorf(parseCertificateFailedErrorTemplate, err) 284 return 285 } 286 csr, err = bcx509.X509CertCsrToHerbtCertCsr(csrOriginal) 287 if err != nil { 288 return nil, nil, nil, nil, fmt.Errorf(parseCertificateFailedErrorTemplate, err) 289 } 290 if err = csr.CheckSignature(); err != nil { 291 return nil, nil, nil, nil, fmt.Errorf("csr CheckSignature failed, %s", err) 292 } 293 sn, err = rand.Int(rand.Reader, big.NewInt(1000000)) 294 if err != nil { 295 return nil, nil, nil, nil, fmt.Errorf("get sn failed, %s", err) 296 } 297 return 298 } 299 300 // ParseCertificate - parse certification 301 func ParseCertificate(certFilePath string) (*x509.Certificate, error) { 302 certRaw, err := ioutil.ReadFile(certFilePath) 303 if err != nil { 304 return nil, fmt.Errorf("read cert file [%s] failed, %s", certFilePath, err) 305 } 306 block, _ := pem.Decode(certRaw) 307 cert, err := bcx509.ParseCertificate(block.Bytes) 308 if err != nil { 309 return nil, fmt.Errorf("ParseCertificate cert failed, %s", err) 310 } 311 return bcx509.HerbtCertToX509Cert(cert) 312 } 313 314 // ParseCertificateRequest - parse certification request 315 func ParseCertificateRequest(csrFilePath string) (*x509.CertificateRequest, error) { 316 csrRaw, err := ioutil.ReadFile(csrFilePath) 317 if err != nil { 318 return nil, fmt.Errorf("read csr file [%s] failed, %s", csrFilePath, err) 319 } 320 block, _ := pem.Decode(csrRaw) 321 csrBC, err := bcx509.ParseCertificateRequest(block.Bytes) 322 if err != nil { 323 return nil, fmt.Errorf(parseCertificateFailedErrorTemplate, err) 324 } 325 return bcx509.HerbtCertCsrToX509CertCsr(csrBC) 326 } 327 328 func ParseCertificateToJson(certFilePath string) (string, error) { 329 cert, err := ParseCertificate(certFilePath) 330 if err != nil { 331 return "", err 332 } 333 ret, err := json.Marshal(cert) 334 if err != nil { 335 return "", fmt.Errorf("json marshal cert failed, %s", err) 336 } 337 return string(ret), nil 338 } 339 340 type subjectPublicKeyInfo struct { 341 Algorithm pkix.AlgorithmIdentifier 342 SubjectPublicKey asn1.BitString 343 } 344 345 func ComputeSKI(hashType crypto.HashType, pub interface{}) ([]byte, error) { 346 encodedPub, err := bcx509.MarshalPKIXPublicKey(pub) 347 if err != nil { 348 return nil, err 349 } 350 var subPKI subjectPublicKeyInfo 351 _, err = asn1.Unmarshal(encodedPub, &subPKI) 352 if err != nil { 353 return nil, err 354 } 355 pubHash, err := hash.Get(hashType, subPKI.SubjectPublicKey.Bytes) 356 if err != nil { 357 return nil, err 358 } 359 return pubHash[:], nil 360 } 361 362 func createCertificate(privKey crypto.PrivateKey, template, parent *x509.Certificate, 363 certPath, certFileName string) error { 364 x509certEncode, err := bcx509.CreateCertificate(rand.Reader, template, parent, 365 privKey.PublicKey().ToStandardKey(), privKey.ToStandardKey()) 366 if err != nil { 367 return err 368 } 369 if err = os.MkdirAll(certPath, os.ModePerm); err != nil { 370 return fmt.Errorf("mk cert dir failed, %s", err.Error()) 371 } 372 f, err := os.Create(filepath.Join(certPath, certFileName)) 373 if err != nil { 374 return fmt.Errorf(createFileFailedErrorTemplate, err.Error()) 375 } 376 defer f.Close() 377 return pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: x509certEncode}) 378 } 379 380 // GenerateCertTemplateConfig contains necessary parameters for creating private key. 381 type GenerateCertTemplateConfig struct { 382 PrivKey crypto.PrivateKey 383 IsCA bool 384 Country string 385 Locality string 386 Province string 387 OrganizationalUnit string 388 Organization string 389 CommonName string 390 ExpireYear int32 391 Sans []string 392 KeyUsages []x509.KeyUsage 393 ExtKeyUsages []x509.ExtKeyUsage 394 } 395 396 func GenerateCertTemplate(cfg *GenerateCertTemplateConfig) (*x509.Certificate, error) { 397 sn, err := rand.Int(rand.Reader, big.NewInt(1000000)) 398 if err != nil { 399 return nil, err 400 } 401 notBefore := time.Now().Add(-10 * time.Minute).UTC() 402 c := cfg.Country 403 if c == "" { 404 c = defaultCountry 405 } 406 l := cfg.Locality 407 if l == "" { 408 l = defaultLocality 409 } 410 p := cfg.Province 411 if p == "" { 412 p = defaultProvince 413 } 414 ou := cfg.OrganizationalUnit 415 if ou == "" { 416 ou = defaultOrganizationalUnit 417 } 418 o := cfg.Organization 419 if o == "" { 420 o = defaultOrganization 421 } 422 cn := cfg.CommonName 423 if cn == "" { 424 cn = defaultCommonName 425 } 426 basicConstraintsValid := false 427 if cfg.IsCA { 428 basicConstraintsValid = true 429 } 430 expireYear := cfg.ExpireYear 431 if expireYear <= 0 { 432 expireYear = defaultExpireYear 433 } 434 signatureAlgorithm, err := getSignatureAlgorithm(cfg.PrivKey) 435 if err != nil { 436 return nil, err 437 } 438 dnsName, ipAddrs := dealSANS(cfg.Sans) 439 var keyUsages x509.KeyUsage 440 if len(cfg.KeyUsages) != 0 { 441 for _, keyUsage := range cfg.KeyUsages { 442 keyUsages |= keyUsage 443 } 444 } 445 template := &x509.Certificate{ 446 SignatureAlgorithm: signatureAlgorithm, 447 SerialNumber: sn, 448 NotBefore: notBefore, 449 NotAfter: notBefore.Add(time.Duration(expireYear) * 365 * 24 * time.Hour).UTC(), 450 BasicConstraintsValid: basicConstraintsValid, 451 IsCA: cfg.IsCA, 452 KeyUsage: keyUsages, 453 ExtKeyUsage: cfg.ExtKeyUsages, 454 IPAddresses: ipAddrs, 455 DNSNames: dnsName, 456 Subject: pkix.Name{ 457 Country: []string{c}, 458 Locality: []string{l}, 459 Province: []string{p}, 460 OrganizationalUnit: []string{ou}, 461 Organization: []string{o}, 462 CommonName: cn, 463 }, 464 } 465 return template, nil 466 } 467 468 func GenerateCSRTemplate(privKey crypto.PrivateKey, 469 country, locality, province, organizationalUnit, organization, commonName string) (*x509.CertificateRequest, error) { 470 c := country 471 if c == "" { 472 c = defaultCountry 473 } 474 l := locality 475 if l == "" { 476 l = defaultLocality 477 } 478 p := province 479 if p == "" { 480 p = defaultProvince 481 } 482 ou := organizationalUnit 483 if ou == "" { 484 ou = defaultOrganizationalUnit 485 } 486 o := organization 487 if o == "" { 488 o = defaultOrganization 489 } 490 cn := commonName 491 if cn == "" { 492 cn = defaultCommonName 493 } 494 signatureAlgorithm, err := getSignatureAlgorithm(privKey) 495 if err != nil { 496 return nil, err 497 } 498 return &x509.CertificateRequest{ 499 SignatureAlgorithm: signatureAlgorithm, 500 Subject: pkix.Name{ 501 Country: []string{c}, 502 Locality: []string{l}, 503 Province: []string{p}, 504 OrganizationalUnit: []string{ou}, 505 Organization: []string{o}, 506 CommonName: cn, 507 }, 508 }, nil 509 } 510 511 func getSignatureAlgorithm(privKey crypto.PrivateKey) (x509.SignatureAlgorithm, error) { 512 if privKey == nil || privKey.PublicKey() == nil { 513 return 0, errors.New("nil key material") 514 } 515 signatureAlgorithm := x509.ECDSAWithSHA256 516 switch privKey.PublicKey().ToStandardKey().(type) { 517 case *rsa.PublicKey: 518 signatureAlgorithm = x509.SHA256WithRSA 519 case *sm2.PublicKey: 520 signatureAlgorithm = x509.SignatureAlgorithm(bcx509.SM3WithSM2) 521 } 522 return signatureAlgorithm, nil 523 } 524 525 func dealSANS(sans []string) ([]string, []net.IP) { 526 var dnsName []string 527 var ipAddrs []net.IP 528 for _, san := range sans { 529 ip := net.ParseIP(san) 530 if ip != nil { 531 ipAddrs = append(ipAddrs, ip) 532 } else { 533 dnsName = append(dnsName, san) 534 } 535 } 536 return dnsName, ipAddrs 537 }