github.com/hxx258456/fabric-ca-gm@v0.0.3-0.20221111064038-a268ad7e3a37/lib/gmsigner.go (about)

     1  package lib
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/rand"
     6  	"crypto/x509/pkix"
     7  	"encoding/asn1"
     8  	"encoding/hex"
     9  	"encoding/pem"
    10  	"fmt"
    11  	"io"
    12  	"math/big"
    13  	"net"
    14  	"net/mail"
    15  	"time"
    16  
    17  	"gitee.com/zhaochuninhefei/zcgolog/zclog"
    18  	"github.com/hxx258456/ccgo/sm2"
    19  	"github.com/hxx258456/ccgo/x509"
    20  	"github.com/hxx258456/cfssl-gm/certdb"
    21  	"github.com/hxx258456/cfssl-gm/csr"
    22  	"github.com/hxx258456/cfssl-gm/signer"
    23  	"github.com/hxx258456/fabric-ca-gm/internal/pkg/util"
    24  	"github.com/hxx258456/fabric-gm/bccsp"
    25  	"github.com/hxx258456/fabric-gm/bccsp/sw"
    26  	"github.com/pkg/errors"
    27  )
    28  
    29  // 使用CA作为签署者生成x509证书
    30  //
    31  // Deprecated: 国密证书的签署改为使用`github.com/hxx258456/cfssl-gm`
    32  func createCertByCA(req signer.SignRequest, ca *CA) (cert []byte, err error) {
    33  	// zclog.Debugf("===== CA服务端开始做证书签名")
    34  	// zclog.Debugf("===== req : %#v\n", req.Subject)
    35  	// 注意,这里只把 req.Request 转为pem,丢掉了 req.Subject 信息
    36  	block, _ := pem.Decode([]byte(req.Request))
    37  	if block == nil {
    38  		return nil, errors.Errorf("decode error")
    39  	}
    40  	if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" {
    41  		return nil, errors.Errorf("not a csr")
    42  	}
    43  	// 基于req.Request(证书请求csr)生成证书模板
    44  	template, err := createCertTemplateByCertificateRequest(block.Bytes)
    45  	if err != nil {
    46  		zclog.Errorf("===== createCertTemplateByCertificateRequest error:[%s]", err)
    47  		return nil, err
    48  	}
    49  	// 补充OU与证书期限
    50  	err = fillOUAndNotAfter(template, req)
    51  	if err != nil {
    52  		zclog.Errorf("===== fillOUAndNotAfter error:[%s]", err)
    53  		return nil, err
    54  	}
    55  	// 获取CA证书路径
    56  	caCertFile := ca.Config.CA.Certfile
    57  	//certfile := req.Profile
    58  	// zclog.Debugf("===== certifle = %s", certfile)
    59  	// 读取CA证书文件,获取CA的私钥与其x509证书
    60  	caRootkey, _, caRootCert, err := util.GetSignerFromCertFile(caCertFile, ca.csp)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	// zclog.Debugf("===== rootca = %v", rootca)
    65  	// rootca := ParseX509Certificate2Sm2(x509cert)
    66  	// 使用CA的私钥与其x509证书给template签名,生成对应证书,并转为pem字节数组
    67  	cert, err = sw.CreateCertificateToMem(template, caRootCert, caRootkey)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	// zclog.Debugf("===== template = %v\n Type = %T", template, template.PublicKey)
    72  	// 将pem字节数组转为x509证书
    73  	clientCert, err := x509.ReadCertificateFromPem(cert)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	// zclog.Debugf("===== template.SerialNumber---,%v", template.SerialNumber)
    78  	// zclog.Debugf("===== clientCert--,%v", clientCert)
    79  	// zclog.Debugf("===== cert--,%v", cert)
    80  	// zclog.Debugf("===== req.Label--,%v", req.Label)
    81  	// zclog.Debugf("===== clientCert.NotAfter--,%v", clientCert.NotAfter)
    82  	// zclog.Debug("===== Exit ParseCertificate")
    83  	zclog.Debugf("===== 生成的证书长度 [%d]", len(cert))
    84  	// 将生成的证书插入DB
    85  	var certRecord = certdb.CertificateRecord{
    86  		Serial:  template.SerialNumber.String(),
    87  		AKI:     hex.EncodeToString(clientCert.AuthorityKeyId),
    88  		CALabel: req.Label,
    89  		Status:  "good",
    90  		Expiry:  clientCert.NotAfter,
    91  		PEM:     string(cert),
    92  	}
    93  	//aki := hex.EncodeToString(cert.AuthorityKeyId)
    94  	//serial := util.GetSerialAsHex(cert.SerialNumber)
    95  	err = ca.certDBAccessor.InsertCertificate(certRecord)
    96  	if err != nil {
    97  		zclog.Errorf("===== error InsertCertificate:[%s]", err)
    98  	}
    99  	return
   100  }
   101  
   102  // 生成自签名的CA根证书
   103  //
   104  // Deprecated: 国密证书的签署改为使用`github.com/hxx258456/cfssl-gm`
   105  func createRootCACert(key bccsp.Key, req *csr.CertificateRequest, priv crypto.Signer) (cert []byte, err error) {
   106  	zclog.Debugf("===== key类型 :%T", key)
   107  	// 生成标准的国密x509证书请求
   108  	csrPEM, err := createCertificateRequest(priv, req, key)
   109  	if err != nil {
   110  		zclog.Errorf("===== createCertificateRequest error:%s", err)
   111  		return nil, err
   112  	}
   113  	block, _ := pem.Decode(csrPEM)
   114  	if block == nil {
   115  		return nil, errors.Errorf("===== lib/gmca.go createRootCACert sm2 csr DecodeFailed")
   116  	}
   117  
   118  	if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" {
   119  		return nil, errors.Errorf("===== lib/gmca.go createRootCACert sm2 not a csr")
   120  	}
   121  	// 根据证书请求生成x509证书模板,需要补充证书期限信息
   122  	certTemplate, err := createCertTemplateByCertificateRequest(block.Bytes)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	// 补充模板的证书期限
   127  	certTemplate.NotBefore = time.Now()
   128  	// 作为CA自签名的根证书,使用期限最长的 defaultRootCACertificateExpiration
   129  	certTemplate.NotAfter = time.Now().Add(parseDuration(defaultRootCACertificateExpiration))
   130  	// 生成自签名的CA根证书
   131  	cert, err = sw.CreateCertificateToMem(certTemplate, certTemplate, key)
   132  	return
   133  }
   134  
   135  // 补充OU信息
   136  //
   137  // Deprecated: 国密证书的签署改为使用`github.com/hxx258456/cfssl-gm`
   138  func fillOUAndNotAfter(template *x509.Certificate, req signer.SignRequest) error {
   139  	subject := req.Subject
   140  	// zclog.Debugf("===== before template.Subject: %#v , subject: %#v", template.Subject, subject)
   141  	if len(subject.Names) > 0 {
   142  		if len(template.Subject.OrganizationalUnit) == 0 {
   143  			var tmpOUs []string
   144  			for _, csrName := range subject.Names {
   145  				if csrName.OU != "" {
   146  					tmpOUs = append(tmpOUs, csrName.OU)
   147  				}
   148  			}
   149  			template.Subject.OrganizationalUnit = tmpOUs
   150  		} else {
   151  			tmpOUs := template.Subject.OrganizationalUnit
   152  			for _, csrName := range subject.Names {
   153  				ouAdd := csrName.OU
   154  				if ouAdd == "" {
   155  					continue
   156  				}
   157  				needAdd := true
   158  				for _, ouExist := range tmpOUs {
   159  					if ouAdd == ouExist {
   160  						needAdd = false
   161  						break
   162  					}
   163  				}
   164  				if needAdd {
   165  					tmpOUs = append(tmpOUs, ouAdd)
   166  				}
   167  			}
   168  			template.Subject.OrganizationalUnit = tmpOUs
   169  		}
   170  	}
   171  	// zclog.Debugf("===== after template.Subject: %#v , subject: %#v", template.Subject, subject)
   172  	template.NotBefore = time.Now()
   173  	// zclog.Debugf("===== req.NotAfter: %s", req.NotAfter.Format(time.RFC3339))
   174  	if req.NotAfter.IsZero() {
   175  		template.NotAfter = time.Now().Add(defaultIssuedCertificateExpiration)
   176  	} else {
   177  		template.NotAfter = req.NotAfter
   178  	}
   179  	// zclog.Debugf("===== template.NotAfter: %s", template.NotAfter.Format(time.RFC3339))
   180  	return nil
   181  }
   182  
   183  // 根据证书请求生成x509证书模板
   184  //  注意,生成的模板缺少证书期限,缺少扩展信息
   185  //
   186  // Deprecated: 国密证书的签署改为使用`github.com/hxx258456/cfssl-gm`
   187  func createCertTemplateByCertificateRequest(csrBytes []byte) (template *x509.Certificate, err error) {
   188  	csrv, err := x509.ParseCertificateRequest(csrBytes)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	err = csrv.CheckSignature()
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	// zclog.Debugf("===== csrv :%#v", csrv)
   197  	template = &x509.Certificate{
   198  		Subject:            csrv.Subject,
   199  		PublicKeyAlgorithm: csrv.PublicKeyAlgorithm,
   200  		PublicKey:          csrv.PublicKey,
   201  		SignatureAlgorithm: csrv.SignatureAlgorithm,
   202  		DNSNames:           csrv.DNSNames,
   203  		IPAddresses:        csrv.IPAddresses,
   204  		EmailAddresses:     csrv.EmailAddresses,
   205  	}
   206  	// zclog.Debugf("===== algorithn = %v, %v\n", template.PublicKeyAlgorithm, template.SignatureAlgorithm)
   207  	// zclog.Debugf("===== publicKey :%T", template.PublicKey)
   208  
   209  	for _, val := range csrv.Extensions {
   210  		zclog.Debugf("===== csrv.Extensions: %s", val)
   211  		// Check the CSR for the X.509 BasicConstraints (RFC 5280, 4.2.1.9)
   212  		// extension and append to template if necessary
   213  		if val.Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 19}) {
   214  			zclog.Debug("===== csrv.Extensions 有 ca constraints信息")
   215  			var constraints csr.BasicConstraints
   216  			// var rest []byte
   217  			// if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil {
   218  			// 	//return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
   219  			// } else if len(rest) != 0 {
   220  			// 	//return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, errors.New("x509: trailing data after X.509 BasicConstraints"))
   221  			// }
   222  			asn1.Unmarshal(val.Value, &constraints)
   223  
   224  			template.BasicConstraintsValid = true
   225  			template.IsCA = constraints.IsCA
   226  			template.MaxPathLen = constraints.MaxPathLen
   227  			template.MaxPathLenZero = template.MaxPathLen == 0
   228  		}
   229  	}
   230  	serialNumber := make([]byte, 20)
   231  	_, err = io.ReadFull(rand.Reader, serialNumber)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	// SetBytes interprets buf as the bytes of a big-endian
   237  	// unsigned integer. The leading byte should be masked
   238  	// off to ensure it isn't negative.
   239  	serialNumber[0] &= 0x7F
   240  
   241  	template.SerialNumber = new(big.Int).SetBytes(serialNumber)
   242  
   243  	return
   244  }
   245  
   246  // 生成标准的国密x509证书请求
   247  //
   248  // Deprecated: 国密证书的签署改为使用`github.com/hxx258456/cfssl-gm`
   249  func createCertificateRequest(priv crypto.Signer, req *csr.CertificateRequest, key bccsp.Key) (csr []byte, err error) {
   250  	sigAlgo := signerAlgo(priv)
   251  	if sigAlgo == x509.UnknownSignatureAlgorithm {
   252  		return nil, errors.Errorf("===== lib/gmca.go generate Private key type is unsupported")
   253  	}
   254  	// zclog.Debug("===== begin create sm2.CertificateRequest")
   255  	reqName := req.Name()
   256  	// 生成证书申请模板
   257  	var tpl = x509.CertificateRequest{
   258  		Subject:            reqName,
   259  		SignatureAlgorithm: sigAlgo,
   260  	}
   261  	// 根据req.Hosts补充SAN字段 优先顺序 IP > EMail > DNS
   262  	for i := range req.Hosts {
   263  		if ip := net.ParseIP(req.Hosts[i]); ip != nil {
   264  			tpl.IPAddresses = append(tpl.IPAddresses, ip)
   265  		} else if email, err := mail.ParseAddress(req.Hosts[i]); err == nil && email != nil {
   266  			tpl.EmailAddresses = append(tpl.EmailAddresses, email.Address)
   267  		} else {
   268  			tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i])
   269  		}
   270  	}
   271  	// 请求CA证书时,补充BasicConstraints信息
   272  	if req.CA != nil {
   273  		err = appendCAInfoToCSR(req.CA, &tpl)
   274  		if err != nil {
   275  			err = fmt.Errorf("===== lib/gmca.go createCertificateRequest appendCAInfoToCSR faild: %s", err)
   276  			return
   277  		}
   278  	}
   279  	csr, err = sw.CreateSm2CertificateRequestToMem(&tpl, key)
   280  	return
   281  }
   282  
   283  // 检查并获取签名算法
   284  //
   285  // Deprecated: 国密证书的签署改为使用`github.com/hxx258456/cfssl-gm`
   286  func signerAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
   287  	switch priv.Public().(type) {
   288  	case *sm2.PublicKey:
   289  		return x509.SM2WithSM3
   290  	default:
   291  		return x509.UnknownSignatureAlgorithm
   292  	}
   293  }
   294  
   295  // appendCAInfoToCSR appends CAConfig BasicConstraint extension to a CSR
   296  //
   297  // Deprecated: 国密证书的签署改为使用`github.com/hxx258456/cfssl-gm`
   298  func appendCAInfoToCSR(reqConf *csr.CAConfig, csreq *x509.CertificateRequest) error {
   299  	pathlen := reqConf.PathLength
   300  	if pathlen == 0 && !reqConf.PathLenZero {
   301  		pathlen = -1
   302  	}
   303  	val, err := asn1.Marshal(csr.BasicConstraints{IsCA: true, MaxPathLen: pathlen})
   304  
   305  	if err != nil {
   306  		return err
   307  	}
   308  	csreq.ExtraExtensions = []pkix.Extension{
   309  		{
   310  			Id:       asn1.ObjectIdentifier{2, 5, 29, 19}, // BasicConstraints的OID
   311  			Value:    val,
   312  			Critical: true,
   313  		},
   314  	}
   315  	return nil
   316  }