Spring RestTemplate with HttpClient Client Certificate - java

How can I include client certificate using latest HttpClient (httpclient5)?
I'm using hakky54 sslcontext with latest RestTemplate (rest framework 6.0.4) and HttpClient. It throws exception when I create an in-mem keystore (same result with "PKCS12" and "JKS") with just my certificate
500 Internal Server Error: "{"ErrorCode":"APPAP330E","ErrorMsg":"Failed to verify application authentication data: Could not obtain client certificate details."}"
Syntax in Kotlin:
val x509 : X509Certificate = getCertificateFromStore(...)
val restTemplate = RestTemplate()
val request = HttpComponentsClientHttpRequestFactory()
val httpClient : HttpClientBuilder = HttpClients.custom()
val clientStore = KeyStore.getInstance(KeyStore.getDefaultType())
val clientManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
clientStore.load(null)
clientStore.setCertificateEntry("clientCert", x509 )
clientManager.init(clientStore, null)
val sslFactory = SSLFactory.builder()
.withIdentityMaterial(clientManager)
.build()
val connectionManager : HttpClientConnectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(Apache5SslUtils.toSocketFactory(sslFactory))
.build()
httpClient.setConnectionManager(connectionManager)
request.httpClient = httpClient.build()
restTemplate.requestFactory = request
...
val response : ResponseEntity<String> = restTemplate.getForEntity(...)
In Windows if I replace keystore init with val clientStore = KeyStore.getInstance("Windows-MY") with just one client cert in the store it works fine
I need the ability to pick a different cert that's in the store or use a cert that's not pre-installed
Am I not configuring the keystore correctly? How can I inject a client cert into a resttemplate request in the latest Spring Framework. It seems the latest Spring Framework and httpclient5 are not compatible with all the samples I have found online that rely on httpclient4

Related

How to use SSL Client certificate with Apache commons http client

