diff --git a/hsm-samples-gateway/.gitignore b/hsm-samples-gateway/.gitignore new file mode 100644 index 00000000..3916f359 --- /dev/null +++ b/hsm-samples-gateway/.gitignore @@ -0,0 +1 @@ +crypto-material \ No newline at end of file diff --git a/hsm-samples-gateway/README.md b/hsm-samples-gateway/README.md index 5ec256b3..9481fa16 100644 --- a/hsm-samples-gateway/README.md +++ b/hsm-samples-gateway/README.md @@ -5,10 +5,29 @@ new embedded Gateway in Fabric. The samples will only run against Fabric v2.4 and higher. -This will create a local docker network comprising five peers across three organisations and a single ordering node. - Sample client applications are available to demonstrate the features of the Fabric Gateway and associated SDKs using this network. +## Running the sample + +The Fabric test network is used to deploy and run this sample. Follow these steps in order: + +1. Create the test network and a channel (from the `test-network` folder). + ``` + ./network.sh up createChannel -ca + ``` + +1. Deploy one of the smart contract implementations (from the `test-network` folder). + ``` + # To deploy the TypeScript chaincode implementation + ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-typescript/ -ccl typescript + + # To deploy the Go chaincode implementation + ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go/ -ccl go + + # To deploy the Java chaincode implementation + ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-java/ -ccl java + ``` + ## C Compilers In order for the client application to run successfully you must ensure you have C compilers and Python 3 (Note that Python 2 may still work however Python 2 is out of support and could stop working in the future) installed otherwise the node dependency `pkcs11js` will not be built and the application will fail. The failure will have an error such as @@ -95,6 +114,8 @@ npm run build npm start ``` -When you are finished running the samples, the local docker network can be brought down with the following command: +When you are finished running the samples, the local test-network can be brought down with the following command (from the `test-network` folder): -`docker rm -f $(docker ps -aq) && docker network prune --force` \ No newline at end of file + ``` +./network.sh down +``` \ No newline at end of file diff --git a/hsm-samples-gateway/ca-client-config/.gitignore b/hsm-samples-gateway/ca-client-config/.gitignore new file mode 100644 index 00000000..09146f84 --- /dev/null +++ b/hsm-samples-gateway/ca-client-config/.gitignore @@ -0,0 +1 @@ +fabric-ca-client-config.yaml \ No newline at end of file diff --git a/hsm-samples-gateway/ca-client-config/fabric-ca-client-config-template.yaml b/hsm-samples-gateway/ca-client-config/fabric-ca-client-config-template.yaml new file mode 100644 index 00000000..6196ebc8 --- /dev/null +++ b/hsm-samples-gateway/ca-client-config/fabric-ca-client-config-template.yaml @@ -0,0 +1,174 @@ + +############################################################################# +# This is a configuration file for the fabric-ca-client command. +# +# COMMAND LINE ARGUMENTS AND ENVIRONMENT VARIABLES +# ------------------------------------------------ +# Each configuration element can be overridden via command line +# arguments or environment variables. The precedence for determining +# the value of each element is as follows: +# 1) command line argument +# Examples: +# a) --url https://localhost:7054 +# To set the fabric-ca server url +# b) --tls.client.certfile certfile.pem +# To set the client certificate for TLS +# 2) environment variable +# Examples: +# a) FABRIC_CA_CLIENT_URL=https://localhost:7054 +# To set the fabric-ca server url +# b) FABRIC_CA_CLIENT_TLS_CLIENT_CERTFILE=certfile.pem +# To set the client certificate for TLS +# 3) configuration file +# 4) default value (if there is one) +# All default values are shown beside each element below. +# +# FILE NAME ELEMENTS +# ------------------ +# The value of all fields whose name ends with "file" or "files" are +# name or names of other files. +# For example, see "tls.certfiles" and "tls.client.certfile". +# The value of each of these fields can be a simple filename, a +# relative path, or an absolute path. If the value is not an +# absolute path, it is interpreted as being relative to the location +# of this configuration file. +# +############################################################################# + +############################################################################# +# Client Configuration +############################################################################# + +# URL of the Fabric-ca-server (default: http://localhost:7054) +url: http://localhost:7054 + +# Membership Service Provider (MSP) directory +# This is useful when the client is used to enroll a peer or orderer, so +# that the enrollment artifacts are stored in the format expected by MSP. +mspdir: caadmin + +############################################################################# +# TLS section for secure socket connection +# +# certfiles - PEM-encoded list of trusted root certificate files +# client: +# certfile - PEM-encoded certificate file for when client authentication +# is enabled on server +# keyfile - PEM-encoded key file for when client authentication +# is enabled on server +############################################################################# +tls: + # TLS section for secure socket connection + certfiles: + client: + certfile: + keyfile: + +############################################################################# +# Certificate Signing Request section for generating the CSR for an +# enrollment certificate (ECert) +# +# cn - Used by CAs to determine which domain the certificate is to be generated for +# +# keyrequest - Properties to use when generating a private key. +# algo - key generation algorithm to use +# size - size of key to generate +# reusekey - reuse existing key during reenrollment +# +# serialnumber - The serialnumber field, if specified, becomes part of the issued +# certificate's DN (Distinguished Name). For example, one use case for this is +# a company with its own CA (Certificate Authority) which issues certificates +# to its employees and wants to include the employee's serial number in the DN +# of its issued certificates. +# WARNING: The serialnumber field should not be confused with the certificate's +# serial number which is set by the CA but is not a component of the +# certificate's DN. +# +# names - A list of name objects. Each name object should contain at least one +# "C", "L", "O", or "ST" value (or any combination of these) where these +# are abbreviations for the following: +# "C": country +# "L": locality or municipality (such as city or town name) +# "O": organization +# "OU": organizational unit, such as the department responsible for owning the key; +# it can also be used for a "Doing Business As" (DBS) name +# "ST": the state or province +# +# Note that the "OU" or organizational units of an ECert are always set according +# to the values of the identities type and affiliation. OUs are calculated for an enroll +# as OU=, OU=, ..., OU=. For example, an identity +# of type "client" with an affiliation of "org1.dept2.team3" would have the following +# organizational units: OU=client, OU=org1, OU=dept2, OU=team3 +# +# hosts - A list of host names for which the certificate should be valid +# +############################################################################# +csr: + cn: admin + keyrequest: + algo: ecdsa + size: 256 + reusekey: false + serialnumber: + names: + - C: US + ST: North Carolina + L: + O: Hyperledger + OU: Fabric + hosts: + - tryfabric + +############################################################################# +# Registration section used to register a new identity with fabric-ca server +# +# name - Unique name of the identity +# type - Type of identity being registered (e.g. 'peer, app, user') +# affiliation - The identity's affiliation +# maxenrollments - The maximum number of times the secret can be reused to enroll. +# Specially, -1 means unlimited; 0 means to use CA's max enrollment +# value. +# attributes - List of name/value pairs of attribute for identity +############################################################################# +id: + name: + type: + affiliation: + maxenrollments: 0 + attributes: + # - name: + # value: + +############################################################################# +# Enrollment section used to enroll an identity with fabric-ca server +# +# profile - Name of the signing profile to use in issuing the certificate +# label - Label to use in HSM operations +############################################################################# +enrollment: + profile: + label: + +############################################################################# +# Name of the CA to connect to within the fabric-ca server +############################################################################# +caname: + +############################################################################# +# BCCSP (BlockChain Crypto Service Provider) section allows to select which +# crypto implementation library to use +############################################################################# +bccsp: + default: PKCS11 + PKCS11: + Library: REPLACE_ME_HSMLIB + Pin: 98765432 + Label: ForFabric + hash: SHA2 + security: 256 + # sw: + # hash: SHA2 + # security: 256 + # filekeystore: + # # The directory used for the software file-based keystore + # keystore: msp/keystore diff --git a/hsm-samples-gateway/go/hsm-sample.go b/hsm-samples-gateway/go/hsm-sample.go index 299fc8d3..423f9b1a 100644 --- a/hsm-samples-gateway/go/hsm-sample.go +++ b/hsm-samples-gateway/go/hsm-sample.go @@ -10,9 +10,11 @@ SPDX-License-Identifier: Apache-2.0 package main import ( + "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/sha256" + "encoding/json" "encoding/pem" "errors" "os" @@ -30,12 +32,14 @@ import ( const ( mspID = "Org1MSP" - cryptoPath = "../../scenario/fixtures/crypto-material/" - certPath = cryptoPath + "hsm/HSMUser/signcerts/cert.pem" - tlsCertPath = cryptoPath + "crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" + certPath = "../crypto-material/hsm/HSMUser/signcerts/cert.pem" + tlsCertPath = "../../test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" peerEndpoint = "localhost:7051" ) +var now = time.Now() +var assetId = fmt.Sprintf("asset%d", now.Unix()*1e3+int64(now.Nanosecond())/1e6) + func main() { fmt.Println("Running the GO HSM Sample") @@ -66,34 +70,34 @@ func main() { } defer gateway.Close() - exampleSubmit(gateway) + exampleTransaction(gateway) fmt.Println() - fmt.Println("Go HSM Sample Completed Successfully") + fmt.Println("Go HSM Sample completed successfully") fmt.Println() } -func exampleSubmit(gateway *client.Gateway) { +func exampleTransaction(gateway *client.Gateway) { network := gateway.GetNetwork("mychannel") contract := network.GetContract("basic") - timestamp := time.Now().String() - fmt.Printf("Submitting \"put\" transaction with arguments: time, %s\n", timestamp) + fmt.Printf("Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments \n") - // Submit transaction, blocking until the transaction has been committed on the ledger - submitResult, err := contract.SubmitTransaction("put", "time", timestamp) + _, err := contract.SubmitTransaction("CreateAsset", assetId, "yellow", "5", "Tom", "1300") if err != nil { panic(fmt.Errorf("failed to submit transaction: %w", err)) } - fmt.Printf("Submit result: %s\n", string(submitResult)) - fmt.Println("Evaluating \"get\" query with arguments: time") + fmt.Printf("*** Transaction committed successfully\n") - evaluateResult, err := contract.EvaluateTransaction("get", "time") + fmt.Printf("Evaluate Transaction: ReadAsset, function returns asset attributes\n") + + evaluateResult, err := contract.EvaluateTransaction("ReadAsset", assetId) if err != nil { panic(fmt.Errorf("failed to evaluate transaction: %w", err)) } + result := formatJSON(evaluateResult) - fmt.Printf("Query result = %s\n", string(evaluateResult)) + fmt.Printf("*** Result:%s\n", result) } // newGrpcConnection creates a gRPC connection to the Gateway server. @@ -185,3 +189,12 @@ func findSoftHSMLibrary() string { panic("No SoftHSM library can be found. The Sample requires SoftHSM to be installed") } + +// Format JSON data +func formatJSON(data []byte) string { + var prettyJSON bytes.Buffer + if err := json.Indent(&prettyJSON, data, " ", ""); err != nil { + panic(fmt.Errorf("failed to parse JSON: %w", err)) + } + return prettyJSON.String() +} diff --git a/hsm-samples-gateway/node/package.json b/hsm-samples-gateway/node/package.json index 587e7239..3229f89e 100644 --- a/hsm-samples-gateway/node/package.json +++ b/hsm-samples-gateway/node/package.json @@ -18,7 +18,7 @@ "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.3.0", - "@hyperledger/fabric-gateway": "file:../../node/fabric-gateway-dev.tgz", + "@hyperledger/fabric-gateway": "^1.0.0", "jsrsasign": "^10.3.0" }, "devDependencies": { diff --git a/hsm-samples-gateway/node/src/hsm-sample.ts b/hsm-samples-gateway/node/src/hsm-sample.ts index bf274a1e..4b231b34 100644 --- a/hsm-samples-gateway/node/src/hsm-sample.ts +++ b/hsm-samples-gateway/node/src/hsm-sample.ts @@ -10,17 +10,20 @@ import { connect, Gateway, HSMSigner, HSMSignerFactory, HSMSignerOptions, signer import * as fs from 'fs'; import * as jsrsa from 'jsrsasign'; import * as path from 'path'; +import { TextDecoder } from 'util'; const mspId = 'Org1MSP'; const user = 'HSMUser'; +const assetId = `asset${Date.now()}`; +const utf8Decoder = new TextDecoder(); // Sample uses fabric-ca-client generated HSM identities, certificate is located in the signcerts directory // and has been stored in a directory of the name given to the identity. -const cryptoPath = path.resolve(__dirname, '..', '..', '..', 'scenario', 'fixtures', 'crypto-material'); -const certPath = path.resolve(cryptoPath, 'hsm', user, 'signcerts', 'cert.pem'); -const tlsCertPath = path.resolve(cryptoPath, 'crypto-config', 'peerOrganizations', 'org1.example.com', 'peers', 'peer0.org1.example.com', 'tls', 'ca.crt'); -const peerEndpoint = 'localhost:7051' +const certPath = path.resolve(__dirname, '..', '..', 'crypto-material', 'hsm', user, 'signcerts', 'cert.pem'); + +const tlsCertPath = path.resolve('..', '..','test-network','organizations','peerOrganizations', 'org1.example.com', 'peers', 'peer0.org1.example.com', 'tls', 'ca.crt'); +const peerEndpoint = 'localhost:7051'; async function main() { console.log('\nRunning the Node HSM sample'); @@ -43,7 +46,7 @@ async function main() { }); try { - await exampleSubmit(gateway); + await exampleTransaction(gateway); console.log(); console.log('Node HSM sample completed successfully'); } finally { @@ -55,22 +58,30 @@ async function main() { } } -async function exampleSubmit(gateway: Gateway) { +async function exampleTransaction(gateway: Gateway):Promise { const network = gateway.getNetwork('mychannel'); const contract = network.getContract('basic'); - const timestamp = new Date().toISOString(); - console.log('Submitting "put" transaction with arguments: time,', timestamp); + console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments'); - // Submit a transaction, blocking until the transaction has been committed on the ledger - const submitResult = await contract.submitTransaction('put', 'time', timestamp); + await contract.submitTransaction( + 'CreateAsset', + assetId, + 'yellow', + '5', + 'Tom', + '1300', + ); - console.log('Submit result:', submitResult.toString()); - console.log('Evaluating "get" query with arguments: time'); + console.log('*** Transaction committed successfully'); - const evaluateResult = await contract.evaluateTransaction('get', 'time'); + console.log('\n--> Evaluate Transaction: ReadAsset, function returns asset attributes'); - console.log('Query result:', evaluateResult.toString()); + const resultBytes = await contract.evaluateTransaction('ReadAsset', assetId); + + const resultJson = utf8Decoder.decode(resultBytes); + const result = JSON.parse(resultJson); + console.log('*** Result:', result); } async function newGrpcConnection(): Promise { diff --git a/hsm-samples-gateway/scripts/generate-hsm-user.sh b/hsm-samples-gateway/scripts/generate-hsm-user.sh index 3bcff2fd..0a7969b7 100755 --- a/hsm-samples-gateway/scripts/generate-hsm-user.sh +++ b/hsm-samples-gateway/scripts/generate-hsm-user.sh @@ -2,8 +2,9 @@ set -eo pipefail # define the CA setup -CA_HOST=127.0.0.1 +CA_HOST=localhost CA_URL=${CA_HOST}:7054 +TLS_CERT='../../test-network/organizations/fabric-ca/org1/tls-cert.pem' # try to locate the Soft HSM library POSSIBLE_LIB_LOC=('/usr/lib/softhsm/libsofthsm2.so' \ @@ -26,19 +27,22 @@ HSM2_CONF=$HOME/softhsm2.conf # Update the client config file to point to the softhsm pkcs11 library # which must be in $HOME/softhsm directory -CLIENT_CONFIG_TEMPLATE=./ca-client-config/fabric-ca-client-config-template.yaml -CLIENT_CONFIG=./ca-client-config/fabric-ca-client-config.yaml +echo 'directory' $PWD + +CLIENT_CONFIG_TEMPLATE=../ca-client-config/fabric-ca-client-config-template.yaml +CLIENT_CONFIG=../ca-client-config/fabric-ca-client-config.yaml cp $CLIENT_CONFIG_TEMPLATE $CLIENT_CONFIG -sed -i s+REPLACE_ME_HSMLIB+${HSM2_LIB}+g $CLIENT_CONFIG +sed -i '' -e s+REPLACE_ME_HSMLIB+${HSM2_LIB}+g $CLIENT_CONFIG # create the users, remove any existing users -CRYPTO_PATH=$PWD/crypto-material/hsm +CRYPTO_PATH=$PWD/../crypto-material/hsm [ -d $CRYPTO_PATH ] && rm -fr $CRYPTO_PATH # user passed in as parameter CAADMIN=admin CAADMIN_PW=adminpw HSMUSER=$1 -SOFTHSM2_CONF=$HSM2_CONF fabric-ca-client enroll -c $CLIENT_CONFIG -u http://$CAADMIN:$CAADMIN_PW@$CA_URL --mspdir $CRYPTO_PATH/$CAADMIN --csr.hosts example.com -! SOFTHSM2_CONF=$HSM2_CONF fabric-ca-client register -c $CLIENT_CONFIG --mspdir $CRYPTO_PATH/$CAADMIN --id.name $HSMUSER --id.secret $HSMUSER --id.type client --caname ca-org1 --id.maxenrollments 0 -m example.com -u http://$CA_URL && echo user probably already registered, continuing -SOFTHSM2_CONF=$HSM2_CONF fabric-ca-client enroll -c $CLIENT_CONFIG -u http://$HSMUSER:$HSMUSER@$CA_URL --mspdir $CRYPTO_PATH/$HSMUSER --csr.hosts example.com + +SOFTHSM2_CONF=$HSM2_CONF fabric-ca-client enroll -c $CLIENT_CONFIG -u https://$CAADMIN:$CAADMIN_PW@$CA_URL --mspdir $CRYPTO_PATH/$CAADMIN --csr.hosts example.com --tls.certfiles ${TLS_CERT} +! SOFTHSM2_CONF=$HSM2_CONF fabric-ca-client register -c $CLIENT_CONFIG --mspdir $CRYPTO_PATH/$CAADMIN --id.name $HSMUSER --id.secret $HSMUSER --id.type client --caname ca-org1 --id.maxenrollments 0 -m example.com -u https://$CA_URL --tls.certfiles ${TLS_CERT} && echo user probably already registered, continuing +SOFTHSM2_CONF=$HSM2_CONF fabric-ca-client enroll -c $CLIENT_CONFIG -u https://$HSMUSER:$HSMUSER@$CA_URL --mspdir $CRYPTO_PATH/$HSMUSER --csr.hosts example.com --tls.certfiles ${TLS_CERT}