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 }