Setting the certificate used by a Java SSL ServerSocket - java

I want to open a secure listening socket in a Java server application. I know that the recommended way to do that is to just do this:
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
ServerSocket ss = ssf.createServerSocket(443);
But this requires to pass the certificate of the server to the JVM when launching java. Because this would make some things in deployment more complicated for me, I would prefer to load the certificate at runtime.
So I have a key file and a password and I want a server socket. How do I get there? Well, I read the documentation and the only way I could find is this:
// these are my parameters for SSL encryption
char[] keyPassword = "P#ssw0rd!".toCharArray();
FileInputStream keyFile = new FileInputStream("ssl.key");
// init keystore
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyFile, keyPassword);
// init KeyManagerFactory
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyPassword);
// init KeyManager
KeyManager keyManagers[] = keyManagerFactory.getKeyManagers();
// init the SSL context
SSLContext sslContext = SSLContext.getDefault();
sslContext.init(keyManagers, null, new SecureRandom());
// get the socket factory
SSLServerSocketFactory socketFactory = sslContext.getServerSocketFactory();
// and finally, get the socket
ServerSocket serverSocket = socketFactory.createServerSocket(443);
And that doesn't even have any error handling. Is it really that complicated? Isn't there an easier way to do it?

But this requires to pass the certificate of the server to the JVM when launching java.
No it doesn't. Just set these system properties before you create the SSLServerSocket:
javax.net.ssl.keyStore ssl.key
javax.net.ssl.keyStorePassword P#ssw0rd!
You can do that with System.setProperties() or on the command line.

If you look at the code, you can see why it's necessarily complicated. This code decouples the implementation of the SSL protocol from:
the source of your key material (KeyStore)
certificate algorithm choice and key management (KeyManager)
management of peer trust rules (TrustManager) - not used here
secure random algorithm (SecureRandom)
NIO or socket implementation (SSLServerSocketFactory) - could use SSLEngine for NIO
Consider what your own implementation would look like if you were trying to reach the same goals!

Use this.
public class KeyMaster
{
public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String sslAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);
SSLContext context = SSLContext.getInstance(sslAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
catch(Exception e)
{
Assistance.log("Err: getSSLSocketFactory(), ");
}
return null;
}
public static SSLServerSocketFactory getSSLServerSocketFactory(KeyStore trustKey, String sslAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);
SSLContext context = SSLContext.getInstance(sslAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);
return context.getServerSocketFactory();
}
catch(Exception e)
{
Assistance.log("Err: getSSLSocketFactory(), ");
}
return null;
}
public static SSLServerSocket getSSLServerSocket(SSLServerSocketFactory socketFactory, int port)
{
try
{
return (SSLServerSocket) socketFactory.createServerSocket(port);
}
catch(Exception e)
{Assistance.log("Err: getSSLSocket(), ");}
return null;
}
public static KeyStore getFromPath(String path, String algorithm, String filePassword)//PKSC12
{
try
{
File f = new File(path);
if(!f.exists())
throw new RuntimeException("Err: File not found.");
FileInputStream keyFile = new FileInputStream(f);
KeyStore keystore = KeyStore.getInstance(algorithm);
keystore.load(keyFile, filePassword.toCharArray());
keyFile.close();
return keystore;
}
catch(Exception e)
{
Assistance.log("Err: getFromPath(), " + e.toString());
}
return null;
}
and in main.
KeyStore key = KeyMaster.getFromPath(".\\cssl.pfx", "PKCS12", "123");
SSLServerSocketFactory fac = KeyMaster.getSSLServerSocketFactory(key, "TLS");
listener = KeyMaster.getSSLServerSocket(fac, 49015);

Related

How to authenticate SSL from java?

