I set system properties for a SSL-enabled MySQL client, which worked fine:
System.setProperty("javax.net.ssl.trustStore","truststore");
System.setProperty("javax.net.ssl.trustStorePassword","12345");
String url = "jdbc:mysql://abc.com:3306/test?" +
"user=abc&password=123" +
"&useUnicode=true" +
"&characterEncoding=utf8&useSSL=true"
A couple days ago I found the client couldn't connect to another web site in which a commercially signed SSL certificate is installed. Obviously the overriding keystores didn't work with regular https connections.
Then I decided to build my version of SocketFactory based on StandardSocketFactory.java in MySQL Connector/J source.
I added a method to create Socket objects in public Socket connect(String hostname, int portNumber, Properties props) method.
private Socket createSSLSocket(InetAddress address, int port) {
Socket socket;
try {
InputStream trustStream = new FileInputStream(new File("truststore"));
KeyStore trustStore = KeyStore.getInstance("JKS");
// load the stream to your store
trustStore.load(trustStream, trustPassword);
// initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance("PKIX", "SunJSSE"); trustFactory.init(trustStore);
// get the trust managers from the factory
TrustManager[] trustManagers = trustFactory.getTrustManagers();
// initialize an ssl context to use these managers and set as default
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustManagers, null);
if(address == null) {
socket = sslContext.getSocketFactory().createSocket();
} else {
socket = sslContext.getSocketFactory().createSocket(address, port);
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
return null;
}
return socket;
}
The url passed to jdbc driver is changed to:
String url = "jdbc:mysql://abc.com:3306/test?" +
"user=abc&password=123" +
"&useUnicode=true" +
"&characterEncoding=utf8&useSSL=true" +
"&socketFactory=" + MySocketFactory.class.getName();
The client did execute my version createSSLSocket() and return a Socket object. However, I got the following Exceptions after continuing the execution:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:
Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
javax.net.ssl.SSLException:
Unrecognized SSL message, plaintext connection?
I'm sure the MySQL was up and running, the address and port passed to createSSLSocket() were correct. Could anyone help? The client has to communicate to 2 sites at the same time: an HTTPS web server and a self-signed MySQL server.
Related
I'm trying to create an SSL VPN connection to a Fortinet firewall with Java.
To build up a socket connection in Java is not a problem, but how do I authenticate to the firewall and create the VPN tunnel? Unfortunately, I haven't found any tutorials. Maybe someone can help me with that.
public static void main(String[] args) throws IOException {
String vpnHost = "fortigateVPNHost";
int vpnPort = 443;
String vpnUser = "vpnUser";
String vpnPassword = "vpnPassword";
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(vpnHost, vpnPort);
InputStream in = sslsocket.getInputStream();
OutputStream out = sslsocket.getOutputStream();
while (in.available() > 0) {
System.out.print(in.read());
}
System.out.println("Secured connection performed successfully");
}
In order to authenticate to the firewall, it must be configured from Fortinet GUI or CLI, adding the user group and adding the user in question; then the socket should automatically connect using the firewall configuration.
I tried to secure the connection to my java server, after downloading a certificate(certificate.crt) and adding it to the keystore (keystore.jks) my server run normally and read the certificate.But if I want to consume a service via https://123.456.88.99:1010/myService from the navigator(firefox) I get a PR_END_OF_FILE_ERROR The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.ps : http://123.456.88.99:1010/myService works and consume the service and retrieve data also by using firefox, I think its a problem of private key that the navigator don't get, I really need help, thank you
ps if I try to use a certificate that I create using keytool it works
private void startHttpsServer(RestFactory factory, int port, int minWorkers, int maxWorkers, int socketTimeoutMS,
boolean keepConnection, boolean ignoreContentLength, boolean debug, Compression compression, boolean useClassicServer, boolean requireCertificate) throws Exception {
String alias = "server-alias";
String pwd = "changeit";
char [] storepass = pwd.toCharArray();
String keystoreName = "c:\\keystore.jks";
FileInputStream in = new FileInputStream(keystoreName);
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(in, storepass);
Certificate cert = keystore.getCertificate(alias);
Log.debug("the certification is here : " + cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
char [] keypass = pwd.toCharArray();
kmf.init(keystore, keypass);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLEngine engine = sslContext.createSSLEngine();
engine.setEnabledCipherSuites(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
SSLParameters defaultSSLParameters = sslContext.getDefaultSSLParameters();
engine.setSSLParameters(defaultSSLParameters);
HttpsRestServer server = new HttpsRestServer(factory, port, minWorkers, maxWorkers, debug, compression, keystore, keypass, false);
server.addCleaner(new CleanupListener() {
#Override
public void cleanup(CleanupEvent event) {
Database.disconnectAllThreadConnections(event.thread, false);
}
});
this.servers.add(server);
log.info("Starting classic HTTPS replication server on port " + port);
server.start();
log.info("Secure XML replication server started on port " + port);
}
We have a thick java client and do SSL server and client authentication.
This scenario is on Mac OS.
Our users get a certificate with non exportable private key.
Now if i want to do SSL client authentication it fails for this certificate. Below is the code snippet which i am using`to crearte the key store( have the certificate alias with me) with which i create the KeyManager which is in turn passed to the SSLContext.
KeyStore systemKeyStore = KeyStore.getInstance("KEYCHAINSTORE");
systemKeyStore.load(null, null);
resultKeyStore = KeyStore.getInstance("JKS");
resultKeyStore.load(null, null);
if (certalias != null && !certalias.trim().equals("") && systemKeyStore.containsAlias(certalias) && systemKeyStore.isKeyEntry(certalias)) {
try {
Key key = systemKeyStore.getKey(certalias, "t".toCharArray());
Certificate certs[] = systemKeyStore.getCertificateChain(certalias);
resultKeyStore.setKeyEntry(certalias, key, "randompw".toCharArray(), certs);
} catch (Exception e) {
}
The line
Key key = systemKeyStore.getKey(certalias, "t".toCharArray()); fails as it the key object returned is null
Question is , is it possible to do SSL client authentication with a certificate with private key marked as non exportable ?
UPDATE
As mentioned below in one of my comments. I tried using the default SSL context property by passing the SSL system properties. But still it did not work. Below is my code for that
System.setProperty("javax.net.ssl.trustStore", "cacerts.jks");
System.setProperty("javax.net.ssl.keyStoreType", "KEYCHAINSTORE");
System.setProperty("javax.net.ssl.keyStore", "KEYCHAINSTORE");
if (socketFactory == null) {
// socketFactory = initializeSocketFactory(Settings.isCertificateAuth(), Settings.getClientCertAlias());
socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
}
Let me know if i need to provide any further details
i found some tutorial to handle with https server and a https client. i created some keystore and it works fine. But i have some question which is not clear from the tutorial.
this is my https-server
public class HTTPSServer {
private int port = 9999;
private boolean isServerDone = false;
public static void main(String[] args) {
HTTPSServer server = new HTTPSServer();
server.run();
}
HTTPSServer() {
}
HTTPSServer(int port) {
this.port = port;
}
// Create the and initialize the SSLContext
private SSLContext createSSLContext() {
try {
//Returns keystore object in definied type, here jks
KeyStore keyStore = KeyStore.getInstance("JKS");
//loads the keystore from givin input stream, and the password to unclock jks
keyStore.load(new FileInputStream("x509-ca.jks"), "password".toCharArray());
// Create key manager
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, "password".toCharArray());
KeyManager[] km = keyManagerFactory.getKeyManagers();
// Create trust manager
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
// opens a secure socket with definied protocol
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
//System.out.println(keyStore.getCertificate("root").getPublicKey());
//System.out.println(keyStore.isKeyEntry("root"));
sslContext.init(km, tm, null);
return sslContext;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
// Start to run the server
public void run() {
SSLContext sslContext = this.createSSLContext();
try {
// Create server socket factory
SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
// Create server socket
SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(this.port);
System.out.println("SSL server started");
while (!isServerDone) {
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
// Start the server thread
new ServerThread(sslSocket).start();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
// Thread handling the socket from client
static class ServerThread extends Thread {
private SSLSocket sslSocket = null;
ServerThread(SSLSocket sslSocket) {
this.sslSocket = sslSocket;
}
public void run() {
sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
//System.out.println("HIER: " + sslSocket.getHandshakeSession());
//Klappt nicht, auch nicht, wenn der Client diese Zeile ebenfalls besitzt
//sslSocket.setEnabledCipherSuites(new String[]{"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"});
try {
// Start handshake
sslSocket.startHandshake();
// Get session after the connection is established
SSLSession sslSession = sslSocket.getSession();
System.out.println(sslSession.getPeerHost());
System.out.println(sslSession.getLocalCertificates());
System.out.println("\tProtocol : " + sslSession.getProtocol());
System.out.println("\tCipher suite : " + sslSession.getCipherSuite());
System.out.println("\tSession context : " + sslSession.getSessionContext());
//System.out.println("\tPeer pricipal of peer : " + sslSession.getPeerPrincipal());
// Start handling application content
InputStream inputStream = sslSocket.getInputStream();
OutputStream outputStream = sslSocket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
String line = null;
String[] suites = sslSocket.getSupportedCipherSuites();
for (int i = 0; i < suites.length; i++) {
//System.out.println(suites[i]);
//System.out.println(sslSession.getCipherSuite());
}
while ((line = bufferedReader.readLine()) != null) {
System.out.println("Inut : " + line);
if (line.trim().isEmpty()) {
break;
}
}
// Write data
printWriter.print("HTTP/1.1 200\r\n");
printWriter.flush();
sslSocket.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
And this is my output:
SSL server started
127.0.0.1
null
Protocol : TLSv1.2
Cipher suite : TLS_DH_anon_WITH_AES_128_GCM_SHA256
Session context : sun.security.ssl.SSLSessionContextImpl#781df1a4
I want to know, why the line
System.out.println(sslSession.getLocalCertificates());
prints out "null"?
Thank you a lot, Mira
From the documentation:
Certificate[] getLocalCertificates()
Returns the certificate(s) that were sent to the peer during handshaking.
Note: This method is useful only when using certificate-based cipher suites.
When multiple certificates are available for use in a handshake, the implementation chooses what it considers the "best" certificate chain available, and transmits that to the other side. This method allows the caller to know which certificate chain was actually used.
Returns:
an ordered array of certificates, with the local certificate first followed by any certificate authorities. If no certificates were sent, then null is returned.
The part we care about is "Returns the certificate(s) that were sent to the peer during handshaking.", and "This method is useful only when using certificate-based cipher suites.".
Given that it is returning null, we can assume you are not sending any certificates to the client. But it's also HTTPS, so what gives? Well, it looks like you're using TLS_DH_anon_WITH_AES_128_GCM_SHA256, which is, as the name suggests, anonymous. As per the OpenSSL Wiki:
Anonymous Diffie-Hellman uses Diffie-Hellman, but without authentication. Because the keys used in the exchange are not authenticated, the protocol is susceptible to Man-in-the-Middle attacks. Note: if you use this scheme, a call to SSL_get_peer_certificate will return NULL because you have selected an anonymous protocol. This is the only time SSL_get_peer_certificate is allowed to return NULL under normal circumstances.
While this is applicable to OpenSSL, it would appear to be the same in Java - that is, you're not using a certificate-based cipher. Someone with more knowledge of TLS would need to jump in, but it looks like AES keys are generated, and they're sent to the client, but the client has no assurance those keys came from you, whereas normally you would generate the keys, and then sign / encrypt (not 100% sure) those keys with an RSA key to prove they came from you.
To fix this, I believe you would need to select a different cipher suite, e.g. TLS_RSA_WITH_AES_128_GCM_SHA256. I'm not 100% sure how you would do this, but that would appear to be the solution.
sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
You are enabling all the anonymous and low-grade cipher suites, so you are allowing the server not to send a certificate, so it doesn't send one, so it doesn't give you one in getLocalCertificates().
Remove this line.
I want to create and run an application (in java) in a computer and allow multiple users to use their android devices as input devices to that main app. It must be in real time for every device.
For example: To do some follow up exercises after a training session. Users would register them selves (a simple form that would send strings to the main app on the PC) then they get some questions and every question as a timer, so who answers correctly and faster gets a better grade.
What's the best way to get this done? And yes, if it makes it easier, the connections can be through internet/LAN.
It looks like there are two parts to this. The first is a database system to handle user registration etc... look into SQL for that. There are many approaches. In terms of getting multiple phones connected to a computer PC you will need a server that can handle threads and a client for the phone.
A server needs server sockets. Server sockets can accept more than one connected client at a time. A threaded server might look like this:
public class ServerThread extends Thread
{
//is the thread running
private boolean running = true;
//ports for the server sockets
private final int dataPort;
private final int filePort;
private final String certificateDir;
private final char[] password;
private Vector<ClientHandlerThread> connectedClients = new Vector<ClientHandlerThread>(20, 5);
private Properties userProperties = new Properties();
public ServerThread(int dataPort,
int filePort,
String certificateDir,
char[] password,
Properties userProperties)
{
this.dataPort = dataPort;
this.filePort = filePort;
this.certificateDir = certificateDir;
this.password = password;
this.userProperties = userProperties;
}
public void run()
{
/*
* We need a server socket that can accept traffic. I use one for file traffic and one
* for data traffic although one socket could be used.
*/
SSLServerSocket sslDataTraffic = null;
SSLServerSocket sslFileTraffic = null;
SSLServerSocketFactory sslFac = null;
/*
* Everything in the following block is related to creating a SSL security manager.
* If you don't need validated communications you don't have to use SSL. Just normal
* sockets.
*/
try
{
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(certificateDir), password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance((KeyManagerFactory.getDefaultAlgorithm()));
kmf.init(keyStore, password);
System.setProperty("https.protocols", "SSL");
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
sslFac = ctx.getServerSocketFactory();
}
catch(Exception e)
{
System.out.println("FAILED.");
e.printStackTrace();
System.exit(-1);
}
try
{
//create data server socket
System.out.print("Creating data socket......... ");
sslDataTraffic = (SSLServerSocket) sslFac.createServerSocket(dataPort);
System.out.println("DONE. Est. on:" + dataPort);
//create file server socket
System.out.print("Creating file socket......... ");
sslFileTraffic = (SSLServerSocket) sslFac.createServerSocket(filePort);
System.out.println("DONE. Est. on:" + filePort);
}
catch (IOException e)
{
System.out.println("FAILED.");
System.out.println(e.toString() + " ::: " + e.getCause());
System.exit(-1);
}
/*
* This block is used to print the ip the server is running on. Easy to incorporate this here
* so the information doesn't have to be gathered form another source.
*/
try
{
System.out.print("Finishing.................... ");
Socket s = new Socket("google.com", 80);
System.out.println("DONE.");
System.out.println("Server online at: " + s.getLocalAddress().getHostAddress());
System.out.println("====================*====================");
s.close();
}
catch (IOException e)
{
e.printStackTrace();
}
/*
* This is the block that accepts connections from clients.
*/
try
{
while (running)
{
//wait here until a connection is bound to new sockets through the server sockets
SSLSocket sslDataTrafficSocketInstance = (SSLSocket) sslDataTraffic.accept();
SSLSocket sslFileTrafficSocketInstance = (SSLSocket) sslFileTraffic.accept();
//sockets to communicate with the client are created. Lets put them in a thread so
//we can continue to accept new clients while we work with the newly and previously
//connected clients
//create a new thread
ClientHandlerThread c = new ClientHandlerThread(
sslDataTrafficSocketInstance,
sslFileTrafficSocketInstance,
userProperties);
//start thread
c.start();
//add newly connected client to the list of connected clients
connectedClients.add(c);
}
}
catch (IOException e)
{
System.out.println("Fatal server error, terminating server and client handler threads");
stopServer();
}
}
}
The constructor of the ClientHandlerThread class looks like this:
private PrintWriter writer;
private BufferedReader reader;
private InputStream inputStream;
private OutputStream outputStream;
public ClientHandlerThread(
SSLSocket dataSocket,
SSLSocket fileSocket,
Properties userProperties)
{
this.dataSocket = dataSocket;
this.fileSocket = fileSocket;
this.userProperties = userProperties;
try
{
this.reader = new BufferedReader(new InputStreamReader(this.dataSocket.getInputStream()));
this.writer = new PrintWriter(this.dataSocket.getOutputStream());
this.inputStream = fileSocket.getInputStream();
this.outputStream = fileSocket.getOutputStream();
}
catch (IOException e)
{
e.printStackTrace();
}
}
Notice streams are created from the sockets. This is what opens the communication channel to the client. The thread can send a receive data and requests. What requests you write and the way you handle them is up to you.
The client will look very similar to the server but with one big difference. The client needs to initialize the handshake. One side must send data first to initialize the communication. Since the client is connecting to the server I typically have the client send the first set of data. The client's connection code might look like this method:
private void connect()
{
try
{
SSLSocketFactory sslFac;
SSLSocket dataSocket = null;
SSLSocket fileSocket = null;
/*
* This block is nearly identical to the security block for the server side.
*/
try
{
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(certificateDir), password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance((KeyManagerFactory.getDefaultAlgorithm()));
kmf.init(keyStore, password.toCharArray());
System.setProperty("https.protocols", "SSL");
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
sslFac = ctx.getSocketFactory();
String ip = "<THE SERVER'S IP ADDRESS>";
dataSocket = (SSLSocket) sslFac.createSocket(ip, dataPort);
fileSocket = (SSLSocket) sslFac.createSocket(ip, filePort);
}
catch(Exception e)
{
System.out.println("FAILED.");
e.printStackTrace();
System.exit(-1);
}
reader = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()));
writer = new PrintWriter(dataSocket.getOutputStream());
OutputStream fileOut = fileSocket.getOutputStream();
writer.println("CLIENT_HANDSHAKE_INIT");
writer.flush();
}
}
At this point you should have a client connected to a server and the client should have initialized the handshake. You have streams open to each other on both ends allowing the server and client to communicate. At this point you can begin polishing and building up the server and client to do what you actually want to do. The code I've provided is missing a dew parts that you will need to fill in as you tailor the system to your specific needs. I provided this system as an example for you to follow. A few notes. Remember someone has to start the handshake for communication to take place. Remember the streams must be flushed for the data to transmit. This security model does not apply to public connections. I was strictly trying to prevent outside connections from being successful. You will need to do more research on SSL if you need secured connections.
Hope this gave you some ideas about the server-client model and what you want to do with it.
Cheers,
Will