I have a small project in which i want to use TLS 1.3.
As main example i used this respo: https://github.com/williamswhy/SSLSocket
This is my server:
Server() {
SSLServerSocket serverSocket = null;
SSLSocket socket = null;
try {
SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(3122);
MariaDB mariaDB = new MariaDB();
new Thread(mariaDB).start();
while (true) {
socket = (SSLSocket) serverSocket.accept();
new Thread(new ClientConnection(socket,mariaDB)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//System.setProperty("javax.net.debug", "all");
System.setProperty("javax.net.ssl.keyStore", "sslserverkeys");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
new Server();
}
My Client uses the following class:
try {
System.setProperty("javax.net.ssl.trustStore", "sslclienttrust");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
//DO NOT CHANGE THIS
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, null, new SecureRandom());
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
socket = (SSLSocket) socketFactory.createSocket("localhost", 3122);
System.out.println("Successfully initialized connection");
try {
this.out = new ObjectOutputStream(this.socket.getOutputStream());
System.out.println("Successfully opened ObjectOutputStream");
} catch (IOException var4) {
var4.printStackTrace();
try {
this.out.close();
} catch (IOException var3) {
var3.printStackTrace();
}
}
} catch (NoSuchAlgorithmException | KeyManagementException | IOException e) {
e.printStackTrace();
}
I have a sslclienttrust file in the root folder of my client and sslserverkeys in the root folder of my server. I'm coding in IntelliJ Ultimate with Maven and the connection works like a charm in it.
But when i build a .jar (using maven-shade-plugin) I get an exception.
The Server throws the following exception:
javax.net.ssl.SSLException: readHandshakeRecord
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1392)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:441)
at java.base/sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:889)
at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:1251)
at java.base/java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1893)
at java.base/java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1802)
at java.base/java.io.ObjectOutputStream.<init>(ObjectOutputStream.java:252)
at ClientConnection.<init>(ClientConnection.java:21)
at Server.<init>(Server.java:20)
at Server.main(Server.java:36)
Suppressed: java.net.SocketException: An established connection was aborted by the software in your host machine
at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:420)
at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440)
at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:826)
at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1052)
at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:82)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:356)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:268)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:451)
... 8 more
Caused by: java.net.SocketException: An established connection was aborted by the software in your host machine
at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:420)
at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440)
at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:826)
at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1052)
at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:268)
at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
at java.base/sun.security.ssl.Finished$T13FinishedProducer.onProduceFinished(Finished.java:783)
at java.base/sun.security.ssl.Finished$T13FinishedProducer.produce(Finished.java:671)
at java.base/sun.security.ssl.SSLHandshake.produce(SSLHandshake.java:440)
at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.goServerHello(ClientHello.java:1252)
at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.consume(ClientHello.java:1188)
at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.onClientHello(ClientHello.java:851)
at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.consume(ClientHello.java:812)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:445)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:423)
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:171)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1475)
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1381)
... 9 more
The client throws the following:
the last modified time is: Mon Sep 28 00:44:21 CEST 2020
javax.net.ssl|DEBUG|15|Thread-4|2020-09-29 24:55:10.430 CEST|TrustStoreManager.java:334|Reload the trust store
javax.net.ssl|DEBUG|15|Thread-4|2020-09-29 24:55:10.468 CEST|TrustManagerFactoryImpl.java:70|SunX509: skip default keystore (
"throwable" : {
java.io.IOException: Keystore was tampered with, or password was incorrect
at java.base/sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:794)
at java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:241)
at java.base/java.security.KeyStore.load(KeyStore.java:1472)
at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.loadKeyStore(TrustStoreManager.java:390)
at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.getTrustedCerts(TrustStoreManager.java:336)
at java.base/sun.security.ssl.TrustStoreManager.getTrustedCerts(TrustStoreManager.java:56)
at java.base/sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:49)
at java.base/javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:281)
at java.base/sun.security.ssl.SSLContextImpl.engineInit(SSLContextImpl.java:94)
at java.base/javax.net.ssl.SSLContext.init(SSLContext.java:313)
at connection.ServerConnection.connectToServer(ServerConnection.java:204)
at connection.ServerConnection.run(ServerConnection.java:42)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.security.UnrecoverableKeyException: Password verification failed
at java.base/sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:792)
... 12 more}
At first i thought this has something to do with wrong pathing, so i tried a couple of ways mentioned on sof. But the error, that the password is incorrect or the keystore is tampered confuses me. The password cannot be wrong, because it is working in the IDE.
Why does the SSL work in my IDE, but not inside the .jar
I saw some threads about SSL not working at all, but not like this.
System property javax.net.ssl.trustStore requires that you pass a file in, and that file must.. exist.
entries in jars are file-like, but not files, so you cannot use a truststore file packed inside a jar file. That kinda explodes the point of a jar in the first place, and is why this isn't 'working' - you're running the jar from someplace where the working dir isn't proper. In general passing a relative entry (something that doesn't start with / or C:\ or whatnot) to javax.net.ssl.trustStore is never going to work in the hands of end users and requires careful scripting by the ops team if you try to deploy that on your own servers.
To load a trust store from any random source, such as 'a resource 'file' that is in the same place my class files live; look in the jar file if you have to', check this tutorial.
Related
I have some code that unit tests fine using https.proxyHost and https.proxyPort to access Google Cloud Storage Buckets on on a corporate network which needs to go via a proxy:
log.info("resolving service....");
Storage storage = StorageOptions.newBuilder()
.setCredentials(
ServiceAccountCredentials.fromStream(
new FileInputStream(fullPath)))
.build()
.getService();
log.info("resolving bucket....");
bucket = storage.get(bucketName);
Yet when I run it in a larger app that starts a lot of other internal services (e.g., RMI) the proxy settings stop working.
Running as:
java -Dhttps.proxyHost=googleapis-dev.gcp.cloud.corporate -Dhttps.proxyPort=3128 ...
When it tries to resolve a bucket with the unit tested code it hangs for ages then throws:
com.google.cloud.storage.StorageException: Error getting access token for service account: oauth2.googleapis.com
at com.google.cloud.storage.spi.v1.HttpStorageRpc.translate(HttpStorageRpc.java:231) ~[htu-gcs-plugin.jar:?]
...
Caused by: java.io.IOException: Error getting access token for service account: oauth2.googleapis.com
at com.google.auth.oauth2.ServiceAccountCredentials.refreshAccessToken(ServiceAccountCredentials.java:444) ~[?:?]
at com.google.auth.oauth2.OAuth2Credentials.refresh(OAuth2Credentials.java:157) ~[?:?]
...
Caused by: java.net.UnknownHostException: oauth2.googleapis.com
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) ~[?:1.8.0_231]
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) ~[?:1.8.0_231]
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[?:1.8.0_231]
at java.net.Socket.connect(Socket.java:606) ~[?:1.8.0_231]
at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:666) ~[?:1.8.0_231]
I can get exactly the same error without proxy settings by adding this to the top of the main method:
String hostname = "https://oauth2.googleapis.com";
HttpURLConnection con = (HttpURLConnection) new URL(hostname).openConnection();
int code = con.getResponseCode();
System.out.println("> https://oauth2.googleapis.com returned: "+code);
Yet if the proxy settings are pass that runs fine then later throws the java.net.UnknownHostException as through the proxy settings are cleared.
To make things a bit more complex a custom URLClassLoader is used to load the code in question. Yet I have made a standalone app that uses the classloader and runs the code fine with the proxy settings passed as normal.
So it appears that something in the larger app is messing with the proxy system settings. Searching the codebase I can see no trace of that.
I have looked at https://googleapis.github.io/google-http-java-client/http-transport.html to see if there is way to plugin in a transport that has a proxy but cannot find a clear example.
Is there a way to coerce the use of a proxy when using google-cloud-storage?
To explicitly force a proxy to not rely upon the standard java System properties add the client libraries:
<!-- https://mvnrepository.com/artifact/com.google.http-client/google-http-client-apache-v2 -->
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-apache-v2</artifactId>
<version>1.37.0</version>
</dependency>
Then you can create a custom HttpTransportFactory with something like:
public class ProxyAwareTransportFactory implements HttpTransportFactory {
public static SSLContext trustAllSSLContext() throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
return sslContext;
}
#Override
public HttpTransport create() {
InetSocketAddress socketAddress = new InetSocketAddress(this.host,this.port);
Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress);
try {
return new NetHttpTransport.Builder()
.setProxy(proxy)
.setConnectionFactory(new DefaultConnectionFactory(proxy) )
.setSslSocketFactory(trustAllSSLContext().getSocketFactory())
.setHostnameVerifier(new DefaultHostnameVerifier())
.build();
} catch (Exception e) {
final String msg = "Could not build HttpTransport due to; " + e.getMessage();
log.error(msg, e);
throw new RuntimeException(e);
}
}
}
You can then use it using something like:
StorageOptions.Builder builder = StorageOptions.newBuilder();
if( useProxy ) {
HttpTransportFactory httpTransportFactory = new ProxyAwareTransportFactory(proxyHost, proxyPort);
HttpTransportOptions options = HttpTransportOptions.newBuilder().setHttpTransportFactory(httpTransportFactory).build();
builder.setTransportOptions(options);
}
Storage storage = builder
.setCredentials(
ServiceAccountCredentials.fromStream(
new FileInputStream(fullPath)))
.build()
.getService();
Note: I am a novice SSLSocket person...
I have a secure connection between a mysql database (Version 5.7.9) and a java application (Java 1.8.0_U73) both running on localhost. The database connection is created using the SpringFramework 4.2.4 API. I am attempting to open a Java SSLSocket on the same socket the database and the java application are communicating over via the SpringFramework API with no success. The printstack error I am getting when I initiate the ssl socket startHandshake() method is (note path to replaced the actual installation folder I am running from):
[*path to*\db\mysql\bin\mysqld, "--basedir=*path to*\db\mysql", "--datadir=*path to*\db\mysql\data", "--secure-file-priv=*path to*\db\mysql\data", --port=3355, "--ssl-ca=*path to*\db\mysql\data\cacert.pem", "--ssl-cert=*path to*\db\mysql\data\server-cert.pem", "--ssl-key=*path to*\db\mysql\data\server-key.pem"]
javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
at sun.security.ssl.InputRecord.handleUnknownRecord(Unknown Source)
at sun.security.ssl.InputRecord.read(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at TestSSL.testSocket(TestSSL.java:209)
at TestSSL.main(TestSSL.java:253)
The mysql database is started with the sample cacert.pem, server-cert.pem, and server-key.pem files that can be found in the mysql-5.7.11-winx64-debug-test.zip I downloaded from the mysql website using the --ssl-ca, --ssl-cert, and --ssl-key startup options.
The Mysql log file shows the following for the mysqld startup:
2016-02-24T19:37:06.710628Z 0 [Note] InnoDB: Loading buffer pool(s) from path to\db\mysql\data\ib_buffer_pool
2016-02-24T19:37:06.808638Z 0 [Warning] CA certificate path to\db\mysql\data\cacert.pem is self signed.
2016-02-24T19:37:06.819639Z 0 [Note] Server hostname (bind-address): '*'; port: 3355
2016-02-24T19:37:06.821639Z 0 [Note] IPv6 is available.
2016-02-24T19:37:06.822639Z 0 [Note] - '::' resolves to '::';
2016-02-24T19:37:06.823639Z 0 [Note] Server socket created on IP: '::'.
2016-02-24T19:37:06.977655Z 0 [Note] InnoDB: Buffer pool(s) load completed at 160224 12:37:06
2016-02-24T19:37:07.107668Z 0 [Note] Event Scheduler: Loaded 0 events
2016-02-24T19:37:07.108668Z 0 [Note] path to\db\mysql\bin\mysqld: ready for connections.
Version: '5.7.9' socket: '' port: 3355 MySQL Community Server (GPL)
Also, a quick connection to the mysql database using MySQL Workbench shows:
Connection:
Name: MyConnection64
Host: localhost
Port: 3355
Server: MySQL Community Server (GPL)
Version: 5.7.9
Login User: root
Current User: root#localhost
SSL: Using DHE-RSA-AES256-SHA
The URL for creating the Java SpringFramework database connection is (from my SpringFramework dbaccess.xml file):
entry key="db.jdbcurl"
value="jdbc:mysql://localhost:3355/imom?useServerPrepStmts=false&rewriteBatchedStatements=true&server.basedir=path to\db\mysql&server.datadir=path to\db\mysql\data&createDatabaseIfNotExist=true&autoReconnect=true&useSSL=true"
The VM arguments I am using to start my java app are: -Djavax.net.ssl.keyStore="path to\db\mysql\data\keystore" -Djavax.net.ssl.keyStorePassword="changeme" -Djavax.net.ssl.trustStore="path to\db\mysql\data\truststore" -Djavax.net.ssl.trustStorePassword="changeme"
The code I am using (successfully) to create the SpringFramework connection is:
private static void testSpring() {
try {
Connection conn = DataSourceUtils.getConnection(m_dataSource);
conn.close();
} catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
The code I am using to create the SSLSocket (Unsuccessfully) is:
private static void testSocket() {
// System.setProperty("jsse.enableSNIExtension", "false");
// System.setProperty("jsse.enabledSSLCipherSuites", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA");
try {
Socket socket = new Socket(m_hostnameString, Integer.parseInt(m_portString));
InputStream keyStoreResource = new FileInputStream(m_baseDir
+ File.separator + "data" + File.separator + "keystore");
char[] keyStorePassphrase = "changeme".toCharArray();
KeyStore ksKeys = KeyStore.getInstance("JKS");
ksKeys.load(keyStoreResource, keyStorePassphrase);
// KeyManager decides which key material to use.
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ksKeys, keyStorePassphrase);
InputStream trustStoreIS = new FileInputStream(m_baseDir
+ File.separator + "data" + File.separator + "truststore");
char[] trustStorePassphrase = "changeme".toCharArray();
KeyStore ksTrust = KeyStore.getInstance("JKS");
ksTrust.load(trustStoreIS, trustStorePassphrase);
// TrustManager decides which certificate authorities to use.
TrustManagerFactory tmf = TrustManagerFactory
.getInstance("SunX509");
tmf.init(ksTrust);
// SSLSocketFactoryEx factory = new SSLSocketFactoryEx(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, m_hostnameString, Integer.parseInt(m_portString),true);
// and go!
sslSocket.startHandshake();
}
catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeyStoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I'm a bit confused as to why you're directly connecting to the MySQL instance with your own SSL socket. In general, you'd still want to use the JDBC library through whatever mechanism (Spring, JPA, etc.) and tell it to use SSL. Just as you wouldn't want to connect directly to the MySQL server with a "normal" socket, you wouldn't want to with SSL either. See this page for more info.
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.
I finally managed to get SSL working between clients and my server... Atleast when running it directly from Netbeans.
Client network init code:
private Network(final String hostname, final int port) {
try {
URL resource = getClass().getResource("/truststore/TCGtruststore.jks");
if (resource == null) {
Controller.getInstance().write(MessageType.DEBUG, "Could not load trust store.");
throw new IllegalStateException("network.Network: Could not load trust store.");
}
else {
Controller.getInstance().write(MessageType.LOG, "Loaded trust store.");
}
System.setProperty("javax.net.ssl.trustStore", resource.getPath());
System.setProperty("javax.net.ssl.trustStorePassword", "tcgadmin");
Socket baseSocket = new Socket();
baseSocket.connect(new InetSocketAddress(hostname, port), Config.TIMEOUT);
SSLSocketFactory socketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
clientSocket = (SSLSocket)socketFactory.createSocket(baseSocket, hostname, port, true);
clientSocket.startHandshake();
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
connected = true;
} catch (IOException ex) {
connected = false;
}
waitForServer = new HashMap<>();
}
Server network init code:
public Server(final int port) {
this.port = port;
try {
URL resource = getClass().getResource("/keystore/TCGkeystore.jks");
if (resource == null) {
throw new IllegalStateException("server.Server: Could not load key store.");
}
System.setProperty("javax.net.ssl.keyStore", resource.getPath());
System.setProperty("javax.net.ssl.keyStorePassword", "tcgadmin");
//serverSocket = new ServerSocket(port);
serverSocket = (SSLServerSocket)SSLServerSocketFactory.getDefault().createServerSocket(port);
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
However when I clean and build it, and run it outside Netbeans, it breaks.
Surprisingly enough the client still behaves fine though, it is the server that starts acting strange with this message:
mei 20, 2013 4:36:16 PM server.ServerConnectionReceiver run
SEVERE: null
javax.net.ssl.SSLException: Received fatal alert: internal_error
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1977)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1093)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1328)
at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:882)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:102)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at server.ServerConnectionReceiver.run(ServerConnectionReceiver.java:45)
at java.lang.Thread.run(Thread.java:722)
Command line code from client:
[16:36:15] [Log] Loaded trust store.
[16:36:16] [Log] Could not establish a connection with the server.
Does anyone have any clue on what went wrong?
When server runs from within netbeans and client aswell, everything is fine.
When server runs from within netbeans and client via commandline, the server gives an exception.
When I try to run the server from commandline, it gives an error. (I was about to test if it would work with both from commandline, but I unfortunately cannot just test that)
Regards.
I have a REST server made in Grizzly that uses HTTPS and works wonderfully with Firefox. Here's the code:
//Build a new Servlet Adapter.
ServletAdapter adapter=new ServletAdapter();
adapter.addInitParameter("com.sun.jersey.config.property.packages", "My.services");
adapter.addInitParameter(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS, SecurityFilter.class.getName());
adapter.setContextPath("/");
adapter.setServletInstance(new ServletContainer());
//Configure SSL (See instructions at the top of this file on how these files are generated.)
SSLConfig ssl=new SSLConfig();
String keystoreFile=Main.class.getResource("resources/keystore_server.jks").toURI().getPath();
System.out.printf("Using keystore at: %s.",keystoreFile);
ssl.setKeyStoreFile(keystoreFile);
ssl.setKeyStorePass("asdfgh");
//Build the web server.
GrizzlyWebServer webServer=new GrizzlyWebServer(getPort(9999),".",true);
//Add the servlet.
webServer.addGrizzlyAdapter(adapter, new String[]{"/"});
//Set SSL
webServer.setSSLConfig(ssl);
//Start it up.
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\n",
"https://localhost:9999/"));
webServer.start();
Now, I try to reach it in Java:
SSLContext ctx=null;
try {
ctx = SSLContext.getInstance("SSL");
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
ClientConfig config=new DefaultClientConfig();
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(null,ctx));
WebResource service=Client.create(new DefaultClientConfig()).resource("https://localhost:9999/");
//Attempt to view the user's page.
try{
service
.path("user/"+username)
.get(String.class);
}
And get:
com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:128)
at com.sun.jersey.api.client.Client.handle(Client.java:453)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:557)
at com.sun.jersey.api.client.WebResource.get(WebResource.java:179)
From examples that I've found on the web, it seems like I would need to setup a Truststore then setup some sort of TrustManager. This seems like a lot of code and setup work for my simple little project. Is there an easier way to just say..I trust this cert and point to a .cert file?
When you say "is there an easier way to... trust this cert", that's exactly what you're doing by adding the cert to your Java trust store. And this is very, very easy to do, and there's nothing you need to do within your client app to get that trust store recognized or utilized.
On your client machine, find where your cacerts file is (that's your default Java trust store, and is, by default, located at <java-home>/lib/security/certs/cacerts.
Then, type the following:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to cacerts>
That will import the cert into your trust store, and after this, your client app will be able to connect to your Grizzly HTTPS server without issue.
If you don't want to import the cert into your default trust store -- i.e., you just want it to be available to this one client app, but not to anything else you run on your JVM on that machine -- then you can create a new trust store just for your app. Instead of passing keytool the path to the existing, default cacerts file, pass keytool the path to your new trust store file:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to new trust store>
You'll be asked to set and verify a new password for the trust store file. Then, when you start your client app, start it with the following parameters:
java -Djavax.net.ssl.trustStore=<path to new trust store> -Djavax.net.ssl.trustStorePassword=<trust store password>
Easy cheesy, really.
Here's the painful route:
SSLContext ctx = null;
try {
KeyStore trustStore;
trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("C:\\truststore_client"),
"asdfgh".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance("SunX509");
tmf.init(trustStore);
ctx = SSLContext.getInstance("SSL");
ctx.init(null, tmf.getTrustManagers(), null);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
ClientConfig config = new DefaultClientConfig();
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
new HTTPSProperties(null, ctx));
WebResource service = Client.create(config).resource(
"https://localhost:9999/");
service.addFilter(new HTTPBasicAuthFilter(username, password));
// Attempt to view the user's page.
try {
service.path("user/" + username).get(String.class);
} catch (Exception e) {
e.printStackTrace();
}
Gotta love those six different caught exceptions :). There are certainly some refactoring to simplify the code a bit. But, I like delfuego's -D options on the VM. I wish there was a javax.net.ssl.trustStore static property that I could just set. Just two lines of code and done. Anyone know where that would be?
This may be too much to ask, but, ideally the keytool would not be used. Instead, the trustedStore would be created dynamically by the code and the cert is added at runtime.
There must be a better answer.
Something to keep in mind is that this error isn't only due to self signed certs. The new Entrust CA certs fail with the same error, and the right thing to do is to update the server with the appropriate root certs, not to disable this important security feature.
Check this out: http://code.google.com/p/resting/. I could use resting to consume
HTTPS REST services.
The answer of delfuego is the simplest way to solve the certificate problem. But, in my case, one of our third party url (using https), updated their certificate every 2 months automatically. It means that I have to import the cert to our Java trust store manually every 2 months as well. Sometimes it caused production problems.
So, I made a method to solve it with SecureRestClientTrustManager to be able to consume https url without importing the cert file.
Here is the method:
public static String doPostSecureWithHeader(String url, String body, Map headers)
throws Exception {
log.info("start doPostSecureWithHeader " + url + " with param " + body);
long startTime;
long endTime;
startTime = System.currentTimeMillis();
Client client;
client = Client.create();
WebResource webResource;
webResource = null;
String output = null;
try{
SSLContext sslContext = null;
SecureRestClientTrustManager secureRestClientTrustManager = new SecureRestClientTrustManager();
sslContext = SSLContext.getInstance("SSL");
sslContext
.init(null,
new javax.net.ssl.TrustManager[] { secureRestClientTrustManager },
null);
DefaultClientConfig defaultClientConfig = new DefaultClientConfig();
defaultClientConfig
.getProperties()
.put(com.sun.jersey.client.urlconnection.HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
new com.sun.jersey.client.urlconnection.HTTPSProperties(
getHostnameVerifier(), sslContext));
client = Client.create(defaultClientConfig);
webResource = client.resource(url);
if(headers!=null && headers.size()>0){
for (Map.Entry entry : headers.entrySet()){
webResource.setProperty(entry.getKey(), entry.getValue());
}
}
WebResource.Builder builder =
webResource.accept("application/json");
if(headers!=null && headers.size()>0){
for (Map.Entry entry : headers.entrySet()){
builder.header(entry.getKey(), entry.getValue());
}
}
ClientResponse response = builder
.post(ClientResponse.class, body);
output = response.getEntity(String.class);
}
catch(Exception e){
log.error(e.getMessage(),e);
if(e.toString().contains("One or more of query value parameters are null")){
output="-1";
}
if(e.toString().contains("401 Unauthorized")){
throw e;
}
}
finally {
if (client!= null) {
client.destroy();
}
}
endTime = System.currentTimeMillis();
log.info("time hit "+ url +" selama "+ (endTime - startTime) + " milliseconds dengan output = "+output);
return output;
}