mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-25 11:05:10 +00:00
Added hsmm Samples using Gateway
Signed-off-by: sapthasurendran <saptha.surendran@ibm.com>
This commit is contained in:
parent
8ca50df4ff
commit
ce8d1ddc5b
8 changed files with 556 additions and 0 deletions
100
hsm-samples-gateway/README.md
Normal file
100
hsm-samples-gateway/README.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
# Fabric Gateway HSM Samples
|
||||||
|
|
||||||
|
The samples show how to create client applications that invoke transactions with HSM Identities using the
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: Cannot find module 'pkcs11js'
|
||||||
|
```
|
||||||
|
|
||||||
|
how to install the required C Compilers and Python will depend on your operating system and version.
|
||||||
|
|
||||||
|
## Install SoftHSM
|
||||||
|
|
||||||
|
In order to run the application in the absence of a real HSM, a software
|
||||||
|
emulator of the PKCS#11 interface is required.
|
||||||
|
For more information please refer to [SoftHSM](https://www.opendnssec.org/softhsm/).
|
||||||
|
|
||||||
|
SoftHSM can either be installed using the package manager for your host system:
|
||||||
|
|
||||||
|
* Ubuntu: `sudo apt install softhsm2`
|
||||||
|
* macOS: `brew install softhsm`
|
||||||
|
* Windows: **unsupported**
|
||||||
|
|
||||||
|
Or compiled and installed from source:
|
||||||
|
|
||||||
|
1. install openssl 1.0.0+ or botan 1.10.0+
|
||||||
|
2. download the source code from <https://dist.opendnssec.org/source/softhsm-2.5.0.tar.gz>
|
||||||
|
3. `tar -xvf softhsm-2.5.0.tar.gz`
|
||||||
|
4. `cd softhsm-2.5.0`
|
||||||
|
5. `./configure --disable-gost` (would require additional libraries, turn it off unless you need 'gost' algorithm support for the Russian market)
|
||||||
|
6. `make`
|
||||||
|
7. `sudo make install`
|
||||||
|
|
||||||
|
## Initialize a token to store keys in SoftHSM
|
||||||
|
|
||||||
|
If you have not initialized a token previously (or it has been deleted) then you will need to perform this one time operation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo directories.tokendir = /tmp > $HOME/softhsm2.conf
|
||||||
|
export SOFTHSM2_CONF=$HOME/softhsm2.conf
|
||||||
|
softhsm2-util --init-token --slot 0 --label "ForFabric" --pin 98765432 --so-pin 1234
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a SoftHSM configuration file called `softhsm2.conf` and will be stored in your home directory. This is
|
||||||
|
where the sample expects to find a SoftHSM configuration file
|
||||||
|
|
||||||
|
The Security Officer PIN, specified with the `--so-pin` flag, can be used to re-initialize the token,
|
||||||
|
and the user PIN (see below), specified with the `--pin` flag, is used by applications to access the token for
|
||||||
|
generating and retrieving keys.
|
||||||
|
|
||||||
|
## Install PKCS#11 enabled fabric-ca-client binary
|
||||||
|
To be able to register and enroll identities using an HSM you need a PKCS#11 enabled version of `fabric-ca-client`
|
||||||
|
To install this use the following command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get -tags 'pkcs11' github.com/hyperledger/fabric-ca/cmd/fabric-ca-client
|
||||||
|
```
|
||||||
|
## Enroll the HSM User
|
||||||
|
|
||||||
|
A user, `HSMUser`, who is HSM managed needs to be registered then enrolled for the sample
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd scripts
|
||||||
|
./generate-hsm-user.sh HSMUser
|
||||||
|
```
|
||||||
|
|
||||||
|
This will register a user `HSMUser` with the CA in Org1 (if not already registered) and then enroll that user which will
|
||||||
|
generate a certificate on the file system for use by the sample. The private key is stored in SoftHSM
|
||||||
|
|
||||||
|
### Go SDK
|
||||||
|
|
||||||
|
For HSM support you need to ensure you include the `pkcs11` build tag.
|
||||||
|
|
||||||
|
```
|
||||||
|
cd hsm-samples-gateway/go
|
||||||
|
go run -tags pkcs11 hsm-sample.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node SDK
|
||||||
|
|
||||||
|
```
|
||||||
|
cd hsm-samples-gateway/node
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
When you are finished running the samples, the local docker network can be brought down with the following command:
|
||||||
|
|
||||||
|
`docker rm -f $(docker ps -aq) && docker network prune --force`
|
||||||
187
hsm-samples-gateway/go/hsm-sample.go
Normal file
187
hsm-samples-gateway/go/hsm-sample.go
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
//go:build pkcs11
|
||||||
|
// +build pkcs11
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright IBM Corp. All Rights Reserved.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/identity"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
peerEndpoint = "localhost:7051"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Running the GO HSM Sample")
|
||||||
|
|
||||||
|
// The gRPC client connection should be shared by all Gateway connections to this endpoint
|
||||||
|
clientConnection := newGrpcConnection()
|
||||||
|
defer clientConnection.Close()
|
||||||
|
|
||||||
|
hsmSignerFactory, err := identity.NewHSMSignerFactory(findSoftHSMLibrary())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer hsmSignerFactory.Dispose()
|
||||||
|
|
||||||
|
certificatePEM, err := ioutil.ReadFile(certPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
id := newIdentity(certificatePEM)
|
||||||
|
ski := getSKI(certificatePEM)
|
||||||
|
hsmSign, hsmSignClose := newHSMSign(hsmSignerFactory, ski)
|
||||||
|
defer hsmSignClose()
|
||||||
|
|
||||||
|
// Create a Gateway connection for a specific client identity
|
||||||
|
gateway, err := client.Connect(id, client.WithSign(hsmSign), client.WithClientConnection(clientConnection))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer gateway.Close()
|
||||||
|
|
||||||
|
exampleSubmit(gateway)
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Go HSM Sample Completed Successfully")
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
func exampleSubmit(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)
|
||||||
|
|
||||||
|
// Submit transaction, blocking until the transaction has been committed on the ledger
|
||||||
|
submitResult, err := contract.SubmitTransaction("put", "time", timestamp)
|
||||||
|
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")
|
||||||
|
|
||||||
|
evaluateResult, err := contract.EvaluateTransaction("get", "time")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to evaluate transaction: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Query result = %s\n", string(evaluateResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGrpcConnection creates a gRPC connection to the Gateway server.
|
||||||
|
func newGrpcConnection() *grpc.ClientConn {
|
||||||
|
certificate, err := loadCertificate(tlsCertPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to obtain commit status: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
certPool.AddCert(certificate)
|
||||||
|
transportCredentials := credentials.NewClientTLSFromCert(certPool, "peer0.org1.example.com")
|
||||||
|
|
||||||
|
connection, err := grpc.Dial(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to evaluate transaction: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
|
||||||
|
// newIdentity creates a client identity for this Gateway connection using an X.509 certificate.
|
||||||
|
func newIdentity(certificatePEM []byte) *identity.X509Identity {
|
||||||
|
cert, err := identity.CertificateFromPEM(certificatePEM)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
id, err := identity.NewX509Identity(mspID, cert)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHSMSign creates a function that generates a digital signature from a message digest using a private key.
|
||||||
|
func newHSMSign(h *identity.HSMSignerFactory, certPEM []byte) (identity.Sign, identity.HSMSignClose) {
|
||||||
|
opt := identity.HSMSignerOptions{
|
||||||
|
Label: "ForFabric",
|
||||||
|
Pin: "98765432",
|
||||||
|
Identifier: string(certPEM),
|
||||||
|
}
|
||||||
|
|
||||||
|
sign, close, err := h.NewHSMSigner(opt)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sign, close
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadCertificate(filename string) (*x509.Certificate, error) {
|
||||||
|
certificatePEM, err := ioutil.ReadFile(filename) //#nosec G304
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return identity.CertificateFromPEM(certificatePEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSKI(certPEM []byte) []byte {
|
||||||
|
block, _ := pem.Decode(certPEM)
|
||||||
|
|
||||||
|
x590cert, _ := x509.ParseCertificate(block.Bytes)
|
||||||
|
pk := x590cert.PublicKey
|
||||||
|
|
||||||
|
return skiForKey(pk.(*ecdsa.PublicKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
func skiForKey(pk *ecdsa.PublicKey) []byte {
|
||||||
|
ski := sha256.Sum256(elliptic.Marshal(pk.Curve, pk.X, pk.Y))
|
||||||
|
return ski[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func findSoftHSMLibrary() string {
|
||||||
|
|
||||||
|
libraryLocations := []string{
|
||||||
|
"/usr/lib/softhsm/libsofthsm2.so",
|
||||||
|
"/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so",
|
||||||
|
"/usr/local/lib/softhsm/libsofthsm2.so",
|
||||||
|
"/usr/lib/libacsp-pkcs11.so",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, libraryLocation := range libraryLocations {
|
||||||
|
if _, err := os.Stat(libraryLocation); !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return libraryLocation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("No SoftHSM library can be found. The Sample requires SoftHSM to be installed")
|
||||||
|
}
|
||||||
29
hsm-samples-gateway/node/.eslintrc.yaml
Normal file
29
hsm-samples-gateway/node/.eslintrc.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
env:
|
||||||
|
node: true
|
||||||
|
es2020: true
|
||||||
|
root: true
|
||||||
|
ignorePatterns:
|
||||||
|
- dist/
|
||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
rules:
|
||||||
|
indent:
|
||||||
|
- error
|
||||||
|
- 4
|
||||||
|
quotes:
|
||||||
|
- error
|
||||||
|
- single
|
||||||
|
overrides:
|
||||||
|
- files:
|
||||||
|
- "**/*.ts"
|
||||||
|
parser: "@typescript-eslint/parser"
|
||||||
|
parserOptions:
|
||||||
|
sourceType: module
|
||||||
|
ecmaFeatures:
|
||||||
|
impliedStrict: true
|
||||||
|
plugins:
|
||||||
|
- "@typescript-eslint"
|
||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
- plugin:@typescript-eslint/eslint-recommended
|
||||||
|
- plugin:@typescript-eslint/recommended
|
||||||
3
hsm-samples-gateway/node/.gitignore
vendored
Normal file
3
hsm-samples-gateway/node/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
35
hsm-samples-gateway/node/package.json
Normal file
35
hsm-samples-gateway/node/package.json
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "gateway-hsm-sample",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "dist/hsm-sample.js",
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.15.0 || ^16.13.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "npm-run-all clean compile lint",
|
||||||
|
"clean": "rimraf dist",
|
||||||
|
"compile": "tsc",
|
||||||
|
"lint": "eslint . --ext .ts",
|
||||||
|
"start": "SOFTHSM2_CONF=${HOME}/softhsm2.conf node dist/hsm-sample.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@grpc/grpc-js": "^1.3.0",
|
||||||
|
"@hyperledger/fabric-gateway": "file:../../node/fabric-gateway-dev.tgz",
|
||||||
|
"jsrsasign": "^10.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/node14": "^1.0.1",
|
||||||
|
"@types/jsrsasign": "^9.0.3",
|
||||||
|
"@types/node": "^14.17.32",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||||
|
"@typescript-eslint/parser": "^5.3.0",
|
||||||
|
"eslint": "^8.1.0",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"typescript": "~4.5.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
140
hsm-samples-gateway/node/src/hsm-sample.ts
Normal file
140
hsm-samples-gateway/node/src/hsm-sample.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as grpc from '@grpc/grpc-js';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import { connect, Gateway, HSMSigner, HSMSignerFactory, HSMSignerOptions, signers } from '@hyperledger/fabric-gateway';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as jsrsa from 'jsrsasign';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
const mspId = 'Org1MSP';
|
||||||
|
const user = 'HSMUser';
|
||||||
|
|
||||||
|
// 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'
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('\nRunning the Node HSM sample');
|
||||||
|
|
||||||
|
// The gRPC client connection should be shared by all Gateway connections to this endpoint
|
||||||
|
const client = await newGrpcConnection();
|
||||||
|
|
||||||
|
// get an HSMSigner Factory. You only need to do this once for the application
|
||||||
|
const hsmSignerFactory = signers.newHSMSignerFactory(findSoftHSMPKCS11Lib());
|
||||||
|
const credentials = await fs.promises.readFile(certPath);
|
||||||
|
|
||||||
|
// Get the signer function and a close function. The close function closes the signer
|
||||||
|
// once there is no further need for it.
|
||||||
|
const {signer, close} = await newHSMSigner(hsmSignerFactory, credentials.toString());
|
||||||
|
|
||||||
|
const gateway = connect({
|
||||||
|
client,
|
||||||
|
identity: {mspId, credentials},
|
||||||
|
signer,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await exampleSubmit(gateway);
|
||||||
|
console.log();
|
||||||
|
console.log('Node HSM sample completed successfully');
|
||||||
|
} finally {
|
||||||
|
gateway.close();
|
||||||
|
client.close();
|
||||||
|
|
||||||
|
// close the HSM Signer
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exampleSubmit(gateway: Gateway) {
|
||||||
|
const network = gateway.getNetwork('mychannel');
|
||||||
|
const contract = network.getContract('basic');
|
||||||
|
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
console.log('Submitting "put" transaction with arguments: time,', timestamp);
|
||||||
|
|
||||||
|
// Submit a transaction, blocking until the transaction has been committed on the ledger
|
||||||
|
const submitResult = await contract.submitTransaction('put', 'time', timestamp);
|
||||||
|
|
||||||
|
console.log('Submit result:', submitResult.toString());
|
||||||
|
console.log('Evaluating "get" query with arguments: time');
|
||||||
|
|
||||||
|
const evaluateResult = await contract.evaluateTransaction('get', 'time');
|
||||||
|
|
||||||
|
console.log('Query result:', evaluateResult.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function newGrpcConnection(): Promise<grpc.Client> {
|
||||||
|
const tlsRootCert = await fs.promises.readFile(tlsCertPath);
|
||||||
|
const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);
|
||||||
|
|
||||||
|
return new grpc.Client(peerEndpoint, tlsCredentials, {
|
||||||
|
'grpc.ssl_target_name_override': 'peer0.org1.example.com'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new HSM Signer
|
||||||
|
async function newHSMSigner(hsmSignerFactory: HSMSignerFactory, certificatePEM: string): Promise<HSMSigner> {
|
||||||
|
const ski = getSKIFromCertificate(certificatePEM);
|
||||||
|
|
||||||
|
// Options for the signer based on using SoftHSM with Token initialized as follows
|
||||||
|
// softhsm2-util --init-token --slot 0 --label "ForFabric" --pin 98765432 --so-pin 1234
|
||||||
|
const hsmSignerOptions: HSMSignerOptions = {
|
||||||
|
label: 'ForFabric',
|
||||||
|
pin: '98765432',
|
||||||
|
identifier: ski
|
||||||
|
}
|
||||||
|
return hsmSignerFactory.newSigner(hsmSignerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility to find the SoftHSM PKCS11 library as it's location can vary based on
|
||||||
|
// operating system and version
|
||||||
|
function findSoftHSMPKCS11Lib(): string {
|
||||||
|
const commonSoftHSMPathNames = [
|
||||||
|
'/usr/lib/softhsm/libsofthsm2.so',
|
||||||
|
'/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so',
|
||||||
|
'/usr/local/lib/softhsm/libsofthsm2.so',
|
||||||
|
'/usr/lib/libacsp-pkcs11.so',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pathnameToTry of commonSoftHSMPathNames) {
|
||||||
|
if (fs.existsSync(pathnameToTry)) {
|
||||||
|
return pathnameToTry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unable to find PKCS11 library')
|
||||||
|
}
|
||||||
|
|
||||||
|
// fabric-ca-client set's the CKA_ID of the public/private keys in the HSM to a generated SKI
|
||||||
|
// value. This function replicates that calculation from a certificate PEM so that the HSM
|
||||||
|
// object associated with the certificate can be found
|
||||||
|
function getSKIFromCertificate(certificatePEM: string): Buffer {
|
||||||
|
const key = jsrsa.KEYUTIL.getKey(certificatePEM);
|
||||||
|
const uncompressedPoint = getUncompressedPointOnCurve(key as jsrsa.KJUR.crypto.ECDSA);
|
||||||
|
const hashBuffer = crypto.createHash('sha256');
|
||||||
|
hashBuffer.update(uncompressedPoint);
|
||||||
|
|
||||||
|
const digest = hashBuffer.digest('hex');
|
||||||
|
return Buffer.from(digest, 'hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUncompressedPointOnCurve(key: jsrsa.KJUR.crypto.ECDSA): Buffer {
|
||||||
|
const xyhex = key.getPublicKeyXYHex();
|
||||||
|
const xBuffer = Buffer.from(xyhex.x, 'hex');
|
||||||
|
const yBuffer = Buffer.from(xyhex.y, 'hex');
|
||||||
|
const uncompressedPrefix = Buffer.from('04', 'hex');
|
||||||
|
const uncompressedPoint = Buffer.concat([uncompressedPrefix, xBuffer, yBuffer]);
|
||||||
|
return uncompressedPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
18
hsm-samples-gateway/node/tsconfig.json
Normal file
18
hsm-samples-gateway/node/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "@tsconfig/node14/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/"
|
||||||
|
]
|
||||||
|
}
|
||||||
44
hsm-samples-gateway/scripts/generate-hsm-user.sh
Executable file
44
hsm-samples-gateway/scripts/generate-hsm-user.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
# define the CA setup
|
||||||
|
CA_HOST=127.0.0.1
|
||||||
|
CA_URL=${CA_HOST}:7054
|
||||||
|
|
||||||
|
# try to locate the Soft HSM library
|
||||||
|
POSSIBLE_LIB_LOC=('/usr/lib/softhsm/libsofthsm2.so' \
|
||||||
|
'/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so' \
|
||||||
|
'/usr/local/lib/softhsm/libsofthsm2.so' \
|
||||||
|
'/usr/lib/libacsp-pkcs11.so'
|
||||||
|
)
|
||||||
|
for TEST_LIB in "${POSSIBLE_LIB_LOC[@]}"
|
||||||
|
do
|
||||||
|
if [ -f $TEST_LIB ]; then
|
||||||
|
HSM2_LIB=$TEST_LIB
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ -z $HSM2_LIB ] && echo No SoftHSM PKCS11 Library found, ensure you have installed softhsm2 && exit 1
|
||||||
|
|
||||||
|
# create a softhsm2.conf file if one doesn't exist
|
||||||
|
HSM2_CONF=$HOME/softhsm2.conf
|
||||||
|
[ ! -f $HSM2_CONF ] && echo directories.tokendir = /tmp > $HSM2_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
|
||||||
|
cp $CLIENT_CONFIG_TEMPLATE $CLIENT_CONFIG
|
||||||
|
sed -i s+REPLACE_ME_HSMLIB+${HSM2_LIB}+g $CLIENT_CONFIG
|
||||||
|
|
||||||
|
# create the users, remove any existing users
|
||||||
|
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
|
||||||
Loading…
Reference in a new issue