Skip to content

Instantly share code, notes, and snippets.

@unclebean
Last active June 10, 2025 03:08
Show Gist options
  • Select an option

  • Save unclebean/09cad2100e71b394b800ea70581cdbd6 to your computer and use it in GitHub Desktop.

Select an option

Save unclebean/09cad2100e71b394b800ea70581cdbd6 to your computer and use it in GitHub Desktop.
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.security.KeyStore;
public class P12HttpClientExample {
public static void main(String[] args) throws Exception {
String p12Path = "src/main/resources/client.p12";
String password = "changeit"; // same as when you exported the file
// Load the PKCS12 keystore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (var is = new java.io.FileInputStream(new File(p12Path))) {
keyStore.load(is, password.toCharArray());
}
// Build SSL context from keystore
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, password.toCharArray())
.build();
// Create HttpClient with SSL
CloseableHttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
HttpGet request = new HttpGet("https://your-api.example.com/secure-endpoint");
client.execute(request, response -> {
System.out.println("Status: " + response.getCode());
return null;
});
client.close();
}
}
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class ClientWithPEM {
public static void main(String[] args) throws Exception {
// Step 1: Load certificate
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(
new FileInputStream("client.crt")
);
// Step 2: Load private key
String keyPEM = new String(Files.readAllBytes(Paths.get("client.key")));
keyPEM = keyPEM
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
byte[] keyBytes = Base64.getDecoder().decode(keyPEM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
// Step 3: Create in-memory KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null);
keyStore.setKeyEntry("client", privateKey, new char[0], new java.security.cert.Certificate[]{certificate});
// Step 4: Build SSLContext
SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(keyStore, new char[0]) // no password
.build();
// Step 5: Create client
CloseableHttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
// Step 6: Make HTTPS request
HttpGet request = new HttpGet("https://your-api");
client.execute(request, response -> {
System.out.println("Status: " + response.getCode());
return null;
});
client.close();
}
}
//
dependencies {
testImplementation 'com.intuit.karate:karate-junit5:1.4.1' // Use latest stable version
}
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.ssl.TLS;
import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.security.KeyStore;
public class MtlsHttpClientFactory {
public static CloseableHttpClient create() throws Exception {
// Load client keystore (PKCS12 with client cert and key)
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream ksStream = new FileInputStream("client-keystore.p12")) {
keyStore.load(ksStream, "changeit".toCharArray());
}
// Load truststore (JKS with server CA)
KeyStore trustStore = KeyStore.getInstance("JKS");
try (FileInputStream tsStream = new FileInputStream("truststore.jks")) {
trustStore.load(tsStream, "changeit".toCharArray());
}
// Build SSL context
SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(keyStore, "changeit".toCharArray())
.loadTrustMaterial(trustStore, null)
.build();
// Use SSLConnectionSocketFactoryBuilder (not deprecated)
var sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
.setSslContext(sslContext)
.setTlsVersions(TLS.V_1_3, TLS.V_1_2)
.build();
var connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslSocketFactory) // ✅ This is correct for classic client
.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(30_000).build())
.build();
return HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
}
}
import org.apache.hc.client5.http.async.methods.*;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.core5.concurrent.FutureCallback;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class SafeHttp5MultiCaller {
private final CloseableHttpAsyncClient httpClient;
private final int maxRetries = 3;
private final long backoffMillis = 300;
public SafeHttp5MultiCaller() {
this.httpClient = HttpAsyncClients.createDefault();
this.httpClient.start();
}
public List<String> callApis(List<String> urls) throws Exception {
List<CompletableFuture<String>> futures = urls.stream()
.map(this::sendWithRetryAsync)
.toList();
CompletableFuture<Void> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
all.join(); // Wait for all
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
private CompletableFuture<String> sendWithRetryAsync(String url) {
CompletableFuture<String> result = new CompletableFuture<>();
sendAttempt(url, result, 0);
return result;
}
private void sendAttempt(String url, CompletableFuture<String> future, int attempt) {
HttpGet request = new HttpGet(url);
httpClient.execute(request, new FutureCallback<>() {
@Override
public void completed(SimpleHttpResponse response) {
future.complete(response.getBodyText());
}
@Override
public void failed(Exception ex) {
if (attempt < maxRetries) {
try {
Thread.sleep(backoffMillis * (1L << attempt)); // Exponential backoff
} catch (InterruptedException ignored) {}
sendAttempt(url, future, attempt + 1);
} else {
future.completeExceptionally(ex);
}
}
@Override
public void cancelled() {
future.cancel(true);
}
});
}
public static void main(String[] args) throws Exception {
var caller = new SafeHttp5MultiCaller();
List<String> responses = caller.callApis(List.of(
"https://httpbin.org/get?1",
"https://httpbin.org/get?2",
"https://httpbin.org/status/500" // fail on purpose
));
responses.forEach(System.out::println);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment