mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
For some signing implementations, such as ed25519, a non-default hash implementation must be specified when creating the Gateway connection in client applications. Rather than relying on the default hash algorithm, it is probably good practice in general to specify an algorithm that is compatible with your signing implementation. This change explicitly specifies the hash algorithm to raise visibility of the option to select the hash algorithm. Signed-off-by: Mark S. Lewis <Mark.S.Lewis@outlook.com>
217 lines
5.7 KiB
Go
217 lines
5.7 KiB
Go
//go:build pkcs11
|
|
// +build pkcs11
|
|
|
|
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"os"
|
|
|
|
"crypto/x509"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
|
"github.com/hyperledger/fabric-gateway/pkg/hash"
|
|
"github.com/hyperledger/fabric-gateway/pkg/identity"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
)
|
|
|
|
const (
|
|
mspID = "Org1MSP"
|
|
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 = "dns:///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")
|
|
|
|
// 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 := os.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.WithHash(hash.SHA256),
|
|
client.WithClientConnection(clientConnection))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer gateway.Close()
|
|
|
|
exampleTransaction(gateway)
|
|
fmt.Println()
|
|
fmt.Println("Go HSM Sample completed successfully")
|
|
fmt.Println()
|
|
}
|
|
|
|
func exampleTransaction(gateway *client.Gateway) {
|
|
|
|
// Override default values for chaincode and channel name as they may differ in testing contexts.
|
|
channelName := "mychannel"
|
|
if cname := os.Getenv("CHANNEL_NAME"); cname != "" {
|
|
channelName = cname
|
|
}
|
|
|
|
chaincodeName := "basic"
|
|
if ccname := os.Getenv("CHAINCODE_NAME"); ccname != "" {
|
|
chaincodeName = ccname
|
|
}
|
|
|
|
network := gateway.GetNetwork(channelName)
|
|
contract := network.GetContract(chaincodeName)
|
|
|
|
fmt.Printf("Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments \n")
|
|
|
|
_, err := contract.SubmitTransaction("CreateAsset", assetId, "yellow", "5", "Tom", "1300")
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to submit transaction: %w", err))
|
|
}
|
|
|
|
fmt.Printf("*** Transaction committed successfully\n")
|
|
|
|
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("*** Result:%s\n", result)
|
|
}
|
|
|
|
// 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.NewClient(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 := os.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",
|
|
"/opt/homebrew/lib/softhsm/libsofthsm2.so",
|
|
}
|
|
pkcs11lib := os.Getenv("PKCS11_LIB")
|
|
if pkcs11lib != "" {
|
|
libraryLocations = append(libraryLocations, pkcs11lib)
|
|
}
|
|
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")
|
|
}
|
|
|
|
// 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()
|
|
}
|