github.com/bestbeforetoday/fabric-ca@v2.0.0-alpha+incompatible/lib/tcert/tcert.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tcert 18 19 import ( 20 "crypto/ecdsa" 21 "crypto/hmac" 22 "crypto/rand" 23 "crypto/sha512" 24 "crypto/x509" 25 "crypto/x509/pkix" 26 "encoding/asn1" 27 "encoding/base64" 28 "fmt" 29 "math/big" 30 "strconv" 31 "time" 32 33 "github.com/cloudflare/cfssl/log" 34 "github.com/hyperledger/fabric-ca/api" 35 "github.com/hyperledger/fabric-ca/util" 36 "github.com/hyperledger/fabric/bccsp" 37 cspsigner "github.com/hyperledger/fabric/bccsp/signer" 38 ) 39 40 var ( 41 // TCertEncTCertIndex is the ASN1 object identifier of the TCert index. 42 TCertEncTCertIndex = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 7} 43 44 // TCertEncEnrollmentID is the ASN1 object identifier of the enrollment id. 45 TCertEncEnrollmentID = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 8} 46 47 // TCertAttributesHeaders is the ASN1 object identifier of attributes header. 48 TCertAttributesHeaders = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 9} 49 50 // Padding for encryption. 51 Padding = []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} 52 53 // tcertSubject is the subject name placed in all generated TCerts 54 tcertSubject = pkix.Name{CommonName: "Fabric Transaction Certificate"} 55 ) 56 57 // LoadMgr is the constructor for a TCert manager given key and certificate file names 58 // @parameter caKeyFile is the file name for the CA's key 59 // @parameter caCertFile is the file name for the CA's cert 60 func LoadMgr(caKeyFile, caCertFile string, myCSP bccsp.BCCSP) (*Mgr, error) { 61 _, caKey, caCert, err := util.GetSignerFromCertFile(caCertFile, myCSP) 62 if err != nil && caCert == nil { 63 return nil, fmt.Errorf("Failed to load cert [%s]", err) 64 } 65 if err != nil { 66 // Fallback: attempt to read out of keyFile and import 67 log.Debugf("No key found in BCCSP keystore, attempting fallback") 68 key, err := util.ImportBCCSPKeyFromPEM(caKeyFile, myCSP, true) 69 if err != nil { 70 return nil, err 71 } 72 signer, err := cspsigner.New(myCSP, key) 73 if err != nil { 74 return nil, fmt.Errorf("Failed initializing CryptoSigner [%s]", err) 75 } 76 caKey = signer 77 } 78 79 return NewMgr(caKey, caCert) 80 } 81 82 // NewMgr is the constructor for a TCert manager given a key and an x509 certificate 83 // @parameter caKey is used for signing a certificate request 84 // @parameter caCert is used for extracting CA data to associate with issued certificates 85 func NewMgr(caKey interface{}, caCert *x509.Certificate) (*Mgr, error) { 86 mgr := new(Mgr) 87 mgr.CAKey = caKey 88 mgr.CACert = caCert 89 mgr.ValidityPeriod = time.Hour * 24 * 365 // default to 1 year 90 mgr.MaxAllowedBatchSize = 1000 91 return mgr, nil 92 } 93 94 // Mgr is the manager for the TCert library 95 type Mgr struct { 96 // CAKey is used for signing a certificate request 97 CAKey interface{} 98 // CACert is used for extracting CA data to associate with issued certificates 99 CACert *x509.Certificate 100 // ValidityPeriod is the duration that the issued certificate will be valid 101 // unless the user requests a shorter validity period. 102 // The default value is 1 year. 103 ValidityPeriod time.Duration 104 // MaxAllowedBatchSize is the maximum number of TCerts which can be requested at a time. 105 // The default value is 1000. 106 MaxAllowedBatchSize int 107 } 108 109 // GetBatch gets a batch of TCerts 110 // @parameter req Is the TCert batch request 111 // @parameter ecert Is the enrollment certificate of the caller 112 func (tm *Mgr) GetBatch(req *GetTCertBatchRequest, ecert *x509.Certificate) (*api.GetTCertBatchResponse, error) { 113 114 log.Debugf("GetBatch req=%+v", req) 115 116 // Set numTCertsInBatch to the number of TCerts to get. 117 // If 0 are requested, retrieve the maximum allowable; 118 // otherwise, retrieve the number requested it not too many. 119 var numTCertsInBatch int 120 if req.Count == 0 { 121 numTCertsInBatch = int(tm.MaxAllowedBatchSize) 122 } else if req.Count <= tm.MaxAllowedBatchSize { 123 numTCertsInBatch = int(req.Count) 124 } else { 125 return nil, fmt.Errorf("You may not request %d TCerts; the maximum is %d", 126 req.Count, tm.MaxAllowedBatchSize) 127 } 128 129 // Certs are valid for the min of requested and configured max 130 vp := tm.ValidityPeriod 131 if req.ValidityPeriod > 0 && req.ValidityPeriod < vp { 132 vp = req.ValidityPeriod 133 } 134 135 // Create a template from which to create all other TCerts. 136 // Since a TCert is anonymous and unlinkable, do not include 137 template := &x509.Certificate{ 138 Subject: tcertSubject, 139 } 140 template.NotBefore = time.Now() 141 template.NotAfter = template.NotBefore.Add(vp) 142 template.IsCA = false 143 template.KeyUsage = x509.KeyUsageDigitalSignature 144 template.SubjectKeyId = []byte{1, 2, 3, 4} 145 146 // Generate nonce for TCertIndex 147 nonce := make([]byte, 16) // 8 bytes rand, 8 bytes timestamp 148 rand.Reader.Read(nonce[:8]) 149 150 pub := ecert.PublicKey.(*ecdsa.PublicKey) 151 152 mac := hmac.New(sha512.New384, []byte(createHMACKey())) 153 raw, _ := x509.MarshalPKIXPublicKey(pub) 154 mac.Write(raw) 155 kdfKey := mac.Sum(nil) 156 157 var set []api.TCert 158 159 for i := 0; i < numTCertsInBatch; i++ { 160 tcertid, uuidError := GenerateIntUUID() 161 if uuidError != nil { 162 return nil, fmt.Errorf("Failure generating UUID: %s", uuidError) 163 } 164 // Compute TCertIndex 165 tidx := []byte(strconv.Itoa(2*i + 1)) 166 tidx = append(tidx[:], nonce[:]...) 167 tidx = append(tidx[:], Padding...) 168 169 mac := hmac.New(sha512.New384, kdfKey) 170 mac.Write([]byte{1}) 171 extKey := mac.Sum(nil)[:32] 172 173 mac = hmac.New(sha512.New384, kdfKey) 174 mac.Write([]byte{2}) 175 mac = hmac.New(sha512.New384, mac.Sum(nil)) 176 mac.Write(tidx) 177 178 one := new(big.Int).SetInt64(1) 179 k := new(big.Int).SetBytes(mac.Sum(nil)) 180 k.Mod(k, new(big.Int).Sub(pub.Curve.Params().N, one)) 181 k.Add(k, one) 182 183 tmpX, tmpY := pub.ScalarBaseMult(k.Bytes()) 184 txX, txY := pub.Curve.Add(pub.X, pub.Y, tmpX, tmpY) 185 txPub := ecdsa.PublicKey{Curve: pub.Curve, X: txX, Y: txY} 186 187 // Compute encrypted TCertIndex 188 encryptedTidx, encryptErr := CBCPKCS7Encrypt(extKey, tidx) 189 if encryptErr != nil { 190 return nil, encryptErr 191 } 192 193 extensions, ks, extensionErr := generateExtensions(tcertid, encryptedTidx, ecert, req) 194 195 if extensionErr != nil { 196 return nil, extensionErr 197 } 198 199 template.PublicKey = txPub 200 template.Extensions = extensions 201 template.ExtraExtensions = extensions 202 template.SerialNumber = tcertid 203 204 raw, err := x509.CreateCertificate(rand.Reader, template, tm.CACert, &txPub, tm.CAKey) 205 if err != nil { 206 return nil, fmt.Errorf("Failed in TCert x509.CreateCertificate: %s", err) 207 } 208 209 pem := ConvertDERToPEM(raw, "CERTIFICATE") 210 211 set = append(set, api.TCert{Cert: pem, Keys: ks}) 212 } 213 214 tcertID, randNumErr := GenNumber(big.NewInt(20)) 215 if randNumErr != nil { 216 return nil, randNumErr 217 } 218 219 tcertResponse := &api.GetTCertBatchResponse{ 220 ID: tcertID, 221 TS: time.Now(), 222 Key: kdfKey, 223 TCerts: set, 224 } 225 226 return tcertResponse, nil 227 228 } 229 230 /** 231 * Create HMAC Key 232 * returns HMAC String 233 */ 234 func createHMACKey() string { 235 key := make([]byte, 49) 236 rand.Reader.Read(key) 237 var cooked = base64.StdEncoding.EncodeToString(key) 238 return cooked 239 } 240 241 // Generate encrypted extensions to be included into the TCert (TCertIndex, EnrollmentID and attributes). 242 func generateExtensions(tcertid *big.Int, tidx []byte, enrollmentCert *x509.Certificate, batchRequest *GetTCertBatchRequest) ([]pkix.Extension, map[string][]byte, error) { 243 // For each TCert we need to store and retrieve to the user the list of Ks used to encrypt the EnrollmentID and the attributes. 244 ks := make(map[string][]byte) 245 attrs := batchRequest.Attrs 246 extensions := make([]pkix.Extension, len(attrs)) 247 248 preK1 := batchRequest.PreKey 249 mac := hmac.New(sha512.New384, []byte(preK1)) 250 mac.Write(tcertid.Bytes()) 251 preK0 := mac.Sum(nil) 252 253 // Compute encrypted EnrollmentID 254 mac = hmac.New(sha512.New384, preK0) 255 mac.Write([]byte("enrollmentID")) 256 enrollmentIDKey := mac.Sum(nil)[:32] 257 258 enrollmentID := []byte(GetEnrollmentIDFromCert(enrollmentCert)) 259 enrollmentID = append(enrollmentID, Padding...) 260 261 encEnrollmentID, err := CBCPKCS7Encrypt(enrollmentIDKey, enrollmentID) 262 if err != nil { 263 return nil, nil, err 264 } 265 266 // save k used to encrypt EnrollmentID 267 ks["enrollmentId"] = enrollmentIDKey 268 269 attributeIdentifierIndex := 9 270 count := 0 271 attributesHeader := make(map[string]int) 272 273 // Append attributes to the extensions slice 274 for i := 0; i < len(attrs); i++ { 275 count++ 276 name := attrs[i].Name 277 value := []byte(attrs[i].Value) 278 279 // Save the position of the attribute extension on the header. 280 attributesHeader[name] = count 281 282 // Encrypt attribute if enabled 283 if batchRequest.EncryptAttrs { 284 mac = hmac.New(sha512.New384, preK0) 285 mac.Write([]byte(name)) 286 attributeKey := mac.Sum(nil)[:32] 287 288 value = append(value, Padding...) 289 value, err = CBCPKCS7Encrypt(attributeKey, value) 290 if err != nil { 291 return nil, nil, err 292 } 293 294 // Save the key used to encrypt the attribute 295 ks[name] = attributeKey 296 } 297 298 // Generate an ObjectIdentifier for the extension holding the attribute 299 TCertEncAttributes := asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, attributeIdentifierIndex + count} 300 301 // Add the attribute extension to the extensions array 302 extensions[count-1] = pkix.Extension{Id: TCertEncAttributes, Critical: false, Value: value} 303 } 304 305 // Append the TCertIndex to the extensions 306 extensions = append(extensions, pkix.Extension{Id: TCertEncTCertIndex, Critical: true, Value: tidx}) 307 308 // Append the encrypted EnrollmentID to the extensions 309 extensions = append(extensions, pkix.Extension{Id: TCertEncEnrollmentID, Critical: false, Value: encEnrollmentID}) 310 311 // Append the attributes header if there was attributes to include in the TCert 312 if len(attrs) > 0 { 313 extensions = append(extensions, pkix.Extension{Id: TCertAttributesHeaders, Critical: false, Value: buildAttributesHeader(attributesHeader)}) 314 } 315 316 return extensions, ks, nil 317 } 318 319 func buildAttributesHeader(attributesHeader map[string]int) []byte { 320 var headerString string 321 for k, v := range attributesHeader { 322 headerString = headerString + k + "->" + strconv.Itoa(v) + "#" 323 } 324 return []byte(headerString) 325 }