I want to start Apache HTTP server in Java.
After a while I found this answer. And did as indicated there.
The KeyStore was created according to this instruction.
My code now:
import me.project.Main;
import org.apache.http.ExceptionLogger;
import org.apache.http.config.SocketConfig;
import org.apache.http.impl.bootstrap.HttpServer;
import org.apache.http.impl.bootstrap.ServerBootstrap;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
public class Server {
private static final Logger logger = LoggerFactory.getLogger(Server.class);
private static HttpServer server;
public static void createAndStart() {
if (server != null) return;
SSLContext sslContext = prepareSSLContext();
SocketConfig config = SocketConfig.custom().setSoTimeout(1500).setTcpNoDelay(true).build();
server = ServerBootstrap.bootstrap()
.setListenerPort(43286)
.setServerInfo("Test")
.setSocketConfig(config)
.setSslContext(sslContext)
.setExceptionLogger(ExceptionLogger.STD_ERR)
.registerHandler("/", new HelloPage())
.create();
System.out.println("Ok");
try {
server.start();
} catch (IOException ioException) {
logger.error("Cannot start server: ", ioException);
}
}
private static SSLContext prepareSSLContext() {
URL keyStoreFile = Main.class.getClassLoader().getResource("keystore.jks");
if (keyStoreFile == null) {
logger.error("Key store not found");
System.exit(1);
}
SSLContext sslContext = null;
try {
sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStoreFile, "mysuperpass".toCharArray(), "mysuperpass".toCharArray()).build();
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException | CertificateException | IOException e) {
logger.error("Cannot init ssl context", e);
}
return sslContext;
}
}
It starts sucessfully and prints "Ok".
But a few things I still don't understand:
It seems to me that this is still SSL protocol, not TLS
How can I make sure that the server really communicates using the encrypted TLS protocol (except to intercept traffic and watch)
How does the loadKeyMaterial (keyStore, storePass, KeyPass) method know which key to take from the store without the key alias?
I found another example of enabling TLS here, but I don't understand where to use the object conn after:
DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection(8 * 1204);
conn.bind(socket);
Sorry, I don't know English well and translated this with Google Translate
It seems to me that this is still SSL protocol, not TLS
The classes were originally implemented for SSL(v3) in the 1990s, but they have implemented TLS since about 2000 (first 1.0, later 1.1 1.2 and now 1.3) while the names have remained the same for compatibility. In fact since 8u31 in 2014 SSLv3 has been disabled by default in the JVM configuration because it was broken by the "POODLE" attack and the 'SSL' classes in fact provide only TLS.
How can I make sure that the server really communicates using the encrypted TLS protocol (except to intercept traffic and watch)
Try connecting to it with TLS and non-TLS tools.
How does the loadKeyMaterial (keyStore, storePass, KeyPass) method know which key to take from the store without the key alias?
It uses the JSSE-default KeyManager which implements chooseServerAlias to return the first key-and-cert entry (i.e. PrivateKeyEntry) it finds that is suitable for a client-enabled ciphersuite -- or for TLS 1.3 where this is no longer in the ciphersuite, a client-enabled sigalg. IIRC 'first' is in a hashmap-defined order which means for practical/human purposes it is arbitrary. But most servers use a keystore that contains only one PrivateKeyEntry so all choice sequences are the same.
Related
I'm new to messing around with APIs (both official and unofficial) and I'm using one called JavaSnap. I've been messing around with a very basic implementation of the example code, but have been running into errors. Here is the very basic code:
Snapchat snapchat = Snapchat.login("xxxx", "xxxxx");
Firstly I ran into loads of ClassNotFound errors and had to keep on downloading apache modules (commons, httpcomponents etc.) to allow the program to progress, but being class files this meant I couldn't see all at once what modules I needed to download. So if anyone wants to tell me how wrong I'm doing something feel free.
Anyway, now having cleared up all the ClassNotFound exceptions (I hope) I'm getting the following exception:
com.mashape.unirest.http.exceptions.UnirestException: javax.net.ssl.SSLPeerUnverifiedException: Host name 'feelinsonice-hrd.appspot.com' does not match the certificate subject provided by the peer (CN=*.appspot.com, O=Google Inc, L=Mountain View, ST=California, C=US)
at com.mashape.unirest.http.HttpClientHelper.request(HttpClientHelper.java:146)
at com.mashape.unirest.request.BaseRequest.asJson(BaseRequest.java:68)
at com.habosa.javasnap.Snapchat.requestJson(Snapchat.java:953)
at com.habosa.javasnap.Snapchat.login(Snapchat.java:160)
at Tester.go(Tester.java:21)
As I understand it, this is because I need to enable trusting all certificates, however to do this I believe I'd need to use HostNameVerifiers with SSLSocketFactorys, but I can't really begin to mess around with this as I only have the source for the JavaSnap API, and tracing the error up the stack the most recent method available for me to edit is this:
private static HttpResponse<JsonNode> requestJson(String path, Map<String, Object> params, File file) throws UnirestException {
MultipartBody req = prepareRequest(path, params, file);
// Execute and return response as JSON
HttpResponse<JsonNode> resp = req.asJson();
// Record
lastRequestPath = path;
lastResponse = resp;
lastResponseBodyClass = JsonNode.class;
return resp;
My question is, am I actually on the right lines with my thinking? If I am how can I achieve my goal of eliminating this error / trusting certificates? If I'm not then what in fact is the problem?
Thanks very much
i answer this old question to remember my search
the certificate error solution is a combination from a few places
https://github.com/Mashape/unirest-java/issues/70, where i started.
http://literatejava.com/networks/ignore-ssl-certificate-errors-apache-httpclient-4-4/ very good explanation.
http://www.baeldung.com/httpclient-ssl, solution for all versions.
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
public class XXX {
private static HttpClient unsafeHttpClient;
static {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy() {
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
unsafeHttpClient = HttpClients.custom().setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
e.printStackTrace();
}
}
public static HttpClient getClient() {
return unsafeHttpClient;
}
public static void main(String[] args) {
try {
HttpClient creepyClient = RestUnirestClient.getClient();
Unirest.setHttpClient(creepyClient);
HttpResponse<JsonNode> response = Unirest.get("https://httpbin.org/get?show_env=1").asJson();
System.out.println(response.getBody().toString());
} catch (UnirestException e) {
e.printStackTrace();
}
}
}
I am using the following code in Java to print out various properties of Google's certificate.
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.SocketFactory;
import java.io.*;
import java.math.*;
import java.net.*;
import java.security.*;
import javax.net.*;
import javax.security.cert.X509Certificate;
/*
* Start an connection with google.com and submit to Google to figure out how to get the certificate.
* Should not pull from artificial context.
*/
public class MWE{
public static void main(String[] args) throws Exception{
SSLContext sslContext = SSLContext.getDefault();
SocketFactory clientSocketFactory = sslContext.getSocketFactory();
String remoteHost = "google.com";
int remotePort = 443;
SSLSocket socket = null;
try {
//Lookup the "common name" field of the certificate from the remote server:
socket = (SSLSocket) clientSocketFactory.createSocket(remoteHost, remotePort);
socket.setEnabledCipherSuites(socket.getSupportedCipherSuites());
socket.startHandshake();
} catch (IOException ioe) {
ioe.printStackTrace();
}
X509Certificate[] c = socket.getSession().getPeerCertificateChain();
X509Certificate serverCertificate = c[0]; //can I control which instance of this is used?
Principal serverDN = serverCertificate.getSubjectDN();
BigInteger serverSerialNumber = serverCertificate.getSerialNumber();
System.out.println(serverCertificate.getClass());
System.out.println(serverDN);
System.out.println(serverSerialNumber.toString(16));
System.out.println(serverCertificate.getSigAlgName());
System.out.println(serverCertificate.getNotBefore());
System.out.println(serverCertificate.getNotAfter());
}
}
The output I get looks like this:
CN=*.google.com, O=Google Inc, L=Mountain View, ST=California, C=US
1484d9a3000000007d35
SHA1withRSA
Wed Feb 20 05:34:43 PST 2013
Fri Jun 07 12:43:27 PDT 2013
However, when I view the certificate from Firefox or Chrome, everything matches except the serial number.
Your Firefox certificate info shows the certificate for www.google.com, while your Java code displays the certificate for google.com.
Those two sites have different certificates, and therefore different serials.
I need to make an ssl handshake in a program in order to get some information about the remote server like the public key and cipher suits. I am aware of something called stealth handshake which does not complete the handshake but get the needed information like what I have mentioned. Can any body give explanation on how to do this in Java. I tried to search but not able to find exact concrete method.
Stealth? Never heard of this.
You could register a javax.net.ssl.HandShakeCompletedListener to your ssl client socket to get the certificate etc. but after the handshake has been completed
javax.net.ssl.HandShakeCompletedListener is an interface implemented
by any class which wants to receive notification of the completion of
an SSL protocol handshake on a given SSLSocket connection.
And you can also process handshaking data via SSLEngine.
Study JSSE Ref guide
Stelath mode not sure about this but I used to do some thing like below to retrieve the certificate chains from the server
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class GetCertificates {
public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, IOException{
String host="google.com";
int port = 443;
SSLContext ssl = SSLContext.getInstance("TLS");
ssl.init(null, new TrustManager[]{new SimpleX509TrustManager()}, null);
SSLSocketFactory factory = ssl.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(host,port);
SSLSession session = socket.getSession();
javax.security.cert.X509Certificate[] certs = session.getPeerCertificateChain();
System.out.println(certs[certs.length-1].getSubjectDN());
// you can display certificates info here and also cipher suites
session.getCipherSuite();
session.invalidate();
}
}
class SimpleX509TrustManager implements X509TrustManager {
public void checkClientTrusted(
X509Certificate[] cert, String a)
throws CertificateException {
}
public void checkServerTrusted(
X509Certificate[] cert, String a)
throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
We wish to buy a wild-card SSL certificate as we have a lot of sub-domains. However I don't know if Java trusts wild-card certificates. As people connect into our API via SSL it will not be sufficient for us to force all third parties we communicate with to add our SSL certificate into their local truststore.
At the moment I'm facing a dilemma to buy a wildcard certificate from a java trusted issuer or buy multiple certs one per sub-domain.
Do other languages also have a truststore? If so does anyone know if wildcard certificates work with them also.
The default implementation in Sun's JSSE doesn't support wildcard. You need to write your own X509TrustManager to handle wildcard.
However, Java supports SAN (Subject Alternative Names) since Java 5. If you have less than 20 names, you can get one certificate for all of them. It may be cheaper than a wildcard cert.
I've attempted this with java 6.
It appears to work correctly. I've succesfully read headers and body content from a file that had a wildcard SSL certificate.
package com.example.test;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class SSLTEST {
public static void main(String[] args) {
try {
URL url = new URL("https://test.example.com/robots.txt");
URLConnection connection = null;
try {
connection = url.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
Map<String, List<String>> fields = connection.getHeaderFields();
Iterator<Entry<String, List<String>>> headerIterator = fields.entrySet().iterator();
System.out.println("HEADERS");
System.out.println("-------------------------------");
while (headerIterator.hasNext()){
Entry<String, List<String>> header = headerIterator.next();
System.out.println(header.getKey()+" :");
Iterator<String> valueIterator = header.getValue().iterator();
while (valueIterator.hasNext()){
System.out.println("\t"+valueIterator.next());
}
}
String inputLine;
DataInputStream input = new DataInputStream(connection.getInputStream());
System.out.println("BODY CONTENT");
System.out.println("-------------------------------");
while ((inputLine = input.readLine()) != null) {
System.out.println(inputLine);
}
} catch (MalformedURLException e) {
System.err.println(e);
} catch (IOException e) {
e.printStackTrace();
}
}
}
EDIT I've just recieved confirmation that this works on java 1.5
I'm accessing an internal database using MATLAB's urlread command, everything was working fine until the service was moved to a secure server (i.e. with an HTTPS address rather than an HTTP address). Now urlread no longer successfully retrieves results. It gives an error:
Error downloading URL. Your network connection may be down or your proxy settings improperly configured.
I believe the problem is that the service is using an invalid digital certificate since if I try to access the resource directly in a web browser I get "untrusted connection" warning which I am able to pass through by adding the site to an Exception list. urlread doesn't have an obvious way of handling this problem.
Under the hood urlread is using Java to access web resources, and the error is thrown at this line:
inputStream = urlConnection.getInputStream;
where urlConnection is a Java object: sun.net.www.protocol.https.HttpsURLConnectionImpl.
Anyone suggest a workaround for this problem?
Consider the following Java class. Borrowing from this code: Disabling Certificate Validation in an HTTPS Connection
C:\MATLAB\MyJavaClasses\com\stackoverflow\Downloader.java
package com.stackoverflow;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.HostnameVerifier;
public class Downloader {
public static String getData(String address) throws Exception {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Create a host name verifier that always passes
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
// open connection
URL page = new URL(address);
HttpURLConnection conn = (HttpURLConnection) page.openConnection();
BufferedReader buff = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// read text
String line;
StringBuffer text = new StringBuffer();
while ( (line = buff.readLine()) != null ) {
//System.out.println(line);
text.append(line + "\n");
}
buff.close();
return text.toString();
}
public static void main(String[] argv) throws Exception {
String str = getData("https://expired.badssl.com/");
System.out.println(str);
}
}
MATLAB
First we compile the Java class (we must use a JDK version compatible with MATLAB):
>> version -java
>> system('javac C:\MATLAB\MyJavaClasses\com\stackoverflow\Downloader.java');
Next we instantiate and use it MATLAB as:
javaaddpath('C:\MATLAB\MyJavaClasses')
dl = com.stackoverflow.Downloader;
str = char(dl.getData('https://expired.badssl.com/'));
web(['text://' str], '-new')
Here are a few URLs with bad SSL certificates to test:
urls = {
'https://expired.badssl.com/' % expired
'https://wrong.host.badssl.com/' % wrong host
'https://self-signed.badssl.com/' % self-signed
'https://revoked.grc.com/' % revoked
};
UPDATE: I should mention that starting with R2014b, MATLAB has a new function webread that supersedes urlread.
thanks for the solution. It worked, however, sometimes, I had received the following exception "java.io.IOException: The issuer can not be found in the trusted CA list." and I was not able to get rid of this error.
Therefore, I tried an alternative solution that works well. You can use the following Java code in Matlab function:
function str = ReadUrl(url)
is = java.net.URL([], url, sun.net.www.protocol.https.Handler).openConnection().getInputStream();
br = java.io.BufferedReader(java.io.InputStreamReader(is));
str = char(br.readLine());
end
Best,
Jan
Note also that the "canonical" way to solve this issue is to import the certificate into MATLAB's keystore (i.e., not your JVM's keystore).
This is documented here: Mathworks on using untrusted SSL certificates.