使用Java的SSLSocket

首先

让我们尝试使用Java的SSLSocket进行加密通信。顺便还可以验证客户端证书。
首先是服务器端。

准备证书

服务器端需要和用于Web服务器的证书相同的服务器证书、私钥和CA局证书。
将其打包成pkcs12格式。
如果使用客户端证书,则需要使用客户端证书的CA局证书。这个证书可以使用JKS格式而无需私钥。
尝试将证书放入pkcs12中似乎可以工作,但不知道为什么没有被识别。

读取服务器端证书

将服务器证书与私钥和CA证书一同保存在pkcs12文件中进行读取。
使用FileInputStream打开文件,然后使用java.security.KeyStore的load方法进行读取。
读取完毕后,将其指定给javax.net.ssl.KeyManagerFactory的init方法。

如果不使用客户端证书,则不需要TrustManager。

代码如下所示。

import java.net.Socket;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import java.io.FileInputStream;

public class App
{
    private static void initSSL()
    {
 
        try (
            FileInputStream p12_file = new FileInputStream("servercert.p12");
            FileInputStream jks_file = new FileInputStream("clienttrust.jks");
        ) {
            KeyManagerFactory kmf;
            KeyStore ks;
            ks = KeyStore.getInstance("pkcs12");
            ks.load(p12_file, "passphrase".toCharArray());
            kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, "passphrase".toCharArray());

            // ここからはクライアント証明書のためのもの
            TrustManagerFactory tmf;
            KeyStore ts;
            ts = KeyStore.getInstance("JKS");
            ts.load(jks_file, "jkspass".toCharArray());
            tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ts);
        }
        catch (Exception e) {
            System.out.println("initSSL exception: " + e.toString());
        }
}

servercert.p12是一个包含服务器证书和私钥的pkcs12文件,其密码为passphrase,所以需要根据环境进行相应修改。
同样地,clienttrust.jks和jkspass也需要进行修改。

创建ServerSocket

使用上面生成的 kmf 和 tmf。(如果不使用客户端证书,则仅使用 kmf)
具体步骤是创建 java.net.ssl.SSLContext,并在初始化时指定从 kmf 和 tmf 中获取的密钥和证书信息。
然后使用 ctx.getServerSocketFactory() 获得 ServerSocketFactory,然后使用它创建 ServerSocket。

代码如下。

            SSLContext ctx = SSLContext.getInstance("TLS");
            // クライアント証明書を使用しない場合は tmf.getTrustManagers() のところをnullにする
            ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            SSLServerSocketFactory ssf = ctx.getServerSocketFactory();
            SSLServerSocket sslsvr = (SSLServerSocket)ssf.createServerSocket(443);
            // クライアント証明書を使用しない場合は、以下不要
            sslsvr.setNeedClientAuth(true);

除了使用常规的ServerSocket与客户端建立连接并获得Socket之外,其他步骤与常规相同。(可以将其转换为SSLSocket)
其中的443是端口号,如果要使用其他端口,需要进行更改。

如何创建PKCS12文件。

在使用openssl时可以创建服务器证书和客户端证书的pkcs12格式。(可能也可以使用keytool创建)

openssl pkcs12 -export -in <証明書ファイル名> -inkey <秘密鍵ファイル名> -certfile <CA局証明書ファイル名> -out <出力ファイル名> 

执行这个操作时,需要设置密码,需要输入两次。

如何制作JKS文件

使用Java附带的keytool创建。

keytool import -file <クライアント証明書用CA局ファイル名> -alias clientca -keystore <出力ファイル名>

别名(alias)部分似乎是可选的,但当使用keytool时可能需要这个。

服务器程序的操作确认

在创建客户端之前,最好使用openssl进行操作验证。
按以下方式执行操作。

openssl s_client -connect <サーバホスト名>:<ポート> -cert <クライアント証明書ファイル名(PEM)> -key <秘密鍵ファイル名(PEM)>

另外,如果无法正常工作,您还可以在运行时的java选项中加上”-Djavax.net.debug=all”来输出多种信息,这可能会给您一些提示。在”all”的位置上,也可以指定为”ssl”,这样就可以指定ssl选项。

可指定的选项请参考此处。

关于TLS版本

在OpenJDK 17中,默认只启用TLS 1.2和1.3版本。可以使用SSLServerSocket.setEnabledProtocols进行指定。例如,如果按以下方式进行设置,则只接受TLS 1.3版本。

    SSLServerSocket sslsvr = (SSLServerSocket)ssf.createServerSocket(443);
    sslsvr.setEnabledProtocols(new String[] { "TLSv1.3" });

可以通过 getEnabledProtocols 方法来确认可用的协议。

使用 setEnabledCipherSuites 可以更改 Cipher Suites,使用 getEnabledCipherSuites 可以确认,可以先使用 getEnabledCipherSuites,然后从中删除不想使用的项,并将其指定给 setEnabledCipherSuites即可。

广告
将在 10 秒后关闭
bannerAds