Java HTTPS Server "Unsupported Protocol Error" in Chrome - java

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;
}

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;
}

Mqtt client ssl example in java

I am tired to connect facebook MQTT server ( edge-mqtt.facebook.com:443 ) with ssl , I'm using Eclipse Paho for MQTT connection .
private final String DEFAULT_HOST = "edge-mqtt.facebook.com";
private final int DEFAULT_PORT = 443;
public void connect(String protogle) throws Exception {
this.broker = protogle + "://"+ DEFAULT_HOST + ":" + DEFAULT_PORT;
this.mqttClient = new MqttClient(broker,getMqttClientId() ,new MemoryPersistence() );
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
connOpts.setKeepAliveInterval( MQTT_KEEPALIVE);
connOpts.setUserName( getMqttUsername() );
connOpts.setPassword( getMqttPassword().toCharArray() );
connOpts.setMqttVersion( 3 );
//connOpts.setSocketFactory(getSocketFactory (caCrtFile,crtFile,keyFile,password) );
Logger.w("Connecting to broker: "+broker);
Logger.w("isConnected:"+mqttClient.isConnected());
try {
IMqttToken cn = mqttClient.connectWithResult(connOpts);
Logger.w("connected");
}catch (MqttException me){
System.out.println("reason "+me.getReasonCode());
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
return;
}
this.mqttClient.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable me) {
Logger.w("Connection lost");
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
}
#Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
Logger.w("message Arrived");
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
Logger.w("deliverd--------");
try {
MqttDeliveryToken token = (MqttDeliveryToken) iMqttDeliveryToken;
String h = token.getMessage().toString();
Logger.w("deliverd message :"+h);
} catch (MqttException me) {
System.out.println("reason "+me.getReasonCode());
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
} catch (Exception e) {
e.printStackTrace();
}
}
});
public SSLSocketFactory getSocketFactory (final String caCrtFile, final String crtFile, final String keyFile,
final String password) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
// load CA certificate
PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
X509Certificate caCert = (X509Certificate)reader.readObject();
reader.close();
// load client certificate
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
X509Certificate cert = (X509Certificate)reader.readObject();
reader.close();
// load client private key
reader = new PEMReader(
new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),
new PasswordFinder() {
#Override
public char[] getPassword() {
return password.toCharArray();
}
}
);
KeyPair key = (KeyPair)reader.readObject();
reader.close();
// CA certificate is used to authenticate server
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
// client key and certificates are sent to server so it can authenticate us
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", cert);
ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
// finally, create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
}
I'm searching for how to create caCrtFile , crtFile and keyFile and got many answers . i confused and now i don't know which answer is true . for example i got this answer . but i can't implement that and all times i got SSL error . any one can give me example to creating these files for edge-mqtt.facebook.com:443 ?
You do only need a crtFile and keyFile if you are trying to do SSL mutual authentication. If this is the case then Facebook would issue you with the required files.
The caCrtFile is a certificate chain to verify the certificate supplied by the Facebook broker when you connect to it. The certificate for edge-mqtt.facebook.com issued by DigiCert Inc so the required CA cert should already be included in the Java Runtime.
All this means you should be able to use the default SSLSocketFactory witoout having to add your own truststore or keystore.
EDIT:
So delete all of the getSocketFactory() method and replace it with SSLContext.getDefault().getSocketFactory();

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 Web Sockets with SSL - SSLHandshakeException - no cipher suites in common

I am trying to create web socket server in Java which can accept connections from web clients (using Https). When open URL "https://localhost:8887" in chrome, Java application throws error: javax.net.ssl.SSLHandshakeException: no cipher suites in common. Below are the steps I have followed.
1) First I have created a keystore using following command (as mentioned # https://github.com/TooTallNate/Java-WebSocket/issues/160):
keytool -genkey -validity 3650 -keystore "keystore.jks" -storepass "storepassword" -keypass "keypassword" -alias "default" -dname "CN=127.0.0.1, OU=MyOrgUnit, O=MyOrg, L=MyCity, S=MyRegion, C=MyCountry"
2) Below is my Java web socket server creation code:
public class SocketServer extends WebSocketServer {
/** The web socket port number */
private static int PORT = 8887;
private static volatile SocketServer socketServer = null;
/**
* private constructor Creates a new WebSocketServer with the wildcard IP
* accepting all connections.
*/
private SocketServer() {
super(new InetSocketAddress(PORT));
}
/**
* Get singleton instance of Socket Server class
*/
public static SocketServer getInstance() {
if (socketServer == null) {
synchronized (SocketServer.class) {
// Double check
if (socketServer == null) {
WebSocketImpl.DEBUG = true;
socketServer = new SocketServer();
// load up the key store
String STORETYPE = "JKS";
String KEYSTORE = "keystore.jks";
String STOREPASSWORD = "storepassword";
String KEYPASSWORD = "keypassword";
try {
KeyStore ks = KeyStore.getInstance( STORETYPE );
File kf = new File( KEYSTORE );
ks.load( new FileInputStream( kf ), STOREPASSWORD.toCharArray() );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( ks, KEYPASSWORD.toCharArray() );
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( ks );
SSLContext sslContext = null;
sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );
socketServer.setWebSocketFactory( new DefaultSSLWebSocketServerFactory( sslContext ) );
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
socketServer.start();
}
}
}
return socketServer;
}
Please suggest what could be the issue?
Thanks in advance!

Setting the certificate used by a Java SSL ServerSocket

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);

Categories