My application is using Apache Commons HTTP Client to consume HTTP service URL. Now we have to move over HTTPS endpoint URL. To consume the same, we received SSL Client Certificate. How we can use .JKS with password while consuming HTTPS URL ? (Due to application limitations cant use other APIs)
KeyStore identityKeyStore = KeyStore.getInstance("JKS");
FileInputStream identityKeyStoreFile = new FileInputStream(new File(certificatePath));
identityKeyStore.load(identityKeyStoreFile, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(identityKeyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(identityKeyStore, password.toCharArray());
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null);
SSLContext.setDefault(sslContext);
PostMethod post = new PostMethod("https://url");
HttpClient httpClient = new HttpClient();
String reqMessage = getSolaceRequestMessage(message,hostName,port,authentication);
Part[] parts = {
new StringPart("reqMessage", message),
};
post.setRequestEntity(
new MultipartRequestEntity(parts, post.getParams())
);
httpClient.executeMethod(post);
The *.jks we use in the back service part.
I can give you a example of my project Java Spring boot, I change http --> https in my back service and I added my certificate in Nginx.
Example of https simple services
When you changed back service you can call https directly in your front application(ex.web angular).
I used below implementation which worked for me as had limitation not to upgrade the http client libraries.
System.setProperty(JAVAX_NET_SSL_TRUSTSTORE, "H://certificateFile.jks");
System.setProperty(JAVAX_NET_SSL_TRUSTSTORE_KEY, "abcd");

OHttpClient get request with valid I.CA certificate

I'm new to setting up certificates, the endpoint requires a valid certificate issued by I.CA. I have a .pfx with the password and can get the data from the endpoint in Postman. However I fail to achieve the same from my local server running on localhost:8080.
I'm using okhttp version 4.7.2
Here is a breakdown of the code I'm using, the loading does not cause any errors and is just for demo code adjusted. I'm having in the keystore 38 providers and only the cert under keystoreSpi that I loaded. Both key and cert are not null and seems to contain the certificate. The endpoint just return a 403 (according to docu means authorization headers ('x-api-key', 'Authorization' or 'x-client-cert') is not valid.) Since I confirmed with Postman that the api key and the token is valid (token flow is working), it can be just the missing cert. But I do not know what else is missing for the request.
KeyStore keystore = KeyStore.getInstance("PKCS12");
try (InputStream is = Files.newInputStream(Paths.get("filepath\\cert.pfx"))) {
keystore.load(is, "password".toCharArray());
} catch (Exception e) {
LOG.error(e.getMessage());
}
String alias = "myAlias";
Key key = keystore.getKey(alias, "password".toCharArray());
Certificate cert = keystore.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
HeldCertificate heldCertificate = new HeldCertificate.Builder()
.keyPair(new KeyPair(publicKey, (PrivateKey) key))
.build();
HandshakeCertificates handshakeCerts = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.heldCertificate(heldCertificate)
.build();
OkHttpClient client = new OkHttpClient().newBuilder().sslSocketFactory(handshakeCerts.sslSocketFactory(), handshakeCerts.trustManager())
.build();
Request request = new Request.Builder()
.url("https://apiendpoint/name")
.method("GET", null)
.addHeader("x-api-key", "Bearer apiKey")
.addHeader("x-correlation-id", "corId")
.addHeader("Authorization", "Bearer " + accessToken.getAccessToken())
.build();
Response response = client.newCall(request).execute();
Edit
I'm getting for response.handshake().peerPrincipal().getName() as CN the name of the endpoint I have to integrate. But response.handshake().localPrincipal().getName() is null. How do is set the localPrincipal to include the certificate that I loaded.
Here is the successful postman call.
To confirm the client authentication is working you should look at the localPrincipal in the handshake.
Call call = client.newCall(new Request.Builder().url(server.url("/")).build());
Response response = call.execute();
assertThat(response.handshake().peerPrincipal()).isEqualTo(
new X500Principal("CN=Local Host"));
assertThat(response.handshake().localPrincipal()).isEqualTo(
new X500Principal("CN=Jethro Willis"));
assertThat(response.body().string()).isEqualTo("abc");
It might be that your client certificate is not being accepted, and falling back allowing the connection but failing the request.
In your request, this header looks troubling, are you sure you provide this as is, which Authorization includes the actual access token?
.addHeader("x-api-key", "Bearer apiKey")
See https://swagger.io/docs/specification/authentication/api-keys/
The actual handshake including client happens at a layer below OkHttp, so if it's a JDK app then you might need to enable debug flags for that. Wireshark or similar can also help you debug if you are really motivated.

How to ignore SSL check while using com.google.api.client.http.HttpRequest

I am using com.google.api.client.http.HttpRequest object to hit a URL for Akamai Purge. However, I am getting an SSL Handshake error but as it is in a lower environment so I am allowed to ignore this. There are multiple examples on the internet ignoring SSL Handshake for Spring REST Template and Apache HTTPRequest. But I could not find much on google HttpRequest. I need a way to ignore SSL Cert in my code. Below is my code:
'''
HttpTransport httpTransport = new ApacheHttpTransport();
HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
URI uri = URI.create(url1);
String requestBody = "{\"objects\": [471642]}";
HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(uri),
ByteArrayContent.fromString("application/json", requestBody));
GoogleHttpClientEdgeGridRequestSigner requestSigner = new
GoogleHttpClientEdgeGridRequestSigner(credential);
requestSigner.sign(request);
HttpResponse response = request.execute();
'''
Finally I got it. Thanks Shivam Puri. I used below code
'''
ApacheHttpTransport.Builder transBuilder = new ApacheHttpTransport.Builder();
ApacheHttpTransport httpTransport =
transBuilder.doNotValidateCertificate().build();
'''

How to prevent root device to bypass certificate pinning in Android?

I am developing a project that require the Android app can prevent bypassing certificate pinning/trust a fake cert when doing network calling even in a rooted devices.
So far I can make it when the device is not rooted. I just need to prevent some bypassing method like using JustTrustMe in Xposed framework.
I am using retrofit and okHttp during network calling.
I have tried the using CertPinner in okHttp and its version is 3.10.0
and also tried to follow the code in android developer https://developer.android.com/training/articles/security-ssl#java
here is the sample code i have tried and copied from google
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
And the cert pinning sample code
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
client.newCall(request).execute();
Both are the simplest code but none of it working
I would like to make it at least prevent some bypassing method like using JustTrustMe in Xposed framework/some easy automated bypassing method.
May i know if it is possible to do it or not, I have also tried some libs like
https://github.com/moxie0/AndroidPinning
suggested by JustTrustMe
https://github.com/Fuzion24/JustTrustMe
After some testing, load CAs from an InputStream would not work for all rooted devices with bypassing module enabled. It still works for normal device
The only way I could prevent it is to use public key cert pinning with proguard at the same time, hope this only help some ppl encounter the same problems.

PoolingHttpClientConnectionManager: How to do Https requests?

I'm currently trying to do multiple HttpGet requests at the same time with CloseableHttpClient.
I googled on how to do that and the answer was to use a PoolingHttpClientConnectionManager.
At this point I got this:
PoolingHttpClientConnectionManager cManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cManager)
.build();
Then I tried a HttpGet request to http://www.google.com and everything worked fine.
Then I created a truststore via cmd and imported the certificate of the targeted website, setup a SSLConnectionSocketFactory with my truststore and set the SSLSocketFactory of httpClient:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream inputStream = new FileInputStream(new File("myTrustStore.truststore"));
trustStore.load(inputStream, "nopassword".toCharArray());
inputStream.close();
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
PoolingHttpClientConnectionManager cManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.setConnectionManager(cManager)
.build();
If I try to execute a Https HttpGet then I get a PKIX path building failed exception.
If I do the same without .setConnectionManager(cManager) everything works fine.
Can anyone of you tell me how I can get this to work? (Don't worry, I don't create any ddos tool)
Thanks in advance!
P.S.: I'm using HttpComponents 4.3.1
Found the answer:
https://stackoverflow.com/a/19950935/1223253
Just had to add
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslsf).build();
and pass socketFactoryRegistry as parameter to the constructor of PoolingHttpClientConnectionManager.
Now it works just fine :)

Categories