github.com/lzy4123/fabric@v2.1.1+incompatible/internal/cryptogen/ca/ca.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  package ca
     7  
     8  import (
     9  	"crypto"
    10  	"crypto/ecdsa"
    11  	"crypto/elliptic"
    12  	"crypto/rand"
    13  	"crypto/sha256"
    14  	"crypto/x509"
    15  	"crypto/x509/pkix"
    16  	"encoding/pem"
    17  	"io/ioutil"
    18  	"math/big"
    19  	"net"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/hyperledger/fabric/internal/cryptogen/csp"
    26  	"github.com/pkg/errors"
    27  )
    28  
    29  type CA struct {
    30  	Name               string
    31  	Country            string
    32  	Province           string
    33  	Locality           string
    34  	OrganizationalUnit string
    35  	StreetAddress      string
    36  	PostalCode         string
    37  	Signer             crypto.Signer
    38  	SignCert           *x509.Certificate
    39  }
    40  
    41  // NewCA creates an instance of CA and saves the signing key pair in
    42  // baseDir/name
    43  func NewCA(
    44  	baseDir,
    45  	org,
    46  	name,
    47  	country,
    48  	province,
    49  	locality,
    50  	orgUnit,
    51  	streetAddress,
    52  	postalCode string,
    53  ) (*CA, error) {
    54  
    55  	var ca *CA
    56  
    57  	err := os.MkdirAll(baseDir, 0755)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	priv, err := csp.GeneratePrivateKey(baseDir)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	template := x509Template()
    68  	//this is a CA
    69  	template.IsCA = true
    70  	template.KeyUsage |= x509.KeyUsageDigitalSignature |
    71  		x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign |
    72  		x509.KeyUsageCRLSign
    73  	template.ExtKeyUsage = []x509.ExtKeyUsage{
    74  		x509.ExtKeyUsageClientAuth,
    75  		x509.ExtKeyUsageServerAuth,
    76  	}
    77  
    78  	//set the organization for the subject
    79  	subject := subjectTemplateAdditional(country, province, locality, orgUnit, streetAddress, postalCode)
    80  	subject.Organization = []string{org}
    81  	subject.CommonName = name
    82  
    83  	template.Subject = subject
    84  	template.SubjectKeyId = computeSKI(priv)
    85  
    86  	x509Cert, err := genCertificateECDSA(
    87  		baseDir,
    88  		name,
    89  		&template,
    90  		&template,
    91  		&priv.PublicKey,
    92  		priv,
    93  	)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	ca = &CA{
    98  		Name: name,
    99  		Signer: &csp.ECDSASigner{
   100  			PrivateKey: priv,
   101  		},
   102  		SignCert:           x509Cert,
   103  		Country:            country,
   104  		Province:           province,
   105  		Locality:           locality,
   106  		OrganizationalUnit: orgUnit,
   107  		StreetAddress:      streetAddress,
   108  		PostalCode:         postalCode,
   109  	}
   110  
   111  	return ca, err
   112  }
   113  
   114  // SignCertificate creates a signed certificate based on a built-in template
   115  // and saves it in baseDir/name
   116  func (ca *CA) SignCertificate(
   117  	baseDir,
   118  	name string,
   119  	orgUnits,
   120  	alternateNames []string,
   121  	pub *ecdsa.PublicKey,
   122  	ku x509.KeyUsage,
   123  	eku []x509.ExtKeyUsage,
   124  ) (*x509.Certificate, error) {
   125  
   126  	template := x509Template()
   127  	template.KeyUsage = ku
   128  	template.ExtKeyUsage = eku
   129  
   130  	//set the organization for the subject
   131  	subject := subjectTemplateAdditional(
   132  		ca.Country,
   133  		ca.Province,
   134  		ca.Locality,
   135  		ca.OrganizationalUnit,
   136  		ca.StreetAddress,
   137  		ca.PostalCode,
   138  	)
   139  	subject.CommonName = name
   140  
   141  	subject.OrganizationalUnit = append(subject.OrganizationalUnit, orgUnits...)
   142  
   143  	template.Subject = subject
   144  	for _, san := range alternateNames {
   145  		// try to parse as an IP address first
   146  		ip := net.ParseIP(san)
   147  		if ip != nil {
   148  			template.IPAddresses = append(template.IPAddresses, ip)
   149  		} else {
   150  			template.DNSNames = append(template.DNSNames, alternateNames...)
   151  		}
   152  	}
   153  
   154  	cert, err := genCertificateECDSA(
   155  		baseDir,
   156  		name,
   157  		&template,
   158  		ca.SignCert,
   159  		pub,
   160  		ca.Signer,
   161  	)
   162  
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	return cert, nil
   168  }
   169  
   170  // compute Subject Key Identifier
   171  func computeSKI(privKey *ecdsa.PrivateKey) []byte {
   172  	// Marshall the public key
   173  	raw := elliptic.Marshal(privKey.Curve, privKey.PublicKey.X, privKey.PublicKey.Y)
   174  
   175  	// Hash it
   176  	hash := sha256.Sum256(raw)
   177  	return hash[:]
   178  }
   179  
   180  // default template for X509 subject
   181  func subjectTemplate() pkix.Name {
   182  	return pkix.Name{
   183  		Country:  []string{"US"},
   184  		Locality: []string{"San Francisco"},
   185  		Province: []string{"California"},
   186  	}
   187  }
   188  
   189  // Additional for X509 subject
   190  func subjectTemplateAdditional(
   191  	country,
   192  	province,
   193  	locality,
   194  	orgUnit,
   195  	streetAddress,
   196  	postalCode string,
   197  ) pkix.Name {
   198  	name := subjectTemplate()
   199  	if len(country) >= 1 {
   200  		name.Country = []string{country}
   201  	}
   202  	if len(province) >= 1 {
   203  		name.Province = []string{province}
   204  	}
   205  
   206  	if len(locality) >= 1 {
   207  		name.Locality = []string{locality}
   208  	}
   209  	if len(orgUnit) >= 1 {
   210  		name.OrganizationalUnit = []string{orgUnit}
   211  	}
   212  	if len(streetAddress) >= 1 {
   213  		name.StreetAddress = []string{streetAddress}
   214  	}
   215  	if len(postalCode) >= 1 {
   216  		name.PostalCode = []string{postalCode}
   217  	}
   218  	return name
   219  }
   220  
   221  // default template for X509 certificates
   222  func x509Template() x509.Certificate {
   223  
   224  	// generate a serial number
   225  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   226  	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
   227  
   228  	// set expiry to around 10 years
   229  	expiry := 3650 * 24 * time.Hour
   230  	// round minute and backdate 5 minutes
   231  	notBefore := time.Now().Round(time.Minute).Add(-5 * time.Minute).UTC()
   232  
   233  	//basic template to use
   234  	x509 := x509.Certificate{
   235  		SerialNumber:          serialNumber,
   236  		NotBefore:             notBefore,
   237  		NotAfter:              notBefore.Add(expiry).UTC(),
   238  		BasicConstraintsValid: true,
   239  	}
   240  	return x509
   241  
   242  }
   243  
   244  // generate a signed X509 certificate using ECDSA
   245  func genCertificateECDSA(
   246  	baseDir,
   247  	name string,
   248  	template,
   249  	parent *x509.Certificate,
   250  	pub *ecdsa.PublicKey,
   251  	priv interface{},
   252  ) (*x509.Certificate, error) {
   253  
   254  	//create the x509 public cert
   255  	certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	//write cert out to file
   261  	fileName := filepath.Join(baseDir, name+"-cert.pem")
   262  	certFile, err := os.Create(fileName)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	//pem encode the cert
   267  	err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
   268  	certFile.Close()
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	x509Cert, err := x509.ParseCertificate(certBytes)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	return x509Cert, nil
   278  }
   279  
   280  // LoadCertificateECDSA load a ecdsa cert from a file in cert path
   281  func LoadCertificateECDSA(certPath string) (*x509.Certificate, error) {
   282  	var cert *x509.Certificate
   283  	var err error
   284  
   285  	walkFunc := func(path string, info os.FileInfo, err error) error {
   286  		if strings.HasSuffix(path, ".pem") {
   287  			rawCert, err := ioutil.ReadFile(path)
   288  			if err != nil {
   289  				return err
   290  			}
   291  			block, _ := pem.Decode(rawCert)
   292  			if block == nil || block.Type != "CERTIFICATE" {
   293  				return errors.Errorf("%s: wrong PEM encoding", path)
   294  			}
   295  			cert, err = x509.ParseCertificate(block.Bytes)
   296  			if err != nil {
   297  				return errors.Errorf("%s: wrong DER encoding", path)
   298  			}
   299  		}
   300  		return nil
   301  	}
   302  
   303  	err = filepath.Walk(certPath, walkFunc)
   304  	if err != nil {
   305  		return nil, err
   306  	}
   307  
   308  	return cert, err
   309  }