Java对接Fabric-Smart-Node提供的grpc接口

Java对接Fabric-Smart-Node提供的grpc接口,第1张

fabric-smart-node(fsn)是基于faric的,通过虚拟orderer以及peer来使整体轻量化的fabric套件。目前这套组件均基于golang开发,java想要调用目前只能通过go开发的api---fabric-smart-client。

由此,java需要直接请求fsn,需要基于fsn提供的proto文件调用。这在技术上通过grpc调用本没有难度,但是因为fabric的调用涉及复杂的证书与签名,所以此文记录一下,避免重复踩坑。

涉及本次调用的proto如下:

service.proto

syntax = "proto3";

option go_package = "protos";
option cc_generic_services = true;
option java_package = "com.xxx.proto.java";
option java_generic_services = true;
option java_multiple_files = true;

package protos;

import "commands.proto";

// ViewService provides support to view management
service ViewService {
    // ProcessCommand processes the passed command ensuring proper access control.
    // The returned response allows the client to understand if the
    // operation was successfully executed and if not, the response
    // reports the reason of the failure.
    rpc ProcessCommand(SignedCommand) returns (SignedCommandResponse);

    rpc StreamCommand(SignedCommand) returns (stream SignedCommandResponse){};
}

2.command.proto

syntax = "proto3";

option go_package = "protos";
option cc_generic_services = true;
option java_package = "com.xxx.proto.java";
option java_generic_services = true;
option java_multiple_files = true;

package protos;

import "google/protobuf/timestamp.proto";
import "finality.proto";


// InitiateView is used to initiate a view
message InitiateView {
    string fid = 1;




    bytes input = 2;
}

message InitiateViewResponse {
    string cid = 1;
}

// InitiateView is used to initiate a view
message CallView {
    string fid = 1;

    bytes input = 2;
}

message CallViewResponse {
    bytes result = 1;
}

message TrackView {
    string cid = 1;
}

message TrackViewResponse {
    bytes payload = 1;
}


// Header is a generic replay prevention and identity message to include in a signed command
message Header {
    // Timestamp is the local time when the message was created
    // by the sender
    google.protobuf.Timestamp timestamp = 1;

    // Nonce is a sufficiently long random value
    // used to ensure the request has enough entropy.
    bytes nonce = 3;

    // Creator of the message.
    bytes creator = 4;

    // TlsCertHash represents the hash of the client's TLS certificate
    // when mutual TLS is enabled
    bytes tls_cert_hash = 5;
}

// Command describes the type of operation that a client is requesting.
message Command {
    // Header is the header of this command
    Header header = 1;

    // Payload is the payload of this command. It can assume one of the following value
    oneof payload {
        InitiateView initiateView = 2;
        TrackView trackView = 3;
        CallView callView = 4;
        IsTxFinal isTxFinal = 5;
    }
}

// SignedCommand is a command that carries the signature of the command's creator.
message SignedCommand {
    // Command is the serialised version of a Command message
    bytes command = 1;

    // Signature is the signature over command
    bytes signature = 2;
}

message CommandResponseHeader {
    // Timestamp is the time that the message
    // was created as  defined by the sender
    google.protobuf.Timestamp timestamp = 1;

    // CommandHash is the hash computed on the concatenation of the SignedCommand's command and signature fields.
    // If not specified differently, SHA256 is used
    // The hash is used to link a response with its request, both for bookeeping purposes on an
    // asynchronous system and for security reasons (accountability, non-repudiation)
    bytes command_hash = 2;

    // Creator is the identity of the party creating this message
    bytes creator = 3;
}

// Error reports an application error
message Error {
    // Message associated with this response.
    string message = 1;

    // Payload that can be used to include metadata with this response.
    bytes payload = 2;
}

// A CommnandResponse is returned from a server to the command submitter.
message CommandResponse {
    // Header of the response.
    CommandResponseHeader header = 1;

    // Payload of the response.
    oneof payload {
        Error err = 2;
        InitiateViewResponse initiateViewResponse = 3;
        TrackViewResponse trackViewResponse = 4;
        CallViewResponse callViewResponse = 5;
        IsTxFinalResponse isTxFinalResponse = 6;
    }
}

// SignedCommandResponse is a signed command response
message SignedCommandResponse {
    // Response is the serialised version of a CommandResponse message
    bytes response = 1;

    // Signature is the signature over command
    bytes signature = 2;
}

3.finality.proto


syntax = "proto3";

option go_package = "protos";
option cc_generic_services = true;

option java_package = "com.xxx.proto.java";
option java_generic_services = true;
option java_multiple_files = true;

package protos;

message IsTxFinal {
    string network = 1;
    string channel = 2;
    string txid = 3;
}

message IsTxFinalResponse {
    bytes payload = 1;
}

根据以上proto文件可生成一下java文件

在这里我用

rpc ProcessCommand(SignedCommand) returns (SignedCommandResponse) 来举个例子

 代码如下:

package com.xxx.proto.java;

import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.ByteString;
import com.google.protobuf.Timestamp;
import com.rongzer.rbaas.platform.server.fabric.sdkintegration.SampleUser;
import io.grpc.ManagedChannel;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;


import org.hyperledger.fabric.sdk.identity.SigningIdentity;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;
import org.hyperledger.fabric.sdk.identity.X509SigningIdentity;
import org.hyperledger.fabric.sdk.security.CryptoSuite;

import javax.net.ssl.SSLException;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.UUID;

public class Test {

    public static void main(String[] args) throws InterruptedException, IOException, IllegalAccessException, InvocationTargetException, InvalidArgumentException, InstantiationException, NoSuchMethodException, CryptoException, ClassNotFoundException {

       

        //该文件是approver的cert.pem证书文件,需要自己在已部署的fsn服务中找到并且换掉
        String identityPath = "/fsc/crypto/peerOrganizations/fsc.example.com/peers/approver.fsc.example.com/msp/signcerts/approver.fsc.example.com-cert.pem";
        //该文件是approver的私钥文件,需要自己在已部署的fsn服务中找到并且换掉
        String privPath = "/fsc/crypto/peerOrganizations/fsc.example.com/peers/approver.fsc.example.com/msp/keystore/priv_sk";
        //该文件是approver的ca文件,需要自己在已部署的fsn服务中找到并且换掉
        String caPath = "/fsc/crypto/peerOrganizations/fsc.example.com/peers/approver.fsc.example.com/tls/ca.crt";
        File ca = new File(caPath);

        //File ca = new File(identityPath);

        String fid = "funtionName";//需要调用的方法,由fsn服务端提供


        JSONObject input = new JSONObject();
        input.put("param","paramValue");//需要调用的方法所需的参数

        CallView callView = CallView.newBuilder()
                .setFid(fid)
                .setInput(ByteString.copyFrom(input.toJSONString().getBytes(StandardCharsets.UTF_8)))
                .build();


        Header header = Header.newBuilder()
                .setTimestamp(Timestamp.getDefaultInstance())
                .setNonce(ByteString.copyFrom(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)))
                .setCreator(ByteString.readFrom(new FileInputStream(identityPath)))
                .setTlsCertHash(com.google.protobuf.ByteString.EMPTY)
                .build();

        Command command = Command.newBuilder()
                .setCallView(callView)
                .setHeader(header)
                .build();

        StringBuilder keyStr = new StringBuilder();
            try (BufferedReader br = Files.newBufferedReader(Paths.get(privPath))){
                String line ;
                while ((line = br.readLine()) != null) {
                    keyStr.append(line).append("\n");;
                }
            }catch (Exception e){
                throw e;
            }
            PrivateKey privateKey;
            String keyString = keyStr.toString();
            try (PEMParser parser = new PEMParser(new StringReader(keyString))) {
                Object key = parser.readObject();
                JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
                privateKey = converter.getPrivateKey((PrivateKeyInfo) key);
            }catch (Exception e){
                throw e;
            }

            SignedCommand signedCommand = SignedCommand.newBuilder()
                    .setCommand(command.toByteString())
                    .setSignature(ByteString.copyFrom(CryptoSuite.Factory.getCryptoSuite().sign(privateKey,command.toByteArray())))
                    .build();
        String target = "ip:port";


        SslContextBuilder builder = GrpcSslContexts.forClient();
        SslContext sslContext = builder.trustManager(ca).build();

        ManagedChannel channel = NettyChannelBuilder.forTarget(target)
                //.usePlaintext()
                .overrideAuthority("approver.fsc.example.com")//这里需要填写approver的server.crt中可使用者的DNS NAME
                .negotiationType(NegotiationType.TLS)
                .sslContext(sslContext)
                .build();

        ViewServiceGrpc.ViewServiceBlockingStub stub = ViewServiceGrpc.newBlockingStub(channel);

        SignedCommandResponse res = stub.processCommand(signedCommand);

        String cr = CommandResponse.parseFrom(res.getResponse()).getCallViewResponse().getResult().toStringUtf8();
        System.out.println(cr);



    }

    private static SslContext buildSslContext(String trustCertCollectionFilePath,
                                              String clientCertChainFilePath,
                                              String clientPrivateKeyFilePath) throws SSLException {
        SslContextBuilder builder = GrpcSslContexts.forClient();
        if (trustCertCollectionFilePath != null) {
            builder.trustManager(new File(trustCertCollectionFilePath));
        }
        if (clientCertChainFilePath != null && clientPrivateKeyFilePath != null) {
            builder.keyManager(new File(clientCertChainFilePath), new File(clientPrivateKeyFilePath));
        }
        return builder.build();
    }

}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/2992177.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-23
下一篇 2022-09-23

发表评论

登录后才能评论

评论列表(0条)

保存