SSL连接的JAVA实现

SSL连接分为双向认证和单向认证。其中双向认证表示服务器和客户端都需要分别校验对方的身份。单向认证则只需要客户端校验服务器的身份。

SSL的双向认证的流程如下图:

从以上流程可见,要完成双向认证,服务器端和客户端都需要验证对方的证书,然后再进行加密的协商。这里基于JAVA来实现一个服务器端和客户端的程序,可以实现双向认证。

首先需要准备服务器和客户端的相关证书:

1. 创建自签名的根密钥

openssl genrsa -out rootkey.pem 2048

2. 生成根证书

openssl req -x509 -new -key rootkey.pem -out root.crt -subj="/C=CN/ST=GD/L=GZ/O=RootCA/OU=RootCA/CN=RootCA"

3. 生成客户端密钥

openssl genrsa -out clientkey.pem 2048

4. 生成客户端证书请求文件,使用根证书进行签发

openssl req -new -key clientkey.pem -out client.csr -subj="/C=CN/ST=GD/L=GZ/O=BMW/OU=Vehicle/CN=Vehicle1"

5. 用根证书来签发客户端请求文件,生成客户端证书client.crt

openssl x509 -req -in client.csr -CA root.crt -CAkey rootkey.pem -CAcreateserial -days 3650 -out client.crt

6. 打包客户端资料为pkcs12格式(client.pkcs12)

openssl pkcs12 -export -in client.crt -inkey clientkey.pem -out client.pkcs12

7. 生成服务器端的密匙

openssl genrsa -out serverkey.pem 2048

8. 生成服务器端证书的请求文件。请求根证书来签发

openssl req -new -key serverkey.pem -out server.csr -subj="/C=CN/ST=GD/L=GZ/O=BMW/OU=IT/CN=Broker"

9. 用根证书来签发服务器端请求文件,生成服务器端证书server.crt

openssl x509 -req -in server.csr -CA root.crt -CAkey rootkey.pem -CAcreateserial -days 3650 -out server.crt

10. 打包服务器端资料为pkcs12格式(server.pkcs12 )

openssl pkcs12 -export -in server.crt -inkey serverkey.pem -out server.pkcs12

11. 生成信任客户端的keystore,把根证书以及需要信任的客户端的证书添加到这个keystore

keytool -importcert -alias ca -file root.crt -keystore clienttrust.jks

keytool -importcert -alias clientcert -file client.crt -keystore clienttrust.jks

12. 生成信任服务器端的keystore,把根证书以及需要信任的服务端的证书添加到这个keystore

keytool -importcert -alias ca -file root.crt -keystore servertrust.jks

keytool -importcert -alias servercert -file server.crt -keystore servertrust.jks

服务器程序

以下是服务器的程序

public class SSLServer 
{
    private SSLServerSocket sslServerSocket;
    public static void main( String[] args ) throws Exception
    {
        SSLServer server = new SSLserver();
        server.init();
        System.out.println( "Server initialted!" );
        server.process();
    }

    public void init() throws Exception {
        int port = 9999;
        String keystorePath = "/home/roy/projects/cert/server.pkcs12";
        String keystorePass = "123456";
        SSLContext context = SSLContext.getInstance("TLSv1.2");

        //加载服务器的证书和Private key
        KeyStore serverKeyStore = KeyStore.getInstance("pkcs12");
        FileInputStream keystoreFis = new FileInputStream(keystorePath);
        serverKeyStore.load(keystoreFis, keystorePass.toCharArray());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
        kmf.init(serverKeyStore, keystorePass.toCharArray());

        //加载要信任的客户端证书的keystore
        String trustClientKeystorePath = "/home/roy/projects/cert/clienttrust.jks";
        KeyStore trustKeyStore = KeyStore.getInstance("jks");
        FileInputStream trustKeystoreFis = new FileInputStream(trustClientKeystorePath);
        trustKeyStore.load(trustKeystoreFis, keystorePass.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
        tmf.init(trustKeyStore);

        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        sslServerSocket = (SSLServerSocket)context.getServerSocketFactory().createServerSocket(port);
        sslServerSocket.setNeedClientAuth(true);
    }

    public void process() throws Exception {
        String bye = "Bye!";
        byte[] buffer = new byte[50];
        while(true) {
            Socket socket = sslServerSocket.accept();
            InputStream in = socket.getInputStream();
            in.read(buffer);
            System.out.println("Received: " + new String(buffer));
            OutputStream out = socket.getOutputStream();
            out.write(bye.getBytes());
            out.flush();
        }
    }
}

客户端程序

如以下代码:

public class SSLClient
{
    private SSLSocket sslSocket;
    public static void main( String[] args ) throws Exception
    {
        SSLClient client = new SSLClient();
        client.init();
        System.out.println( "Client initiated." );
        client.process();
    }

