I am writing the client code which has to consume a web service which requires client certificate to authenticate.
Code:
String KEYSTOREPATH = "C:\\jks\\client.p12";
String KEYPASS = "password";
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(
new File("C:\\jks\\client.p12"),
KEYPASS.toCharArray(), KEYPASS.toCharArray(),
(PrivateKeyStrategy) (aliases, socket) -> "client")
.loadTrustMaterial(new File(KEYSTOREPATH), KEYPASS.toCharArray(), (chain, authType) -> true).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
new String[] { "TLSv1.2" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpGet httpget = new HttpGet("https://localhost:8443/test");
System.out.println("Executing request " + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
Error:
javax.net.ssl|DEBUG|01|main|2019-06-29 19:29:33.244 IST|SunX509KeyManagerImpl.java:401|matching alias: 1
javax.net.ssl|WARNING|01|main|2019-06-29 19:29:33.245 IST|CertificateRequest.java:699|No available client private key
javax.net.ssl|DEBUG|01|main|2019-06-29 19:29:33.246 IST|ServerHelloDone.java:142|Consuming ServerHelloDone handshake message (
<empty>
)
javax.net.ssl|DEBUG|01|main|2019-06-29 19:29:33.246 IST|CertificateMessage.java:291|No X.509 certificate for client authentication, use empty Certificate message instead
javax.net.ssl|DEBUG|01|main|2019-06-29 19:29:33.247 IST|CertificateMessage.java:322|Produced client Certificate handshake message (
"Certificates": <empty list>
)
Command to generate the p12 file
openssl pkcs12 -export -out client.p12 -inkey client.key.pem -in client.cert.pem
Why it is not able to find the client certificate from the client.p12 file? What I am missing here?
Related
I am trying to hit an url with client certification have generate key with:
keytool -genkey -alias server -keyalg RSA -keystore /example.jks -validity 10950
and key store with:
keytool -import -trustcacerts -alias root -file /example.cer -keystore /example.jks
and trying to connect:
System.out.println("------------------------------------------- In SendRequest ------------------------------------################");
SSLContext context = SSLContext.getInstance("TLS");
Certificate cert=getCertificate();
URL url = new URL("url");
URLConnection urlConnection = url.openConnection();
HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
SSLSocketFactory sslSocketFactory = getFactory();
httpsUrlConnection.setSSLSocketFactory(sslSocketFactory);
DataOutputStream wr = new DataOutputStream(httpsUrlConnection.getOutputStream());
System.out.println(wr.toString());
File req_xml = new File("request.xml");
//SOAPMessage req = TestCase.createSoapSubsribeRequest("SUBSCRIBE");
HttpPost post = new HttpPost("url");
post.setEntity(new InputStreamEntity(new FileInputStream(req_xml), req_xml.length()));
post.setHeader("Content-type", "text/xml; charset=UTF-8");
//post.setHeader("SOAPAction", "");
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(post);
LOG.info("************************************************************RESPONSE****************"+response.getStatusLine());
// SOAP response(xml) get String res_xml = EntityUtils.toString(response.getEntity());
LOG.info("Response"+res_xml);
}
private SSLSocketFactory getFactory( ) {
try{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
System.out.println("------------------------------------------- In getFactory ------------------------------------################");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
//InputStream keyInput = new FileInputStream(pKeyFile);
String password = "obsmesh";
char[] passwd = password.toCharArray(example.jks");
keystore.load(is, passwd);
// keyInput.close();
keyManagerFactory.init(keystore, password.toCharArray());
System.out.println("------------------------------------------- In jsdkl ------------------------------------################");
SSLContext context = SSLContext.getInstance("TLS");
TrustManager[] trust = null;
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
return context.getSocketFactory();
}catch(Exception e){
System.out.println(e);
}
return null;
}
Try with this code I hope it will help you.
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File("//your jks file path "), "//key password here",
new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
File req_xml = new File("// your request xml file path");
HttpPost post = new HttpPost("//https client url");
post.setEntity(new InputStreamEntity(new FileInputStream(req_xml), req_xml.length()));
post.setHeader("Content-type", "text/xml; charset=UTF-8");
System.out.println("Executing request " + post.getRequestLine());
CloseableHttpResponse response = httpclient.execute(post);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(entity);
System.out.println(response.getEntity());
} finally {
response.close();
}
} finally {
httpclient.close();
}
I am trying to write a Java code where I need to fetch data from a RESTful Api using Apache HttpClient.
My web server has a self signed certificate. The code I have used is as follows:
public class WebClientDevWrapper {
public static HttpClient wrapClient(HttpClient base) {
try {
ClientConnectionManager ccm = base.getConnectionManager();
SSLSocketFactory sslsf = new SSLSocketFactory(new TrustSelfSignedStrategy());
Scheme https = new Scheme("https", 444, sslsf);
ccm.getSchemeRegistry().register(https);
return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public static void main(String[] args) throws ClientProtocolException, IOException{
HttpClient client ;
client = new DefaultHttpClient();
client = WebClientDevWrapper.wrapClient(client);
HttpGet request = new HttpGet("https://localhost/restapi");
HttpResponse response = client.execute(request);
BufferedReader rd = new BufferedReader (new InputStreamReader(response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
}
}
On running this code, I am getting the following error:
"javax.net.ssl.SSLException: Certificate for doesn't contain CN or DNS subjectAlt"
Please help.
You need to import the SSL certificate in your JAVA. Use the below line
<JAVA_HOME>\bin\keytool -import -v -trustcacerts
-alias [Your Alias name] -file [Your Certificate location]
-keystore [Keystore location] -keypass [Your Password]
-storepass [Your Password]
I am trying to setup 2 - way SSL between client and server using HttpClient 4.3.3 library for a WebApp to communicate with a server component.
I have the client / server commuicating successfully over SSL in what I believe looks to be one-way SSL in that the CA hierarchy is not being strictly validated from what I can see, or maybe HttpClient is hiding all the details. It also seems quite difficult to get the peer certificate chain, this seems to be accessible through SSLSession object which would be present in strict JSSE interaction but HttpClient abstracts away from and does not seem possible to access?
Looking at the debug SSL logging it all seems to be fine, I guess i just wanted to confirm that 2 way SSL is happening even if it is happening within HttpClient.
Also, the TrustStrategy only seems to access the client Certificate chain and regardless of true or false returned for 'isTrusted' never seems to behave differently.
TLDR; is this 2 way SSL, if not what needs to change? How does one get access to peer certificate chain using HttpClient? Does the TrustStrategy actually do anything?
This is my code thus far which works with the server which I know to be running SSL:
try{
KeyStore trustStore = KeyStore.getInstance(keystoreType, keystoreProvider);
FileInputStream instream = new FileInputStream(new File("/path/to/keystore"));
try {
trustStore.load(instream,keystorePassword.toCharArray());
} finally {
instream.close();
}
//establish trust strategy
TrustStrategy trustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
for(X509Certificate cert : x509Certificates){
System.out.println("cert = " + cert);
}
return true;
}
};
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(trustStore, keystorePassword.toCharArray())
.loadTrustMaterial(trustStore, trustStrategy).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpPost post = new HttpPost(existingSSLServerURL);
HttpEntity requestEntity = new ByteArrayEntity(sampleAuthenticationForSSL.getBytes("UTF-8"));
post.setEntity(requestEntity);
System.out.println("executing request" + post.getRequestLine());
CloseableHttpResponse response = httpclient.execute(post);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent()));
String inputline = null;
while((inputline = in.readLine()) != null){
System.out.println(inputline);
}
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
}catch(Exception e){
e.printStackTrace();
fail();
}
I'd like to programmatically access a site that requires Client certificates, which I have in PEM files. In this application I don't want to add them to my keystore, use keytool, or openssl if I can avoid doing so. I need to deal with them directly in code.
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("https://my.secure.site.com/url");
// TODO: Specify ca.pem and client.pem here?
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity.consumeContent();
}
httpclient.getConnectionManager().shutdown();
How would I 'send' the certificate with the request?
Easiest may well be to use the .p12 format (though the others work fine too - just be careful with extra lines outside the base64 blocks) and add something like:
// systems I trust
System.setProperty("javax.net.ssl.trustStore", "foo");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
// my credentials
System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");
System.setProperty("javax.net.ssl.keyStore", "cert.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
Or alternatively - use things like
KeyStore ks = KeyStore.getInstance( "pkcs12" );
ks.load( new FileInputStream( ....), "mypassword".toCharArray() );
KeyStore jks = KeyStore.getInstance( "JKS" );
ks.load(...
to create above on the fly instead. And rather than rely on the system property - use somethng like:
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(aboveKeyStore, "changeme".toCharArray());
sslContext = SSLContext.getInstance("SSLv3");
sslContext.init(kmf.getKeyManagers(), null, null);
which keeps it separate from keystore.
DW.
You can create a KeyStore from .pem files like so:
private KeyStore getTrustStore(final InputStream pathToPemFile) throws IOException, KeyStoreException,
NoSuchAlgorithmException, CertificateException {
final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
// load all certs
for (Certificate cert : CertificateFactory.getInstance("X509")
.generateCertificates(pathToPemFile)) {
final X509Certificate crt = (X509Certificate) cert;
try {
final String alias = crt.getSubjectX500Principal().getName();
ks.setCertificateEntry(alias, crt);
LOG.info("Added alias " + alias + " to TrustStore");
} catch (KeyStoreException exp) {
LOG.error(exp.getMessage());
}
}
return ks;
}
Does anyone have any friendly tips on how to perform client authentication via an x509 certificate using HTTPClient 4.0.1?
Here is some code to get you going. The KeyStore is the object that contains the client certificate. If the server is using a self-signed certificate or a certificate that isn't signed by a CA as recognized by the JVM in the included cacerts file then you will need to use a TrustStore. Otherwise to use the default cacerts file, pass in null to SSLSockeFactory for the truststore argument..
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
...
final HttpParams httpParams = new BasicHttpParams();
// load the keystore containing the client certificate - keystore type is probably jks or pkcs12
final KeyStore keystore = KeyStore.getInstance("pkcs12");
InputStream keystoreInput = null;
// TODO get the keystore as an InputStream from somewhere
keystore.load(keystoreInput, "keystorepassword".toCharArray());
// load the trustore, leave it null to rely on cacerts distributed with the JVM - truststore type is probably jks or pkcs12
KeyStore truststore = KeyStore.getInstance("pkcs12");
InputStream truststoreInput = null;
// TODO get the trustore as an InputStream from somewhere
truststore.load(truststoreInput, "truststorepassword".toCharArray());
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("https", new SSLSocketFactory(keystore, keystorePassword, truststore), 443));
final DefaultHttpClient httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(httpParams, schemeRegistry), httpParams);
Another solution (copied from another example). I've used the same keystore for both 'trusting' (trustStore) and for authenticate myself (keyStore).
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File("miller.keystore"));
try {
trustStore.load(instream, "pw".toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(trustStore) /* this key store must contain the certs needed & trusted to verify the servers cert */
.loadKeyMaterial(trustStore, "pw".toCharArray()) /* this keystore must contain the key/cert of the client */
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpGet httpget = new HttpGet("https://localhost");
System.out.println("executing request" + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
I used the following from a sample code on HttpClient's website (custom SSL context if I remember correctly).
{
KeyStore keyStore = KeyStore.getInstance("PKCS12"); //client certificate holder
FileInputStream instream = new FileInputStream(new File(
"client-p12-keystore.p12"));
try {
trustStore.load(instream, "password".toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "password".toCharArray())
// .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) //if you have a trust store
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients
.custom()
.setHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) //todo
.setSSLSocketFactory(sslsf).build();
try {
HttpGet httpget = new HttpGet("https://localhost:8443/secure/index");
System.out.println("executing request" + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: "
+ entity.getContentLength());
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
}