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
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'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 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 have a scenario in which I must pass a certficate to my server, then the server sends me his certificate, which I must accept to access the server. I was using HttpURLConnection for this, with no problems.
However, I recently had a problem with HttpURLConnection. The code I was using retrieved an image from a HTTPS server. If the image was small (< 500kb), no problem whatsoever occured. However, with larger images I got this:
javax.net.ssl.SSLProtocolException: Read error: ssl=0x3c97e8: Failure in SSL library, usually a protocol error
I was reading about it on the Internet, and many people said that using HttpClient instead of HttpURLConnection was the way to go (an example is this site http://soan.tistory.com/62 , think that is written in korean, I can't read it but that's what I think it says).
This is my old code, using URLConnection:
public static URLConnection CreateFromP12(String uri, String keyFilePath,
String keyPass, TrustManager[] trustPolicy, HostnameVerifier hv) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
keyStore.load(new FileInputStream(keyFilePath),
keyPass.toCharArray());
kmf.init(keyStore, keyPass.toCharArray());
sslContext.init(kmf.getKeyManagers(), trustPolicy, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext
.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
} catch (Exception ex) {
return null;
}
URL url;
URLConnection conn;
try {
url = new URL(uri);
conn = url.openConnection();
} catch (MalformedURLException e) {
return null;
} catch (IOException e) {
return null;
}
return conn;
}
And this is the new one, using HttpClient:
public class HttpC2Connection {
public static HttpEntity CreateHttpEntityFromP12(String uri,
String keyFilePath, String keyPass) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream(keyFilePath), keyPass.toCharArray());
SSLSocketFactory sf = new MySSLSocketFactory(keyStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params,
registry);
HttpClient httpclient = new DefaultHttpClient(ccm, params);
HttpGet httpget = new HttpGet(uri);
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
return entity;
}
But now, using HttpClient, my server returns me an error saying that I must pass a certificate, so I guess that
SSLSocketFactory sf = new MySSLSocketFactory(keyStore);
isn't loading my certificate.
So, how can I do the following two things at the same time:
1.) Pass a certificate to my server;
2.) Accept any certificate from my server
Using the HttpClient class?
PS: I'm using Android 3.0
Thanks
The following code disables SSL certificate checking for any new instances of HttpsUrlConnection:
https://gist.github.com/aembleton/889392
/**
* Disables the SSL certificate checking for new instances of {#link HttpsURLConnection} This has been created to
* aid testing on a local box, not for use on production.
*/
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
Don't just accept any certificates. Don't use home-made SSLSocketFactory's that compromise security. Use the SSLSocketFactory from the SDK, and pass both a trust store (containing the server certificate or the CA certificate that issued it) and a keystore (containing your client certificate and private key). You can use this constructor to achieve this, the JavaDoc has details on how to create the key stores.
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();
}
}