    public void init() throws Exception {
        String host = "127.0.0.1";
        int port = 9999;
        String keystorePath = "/home/roy/projects/cert/client.pkcs12";
        String keystorePass = "123456";

        SSLContext context = SSLContext.getInstance("TLSv1.2");

        //加载客户端的证书和private key
        KeyStore clientKeyStore = KeyStore.getInstance("pkcs12");
        FileInputStream keystoreFis = new FileInputStream(keystorePath);
        clientKeyStore.load(keystoreFis, keystorePass.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
        kmf.init(clientKeyStore, keystorePass.toCharArray());

        //加载信任的服务器证书的keystore
        String trustServerKeystorePath = "/home/roy/projects/cert/servertrust.jks";
        KeyStore trustKeyStore = KeyStore.getInstance("jks");
        FileInputStream trustKeystoreFis = new FileInputStream(trustServerKeystorePath);
        trustKeyStore.load(trustKeystoreFis, keystorePass.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
        tmf.init(trustKeyStore);

        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        sslSocket = (SSLSocket)context.getSocketFactory().createSocket(host, port);
    }

    public void process() throws Exception {
        String hello = "Hello World";
        OutputStream out = sslSocket.getOutputStream();
        out.write(hello.getBytes(), 0, hello.getBytes().length);
        out.flush();
        Thread.sleep(1000);
        InputStream in = sslSocket.getInputStream();
        byte [] buffer = new byte[50];
        in.read(buffer);
        System.out.println(new String(buffer));
    }
}

之后分别运行服务器和客户端程序,可以见到SSL的双向认证通过,建立连接并成功收发信息。

如果要实现单向认证,那么客户端的代码不需要改动,服务器端的代码改动如下:

public class SSLServer 
{
    private SSLServerSocket sslServerSocket;
    public static void main( String[] args ) throws Exception
    {
        SSLServer server = new SSLserver();
        server.init();
        System.out.println( "Server initialted!" );
        server.process();
    }

    public void init() throws Exception {
        int port = 9999;
        String keystorePath = "/home/roy/projects/cert/server.pkcs12";
        String keystorePass = "123456";
        SSLContext context = SSLContext.getInstance("TLSv1.2");

        //加载服务器的证书和Private key
        KeyStore serverKeyStore = KeyStore.getInstance("pkcs12");
        FileInputStream keystoreFis = new FileInputStream(keystorePath);
        serverKeyStore.load(keystoreFis, keystorePass.toCharArray());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
        kmf.init(serverKeyStore, keystorePass.toCharArray());

        context.init(kmf.getKeyManagers(), null, null);
        sslServerSocket = (SSLServerSocket)context.getServerSocketFactory().createServerSocket(port);
        sslServerSocket.setNeedClientAuth(false);
    }

    public void process() throws Exception {
        String bye = "Bye!";
        byte[] buffer = new byte[50];
        while(true) {
            Socket socket = sslServerSocket.accept();
            InputStream in = socket.getInputStream();
            in.read(buffer);
            System.out.println("Received: " + new String(buffer));
            OutputStream out = socket.getOutputStream();
            out.write(bye.getBytes());
            out.flush();
        }
    }
}

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ESClientSpringFactory 是一个基于 Spring 框架的 Elasticsearch 客户端工厂类,可以很方便地使用 Spring 来管理 Elasticsearch 客户端的连接。在开启 SSL 的情况下,我们需要在配置文件中添加相关的 SSL 配置。下面是一个简单的使用 ESClientSpringFactory 连接开启了 SSL 的 Elasticsearch 集群的示例。 1. 首先,我们需要在配置文件中添加 elasticsearch.client 节点,并设置相应的 SSL 参数: ```yaml elasticsearch: client: nodes: - host: localhost port: 9200 scheme: https ssl: enabled: true key-store: classpath:client.jks key-store-password: my_keystore_password trust-store: classpath:client-truststore.jks trust-store-password: my_truststore_password verify-hostname: false ``` 上面的配置文件中,我们开启了 SSL 连接,并指定了客户端使用的 key store 和 trust store 的位置和密码。其中,verify-hostname 参数设置为 false,表示不验证服务器的主机名,这在开发和测试阶段是比较方便的,但在生产环境中应该设置为 true。 2. 然后,我们需要定义一个 ESClientSpringFactory 的 Bean,并注入配置文件中的 elasticsearch.client 节点: ```java import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.RestClients; import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; @Configuration public class ElasticsearchConfig extends AbstractElasticsearchConfiguration { @Autowired private ElasticsearchProperties elasticsearchProperties; @Bean @Override public RestHighLevelClient elasticsearchClient() { ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(elasticsearchProperties.getClient().getNodes().get(0).getHost() + ":" + elasticsearchProperties.getClient().getNodes().get(0).getPort()) .withBasicAuth(elasticsearchProperties.getClient().getUsername(), elasticsearchProperties.getClient().getPassword()) .usingSsl() .withKeyStore(elasticsearchProperties.getClient().getSsl().getKeyStore(), elasticsearchProperties.getClient().getSsl().getKeyStorePassword()) .withTrustStore(elasticsearchProperties.getClient().getSsl().getTrustStore(), elasticsearchProperties.getClient().getSsl().getTrustStorePassword()) .verifyHostnames(elasticsearchProperties.getClient().getSsl().isVerifyHostname()) .build(); return RestClients.create(clientConfiguration).rest(); } } ``` 上面的示例代码中,我们定义了一个名为 elasticsearchClient 的 Bean,并注入了配置文件中的 elasticsearch.client 节点。在 Bean 的实现中,我们使用 ClientConfiguration.builder() 方法来构建一个 Elasticsearch 客户端的配置对象,其中设置了连接地址、认证信息、SSL 相关参数等。最后,我们使用 RestClients.create() 方法来创建一个 RestHighLevelClient 对象,并返回给 Spring 容器来管理。 完成上述步骤后,我们就可以在其他需要使用 Elasticsearch 客户端的地方直接注入 RestHighLevelClient 对象,来操作 Elasticsearch 集群了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gzroy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值