使用 GRPC Java 客户端和 NodeJS 服务器调试 ssl 连接问题

debug ssl connection issues using GRPC Java client and NodeJS server

我正在尝试在 mac OS 上使用 GRPC 将 Java 客户端连接到 NodeJS 服务器。尽管我可以使用相同的证书将示例 JS 客户端连接到 NodeJS 服务器,但我一直遇到 ssl 握手问题。关于如何进一步调试的任何想法:

服务器日志:

chttp2_server.c:123] Handshaking failed: {"created":"@1489747510.536841000","description":"Handshake read failed","file":"../src/core/lib/security/transport/security_handshaker.c","file_line":238,"referenced_errors":[{"created":"@1489747510.536836000","description":"Socket closed","fd":27,"file":"../src/core/lib/iomgr/tcp_posix.c","file_line":249,"target_address":"ipv4:127.0.0.1:61964"}]}

客户端

public class Connection implements IConnection {

    private static final Logger log = LogManager.getLogger(Connection.class.getName());
    private final String host;
    private final int port;

    public Connection(String host, int port) {
        this.host = host;
        this.port = port;
    }

    /*public ManagedChannelBuilder getInsecure() {
        return ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext(true);
    }*/

    public ManagedChannelBuilder getSecure() {
        ManagedChannelBuilder<?> channelBuilder = null;
        Optional<SslContext> optional = getSslContext();
        if (optional.isPresent()) {
            final SslContext sslContext = optional.get();
            log.info("building channel for connection");
            channelBuilder = NettyChannelBuilder.forAddress(host, port)
                    .overrideAuthority("localhost")
                    .negotiationType(NegotiationType.TLS)
                    .usePlaintext(false)
                    .sslContext(sslContext);
        }
        return channelBuilder;
    }

    private Optional<SslContext> getSslContext() {
        SslContext sslContext = null;
        Optional<ICertificateRepository> optional = getCertificates();
        if (optional.isPresent()) {
            final ICertificateRepository certificateRepo = optional.get();
            final File publicCert = certificateRepo.getPublicCert();
            final File clientCert = certificateRepo.getClientCert();
            final File clientKey = certificateRepo.getClientKey();
            try {
                java.security.Security.addProvider(
                        new org.bouncycastle.jce.provider.BouncyCastleProvider()
                );
                log.info("attempting to create the ssl context");
                sslContext = GrpcSslContexts.forClient()
                        .startTls(true)
                        .sslProvider(defaultSslProvider())
                        .trustManager(publicCert)
                        .keyManager(clientCert, clientKey)
                        .ciphers(null)  //testing
                        .build();
            } catch (SSLException se) {
                log.error("ssl exception before connection attempt {}", se);
            }
        }
        Optional<SslContext> sslOptional = Optional.ofNullable(sslContext);
        return sslOptional;
    }

    private Optional<ICertificateRepository> getCertificates() {
        ICertificateRepository certificateRepo = null;
        try {
            certificateRepo = new CertificateRepository();
            log.info("path: {} | {} | {}", certificateRepo.getPublicCert().getAbsolutePath(),
                    certificateRepo.getPublicCert().exists(), certificateRepo.getPublicCert().isFile());
            log.info("clientCert: {} | {}", certificateRepo.getClientCert().getAbsolutePath(),
                    certificateRepo.getClientCert().exists());
            log.info("clientKey: {} | {}", certificateRepo.getClientKey().getAbsolutePath(),
                    certificateRepo.getClientKey().exists());
        } catch (Exception fe) {
            log.error("unable to read SSL certificates in keys directory");
        }
        Optional<ICertificateRepository> optional = Optional.ofNullable(certificateRepo);
        return optional;
    }

    private static SslProvider defaultSslProvider() {
        log.info("is OpenSsl available: {}", OpenSsl.isAvailable());
        return OpenSsl.isAvailable() ? SslProvider.OPENSSL : SslProvider.JDK;
    }
}

证书文件位置正确,证书存储库创建为:

 public CertificateRepository() {
        final ClassLoader classLoader = getClass().getClassLoader();
        try {
            this.publicCert = new File(classLoader.getResource(
                    new StringBuilder(MonetagoProps.BASE_DIR_FOR_CERTS)
                            .append(TestProps.CERT_NAME)
                            .toString()).getFile());
            this.clientCert = new File(classLoader.getResource(
                    new StringBuilder(MonetagoProps.BASE_DIR_FOR_CERTS)
                            .append(MonetagoProps.CLIENT_CERT_NAME)
                            .toString()).getFile());
            this.clientKey = new File(classLoader.getResource(
                    new StringBuilder(TestProps.BASE_DIR_FOR_CERTS)
                            .append(TestProps.CLIENT_KEY_NAME)
                            .toString()).getFile());
        } catch (Exception fe) {
            log.error("unable to read ssl certificate files for testConnection");
            throw new IllegalStateException("unable to read ssl certificate files for test Connection");
        }
    } 

我只是注释掉了通道构建器上的 usePlaintext(false) 调用,并且能够与服务器建立 SSL 连接。

channelBuilder = NettyChannelBuilder.forAddress(host, port)
                    .overrideAuthority("localhost")
                    .negotiationType(NegotiationType.TLS)
                    **//.usePlaintext(false)**
                    .sslContext(sslContext);