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  }