For university security lab work I have to create a simple client/server application using RMI. For secure communication between client and server I wanted to use SSL. Oracle has example so I tried to use it. And I get errors. I try to start server rmi.HelloImpl.java which uses rmi.RMISSLServerSocketFactory.java where the file mentioned in error is defined. And I am getting this error:
"C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.2.5\lib\idea_rt.jar=54269:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.2.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;C:\Users\Agne\IdeaProjects\jssesamples\out\production\jssesamples" rmi.HelloImpl
java.security.AccessControlException: access denied ("java.io.FilePermission" "testkeys" "read")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
at java.lang.SecurityManager.checkRead(SecurityManager.java:888)
at java.io.FileInputStream.<init>(FileInputStream.java:127)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at rmi.RMISSLServerSocketFactory.<init>(RMISSLServerSocketFactory.java:27)
at rmi.HelloImpl.main(HelloImpl.java:34)
HelloImpl err: access denied ("java.io.FilePermission" "testkeys" "read")
java.security.AccessControlException: access denied ("java.io.FilePermission" "testkeys" "read")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
I checked my Java is 8 version, I use IntelliJ IDEA, I run it as administrator. Same error I got, when I tried to create the file in this code too, before it goes to testkeys. Then almost indentical error with new file name and access is denied in write. What am I missing?
And code in these two classes which are the main participations:
HelloImpl
package rmi;
import java.io.*;
import java.net.InetAddress;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
private static final int PORT = 2019;
public HelloImpl() throws Exception {
super(PORT,
new RMISSLClientSocketFactory(),
new RMISSLServerSocketFactory());
}
public String sayHello() {
return "Hello World!";
}
public static void main(String args[]) {
// Create and install a security manager
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
// Create SSL-based registry
Registry registry = LocateRegistry.createRegistry(PORT,
new RMISSLClientSocketFactory(),
new RMISSLServerSocketFactory());
HelloImpl obj = new HelloImpl();
// Bind this object instance to the name "HelloServer"
registry.bind("HelloServer", obj);
System.out.println("HelloServer bound in registry");
} catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
}
RMISSLServerSocketFactory
package rmi;
import java.io.*;
import java.net.*;
import java.rmi.server.*;
import javax.net.ssl.*;
import java.security.KeyStore;
import javax.net.ssl.*;
public class RMISSLServerSocketFactory implements RMIServerSocketFactory {
/*
* Create one SSLServerSocketFactory, so we can reuse sessions
* created by previous sessions of this SSLContext.
*/
private SSLServerSocketFactory ssf = null;
public RMISSLServerSocketFactory() throws Exception {
try {
// set up key manager to do server authentication
SSLContext ctx;
KeyManagerFactory kmf;
KeyStore ks;
char[] passphrase = "passphrase".toCharArray();
ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("testkeys"), passphrase);
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), null, null);
ssf = ctx.getServerSocketFactory();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public ServerSocket createServerSocket(int port) throws IOException {
return ssf.createServerSocket(port);
}
public int hashCode() {
return getClass().hashCode();
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj == null || getClass() != obj.getClass()) {
return false;
}
return true;
}
}
Once you install an RMISecurityManager, you need to have a policy file that specifies the permissions that your application will need in a security policy file. I think there used to be a policytool application that would help you write that file, and the error message tells you what permission you need to add to the file. In your case, it looks like something like:
grant {
filePermission "testKeys", "read"
}
would need to be part of your security policy.
Related
I have a Java Desktop Application, and this application starts a secure websocket server, written in Java. Right now it is using a self-signed certificate which requires the web browser user to manually accept the self-signed certificate to be able to start a client connection to the same websocket server.
I have a SSL certificate for a domain like "mydomain.com", and I'd like to use that SSL certificate to start the Java Desktop Application's secure websocket. Of course the computer will have to point that domain to the local machine, so I added 127.0.0.1 mydomain.com to my HOSTS file (I'm in Windows). Even so, it is not working.
So the question is:
Is it possible to create a Secure Websocket Server from this Java
Desktop Application, so that if my local browser tries to access
wss://mydomain.com/appservices it accepts the certificate as usual
(given the HOSTS file modification) ?, If this is possible, How can I
do that given the following example code?
Additional info and examples of current code
Here is the code that starts the secure websocket server in my Java Desktop App:
package com.mydomain.desktopclient.websockets.server;
import com.mydomain.desktopclient.resources.ResourceLoader;
import com.mydomain.desktopclient.websockets.server.endpoint.RequestServerEndpoint;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
import javax.websocket.server.ServerContainer;
import java.net.URL;
public class SecureWebSocketServer {
Server server = null;
public void start() throws Exception {
server = new Server();
int httpsPort = 443;
URL keystore = ResourceLoader.getLocalResourceURL("/resources/misc/keystore/keystore.jks");
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath(keystore.toExternalForm());
sslContextFactory.setCertAlias("mydomain.com.key");
sslContextFactory.setKeyStorePassword("keystorepass");
sslContextFactory.setKeyManagerPassword("keymanagerpass");
sslContextFactory.addExcludeProtocols("SSLv3");
sslContextFactory.addExcludeCipherSuites(".*_GCM_.*");
HttpConfiguration httpsConf = new HttpConfiguration();
httpsConf.setSecurePort(httpsPort);
httpsConf.setSecureScheme("https");
httpsConf.addCustomizer(new SecureRequestCustomizer());
ServerConnector httpsConnector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory, "http/1.1"),
new HttpConnectionFactory(httpsConf));
httpsConnector.setHost("mydomain.com");
httpsConnector.setPort(httpsPort);
server.addConnector(httpsConnector);
HandlerList baseHandlers = new HandlerList();
server.setHandler(baseHandlers);
ServletContextHandler context = new ServletContextHandler();
// context.setVirtualHosts(new String[] {"mydomain.com"});
context.setContextPath("/");
baseHandlers.addHandler(context);
// Add WebSocket
ServerContainer jsrContainer = WebSocketServerContainerInitializer.configureContext(context);
jsrContainer.setDefaultMaxSessionIdleTimeout(1000 * 60 * 60 * 24);
jsrContainer.addEndpoint(RequestServerEndpoint.class);
Handler handler = new DefaultHandler();
baseHandlers.addHandler(handler);
server.setDumpAfterStart(true);
server.setDumpBeforeStop(true);
try {
server.start();
server.join();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
public void stop() throws Exception {
if( server != null ){
server.stop();
server = null;
}
}
}
This code uses jetty-all-9.3.1.v20150714-uber.jar as websocket library.
The class com.mydomain.desktopclient.resources.ResourceLoader is a simple class to find and return files as resources from the app's jar file.
Here is the RequestServerEndpoint if needed:
package com.mydomain.desktopclient.websockets.server.endpoint;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.logging.Logger;
#ServerEndpoint(value = "/appservices")
public class RequestServerEndpoint {
private static Logger logger = Logger.getLogger(RequestServerEndpoint.class.getName());
#OnOpen
public void onOpen(Session session) {
logger.info("Connected ... " + session.getId());
}
#OnMessage
public String onMessage(String incommingMessage, Session session) {
logger.info("Received Message: \n\n" + incommingMessage);
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason));
}
}
Recently I spent couple of hours trying to get WSImport working on web service that is hosted over HTTPS, with fake certificate (dev deployment).
I tried to use windows version of wsimport.exe, from Java 8 jdk.
I specified option -XdisableSSLHostnameVerification, but it kept complaining about wrong certificate.
That is true, certificate is not valid, but in dev environment it should be acceptable.
I did not find easy way to make wsimport to skip certificate check.
Finally I got a solution, using wrapper class.
I think it makes sense to share the solution.
Hope it will save some someone's time for better purposes.
The solution (assuming that java is installed in c:\Program Files\Java\jdk1.8.0_40):
How to compile
"c:\Program Files\Java\jdk1.8.0_40\bin\javac" -cp "c:\Program Files\Java\jdk1.8.0_40\lib\tools.jar" WSImportSSLByPass.java
How to use
"c:\Program Files\Java\jdk1.8.0_40\bin\java" -cp "c:\Program Files\Java\jdk1.8.0_40\lib\tools.jar";. WSImportSSLByPass %wsimport args%
The code
to be put into WSImportSSLByPass.java
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class WSImportSSLByPass {
public static void main(String[] args) throws Throwable{
configureBypassSSL();
com.sun.tools.internal.ws.WsImport.main(args);
}
private static void configureBypassSSL() throws NoSuchAlgorithmException,
KeyManagementException {
SSLContext ssl_ctx = SSLContext.getInstance("SSL");
TrustManager[] trust_mgr = get_trust_mgr();
ssl_ctx.init(null, // key manager
trust_mgr, // trust manager
new SecureRandom()); // random number generator
SSLSocketFactory sf = ssl_ctx.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sf);
HttpsURLConnection.setDefaultHostnameVerifier(new DummyHostVerifier());
}
private static TrustManager[] get_trust_mgr() {
TrustManager[] certs = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String t) {
}
public void checkServerTrusted(X509Certificate[] certs, String t) {
}
} };
return certs;
}
}
class DummyHostVerifier implements HostnameVerifier {
public boolean verify(String name, SSLSession sess) {
return true;
}
}
In case of 2-way ssl handshake, we can modify the WSImportSSLByPass class like this
import com.sun.tools.internal.ws.WsImport;
public class OCBWSImport {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws Throwable {
// TODO code application logic here
//System.setProperty("javax.net.ssl.trustStore", "C:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\security\\cacerts");
//System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
//System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
//Certificate for 2-way handshake
System.setProperty("javax.net.ssl.keyStore", "D:\\tuanpa\\yourp12file.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "password of p12 file");
//Hostname checking bypass
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier() {
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
//return hostname.equals("192.168.1.10");
return true;
}
});
WsImport.main(args);
}
}
I am creating RMI program for my class assignment in Netbeans. It is a simple RMI program and The server side is working properly. But as I run my client side file. It ends up giving me error
Exception in thread "main" java.security.AccessControlException: access denied ("java.net.SocketPermission" "127.0.0.1:1099" "connect,resolve")
plus it is saying some error at line 26 at client code.
For clear understanding I am giving full code of all three files.
Interface.java :
package RMI;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface DemoInterface extends Remote {
public String SayDemo() throws RemoteException;
}
Server.java
package RMI;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class Server implements Interface{
public Server()
{
super();
}
private String message;
public Server(String msg) throws RemoteException
{
message = msg;
}
public static void main(String[] args) {
try {
DemoInterface h = new Server("Hello");
DemoInterface stub = (DemoInterface) UnicastRemoteObject.exportObject(h,0);
LocateRegistry.createRegistry(4096);
Registry registry = LocateRegistry.getRegistry("127.0.0.1",4096);
registry.rebind("Hello", stub);
System.out.println("Server is connected and ready to use");
}
catch(Exception e)
{
System.out.println("server not connected\n"+e);
}
}
#Override
public String SayDemo() throws RemoteException {
System.out.println("Server.saydemo override");
return message;
}
}
Client.java
package RMI;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
public static void main(String[] args) {
if(System.getSecurityManager() == null)
{
System.setSecurityManager(new SecurityManager());
}
try {
Registry reg = LocateRegistry.getRegistry("127.0.0.1", 4096);
System.out.println("in try after reg locate");
DemoInterface h = (DemoInterface) reg.lookup("Hello");//Error Showed on this line by netbeans
System.out.println(h.SayDemo());
}
catch(RemoteException | NotBoundException e)
{
System.out.println(""+e );
}
}
}
please guide me where I am wrong. Thank You in advance.
You set a SecurityManager in your client main method. Did you also provide a security policy file? The default policy is not very permissive, and denies, among other things, Socket operations.
You can specify a policy that allows all permissions to all code bases like so.
grant {
permission java.security.AllPermission;
};
add it to your command line for invoking java. Substitute mypolicy for your policy file and SomeApp for your main class. Note the two = characters in the second argument
java -Djava.security.manager -Djava.security.policy==mypolicy SomeApp
Note that this is not a safe policy to run for RMI in a production environment (RMI can load remote code bases).
Proper use of the SecurityManager class and policy configuration is a complex topic, for further reading I suggest Java SE 7 Security Documentation and in particular Default Policy Implementation and Policy File Syntax
Below is the sample code which invoke 1st web service using dev keystore
and invoke 2nd web service using stage keystore.
public static void main(String args[]) {
System.setProperty("javax.net.ssl.trustStore",
"C:\\Users\\shahire\\Desktop\\Keystores\\Keystores\\dev\\dev.keystore");
System.out.println("1st web service call");
// 1st axis2 web service call code
System.setProperty("javax.net.ssl.trustStore",
"C:\\Users\\shahire\\Desktop\\Keystores\\Keystores\\stage\\stage.keystore");
System.out.println("2nd web service call");
// 2nd axis2 web service call code
}
I am able to call first web service call however i have been getting below error while accessing 2nd web service call
org.apache.axis2.AxisFault: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.apache.axis2.AxisFault.makeFault(AxisFault.java:417)
By looking at the exception i feel that it caching "javax.net.ssl.trustStore" location.
When i comment 1st web service call then i can able access the 2nd web service.
Just to be clear. I don't know if Axis2 actually for some reason "reuses" or caches as you say the truststore system property that you have provided; my best guess is that it initializes some object under the hood which reads the property and after it has been configured does not need to read it again.
But you can work arround this by putting all your trusted certificates in the same truststore. This will definetely solve your problem since as you say you actually can connect succesfully to the 2nd web service.
Why are you using different truststores in the first place?
If you have to, due to some security requirement (do you have one?) you should look into whether there are other ssl properties for Axis specifically that you are not using.
Υou could try an alternative way without modifying the properties provided by the JVM.
Here is a sample example:
package test.ssl;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyStore;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
public class SSLClient {
public void provider() throws Exception {
// first call
invokeWebServiceSSL(".../.../.../name.keystore", "changeit",
"https://../../");
// second call
// invokeWebServiceSSL(String keystorePath, String pass, String
// endpointURL)
}
public static void invokeWebServiceSSL(String keystorePath, String pass, String endpointURL) {
HttpsURLConnection conn = null;
try {
char[] password = pass.toCharArray();
FileInputStream fis = new FileInputStream(keystorePath);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(fis, password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
fis.close();
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLSocketFactory sf = ctx.getSocketFactory();
URL url = new URL(endpointURL);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sf);
InputStream inputstream = conn.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
String rs = null;
while ((rs = bufferedreader.readLine()) != null) {
System.out.println("Received: " + rs);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.getInputStream().close();
} catch (Exception e) {
}
}
}
}
I hope this helps.
I have been trying to get a connection between a Cpp server and a Java applet client but when I run the applet I get this error in the browser. I have the certificate in the trust store file (let me know if I don't need it). Any help would be appreciated.
access denied ("java.util.propertypermission"
"javax.net.ssl.truststore" "write")
import java.awt.*;
import java.applet.*;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import javax.swing.*;
public class no2 extends Applet {
int width, height;
public void init() {
System.setProperty("javax.net.ssl.trustStore", "keystore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
String trustStore = System.getProperty("javax.net.ssl.trustStore");
if (trustStore == null) {
System.out.println("javax.net.ssl.trustStore is not defined");
} else {
System.out.println("javax.net.ssl.trustStore = " + trustStore);
}
try {
PrintWriter toServer = null;
BufferedReader fromServer = null;
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("192.168.0.10", 12120);
String[] newProtocols = {"TLSv1"};
sslsocket.setEnabledProtocols(newProtocols);
toServer = new PrintWriter(sslsocket.getOutputStream(), true);
toServer.println("Data1\n");
toServer.println("Data2\n");
InputStreamReader isr = new InputStreamReader(sslsocket.getInputStream());
fromServer = new BufferedReader(isr, 1);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
You can't set system properties within an Applet. You can't access local file (keystore.jks) systems either (unless you request for a specific permission to do so).
You can pass special parameters to JVM like stated in documentation like this:
<APPLET archive="my_applet.jar" code="MyApplet" width="300" height="300">
<PARAM name="java_arguments" value="-Djavax.net.ssl.trustStore=keystore.jks">
</APPLET>
But again, take care of the special permissions you'd need to access local files.