Update TypeScript HSM sample application

- Update to Node 16.
- Simplify SKI code using Node standard packages and avoiding use of jsrsasign.

Signed-off-by: Mark S. Lewis <Mark.S.Lewis@outlook.com>
This commit is contained in:
Mark S. Lewis 2023-09-07 17:13:39 +01:00 committed by Dave Enyeart
parent f2c1c59c50
commit 30f70f6e73
3 changed files with 31 additions and 31 deletions

View file

@ -4,7 +4,7 @@
"description": "", "description": "",
"main": "dist/hsm-sample.js", "main": "dist/hsm-sample.js",
"engines": { "engines": {
"node": ">=14" "node": ">=16"
}, },
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
@ -17,18 +17,16 @@
"author": "", "author": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@hyperledger/fabric-gateway": "^1.1.1", "@hyperledger/fabric-gateway": "^1.1.1"
"jsrsasign": "^10.3.0"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node14": "^1.0.1", "@tsconfig/node16": "^16.1.1",
"@types/jsrsasign": "^9.0.3", "@types/node": "^16.18.48",
"@types/node": "^14.17.32", "@typescript-eslint/eslint-plugin": "^6.6.0",
"@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^6.6.0",
"@typescript-eslint/parser": "^5.3.0",
"eslint": "^8.1.0", "eslint": "^8.1.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"rimraf": "^3.0.2", "rimraf": "^5.0.1",
"typescript": "~4.5.4" "typescript": "~5.2.2"
} }
} }

View file

@ -5,10 +5,9 @@
*/ */
import * as grpc from '@grpc/grpc-js'; import * as grpc from '@grpc/grpc-js';
import * as crypto from 'crypto';
import { connect, Gateway, HSMSigner, HSMSignerFactory, HSMSignerOptions, signers } from '@hyperledger/fabric-gateway'; import { connect, Gateway, HSMSigner, HSMSignerFactory, HSMSignerOptions, signers } from '@hyperledger/fabric-gateway';
import * as crypto from 'crypto';
import * as fs from 'fs'; import * as fs from 'fs';
import * as jsrsa from 'jsrsasign';
import * as path from 'path'; import * as path from 'path';
import { TextDecoder } from 'util'; import { TextDecoder } from 'util';
@ -42,7 +41,7 @@ async function main() {
// Get the signer function and a close function. The close function closes the signer // Get the signer function and a close function. The close function closes the signer
// once there is no further need for it. // once there is no further need for it.
hsmSigner = await newHSMSigner(hsmSignerFactory, credentials.toString()); hsmSigner = newHSMSigner(hsmSignerFactory, credentials);
gateway = connect({ gateway = connect({
client, client,
identity: { mspId, credentials }, identity: { mspId, credentials },
@ -100,8 +99,9 @@ async function newGrpcConnection(): Promise<grpc.Client> {
} }
// Create a new HSM Signer // Create a new HSM Signer
async function newHSMSigner(hsmSignerFactory: HSMSignerFactory, certificatePEM: string): Promise<HSMSigner> { function newHSMSigner(hsmSignerFactory: HSMSignerFactory, certificatePEM: Buffer): HSMSigner {
const ski = getSKIFromCertificate(certificatePEM); const certificate = new crypto.X509Certificate(certificatePEM);
const ski = getSKIFromCertificate(certificate);
// Options for the signer based on using SoftHSM with Token initialized as follows // 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 // softhsm2-util --init-token --slot 0 --label "ForFabric" --pin 98765432 --so-pin 1234
@ -139,23 +139,25 @@ function findSoftHSMPKCS11Lib(): string {
// fabric-ca-client set's the CKA_ID of the public/private keys in the HSM to a generated SKI // 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 // value. This function replicates that calculation from a certificate PEM so that the HSM
// object associated with the certificate can be found // object associated with the certificate can be found
function getSKIFromCertificate(certificatePEM: string): Buffer { function getSKIFromCertificate(certificate: crypto.X509Certificate): Buffer {
const key = jsrsa.KEYUTIL.getKey(certificatePEM); const uncompressedPoint = getUncompressedPointOnCurve(certificate.publicKey);
const uncompressedPoint = getUncompressedPointOnCurve(key as jsrsa.KJUR.crypto.ECDSA); return crypto.createHash('sha256').update(uncompressedPoint).digest();
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 { function getUncompressedPointOnCurve(key: crypto.KeyObject): Buffer {
const xyhex = key.getPublicKeyXYHex(); const jwk = key.export({ format: 'jwk' });
const xBuffer = Buffer.from(xyhex.x, 'hex'); const x = Buffer.from(assertDefined(jwk.x), 'base64url');
const yBuffer = Buffer.from(xyhex.y, 'hex'); const y = Buffer.from(assertDefined(jwk.y), 'base64url');
const uncompressedPrefix = Buffer.from('04', 'hex'); const prefix = Buffer.from('04', 'hex');
const uncompressedPoint = Buffer.concat([uncompressedPrefix, xBuffer, yBuffer]); return Buffer.concat([prefix, x, y]);
return uncompressedPoint; }
function assertDefined<T>(value: T | undefined): T {
if (value === undefined) {
throw new Error('required value was undefined');
}
return value;
} }
/** /**

View file

@ -1,6 +1,6 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node14/tsconfig.json", "extends": "@tsconfig/node16/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,