Permalink
Browse files

Merge pull request #690 from igmar/LH1749-fix-SSL-custom-keystores

[#1749] fix ssl custom keystores
  • Loading branch information...
1 parent 8befcd7 commit 9d5fcccfb6b31c0cb8fc70cd1c8173163b1a28c6 @pepite pepite committed Jan 12, 2014
@@ -895,6 +895,22 @@ bc. ssl.KeyManagerFactory.algorithm=SunX509
Default: @SunX509@
+h2(#keystores). Custom key stores
+
+Allows for custom SSL certificates to be used with connection initiated through WebServices
+
+h3(#ssl.keyStore). ssl.Keystore
+
+The custom keystore that should be added to Play! WebServices connections
+
+h3(#ssl.keyStorePassword). ssl.keyStorePassword
+
+The password neede to access the keys inside the keystore. This is required.
+
+h3(#ssl.cavalidation). ssl.cavalidation
+
+If set to true, the CA chain is validated upon connection.
+
h2(#trustmanager). trustmanager
@@ -38,7 +38,7 @@ require: &allDependencies
- net.sf.jsr107cache -> jsr107cache 1.0
- net.sf.oval -> oval 1.82
- mysql -> mysql-connector-java 5.1.20
- - oauth.signpost -> signpost-core 1.2
+ - oauth.signpost -> signpost-core 1.2.1.2
- org.apache.geronimo.specs -> geronimo-servlet_2.5_spec 1.2
- org.apache.ivy -> ivy 2.2.0
- org.bouncycastle -> bcprov-jdk15 1.45
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,10 @@
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -37,6 +41,8 @@
import com.ning.http.client.Realm.RealmBuilder;
import com.ning.http.client.Response;
+import javax.net.ssl.*;
+
/**
* Simple HTTP client to make webservices requests.
*
@@ -60,6 +66,7 @@
public class WSAsync implements WSImpl {
private AsyncHttpClient httpClient;
+ private static SSLContext sslCTX = null;
public WSAsync() {
String proxyHost = Play.configuration.getProperty("http.proxyHost", System.getProperty("http.proxyHost"));
@@ -68,6 +75,9 @@ public WSAsync() {
String proxyPassword = Play.configuration.getProperty("http.proxyPassword", System.getProperty("http.proxyPassword"));
String nonProxyHosts = Play.configuration.getProperty("http.nonProxyHosts", System.getProperty("http.nonProxyHosts"));
String userAgent = Play.configuration.getProperty("http.userAgent");
+ String keyStore = Play.configuration.getProperty("ssl.keyStore", System.getProperty("javax.net.ssl.keyStore"));
+ String keyStorePass = Play.configuration.getProperty("ssl.keyStorePassword", System.getProperty("javax.net.ssl.keyStorePassword"));
+ Boolean CAValidation = Boolean.parseBoolean(Play.configuration.getProperty("ssl.cavalidation", "true"));
Builder confBuilder = new AsyncHttpClientConfig.Builder();
if (proxyHost != null) {
@@ -90,6 +100,19 @@ public WSAsync() {
if (userAgent != null) {
confBuilder.setUserAgent(userAgent);
}
+
+ if (keyStore != null && !keyStore.equals("")) {
+
+ Logger.info("Keystore configured, loading from '%s', CA validation enabled : %s", keyStore, CAValidation);
+ if (Logger.isTraceEnabled()) {
+ Logger.trace("Keystore password : %s, SSLCTX : %s", keyStorePass, sslCTX);
+ }
+
+ if (sslCTX == null) {
+ sslCTX = WSSSLContext.getSslContext(keyStore, keyStorePass, CAValidation);
+ confBuilder.setSSLContext(sslCTX);
+ }
+ }
// when using raw urls, AHC does not encode the params in url.
// this means we can/must encode it(with correct encoding) before passing it to AHC
confBuilder.setUseRawUrl(true);
@@ -105,6 +128,8 @@ public WSRequest newRequest(String url, String encoding) {
return new WSAsyncRequest(url, encoding);
}
+
+
public class WSAsyncRequest extends WSRequest {
protected String type = null;
@@ -654,6 +679,11 @@ public String getContentType() {
return request.mimeType;
}
+ @Override
+ public Object unwrap() {
+ return null;
+ }
+
public String getHeader(String name) {
return request.headers.get(name);
}
@@ -0,0 +1,65 @@
+package play.libs.ws;
+
+import play.Logger;
+
+import javax.net.ssl.*;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+public class WSSSLContext {
+ public static SSLContext getSslContext(final String keyStore, final String keyStorePass, Boolean CAValidation) {
+ SSLContext sslCTX = null;
+
+ try {
+ // Keystore
+ InputStream kss = new FileInputStream(keyStore);
+ char[] storePass = keyStorePass.toCharArray();
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(kss, storePass);
+
+ // Keymanager
+ char[] certPwd = keyStorePass.toCharArray();
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(ks, certPwd);
+ KeyManager[] keyManagers = kmf.getKeyManagers();
+
+ // Trustmanager
+ TrustManager[] trustManagers = null;
+ if (CAValidation == true) {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(ks);
+ trustManagers = tmf.getTrustManagers();
+ } else {
+ trustManagers = new TrustManager[]{
+ new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ }
+ };
+ }
+
+ SecureRandom secureRandom = new SecureRandom();
+
+ // SSL context
+ sslCTX = SSLContext.getInstance("TLS");
+ sslCTX.init(keyManagers, trustManagers, secureRandom);
+ } catch (Exception e) {
+ throw new RuntimeException("Error setting SSL context " + e.toString());
+ }
+ return sslCTX;
+ }
+}
@@ -3,12 +3,16 @@
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import play.Logger;
+import play.Play;
import play.libs.IO;
import play.libs.WS.HttpResponse;
import play.libs.WS.WSImpl;
import play.libs.WS.WSRequest;
import play.mvc.Http.Header;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -17,6 +21,7 @@
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -30,6 +35,8 @@
*/
public class WSUrlFetch implements WSImpl {
+ private static SSLContext sslCTX = null;
+
public WSUrlFetch() {}
public void stop() {}
@@ -159,6 +166,10 @@ public HttpResponse trace() {
}
private HttpURLConnection prepare(URL url, String method) {
+ String keyStore = Play.configuration.getProperty("ssl.keyStore", System.getProperty("javax.net.ssl.keyStore"));
+ String keyStorePass = Play.configuration.getProperty("ssl.keyStorePassword", System.getProperty("javax.net.ssl.keyStorePassword"));
+ Boolean CAValidation = Boolean.parseBoolean(Play.configuration.getProperty("ssl.cavalidation", "true"));
+
if (this.username != null && this.password != null && this.scheme != null) {
String authString = null;
switch (this.scheme) {
@@ -167,11 +178,35 @@ private HttpURLConnection prepare(URL url, String method) {
}
this.headers.put("Authorization", authString);
}
+
+ if (keyStore != null && !keyStore.equals("")) {
+ Logger.info("Keystore configured, loading from '%s', CA validation enabled : %s", keyStore, CAValidation);
+ if (Logger.isTraceEnabled()) {
+ Logger.trace("Keystore password : %s, SSLCTX : %s", keyStorePass, sslCTX);
+ }
+
+ if (sslCTX == null) {
+ sslCTX = WSSSLContext.getSslContext(keyStore, keyStorePass, CAValidation);
+ }
+ }
+
try {
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestMethod(method);
+ URLConnection connection = url.openConnection();
+ if (connection instanceof HttpsURLConnection) {
+ HttpsURLConnection cssl = (HttpsURLConnection) connection;
+ if (sslCTX != null) {
+ SSLSocketFactory sslSocketFactory = sslCTX.getSocketFactory();
+ cssl.setSSLSocketFactory(sslSocketFactory);
+ }
+ cssl.setRequestMethod(method);
+ cssl.setInstanceFollowRedirects(this.followRedirects);
+ } else {
+ HttpURLConnection c = (HttpURLConnection) connection;
+ c.setRequestMethod(method);
+ c.setInstanceFollowRedirects(this.followRedirects);
+ }
+
connection.setDoInput(true);
- connection.setInstanceFollowRedirects(this.followRedirects);
connection.setReadTimeout(this.timeout * 1000);
for (String key : this.headers.keySet()) {
connection.setRequestProperty(key, headers.get(key));
@@ -182,8 +217,8 @@ private HttpURLConnection prepare(URL url, String method) {
consumer.setTokenWithSecret(oauthToken, oauthSecret);
consumer.sign(connection);
}
- checkFileBody(connection);
- return connection;
+ checkFileBody((HttpURLConnection) connection);
+ return (HttpURLConnection) connection;
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -339,4 +374,4 @@ public InputStream getStream() {
}
}
}
-}
+}

0 comments on commit 9d5fccc

Please sign in to comment.