使用 Java實現mTLS呼叫

banq發表於2021-11-04

本教程展示使用 Java作為客戶端 與受 mTLS 保護的服務互動。
為了對我們的 Java 客戶端進行 ssl 配置,我們需要先設定一個 SSLContext。這簡化了事情,因為 SSLContext 可用於各種 http 客戶端。
由於我們有客戶端公鑰和私鑰,我們需要將私鑰從 PEM 格式轉換為 DER。

openssl pkcs8 -topk8 -inform PEM -outform PEM -in /path/to/generated/client.key -out /path/to/generated/client.key.pkcs8 -nocrypt


下一步是將客戶端金鑰載入到 Java 程式碼中並建立一個 KeyManagerFactory:

String privateKeyPath = "/path/to/generated/client.key.pkcs8";
String publicKeyPath = "/path/to/generated/client.crt";
 
final byte[] publicData = Files.readAllBytes(Path.of(publicKeyPath));
final byte[] privateData = Files.readAllBytes(Path.of(privateKeyPath));
 
String privateString = new String(privateData, Charset.defaultCharset())
        .replace("-----BEGIN PRIVATE KEY-----", "")
        .replaceAll(System.lineSeparator(), "")
        .replace("-----END PRIVATE KEY-----", "");
 
byte[] encoded = Base64.getDecoder().decode(privateString);
 
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
final Collection<? extends Certificate> chain = certificateFactory.generateCertificates(
        new ByteArrayInputStream(publicData));
 
Key key = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encoded));
 
KeyStore clientKeyStore = KeyStore.getInstance("jks");
final char[] pwdChars = "test".toCharArray();
clientKeyStore.load(null, null);
clientKeyStore.setKeyEntry("test", key, pwdChars, chain.toArray(new Certificate[0]));
 
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(clientKeyStore, pwdChars);



在上面的片段中
  • 我們從檔案中讀取位元組。
  • 我們從公鑰建立了一個證照鏈。
  • 我們使用私鑰建立了一個金鑰例項。
  • 使用鏈和金鑰建立了一個金鑰庫
  • 建立了一個 KeyManagerFactory

現在我們已經建立了一個 KeyManagerFactory 我們可以使用它來建立一個 SSLContext
由於使用自簽名證照,我們需要使用接受它們的 TrustManager。在此示例中,信任管理器將接受伺服器提供的所有證照。

TrustManager[] acceptAllTrustManager = {
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
 
                    public void checkClientTrusted(
                            X509Certificate[] certs, String authType) {
                    }
 
                    public void checkServerTrusted(
                            X509Certificate[] certs, String authType) {
                    }
                }
        };


然後ssl上下文初始化。

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), acceptAllTrustManager, new java.security.SecureRandom());


客戶端程式碼:

HttpClient client = HttpClient.newBuilder()
                                     .sslContext(sslContext)
                                     .build();
 
 
 
       HttpRequest exactRequest = HttpRequest.newBuilder()
                                     .uri(URI.create("https://127.0.0.1"))
                                     .GET()
                                     .build();
 
       var exactResponse = client.sendAsync(exactRequest, HttpResponse.BodyHandlers.ofString())
                                 .join();
       System.out.println(exactResponse.statusCode());


我們將收到一個 404 程式碼,這意味著我們的請求成功進行了 mTLS 握手。
 
注意:如果伺服器端是使用本地 Nginx 服務,我們需要禁用主機名驗證。

final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());

在其他客戶端中,這可能需要設定一個接受所有連線的 HostVerifier。

HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};



 

相關文章