8 Eylül 2023 Cuma

HttpClient.Builder Sınıfı SSL - HttpClient API

Giriş
 Açıklaması şöyle
The Java 11 HTTP Client API provides extensive support for SSL/TLS configurations, ensuring secure communication over HTTPS. Developers can customize SSL/TLS parameters, specify trust stores, and handle scenarios where client certificates are required for authentication. This allows applications to securely communicate with HTTPS-based services.
Örnek
Şöyle yaparız
HttpClient client = HttpClient.newBuilder()
    .sslContext(SSLContext.getDefault())
    .build();
Örnek - Self Signed Certificate
Sunucu için key store oluşturalım
keytool -genkeypair -keyalg RSA -keysize 2048 -alias server1 -keystore server1.keystore \
  -storepass password -validity 3650 -dname "CN=localhost" \
  -ext SAN=ip:127.0.0.1,dns:localhost
İstemci için trust store oluşturalım
#export certificate from server
keytool -exportcert -keystore server1.keystore -alias server1 -file server.crt \
  -storepass password

#import certificate to client trust store
keytool -import -file server.crt -keystore client1.truststore -storepass password \
  -storetype JKS -alias server1
Sunucuyu başlatalım
Properties serverProps = new Properties();
serverProps.setProperty("javax.net.ssl.keyStore","path to server1.keystore"));
serverProps.setProperty("javax.net.ssl.keyStorePassword", "password");
serverProps.setProperty("javax.net.ssl.trustStorePassword", "password");
serverProps.setProperty("javax.net.ssl.protocol", "TLS");

// Start the server
İstemciyi başlatalım. Elimizde şöyle bir kod olsun
public static TrustManagerFactory loadTrustManagerFactory(
  String trustStorePassword,
  String trustStore,
  String trustManagerAlgorithm,
  String trustStoreType)
throws IOException, GeneralSecurityException {
  if (trustStore == null) {
    return null;
  }
  TrustManagerFactory tmf = TrustManagerFactory.getInstance(trustManagerAlgorithm);
  KeyStore ts = KeyStore.getInstance(trustStoreType);
  char[] passPhrase = trustStorePassword == null ? 
    null :
    trustStorePassword.toCharArray();

  loadKeyStore(ts, passPhrase, trustStore);
  tmf.init(ts);
  return tmf;
}

public static void loadKeyStore(KeyStore ks, char[] passPhrase, String keyStoreFile)
throws IOException, NoSuchAlgorithmException, CertificateException {
  try (InputStream in = new FileInputStream(keyStoreFile)) {
    ks.load(in, passPhrase);
  }
}
TrustManagerFactory nesnesini yükleriz.
TrustManagerFactory trustManagerFactory = loadTrustManagerFactory(
                "password",
                " path to client1.truststore"),
                TrustManagerFactory.getDefaultAlgorithm(), 
		"JKS");
Şöyle yaparız
// Create SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] clientTrustManagers = trustManagerFactory.getTrustManagers();
sslContext.init(null, clientTrustManagers, new SecureRandom());

// Use SSLContext for building HttpClient
HttpClient.Builder builder = HttpClient.newBuilder()
  .version(HttpClient.Version.HTTP_1_1)
  .sslContext(sslContext)
  .connectTimeout(Duration.ofSeconds(20))

HttpClient httpClient = builder.build();
Örnek - Trust All Certificates
Elimizde şöyle bir kod olsun
// Create a trust manager that trusts all certificates.
TrustManager [] trustAllCerts = new TrustManager [] {new X509ExtendedTrustManager () {
@Override public void checkClientTrusted (X509Certificate [] chain, String authType, Socket socket) { } @Override public void checkServerTrusted (X509Certificate [] chain, String authType, Socket socket) { } @Override public void checkClientTrusted (X509Certificate [] chain, String authType, SSLEngine engine) { } @Override public void checkServerTrusted (X509Certificate [] chain, String authType, SSLEngine engine) { } @Override public X509Certificate [] getAcceptedIssuers () { return null; } @Override
public void checkClientTrusted (X509Certificate [] c, String a) { } @Override
public void checkServerTrusted (X509Certificate [] c, String a) { } }};
Şöyle yaparız
public void testIgnoreSSL(String url) {
  // Create an SSL context and initialize it with the trust manager.
  SSLContext sslContext = SSLContext.getInstance("TLS");
  sslContext.init(null, trustAllCerts, new SecureRandom());

  // Create an HTTP client and configure it to use the SSL context.
  HttpClient httpClient = HttpClient.newBuilder()
    .connectTimeout(Duration.ofMillis(10000))
    .sslContext(sslContext)
    .build();

  // Create a request and configure it to use the URL.
  HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(url))
    .GET()
    .build();

  // Send the request and print the response body.
  HttpResponse<String> response = httpClient.send(request,
    HttpResponse.BodyHandlers.ofString());
  System.out.println(response.body());
}
disableHostnameVerification
Açıklaması şöyle
When you disable hostname verification, you are essentially telling the system to ignore the hostname in the server's SSL certificate when making a secure connection. This means that the client won't check whether the server's certificate matches the hostname it's trying to connect to.
Örnek - API İl Değiştirme
API ile değiştirme kodunu ilk defa burada gördüm. Daha sonra şöyle yaptım ama çalışmadı.
private HttpClient newClient() throws IOException {
HttpClient.Builder builder = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_1_1); if (sslEnabled) { SSLContext sslContext; try { sslContext = SSLContext.getInstance(tlsProtocol); } catch (NoSuchAlgorithmException e) { throw new IOException(e); } try { sslContext.init(clientKeyManagers, clientTrustManagers, new SecureRandom()); } catch (KeyManagementException e) { throw new IOException(e); } builder.sslContext(sslContext); // Disable host name verification in the certificate SSLParameters supportedSSLParameters = sslContext.getDefaultSSLParameters(); supportedSSLParameters.setEndpointIdentificationAlgorithm(null); builder.sslParameters(supportedSSLParameters); } // configure timeout on the entire client int timeout = 20; builder.connectTimeout(Duration.ofSeconds(timeout)); return builder .build(); }
Çünkü jdk/internal/net/http/AbstractAsyncSSLConnection.java sınıfında şu kod var ve System Property değerine bakarak benim verdiğim değeri eziyor
if (!disableHostnameVerification)
  sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
Örnek - System Property
Şöyle yaparız. Burada SSL sertifikası için yapılan host name verification etkisiz kılınıyor
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());


HttpClient.Builder builder = HttpClient.newBuilder();
SSLContext sslContext = ...;
builder.sslContext(sslContext);

HttpClient client = builder.build();
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create(url))
  .build();
client.send(request, HttpResponse.BodyHandlers.ofString());

Hiç yorum yok:

Yorum Gönder