I need to call soap web services from java so i'm using ".p12" file for authentication. I'm using the same file in soap ui there it is working fine but in java it is giving SSL error.. how to link p12 file for authentication using ssl from java..
public static void setUp() {
System.setProperty("javax.net.ssl.keyStore", "ex.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
}
private static void initSSLFactories() {
final String KEYSTOREPATH = "ex.p12";
final char[] KEYSTOREPASS = "ff".toCharArray();
final char[] KEYPASS = "ff".toCharArray();
//ssl config
try (InputStream storeStream = FirstTest.class.getResourceAsStream(KEYSTOREPATH)) {
setSSLFactories(storeStream, "PKCS12", KEYSTOREPASS, KEYPASS);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void setSSLFactories(InputStream keyStream, String keystoreType, char[] keyStorePassword, char[] keyPassword) throws Exception
{
KeyStore keyStore = KeyStore.getInstance(keystoreType);
keyStore.load(keyStream, keyStorePassword);
KeyManagerFactory keyFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, keyPassword);
KeyManager[] keyManagers = keyFactory.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagers, null, null);
SSLContext.setDefault(sslContext);
}
You can create a client something like this:
public Client getClient() {
SslConfigurator sslConfig = SslConfigurator
.newInstance()
.trustStoreFile(TRUST_STORE_FILE_PATH) //The key-store file where the certificate is saved.
.trustStorePassword(TRUST_STORE_PASSWORD_PATH);//password of the key-store file.
SSLContext sslContext = sslConfig.createSSLContext();
Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();
return client;
}

Using .cer certificate to make an HTTPS request

I've already saw this question: Need to do a GET&POST HTTPS Request using a .cer certificate
Mine is quite different:
It is possible to make an HTTPS request using Java (vanilla, or using any library), trusting a server certificate and providing a client certificate, without using a keystore but using plain certificates?
I have both certs in X.509 format, and I don't want to have every certificate in a keystore.
This is a rough example. Represents the X509KeyManager decorator.
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(null, null);
X509KeyManager manager = (X509KeyManager) kmf.getKeyManagers()[0];
KeyManager km = new X509KeyManager() {
#Override
public String[] getClientAliases(String s, Principal[] principals) {
return manager.getServerAliases(s, principals);
}
#Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return manager.chooseClientAlias(strings, principals, socket);
}
#Override
public String[] getServerAliases(String s, Principal[] principals) {
return manager.getServerAliases(s, principals);
}
#Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return manager.chooseServerAlias(s, principals, socket);
}
#Override
public X509Certificate[] getCertificateChain(String s) {
// You can use `s` to select the appropriate file
try {
File file = new File("path to certificate");
try(InputStream is = new FileInputStream(file)) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return new X509Certificate[] {
(X509Certificate) factory.generateCertificate(is)
};
}
}
catch (CertificateException| IOException e) {
e.printStackTrace();
}
return null;
}
#Override
public PrivateKey getPrivateKey(String s) {
// You can use `s` to select the appropriate file
// load and private key from selected certificate
// this use for certificate authorisation
try {
File file = new File("private key file");
byte buffer[] = Files.readAllBytes(file.toPath());
KeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(keySpec);
}
catch (NoSuchAlgorithmException | IOException | InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
};
TrustManager tm = 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() {
try {
File file = new File("path to certificate");
try(InputStream is = new FileInputStream(file)) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return new X509Certificate[] {
(X509Certificate) factory.generateCertificate(is)
};
}
}
catch (CertificateException| IOException e) {
e.printStackTrace();
}
return null;
}
};
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore)null); //use java system trust certificates
TrustManager managers[] = new TrustManager[tmf.getTrustManagers().length + 1];
System.arraycopy(tmf.getTrustManagers(), 0, managers, 0, tmf.getTrustManagers().length);
managers[managers.length - 1] = tm;
SSLContext context = SSLContext.getInstance("TLS");
context.init(new KeyManager[]{ km }, managers, new SecureRandom());
URL url = new URL("https://............/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(connection.getSSLSocketFactory());
connection.connect();
If you really don't want to create a new keystore file, then can use KeyStore API to create in memory and load certificate directly.
InputStream is = new FileInputStream("somecert.cer");
// You could get a resource as a stream instead.
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); // You don't need the KeyStore instance to come from a file.
ks.setCertificateEntry("caCert", caCert);
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
Alternatively, if you want to avoid modifying your default cacerts file, then you'll need to implement your own TrustManager. However a TrustManager needs a keystore to load, so you can either create a new keystore file importing just your certificate.
keytool -import -alias ca -file somecert.cer -keystore truststore.jks -storepass changeit
And use something like following snippet to load the keystore file.
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);
// Get hold of the default trust manager
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
defaultTm = (X509TrustManager) tm;
break;
}
}
FileInputStream myKeys = new FileInputStream("truststore.jks");
// Do the same with your trust store this time
// Adapt how you load the keystore to your needs
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "password".toCharArray());
myKeys.close();
tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

Java HTTPS Server "Unsupported Protocol Error" in Chrome

