github.com/hyperledger-labs/bdls@v2.1.1+incompatible/cmd/idemixgen/main.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package main 8 9 // idemixgen is a command line tool that generates the CA's keys and 10 // generates MSP configs for siging and for verification 11 // This tool can be used to setup the peers and CA to support 12 // the Identity Mixer MSP 13 14 import ( 15 "crypto/ecdsa" 16 "crypto/x509" 17 "encoding/pem" 18 "fmt" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 23 "github.com/golang/protobuf/proto" 24 "github.com/hyperledger/fabric/common/tools/idemixgen/idemixca" 25 "github.com/hyperledger/fabric/common/tools/idemixgen/metadata" 26 "github.com/hyperledger/fabric/idemix" 27 "github.com/hyperledger/fabric/msp" 28 "github.com/pkg/errors" 29 "gopkg.in/alecthomas/kingpin.v2" 30 ) 31 32 const ( 33 IdemixDirIssuer = "ca" 34 IdemixConfigIssuerSecretKey = "IssuerSecretKey" 35 IdemixConfigRevocationKey = "RevocationKey" 36 ) 37 38 // command line flags 39 var ( 40 app = kingpin.New("idemixgen", "Utility for generating key material to be used with the Identity Mixer MSP in Hyperledger Fabric") 41 42 outputDir = app.Flag("output", "The output directory in which to place artifacts").Default("idemix-config").String() 43 44 genIssuerKey = app.Command("ca-keygen", "Generate CA key material") 45 genSignerConfig = app.Command("signerconfig", "Generate a default signer for this Idemix MSP") 46 genCAInput = genSignerConfig.Flag("ca-input", "The folder where CA's secrets are stored").String() 47 genCredOU = genSignerConfig.Flag("org-unit", "The Organizational Unit of the default signer").Short('u').String() 48 genCredIsAdmin = genSignerConfig.Flag("admin", "Make the default signer admin").Short('a').Bool() 49 genCredEnrollmentId = genSignerConfig.Flag("enrollmentId", "The enrollment id of the default signer").Short('e').String() 50 genCredRevocationHandle = genSignerConfig.Flag("revocationHandle", "The handle used to revoke this signer").Short('r').Int() 51 52 version = app.Command("version", "Show version information") 53 ) 54 55 func main() { 56 app.HelpFlag.Short('h') 57 58 switch kingpin.MustParse(app.Parse(os.Args[1:])) { 59 60 case genIssuerKey.FullCommand(): 61 isk, ipk, err := idemixca.GenerateIssuerKey() 62 handleError(err) 63 64 revocationKey, err := idemix.GenerateLongTermRevocationKey() 65 handleError(err) 66 encodedRevocationSK, err := x509.MarshalECPrivateKey(revocationKey) 67 handleError(err) 68 pemEncodedRevocationSK := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: encodedRevocationSK}) 69 handleError(err) 70 encodedRevocationPK, err := x509.MarshalPKIXPublicKey(revocationKey.Public()) 71 handleError(err) 72 pemEncodedRevocationPK := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: encodedRevocationPK}) 73 74 // Prevent overwriting the existing key 75 path := filepath.Join(*outputDir, IdemixDirIssuer) 76 checkDirectoryNotExists(path, fmt.Sprintf("Directory %s already exists", path)) 77 78 path = filepath.Join(*outputDir, msp.IdemixConfigDirMsp) 79 checkDirectoryNotExists(path, fmt.Sprintf("Directory %s already exists", path)) 80 81 // write private and public keys to the file 82 handleError(os.MkdirAll(filepath.Join(*outputDir, IdemixDirIssuer), 0770)) 83 handleError(os.MkdirAll(filepath.Join(*outputDir, msp.IdemixConfigDirMsp), 0770)) 84 writeFile(filepath.Join(*outputDir, IdemixDirIssuer, IdemixConfigIssuerSecretKey), isk) 85 writeFile(filepath.Join(*outputDir, IdemixDirIssuer, IdemixConfigRevocationKey), pemEncodedRevocationSK) 86 writeFile(filepath.Join(*outputDir, IdemixDirIssuer, msp.IdemixConfigFileIssuerPublicKey), ipk) 87 writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileRevocationPublicKey), pemEncodedRevocationPK) 88 writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileIssuerPublicKey), ipk) 89 90 case genSignerConfig.FullCommand(): 91 roleMask := 0 92 if *genCredIsAdmin { 93 roleMask = msp.GetRoleMaskFromIdemixRole(msp.ADMIN) 94 } else { 95 roleMask = msp.GetRoleMaskFromIdemixRole(msp.MEMBER) 96 } 97 if *genCAInput == "" { 98 genCAInput = outputDir 99 } 100 ipk, ipkRaw := readIssuerKey() 101 rsk := readRevocationKey() 102 rpk := readRevocationPublicKey() 103 104 config, err := idemixca.GenerateSignerConfig( 105 roleMask, 106 *genCredOU, 107 *genCredEnrollmentId, 108 *genCredRevocationHandle, 109 ipk, rsk, 110 ) 111 handleError(err) 112 113 path := filepath.Join(*outputDir, msp.IdemixConfigDirUser) 114 checkDirectoryNotExists(path, fmt.Sprintf("This MSP config already contains a directory \"%s\"", path)) 115 116 // Write config to file 117 handleError(os.MkdirAll(filepath.Join(*outputDir, msp.IdemixConfigDirUser), 0770)) 118 writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirUser, msp.IdemixConfigFileSigner), config) 119 120 // Write CA public info in case genCAInput != outputDir 121 if *genCAInput != *outputDir { 122 handleError(os.MkdirAll(filepath.Join(*outputDir, msp.IdemixConfigDirMsp), 0770)) 123 writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileRevocationPublicKey), rpk) 124 writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileIssuerPublicKey), ipkRaw) 125 } 126 127 case version.FullCommand(): 128 printVersion() 129 130 } 131 } 132 133 func printVersion() { 134 fmt.Println(metadata.GetVersionInfo()) 135 } 136 137 // writeFile writes bytes to a file and panics in case of an error 138 func writeFile(path string, contents []byte) { 139 handleError(ioutil.WriteFile(path, contents, 0640)) 140 } 141 142 // readIssuerKey reads the issuer key from the current directory 143 func readIssuerKey() (*idemix.IssuerKey, []byte) { 144 path := filepath.Join(*genCAInput, IdemixDirIssuer, IdemixConfigIssuerSecretKey) 145 isk, err := ioutil.ReadFile(path) 146 if err != nil { 147 handleError(errors.Wrapf(err, "failed to open issuer secret key file: %s", path)) 148 } 149 path = filepath.Join(*genCAInput, IdemixDirIssuer, msp.IdemixConfigFileIssuerPublicKey) 150 ipkBytes, err := ioutil.ReadFile(path) 151 if err != nil { 152 handleError(errors.Wrapf(err, "failed to open issuer public key file: %s", path)) 153 } 154 ipk := &idemix.IssuerPublicKey{} 155 handleError(proto.Unmarshal(ipkBytes, ipk)) 156 key := &idemix.IssuerKey{Isk: isk, Ipk: ipk} 157 158 return key, ipkBytes 159 } 160 161 func readRevocationKey() *ecdsa.PrivateKey { 162 path := filepath.Join(*genCAInput, IdemixDirIssuer, IdemixConfigRevocationKey) 163 keyBytes, err := ioutil.ReadFile(path) 164 if err != nil { 165 handleError(errors.Wrapf(err, "failed to open revocation secret key file: %s", path)) 166 } 167 168 block, _ := pem.Decode(keyBytes) 169 if block == nil { 170 handleError(errors.Errorf("failed to decode ECDSA private key")) 171 } 172 key, err := x509.ParseECPrivateKey(block.Bytes) 173 handleError(err) 174 175 return key 176 } 177 178 func readRevocationPublicKey() []byte { 179 path := filepath.Join(*genCAInput, msp.IdemixConfigDirMsp, msp.IdemixConfigFileRevocationPublicKey) 180 keyBytes, err := ioutil.ReadFile(path) 181 if err != nil { 182 handleError(errors.Wrapf(err, "failed to open revocation secret key file: %s", path)) 183 } 184 185 return keyBytes 186 } 187 188 // checkDirectoryNotExists checks whether a directory with the given path already exists and exits if this is the case 189 func checkDirectoryNotExists(path string, errorMessage string) { 190 _, err := os.Stat(path) 191 if err == nil { 192 handleError(errors.New(errorMessage)) 193 } 194 } 195 196 func handleError(err error) { 197 if err != nil { 198 fmt.Fprintln(os.Stderr, err) 199 os.Exit(1) 200 } 201 }