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();
}
Related
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?
I'm trying to update a code that uses HttpClient 4.5 to have no deprecated methods, but it was completely impossible to find a solution, I'm totally lost.
This is my code:
public int sendGetHTTP() throws QAException, IOException {
HttpResponse httpResponse = null;
try {
DefaultHttpClient client = new DefaultHttpClient();
InputStream is = new FileInputStream("my");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate)cf.generateCertificate(is);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
ks.setCertificateEntry("cert", caCert);
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sf = new SSLSocketFactory(sslContext);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Scheme scheme = new Scheme("https", sf, 444);
client.getConnectionManager().getSchemeRegistry().register(scheme);
client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
HttpGet httpGet = new HttpGet("https://mysite:444/en.html");
httpGet.addHeader("SSO-EMPLOYEENUMBER", "1234");
httpResponse = client.execute(httpGet);
} catch (Exception e) {
e.printStackTrace();
}
int status = httpResponse.getStatusLine().getStatusCode();
if (status != HTTP_STATUS_OK && status != HTTP_STATUS_CREATED) {
throw new QAException("Server Response: " + status + ": " + httpResponse.getStatusLine().getReasonPhrase());
}
return status;
}
How can I change this code to not have deprecated methods and instances (like the DefaultHttpClient) ?
Is there any useful documentation that I can read ?
Apache maintains a deprecated list
You can check on that list to see what they recommend you replace it with, and the version of the client it was deprecated in.
For DefaultHttpClient, they recommend
org.apache.http.impl.client.DefaultHttpClient (4.3) use
HttpClientBuilder see also CloseableHttpClient.
You can also go to the HttpComponents Home Page which has links to examples and doc's
I'm working on a Java program that will send POST requests to a website for my company to use. We do not own this website, they are separate from us. I've been fighting with various ways to actually pass it the very picky parameters it wants in order for me to do work on it from a program (as opposed to doing it manually).
I've found that the Apache HttpClient 4.3 seems to be my best route for actually trying to access it, anything results in a angry response from the website telling me my username and password and not valid/authorized.
But then I got an error because the site certificate doesn't match, I contacted their support and they reportedly share an infrastructure with another site so the certificate mismatch is expected.
So I went commandline and generated a keystore, passed that to the program and then got the error "java.security.cert.CertificateException: No subject alternative DNS name matching".
Some hunting lead me to utilize a verifier, which removed errors.
Then I realized that I can't make URLConnection/HttpsURLConnection and HttpClient/HttpPost work together. That's where I'm stuck. I'm not sure how to make the code that handles my keystore, TrustManager, SSLSocketFactory, etc connect to the part where I actually have to connect and POST.
Code that handles the certificates and verification:
InputStream in = new FileInputStream(new File("C:\\Program Files (x86)\\Java\\jre7\\bin\\my.keystore"));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(in, "blahblah".toCharArray());
in.close(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] {defaultTrustManager}, null);
javax.net.ssl.SSLSocketFactory sslSocketFactory = context.getSocketFactory();
URL url = new URL("https://emailer.driveclick.com/dbadmin/xml_post.pl");
URLConnection con = url.openConnection();
((HttpsURLConnection) con).setSSLSocketFactory(sslSocketFactory);
((HttpsURLConnection) con).setHostnameVerifier(new Verifier());
con.connect();
in = con.getInputStream();
Code that should be connecting me to the website:
try {
//log into the website
String url2 = "https://emailer.driveclick.com/dbadmin/xml_post.pl";
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url2);
post.setHeader("User-Agent", USER_AGENT);
List<BasicNameValuePair> urlParameters = new ArrayList<>();
urlParameters.add(new BasicNameValuePair("username", "namefoo"));
urlParameters.add(new BasicNameValuePair("api_password", "passfoo"));
post.setEntity(new UrlEncodedFormEntity(urlParameters));
org.apache.http.HttpResponse response = client.execute(post);
System.out.println("\nSending 'POST' request to URL : " + url2);
System.out.println("Post parameters : " + post.getEntity());
System.out.println("Response Code : " + response.getStatusLine().getStatusCode());
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null)
{
result.append(line);
}
System.out.println(result.toString());
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(LastFileMove.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(LastFileMove.class.getName()).log(Level.SEVERE, null, ex);
}
EDIT: I forgot to include the little class I made for the Verifier that I referenced.
public class Verifier implements HostnameVerifier
{
public boolean verify(String arg0, SSLSession arg1) {
return true; // mark everything as verified
}
}
Update 5/8/2014
SSLConext and Verifier are now set up like this:
SSLContext sslContext = SSLContexts.custom()
.useTLS()
.loadTrustMaterial(ks)
.build();
X509HostnameVerifier verifier = new AbstractVerifier()
{
#Override
public void verify(final String host, final String[]
cns, final String[] subjectAlts) throws SSLException
{
verify(host, cns, subjectAlts, true);
}
};
And I've gone ahead and changed my HttpClient to a closeable one here:
CloseableHttpClient client = HttpClients.custom()
sslSocketFactory)
.setHostnameVerifier(verifier)
.setSslcontext(sslContext)
.build();
And I'm back to having "javax.net.ssl.SSLException: hostname in certificate didn't match" errors. Suggestions?
I have no idea how Verifier is implemented but this code snippet demonstrates how one can create a custom hostname verifier none of those shipped with HttpClient fits their needs
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = new FileInputStream(new File("C:\\Program Files (x86)\\Java\\jre7\\bin\\my.keystore"));
try {
ks.load(in, "blahblah".toCharArray());
} finally {
in.close();
}
SSLContext sslContext = SSLContexts.custom()
.useTLS()
.loadTrustMaterial(ks)
.build();
X509HostnameVerifier verifier = new AbstractVerifier() {
#Override
public void verify(final String host, final String[] cns, final String[] subjectAlts) throws SSLException {
verify(host, cns, subjectAlts, true);
}
};
CloseableHttpClient hc = HttpClients.custom()
.setSslcontext(sslContext)
.setHostnameVerifier(verifier)
.build();
I tried to use client certificate authentication with URLConnection on java 6u45 and it returns me 400 HTTP code, but when I build the same with java 7u25 it works fine and gives back OK 200.
When I tried Apache HttpPost it gives 400 error for both 6u45 as well as 7u25.
C:\java\test>"C:\Program Files (x86)\Java\jdk1.6.0_45\bin\javac" Test.java -cp ".;lib/*"
C:\java\test>"C:\Program Files (x86)\Java\jdk1.6.0_45\bin\java" -cp ".;lib/*" Test
Response Code 1: 400
Response Code 2: 400
C:\java\test>"C:\Program Files (x86)\Java\jdk1.7.0_25\bin\javac" Test.java -cp ".;lib/*"
C:\java\test>"C:\Program Files (x86)\Java\jdk1.7.0_25\bin\java" -cp ".;lib/*" Test
Response Code 1: 200
Response Code 2: 400
I believe that it is quite easy to make it work on java 6 at least with Apache HttpPost, but I do something wrong.
My test code is as follows:
import java.io.*;
import java.net.*;
import java.security.*;
import javax.net.ssl.*;
import org.apache.http.*;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.*;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.BasicClientConnectionManager;
import org.apache.http.params.*;
import org.apache.http.entity.StringEntity;
public class Test {
public static void main(String[] args) {
String keyStorePath = "C:\\java\\keys\\client_certificate.p12";
String keyStorePass = "someKeyStorePass";
String trustStorePath = "C:\\java\\keys\\truststore.jks";
String trustStorePass = "someTrustStorePath";
String postData = "<request></request>";
String url = "https://api.some-url.com/v2";
SSLContext sslContext = GetSSLContext(keyStorePath, keyStorePass, trustStorePath, trustStorePass);
PostData1(postData, url, sslContext);
SchemeRegistry schemeRegistry = GetSchemeRegistry(keyStorePath, keyStorePass, trustStorePath, trustStorePass);
PostData2(postData, url, schemeRegistry);
}
public static void PostData1(String dataToPost, String serviceUrl, SSLContext sslContext) {
try {
URL url = new URL(serviceUrl);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection urlConn = (HttpsURLConnection)url.openConnection();
urlConn.setDoOutput(true);
urlConn.setDoInput(true);
urlConn.setUseCaches(false);
urlConn.setRequestMethod("POST");
urlConn.setRequestProperty("Content-Type", "application/xml");
urlConn.connect();
PrintWriter out = new PrintWriter(urlConn.getOutputStream());
out.println(dataToPost);
out.close();
System.out.println("Response Code 1: " + urlConn.getResponseCode());
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void PostData2(String dataToPost, String serviceUrl, SchemeRegistry schemeRegistry) {
try {
HttpParams httpParams = new BasicHttpParams();
DefaultHttpClient httpClient = new DefaultHttpClient(new BasicClientConnectionManager(schemeRegistry), httpParams);
HttpPost httpPost = new HttpPost(serviceUrl);
HttpEntity lEntity = new StringEntity(dataToPost, "UTF-8");
httpPost.setEntity(lEntity);
HttpResponse response = httpClient.execute(httpPost);
try {
System.out.println("Response Code 2: " + response.getStatusLine().getStatusCode());
} finally {
httpPost.releaseConnection();
}
httpClient.getConnectionManager().shutdown();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static SSLContext GetSSLContext(String clientStorePath, String clientStorePass, String trustStorePath, String trustStorePass) {
SSLContext sslContext = null;
try {
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream(clientStorePath), clientStorePass.toCharArray());
KeyManagerFactory kmf =KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, clientStorePass.toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream(trustStorePath), trustStorePass.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, tms, new SecureRandom());
} catch (Exception ex) {
ex.printStackTrace();
}
return sslContext;
}
public static SchemeRegistry GetSchemeRegistry(String clientStorePath, String clientStorePass, String trustStorePath, String trustStorePass) {
final SchemeRegistry schemeRegistry = new SchemeRegistry();
try {
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream(clientStorePath), clientStorePass.toCharArray());
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream(trustStorePath), trustStorePass.toCharArray());
schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(clientStore, clientStorePass, trustStore)));
} catch (Exception ex) {
ex.printStackTrace();
}
return schemeRegistry;
}
}
After a deep investigation of HTTPS protocol traffic I found out that Java 6 doesn't support SNI and this feature was added only in Java 7.
My Nginx server have several HTTPS VirtualHosts on the same IP and because of Java 6 doesn't pass the exact hostname the default host was returned.
To resolve this issue you just need to mark your VirtualHost with client side authentication as the default one:
listen xxx.xxx.xxx.xxx:443 default;
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();
}
}