I'm making a custom HTTP/1.1 server implementation in Java. It's working fine in HTTP mode, but I also want to support HTTPS. I haven't generated a certificate for the server yet, but it should at least be trying to connect. I set the protocol and cipher suite to the same settings as google.com (TLS 1.2, ECDHE_RSA, AES_128_GCM), so I know Chrome supports them.
But when I try to connect to https://localhost in Chrome, it gives ERR_SSL_VERSION_OR_CIPHER_MISMATCH (localhost uses an unsupported protocol) error. On the Java side, I get "no cipher suites in common" error.
Java Code:
public class Server {
private final String dir;
private final ServerSocket server;
private final SSLServerSocket sslServer;
public static String jarDir() {
String uri = ClassLoader.getSystemClassLoader().getResource(".").getPath();
try { return new File(URLDecoder.decode(uri,"UTF-8")).getPath()+File.separator; }
catch (Exception e) { return null; }
}
private static SSLContext createSSLContext(String cert, char[] pass) throws Exception {
/*//Load KeyStore in JKS format:
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(new FileInputStream(cert), pass);
//Create key manager:
KeyManagerFactory kmFactory = KeyManagerFactory.getInstance("SunX509");
kmFactory.init(keyStore, pass); KeyManager[] km = kmFactory.getKeyManagers();
//Create trust manager:
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("SunX509");
tmFactory.init(keyStore); TrustManager[] tm = tmFactory.getTrustManagers();
//Create SSLContext with protocol:
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(km, tm, null); return ctx;*/
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(null, null, null); return ctx;
}
Server(String localPath, int port) throws Exception {
this(localPath, port, 0);
}
//Server is being initialized with:
//new Server("root", 80, 443);
Server(String localPath, int port, int httpsPort) throws Exception {
dir = localPath; File fdir = new File(jarDir(), dir);
if(!fdir.isDirectory()) throw new Exception("No such directory '"+fdir.getAbsolutePath()+"'!");
//Init Server:
server = new ServerSocket(port);
if(httpsPort > 0) {
SSLContext ctx = createSSLContext("cert.jks", "pass".toCharArray());
sslServer = (SSLServerSocket)ctx.getServerSocketFactory().createServerSocket(httpsPort);
//TLS_DH_anon_WITH_AES_128_GCM_SHA256
sslServer.setEnabledCipherSuites(new String[]{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"});
sslServer.setEnabledProtocols(new String[]{"TLSv1.2"});
//Also does not work, same error:
//sslServer.setEnabledCipherSuites(sslServer.getSupportedCipherSuites());
//sslServer.setEnabledProtocols(sslServer.getSupportedProtocols());
} else sslServer = null;
/*new Thread(() -> { while(true) try {
new HTTPSocket(server.accept(), this);
} catch(Exception e) { Main.err("HTTP Server Error",e); }}).start();*/
if(httpsPort > 0) new Thread(() -> { while(true) try {
new HTTPSocket(sslServer.accept(), this);
} catch(Exception e) { Main.err("HTTPS Server Error",e); }}).start();
}
/* ... Other Stuff ... */
}
EDIT: I generated a certificate using keytool -genkey -keyalg RSA -alias selfsigned -keystore cert.jks -storepass password -validity 360 -keysize 2048, but now Java throws Keystore was tampered with, or password was incorrect error.
Like I said in the comments, using "password" in keyStore.load solved the issue.
private static SSLContext createSSLContext(String cert, char[] pass) throws Exception {
//Load KeyStore in JKS format:
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(new FileInputStream(cert), "password".toCharArray());
//Create key manager:
KeyManagerFactory kmFactory = KeyManagerFactory.getInstance("SunX509");
kmFactory.init(keyStore, pass); KeyManager[] km = kmFactory.getKeyManagers();
//Create trust manager:
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("SunX509");
tmFactory.init(keyStore); TrustManager[] tm = tmFactory.getTrustManagers();
//Create SSLContext with protocol:
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(km, tm, null); return ctx;
}

Android: HTTPS (SSL) connection using HttpsURLConnection

I have 2 apps, one is a Servlet/Tomcat Server, and the other is an Android app.
I want to use HttpURLConnection to send and receive XML between both.
Code:
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
try {
URL url = new URL(this.getServerURL());
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(requeststring.getBytes());
out.flush();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
dis = new DataInputStream(in);
int ch;
long len = urlConnection.getContentLength();
if (len != -1) {
for (int i = 0; i < len; i++)
if ((ch = dis.read()) != -1) {
messagebuffer.append((char) ch);
}
} else {
while ((ch = dis.read()) != -1)
messagebuffer.append((char) ch);
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return messagebuffer.toString();
}
Now, I need to use SSL to send the XMLs for security.
First, I use Java Keytool to generate the .keystore file.
Keytool -keygen -alias tomcat -keyalg RSA
Then I put the XML Code on server.xml file of Tomcat to use SSL
<Connector
port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="c:/Documents and Settings/MyUser/.keystore"
keystorePass="password"
clientAuth="false" sslProtocol="TLS"
/>
Then, I change it the HttpURLConnection for HttpsURLConnection
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
//Conexion por HTTPS
HttpsURLConnection urlHttpsConnection = null;
try {
URL url = new URL(this.getServerURL());
//urlConnection = (HttpURLConnection) url.openConnection();
//Si necesito usar HTTPS
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts();
//Creo la Conexion
urlHttpsConnection = (HttpsURLConnection) url.openConnection();
//Seteo la verificacion para que NO verifique nada!!
urlHttpsConnection.setHostnameVerifier(DO_NOT_VERIFY);
//Asigno a la otra variable para usar simpre la mism
urlConnection = urlHttpsConnection;
} else {
urlConnection = (HttpURLConnection) url.openConnection();
}
//Do the same like up
and add a trustAllHosts method to Trust every server (dont check for any certificate)
private static void trustAllHosts() {
X509TrustManager easyTrustManager = new X509TrustManager() {
public void checkClientTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public void checkServerTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
Those changes worked very good, but I don´t want to Trust every server. I want to use my keystore file to validate the connection and use SSL in the right way.
I read a lot on the internet and made a lot of tests, but I can´t understand what I have to do and how to do it.
Can somebody help me?
Thank you very much
Sorry for my poor english
-------------------------UPDATE 2011/08/24-------------------------------------------------
Well, I'm still working on this. I made a new method to set the KeyStore, InputStream, etc
The method looks like this:
private static void trustIFNetServer() {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
String keyPassword = "password";
ks.load(in, keyPassword.toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tms, new java.security.SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
}
First I had a lot of problems with the Key and the Certificate, but now it is working (I think so)
My problem right now is a TimeOut Exception. I don´t know why it is generated. I'm think it's something with the data write, but I can't solve yet.
Any Idea?
You need to create a trust store file for your self-signed certificate as described here.
Use it on the client side to connect with your server. It doesn't really matter if you use JKS or another format, I'll assume JKS for now.
To accomplish what you have in mind you need a different TrustManager, obviously. You can use TrustManagerFactory and feed its trust settings with your newly created trust store.
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("<path to your key store>");
ks.load(in, "password".toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
Use tms to init your SSLContextinstead for the new trust settings to be used for your SSL/TLS connection.
Also you should make sure that the CN part of the server TLS certificate is equal to the FQDN (fully qualified domain name) of your server, e.g. if your server base URL is 'https://www.example.com', then the CN of the certificate should be 'www.example.com'. This is needed for host name verification, a feature that prevents man-in-the-middle-attacks. You could disable this, but only when using this your connection will be really secure.
Create your trust store, store at as an asset and use it initialize this SocketFactory. Then use the factory instead of your own 'trust everybody' one.
If you want to ignore all the certificate, ignore the handshake, then this works:
HttpsURLConnection and intermittent connections

Java: Loading SSL Keystore via a resource

If I have:
System.setProperty("javax.net.ssl.keyStore", '/etc/certificates/fdms/WS1001237590._.1.ks');
System.setProperty("javax.net.ssl.keyStorePassword", 'DV8u4xRVDq');
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
I'm able to open a secure connection without a problem.
However, I'd like to have the certificates stored directly in the war, so I use: (The file input stream will eventually become a resource stream, but I'm doing this to get it to work.)
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("/etc/certificates/fdms/WS1001237590._.1.ks"), "DV8u4xRVDq".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "DV8u4xRVDq".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
Now, if I open the same connection, I get: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
I had to do something similar a while back. I had a certificate file and I had to figure out a way to load it in and use it for an SSL connection. Hopefully what I did will help you out.
First I had to create a trust manager:
public class MyX509TrustManager implements X509TrustManager {
X509TrustManager pkixTrustManager;
MyX509TrustManager() throws Exception {
String certFile = "/certificates/MyCertFile.cer";
Certificate myCert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile));
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "".toCharArray());
keyStore.setCertificateEntry("myCert", myCert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(keyStore);
TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
for(TrustManager trustManager : trustManagers) {
if(trustManager instanceof X509TrustManager) {
pkixTrustManager = (X509TrustManager) trustManager;
return;
}
}
throw new Exception("Couldn't initialize");
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
pkixTrustManager.checkServerTrusted(chain, authType);
}
public X509Certificate[] getAcceptedIssuers() {
return pkixTrustManager.getAcceptedIssuers();
}
}
After that I had to create a socket factory that used my trust manager:
public class MySSLProtocolSocketFactory implements SecureProtocolSocketFactory {
private SSLContext sslContext = null;
public MySSLProtocolSocketFactory() {
super();
}
private static SSLContext createMySSLContext() {
try {
MyX509TrustManager myX509TrustManager = new MyX509TrustManager();
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new MyX509TrustManager[] { myX509TrustManager}, null);
return context;
}
catch(Exception e) {
Log.error(Log.Context.Net, e);
return null;
}
}
private SSLContext getSSLContext() {
if(this.sslContext == null) {
this.sslContext = createMySSLContext();
}
return this.sslContext;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException {
if(params == null) {
throw new IllegalArgumentException("Parameters may not be null");
}
int timeout = params.getConnectionTimeout();
SocketFactory socketFactory = getSSLContext().getSocketFactory();
if(timeout == 0) {
return socketFactory.createSocket(host, port, localAddress, localPort);
}
else {
Socket socket = socketFactory.createSocket();
SocketAddress localAddr = new InetSocketAddress(localAddress, localPort);
SocketAddress remoteAddr = new InetSocketAddress(host, port);
socket.bind(localAddr);
socket.connect(remoteAddr, timeout);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException {
return getSSLContext().getSocketFactory().createSocket(host, port);
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
public boolean equals(Object obj) {
return ((obj != null) && obj.getClass().equals(MySSLProtocolSocketFactory.class));
}
public int hashCode() {
return MySSLProtocolSocketFactory.class.hashCode();
}
}
Then I used that socket factory to send my POST:
Protocol.registerProtocol("myhttps", new Protocol("myhttps", new MySSLProtocolSocketFactory(), 443));
PostMethod postMethod = new PostMethod("myhttps://some.url.here");
HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
The only thing I couldn't figure out was how to simply add the certificate file to the regular keystore. All the example source code I found during my research pointed to creating a socket factor and then registering a protocol with that socket factory. Perhaps there is a way to simply use the socket factory to make a connection without registering a protocol; I haven't investigated that thoroughly. In my particular situation, creating a specific protocol was necessary. Hopefully this will get your further along the way. I admit it seems a bit roundabout; I felt the same way when I did it initially. But this was the only way I got it to work. Maybe other people have a better solution.
For posterity's sake, all of this was far too complicated, and we pretty much just had a check in the static block:
if( environment == 'production') {
System.setProperty("javax.net.ssl.keyStore", '/etc/certificates/prod/keystore.ks');
System.setProperty("javax.net.ssl.keyStorePassword", 'password');
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
} else {
System.setProperty("javax.net.ssl.keyStore", '/etc/certificates/test/keystore.ks');
System.setProperty("javax.net.ssl.keyStorePassword", 'password');
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
}
With Axis, I think you need to configure its SSLSocketFactory via:
AxisProperties.setProperty("axis.socketSecureFactory",
"com.example.MySSLSocketFactory");
where com.example.MySSLSocketFactory is your class that implements org.apache.axis.components.net.SecureSocketFactory (you could extend org.apache.axis.components.net.JSSESocketFactory perhaps).
In the create method, create a socket using the socket factory obtained from the SSLContext you've configured.
If you want, here's an API to create SSLSocket and SSLServerSocket easily:
https://github.com/gpotter2/SSLKeystoreFactories
It does not require any other jars.... just get the files and use them like:
SSLSocket s = SSLSocketKeystoreFactory.getSocketWithCert(ip, port,
Main.class.getResourceAsStream("/mykey.jks"), "password")
Or:
SSLServerSocket s = SSLServerSocketKeystoreFactory.getSocketWithCert(port,
Main.class.getResourceAsStream("/mykey.jks"), "password")
That's much easier to use :)
I had similar problem, I solved creating a function that returns an SSL context using a keystore coming from and input stream.
protected SSLContext getSslCtx(InputStream is, String password) {
try {
// Load keystore
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(is, password.toCharArray());
// Load trust manager
TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustMgrFactory.init(keystore);
// Load key manager
KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyMgrFactory.init(keystore, password.toCharArray());
// Create SSL context
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(keyMgrFactory.getKeyManagers(), trustMgrFactory.getTrustManagers(), null);
return ctx;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Hope this helps.

Categories