I need to write a Cucumber Test case to verify if a website supports 2-way SSL in Java. I'm not sure how to exactly do it after countlessly reading through different articles and answers. I've generated a self signed certificate for the client side of the test case, and I add it in the request, but I'm not sure how exactly to verify that the website I'm accessing supports 2-way SSL. This is my code so far that I've gotten from using different bits of answers and articles online.
org.apache.log4j.BasicConfigurator.configure();
try {
String CERT_PASSWORD = "somePass";
KeyStore identityKeyStore = KeyStore.getInstance("jks");
FileInputStream identityKeyStoreFile = new FileInputStream(new File(System.getProperty("user.dir") + "/src/main/resources/files-for-testcases/ultimate.jks"));
identityKeyStore.load(identityKeyStoreFile, CERT_PASSWORD.toCharArray());
SSLContext sslContext = SSLContexts.custom()
// load identity keystore
.loadKeyMaterial(identityKeyStore, CERT_PASSWORD.toCharArray(), (aliases, socket) -> "bddsecurity")
.build();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1.2", "TLSv1.1"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
// Call a SSL-endpoint
return callEndPoint (client, url);
} catch (Exception ex) {
System.out.println("Boom, we failed: " + ex);
ex.printStackTrace();
}
return 404;
}
private static int callEndPoint (CloseableHttpClient aHTTPClient, String aEndPointURL) {
try {
HttpGet httpGet = new HttpGet(aEndPointURL);
LOG.info("**GET** request Url: " + httpGet.getURI());
HttpResponse response = aHTTPClient.execute(httpGet);
int responseCode = response.getStatusLine().getStatusCode();
LOG.info("Response Code: " + responseCode);
return responseCode;
} catch (Exception ex) {
System.out.println("Boom, we failed: " + ex);
ex.printStackTrace();
}
return 404;
}
So just to update, the issue was with my server that was alright with connections that didn't have a certificate. It wasn't, and then my application threw an error but the diagnosis was straightforward and it now is all good.
Related
I wrote some code which authenticates in HTTPS server over SSL. It working fine.
Now I have to move this part to my Mule ESB project.
Here is my working method:
public boolean authenticate() {
try {
System.setProperty("jsse.enableSNIExtension", "false");
System.setProperty("com.sun.net.ssl.enableECC", "false");
CookieManager manager = new CookieManager();
manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(manager);
URL url = new URL("https://...");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setUseCaches(false);
con.setInstanceFollowRedirects(true);
con.setConnectTimeout(5000);
con.setReadTimeout(5000);
// KeyStore
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("PATH/TO/.P12/file"), "P12password".toCharArray());
keyManagerFactory.init(keyStore, "P12password".toCharArray());
// ---
// TrustStore
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("PATH/TO/.JKS/file"), "JKSpassword".toCharArray());
trustManagerFactory.init(trustStore);
// ---
SSLContext context = SSLContext.getInstance("SSLv3");
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
con.setSSLSocketFactory(context.getSocketFactory());
con.getContent();
CookieStore cookieJar = manager.getCookieStore();
List<HttpCookie> cookies = cookieJar.getCookies();
for (HttpCookie cookie: cookies) {
if (COOKIE_NAME.equals(cookie.getName())) {
COOKIE_VALUE = cookie.getValue();
return true;
}
}
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
In Mule ESB project I call authenticate in processor:
#Override
public MuleEvent process(MuleEvent event) throws MuleException {
MuleMessage message = event.getMessage();
try {
String payloadString = new String(message.getPayloadAsBytes());
LOGGER.info("\nMessage payload:\n" + payloadString + "\n\n");
String xml = extractXMLFromSOAPMessage(payloadString);
LOGGER.info("\nXML: " + xml + "\n\n");
if (authenticate()) {
//send request to server
}
} catch (Exception e) {
LOGGER.error("EXCEPTION: " + e.getMessage());
e.printStackTrace();
}
return event;
}
On this line con.getContent(); exception is raised: SSLException: Received fatal alert: illegal_parameter
This error also appeared in my JAVA project. But adding these parameters helped:
System.setProperty("jsse.enableSNIExtension", "false");
System.setProperty("com.sun.net.ssl.enableECC", "false");
Both JAVA and Mule are on the same machine.
Thanks in advance!
P.S. Sorry for my english (:
Solution is turned out to be very simple.
System.setProperty not working in Mule project.
So all JVM parameters can be configured in MULE_HOME/conf/wrapper.conf.
Here is my solution:
wrapper.java.additional.16=-Djsse.enableSNIExtension=FALSE
wrapper.java.additional.17=-Dcom.sun.net.ssl.enableECC=FALSE
Thank to Vijay Pande.
Have you tried setting JVM parameters as described in mule documentation.
My biggest issue is that I do not have the certificate (public and private key pair). The certificate is installed in the weblogic trust store and is the default certificate presented when making a request to the web application I am just not sure how I can make the call without the certificate loaded on the file system.
I tried using Apache HTTP client and Javax Http Client hoping that when I would make the call it would also send the certificate.
private void testCall(String path) {
URI uri = null;
try {
uri = new URIBuilder().setScheme("https").setHost("secure.url.com")
.setPath("/"+path)
.build();
} catch (URISyntaxException e) {
status = "Error Occured Building URL";
}
HttpGet httpget = new HttpGet(uri);
builtUrl = httpget.getURI().toString();
printContent(httpget);
}
private void printContent(HttpGet getRequest){
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpResponse response = httpClient.execute(getRequest);
status = response.getStatusLine().toString();
HttpEntity entity = response.getEntity();
contentRes = EntityUtils.toString(entity );
} catch (ClientProtocolException e) {
status = "Error Occured during Client Protocol" + e.toString();
} catch (ParseException e) {
status = "Error Occured during Parse Protocol" + e.toString();
} catch (IOException e) {
status = "Error Occured during Client Protocol" + e.toString();
}
}
I need to be able to set a system property in a Mobilefirst Java adapter (7.1). When I do add it, it seems to have no effect as it does in a stand-alone java app in eclipse. Any ideas on how to get the same function?
UPDATE: I apologize...should have added code sample....
You can see which setProperties I am referring to below....neither seem to take affect. The first one should dump out a bunch of debug info...second one will not print anything...but will fix another issue with TLSv1.2.
public String getHTTPRequest(String baseURL, String authHeader) {
logger.info("In getHTTPRequest");
logger.info("Parms: URL = " + baseURL + " auth = " + authHeader);
System.setProperty("javax.net.debug", "ssl:handshake:verbose");
System.setProperty("jsse.enableSNIExtension", "false");
String json = "";
try {
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(null, null, null);
SSLConnectionSocketFactory sslCF = new SSLConnectionSocketFactory(context, new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
// or add your own test here
return true;
}
});
//CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLSocketFactory(sslCF).build();
HttpClientBuilder clientBuilder = HttpClientBuilder.create().setSslcontext(context);
CloseableHttpClient httpClient = clientBuilder.build();
HttpGet request = new HttpGet(new URI(baseURL));
request.addHeader("Authorization", authHeader);
CloseableHttpResponse httpResponse = httpClient.execute(request);
json = EntityUtils.toString(httpResponse.getEntity());
System.out.println(" Response = " + json);
} catch (Exception e) {
logger.info(" Exception in getHTTPRequest = " + e.toString());
}
return json;
}
Solution from the comments section:
I added the set property value to the jvm.options file and that seemed to work....-Djsse.enableSNIExtension=false
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();
}