HttpsUrlConnection and keep-alive - java

I am using com.sun.net.httpserver.HttpsServer in my current project which deals with client-authentification etc.. Currently it only prints out the clients address/port, so that I can check if one TCP-connection is used for multiple requests (keep-alive) or if a new connection is established for every request (and thus a new SSL-handshake is made every time). When I use FireFox to make multiple request against the server I can see that keep-alive is working. So the server part works fine with GET and POST-requests.
If I use HttpURLConnection to make a request against the Server (in this case using no SSL) keep-alive works, too: Only one connection is established for multiple sequentially started requests.
But if I use HttpsURLConnection (using exactly the same code, but using SSL) then keep-alive is not working anymore. So for each request a new connection is established, although I am using the same SSLContext (and SSLSocketFactory):
// URL myUrl = ...
// SSLContext mySsl = ...
HttpsURLConnection conn = (HttpsURLConnection) myUrl.openConnection();
conn.setUseCaches(false);
conn.setSSLSocketFactory(mySsl.getSocketFactory());
conn.setRequestMethod("POST");
// send Data
// receive Data
How do I force HttpsURLConnection to use keep-alive because many requests will lead to many SSL-handshakes which is a real performance issue?
Update (2012-04-02):
Instead of calling mySsl.getSocketFactory() each time, I tried to cache the SSLSocketFactory. But nothing changed. The problem still exists.

I ran into this exact same problem and finally have a solution after some in-depth debugging.
Http(s)UrlConnection does handle Keep-Alive by default but sockets must be in a very specific condition in order to be reused.
These are:
Input streams must be fully consumed. You must call read on the input stream until it returns -1 and also close it.
Settings on the underlying socket must use the exact same objects.
You should call disconnect (yes this is counter-intuitive) on the Http(s)URLConnection when done with it.
In the above code, the problem is:
conn.setSSLSocketFactory(mySsl.getSocketFactory());
Saving the result of getSocketFactory() to a static variable during initialization and then passing that in to conn.setSSLSocketFactory should allow the socket to be reused.

I couldn't get it working with HttpsUrlConnection. But Apache's HTTP client handles keep-alive with SSL connections very well.

SSL connection establishment is really expensive either for service calls or when getting many resources from a browser.
Java Http(s)UrlConnection handles HTTP(S) Keep-Alive by default.
I have not found the source code of the default SSLSocketFactory and probably the keep-alive mechanism is implemented there. As a confirmation, disable your own SSLSocketFactory implementation for a test, with a custom trust store in javax.net.ssl.trustStore so that your self-signed certificate is accepted.
According to OpenJDK 7 ServerImpl implementation which uses ServerConfig the HttpsServer you used emits a keep-alive with 5 minutes timeout per default.
I propose you set the property sun.net.httpserver.debug to true server-side to get details.
Take care your code does not add the header Connection: close which disables keep-alive mechanism.

As far as I can understand HTTP/1.1 and HTTPS protocol, also documented here, Keep-Alive is not an end-to-end header but a hop-to-hop header. Since SSL involves multiple steps of handshaking among "different hops" (e.g. CA and the server) for each new connection, I think Keep-Alive may not be applicable in SSL context. So, that can be why Keep-Alive header is ignored using HTTPS connections. Based on this this question, you may need to ensure one instance of HTTP connection is used to guarantee Keep-Alive observation. Also, in the question, it seems that Apache HTTPClient has been a better solution.

We may setup an Apache Webserver, add following directives to see whether the Apache's access.log has a keep-alive connection for the http client.
LogFormat "%k %v %h %l %u %t \"%r\" %>s %b" common
CustomLog "logs/access.log" common
http://httpd.apache.org/docs/current/mod/mod_log_config.html
"%k" Number of keepalive requests handled on this connection. Interesting if KeepAlive is being used, so that, for example, a '1' means the first keepalive request after the initial one, '2' the second, etc...; otherwise this is always 0 (indicating the initial request).

I faced the same problem, and Bill Healey is right.
I tested my example code below with few https libraries.
HttpsURLConnection and OKHTTP are exact same behavior.
Volley is a bit different when session resumption, but almost same behavior.
I hope this will be some help.
public class SampleActivity extends Activity implements OnClickListener {
// Keep default context and factory
private SSLContext mDefaultSslContext;
private SSLSocketFactory mDefaultSslFactory;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
findViewById(R.id.button_id).setOnClickListener(this);
try {
// Initialize context and factory
mDefaultSslContext = SSLContext.getInstance("TLS");
mDefaultSslContext.init(null, null, null);
mDefaultSslFactory = mDefaultSslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
Log.e(TAG, e.getMessage(), e);
}
}
#Override
public void onClick(View v){
SSLContext sslcontext;
SSLSocketFactory sslfactory;
try {
// If using this factory, enable Keep-Alive
sslfactory = mDefaultSslFactory;
// If using this factory, enable session resumption (abbreviated handshake)
sslfactory = mDefaultSslContext.getSocketFactory();
// If using this factory, enable full handshake each time
sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, null);
sslfactory = sslcontext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
Log.e(TAG, e.getMessage(), e);
}
URL url = new URL("https://example.com");
HttpsURLConnection = conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslfactory);
conn.connect();
}
}
Update:
Sharing SSLSocketFactory enables keep-alive. Sharing SSLContext and getting facotry each request enable session resumption. I don't know how TLS stack works, but just confirmed these connection behaviors with some mobile devices.
If you want to enable keep-alive among multiple classes, you should share the instance of SSLSocketFactory using singleton pattern.
If you want to enable session resumption, make sure the session timeout settings is long enough on server side, such as SSLSessionCacheTimeout(apache), ssl_session_timeout(nginx).

In addition to #Bill Healey answer the HostnameVerifier also must be declared static.
I've tried several patterns with and without closing input stream and connection they make no change for me. The only thing that matters is the static declarations of mentioned properties.
/**
SSLSocketFactory and HostnameVerifier must be declared static in order to be able to use keep-alive option
*/
private static SSLSocketFactory factory = null;
private static HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
};
public static void prepareForCustomTrustIfNeeded(HttpsURLConnection connection) {
try {
if(factory == null) {
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(null, customTrustedCerts, new SecureRandom());
factory = sslc.getSocketFactory();
}
connection.setSSLSocketFactory(factory);
connection.setHostnameVerifier(hostnameVerifier);
} catch (Exception e) {
e.printStackTrace();
}
}

try to add the following code:
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");

Related

HttpsURLConnections Default Hostname Verifier

I'm using a HttpURLConnection in order create a POST request (for fetching a token at some OAuth2 token endpoint). The token endpoint uses HTTPS. I wonder how the hostname verification with regards to HTTPS works. The default hostname verifier of HttpsURLConnection seems to be the following [1]:
/**
* HostnameVerifier provides a callback mechanism so that
* implementers of this interface can supply a policy for
* handling the case where the host to connect to and
* the server name from the certificate mismatch.
*
* The default implementation will deny such connections.
*/
private static HostnameVerifier defaultHostnameVerifier =
new HostnameVerifier() {
public boolean verify(String urlHostname, String certHostname) {
return false;
}
};
I expected my POST request to fail as this verifier always returns false. This is not the case. The comment already states that there is some kind of callback mechanism. What I do not know is: Does the defaultHostnameVerifier verify the hostname of the connection and the certificate or is it rather a dummy implementation?
My current coding looks like the following piece:
private HttpURLConnection openConnection(String url) throws IOException {
URL urly = new URL(url);
final HttpURLConnection con;
Proxy proxy = getProxy();
if (proxy == null) {
con = (HttpURLConnection) urly.openConnection();
} else {
con = (HttpURLConnection) urly.openConnection(proxy);
}
if (con instanceof HttpsURLConnection) {
HostnameVerifier verifier = ((HttpsURLConnection) con).getHostnameVerifier(); // there is a default set
System.out.println(verifier.getClass().getName());
}
return con;
}
I've found some explanation with regards to the AsyncHttpClient [2]. As I do not use it at this point of time am I safe going with the default implementation?
[1] https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/com/sun/net/ssl/HttpsURLConnection.java#L76
[2] https://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/
As the comments at the top say, that class is no longer used; it was the abstract user-visible class that is now replaced by javax.net.HttpsURLConnection which you will observe has the same code. But the implementation class for https URL is sun.net.www.protocol.https.HttpsURLConnectionImpl which just wraps (delegates to) sun.net.www.protocol.https.DelegateHttpsURLConnection which subclasses sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection which to make the actual connection uses sun.net.www.protocol.HttpsClient and in particular .afterConnect(). As background, in earlier versions of Java SSLSocket did not do hostname verification, so the implementation of HttpsURLConnection had to add it. In Java 7 up, SSLSocket does support 'endpoint identification', so when afterConnect() recognizes that the hostnameVerifier on this URLconnection was the default one, it turns off needToCheckSpoofing and sets the SSLSocket to do endpoint identification.
javax.net.ssl.SSLSocket is similarly an abstract class that is actually implemented by sun.security.ssl.SSLSocketImpl and several dozen related classes including sun.security.ssl.ClientHandshaker.serverCertificate() which calls the configurable trustmanager which by default is sun.security.ssl.X509TrustManagerImpl which in checkTrusted() since endpoint identification was requested calls checkIdentity() which calls sun.security.util.HostnameChecker with TYPE_TLS (actually meaning HTTPS RFC 2818), and that does the actual checking.
Glad you asked?
PS: the analysis on that webpage, that HttpsURLConnection.DefaultHostnameVerifier is called only for mismatch, is quite wrong. As above it is bypassed and never called by the actual implementation.
Also I assume you realize java 7 has not been supported for years unless you pay. Although this area hasn't changed that I know of in more recent versions. Java 11 does add a new java.net.http.HttpClient which functionally supersedes [Http,Https]URLConnection.

Connection timeouts to HTTPS URLs

I'm needing to ignore all SSL certificates in Java, but I can't for the life of me get it to work. I've looked through the following pages listed below already, but nothing seems to work on every https link.
stackoverflow.com/questions/19517538/ignoring-ssl-certificate-in-apache-httpclient-4-3
stackoverflow.com/questions/13470998/ignoring-ssl-validation-in-java
stackoverflow.com/questions/12060250/ignore-ssl-certificate-errors-with-java
stackoverflow.com/questions/2694281/ignore-certificate-errors-when-requesting-a-url-in-java
stackoverflow.com/questions/6681969/java-ignore-certificate-validation
www.nakov.com/blog/2009/07/16/disable-certificate-validation-in-java-ssl-connections/
code.google.com/p/misc-utils/wiki/JavaHttpsUrl
www.exampledepot.8waytrips.com/egs/javax.net.ssl/TrustAll.html
www.obsidianscheduler.com/blog/ignoring-self-signed-certificates-in-java/
java.dzone.com/articles/how-ignore-cert-and-host-name
gist.github.com/henrik242/1510165
I have a good reason for needing to do this so don't worry, but I really need to be able to do it. Basically, I'm needing to go through a list of internal https links and check to make sure that they are all still valid and aren't broken links. Some links works fine since the Java code ignores the certificate and can get an HTTP response header back, but others just timeout even though they work fine in my web browser. All of these links are internal company links.
I've tried using HttpsURLConnection as well as HttpGet and HttpClient. Could there be something else that I'm not thinking of, or something unrelated to Java that could be causing the pages to timeout? I just want to make sure the URL of the link exists. Here are the exceptions I am getting.
With HttpGet/SSLContextBuilder/PoolingHttpClientConnectionManager:
org.apache.http.conn.HttpHostConnectException: Connect to -removed- [-removed-] failed: Connection timed out: connect
With HttpsUrlConnection using X509TrustManager:
java.net.ConnectException: Connection timed out: connect
Specifically, I've tried the following and many variations of it based on the links posted above:
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
// Install the all-trusting trust manager
javax.net.ssl.SSLContext sc = null;
try {
sc = javax.net.ssl.SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
I've also tried this as well as several variations: https://stackoverflow.com/a/19950935/1727920
Connection timeouts have nothing whatsoever to do with SSL certificates.
More likely you don't have the same HTTP Proxy settings as the browser. You need to set the system properties http.proxyHost and http.proxyPort to the same values used by the browser. If the HTTPS proxy settings are different from the HTTP proxy settings, set https.proxyHost and https.proxyPort accordingly.
EDIT For completeness: A lot of old sources erroneously mention a proxySet property. There is not and has never been such a property in the JDK. It was in the short-lived and long-defunct HotJava Bean of 1997. Similarly http.proxySet doesn't exist either. Proof: try setting them to false in circumstances where they should be true, and watch your program keep working.

ProxySelector changes URL's scheme from https:// to socket://

I need to access Facebook but all outgoing communication is blocked on our server so I have to use proxy.
I initialize proxies with:
ProxySelector.setDefault(new ConfigurableProxySelector(mapping));
Proxy type is HTTP, proxy host and port are working (confirmed by simple wget test).
I'm trying to do this:
HttpClient httpClient = new HttpClient();
HttpMethod method = new GetMethod("https://graph.facebook.com:443");
int status = httpClient.executeMethod(method);
Now, in my class ConfigurableProxySelector I have select method on which I have breakpoint:
public List<Proxy> select(URI uri) {
...
}
So, using HttpClient I make an request, which should be proxied and code stops at breakpoint in select() method in ConfigurableProxySelector.
But what is strange is that uri.scheme = "socket" and .toString() gives "socket://graph.facebook.com:443" instead of "https://graph.facebook.com:443".
Because ProxySelector have mapping for "https://" and not for "socket://", it does not find it and it ends with "Connection refused". What is strange is that select() method is called 4 times before execution ends with "Connection refused".
Any help would be appreciated.
Apache HTTP Client 3.1 will not natively honor HTTP Proxies returned from the default ProxySelector or user implementations.
Quick Summary of ProxySelector
ProxySelector is a service class which selects and returns a suitable Proxy for a given URL based on its scheme. For example, a request for http://somehost will try to provide an HTTP proxy if one is defined. The default ProxySelector can be configured at runtime using System Properties, such as http.proxyHost and http.proxyPort.
HTTPUrlConnection
An instance of HTTPUrlConnection will check against the default ProxySelector multiple times: 1st to select for http or https, then later when it builds the raw tcp socket, using the socket scheme. A SOCKS proxy could be used to proxy a raw tcp socket but are not often found in corporate environments, so a raw tcp socket will usually receive no proxy.
HTTP Client 3.1
HC 3.1, on the other hand, will never check the default ProxySelector for the http/https schemes. It will check, however, at a later points for the socket scheme when it eventually builds the raw socket - This is the request you are seeing. This means the System Properties http.proxyHost and http.proxyPort are ineffective. This is obviously not ideal for most people who only have an HTTP/HTTPS proxy.
To work around this, you have two options: define a proxy on each HC 3.1 connection or implement your own HC 3.1 HTTPConnectionManager.
HTTPConnectionManager
The HTTPConnectionManager is responsible for building connections for the HC 3.1 client.
The default HC 3.1 HTTPConnectionManager can be extended so that it looks for a suitable proxy from a ProxySelector (default or custom) when building the request in the same way HTTPUrlConnection does:
public class MyHTTPConnectionManager extends SimpleHttpConnectionManager {
#Override
public HttpConnection getConnectionWithTimeout(
HostConfiguration hostConfiguration, long timeout) {
HttpConnection hc = super.getConnectionWithTimeout(hostConfiguration, timeout);
try {
URI uri = new URI( hostConfiguration.getHostURL());
List<Proxy> hostProxies = ProxySelector.getDefault().select(uri);
Proxy Proxy = hostProxies.get(0);
InetSocketAddress sa = (InetSocketAddress) Proxy.address();
hc.setProxyHost(sa.getHostName());
hc.setProxyPort(sa.getPort());
} catch (URISyntaxException e) {
return hc;
}
return hc;
}
}
Then, when you create an HC 3.1 client, use your new connection manager:
HttpClient client = new HttpClient(new MyHTTPConnectionManager() );
It's not the ProxySelector that changes the scheme, but the SocketFactory opening a Socket.
If the SocketFactory is null a SOCKS socket will be created by default which only allows SOCKS proxies. I don't know anything about Sockets and cannot tell you if there's a way to make it work with HTTP proxies.
But using another approach may help, since Apache HttpClient seems to have its own way to configure proxies.
client.getHostConfiguration().setProxy(proxyHost, proxyPort);
if (proxyUser != null) {
client.getState().setProxyCredentials(new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUser, proxyPassword));
}

How can I override the "Host" header in the request when using Apache commons HttpClient

I am using Jakarta Commons HttpClient 3.1 writing a load test tool that needs to target different servers and pretend like it targeted the correct virtual host in the HTTP server. For that I need to be able to set the "Host" HTTP header in the request to a different host name then the actual host name that I'm connecting to.
It seemed pretty obvious that I should use Method.setRequestHeader("Host","fakehostname"), but HttpClient just ignores this and always sends the real host name I'm connecting to in the "Host" header (I've enabled debug logging for "httpclient.wire" and I can it does this specifically).
How can I override the header so that HttpClient takes heed?
After searching some more, and taking a hint from Oleg's answer, I've found the method HttpMethodParams::setVirtualHost().
when HttpClient formats a request, it always creates the "Host" header itself just before sending the request - so it cannot be overridden as a standard header. But before the host name for the "Host" header is generated from the URL, HttpClient checks the HttpMethodParams object to see if the user wants to override the host name. This only overrides the host name and not the port so it would be easier to use, though not as intuitive as I'd like.
The code to use this can look like this:
Method m = new GetMethod("http://some-site/some/path");
m.getParams().setVirtualHost("some-other-site");
client.executeMethod(m);
Because I like one liners, this can also be written as:
client.executeMethod(new GetMethod("http://some-site/some/path") {{
getParams().setVirtualHost("some-other-site"); }});
I believe you want http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/HttpHost.html: this lets you configure the host for a specific connection. If I understand it correctly, you can either use the execute method (see http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/AbstractHttpClient.html#execute(org.apache.http.HttpHost,%20org.apache.http.HttpRequest)) and pass it a custom HttpHost object, or do this:
Construct an HttpHost instance, passing it your Host header.
Use that to create an HttpRoute instance (see http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/routing/HttpRoute.html)
Pass that to the connection manager when you request a connection (see http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ClientConnectionManager.html#requestConnection(org.apache.http.conn.routing.HttpRoute,%20java.lang.Object)).
Use the connection with your method: see http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html for more details.
Let me know how that works.
EDIT: principle remains the same.
1. Construct an HttpHost instance, passing it your Host header (see http://hc.apache.org/httpclient-legacy/apidocs/index.html?org/apache/commons/httpclient/HttpHost.html).
2. Create an HttpConfiguration instance and then pass it the HttpHost you created (see http://hc.apache.org/httpclient-legacy/apidocs/index.html?org/apache/commons/httpclient/HostConfiguration.html).
3. Use the execute method on HttpClient with that configuration (see http://hc.apache.org/httpclient-legacy/apidocs/org/apache/commons/httpclient/HttpClient.html#executeMethod(org.apache.commons.httpclient.HostConfiguration,%20org.apache.commons.httpclient.HttpMethod))
Following works on android:
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
InputStream stream_content=null;
try
{URL url=new URL("http://74.125.28.103/");
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("GET");
conn.setRequestProperty("Host", "www.google.com");
stream_content=conn.getInputStream();
}
catch (Exception e) {}
for https url:
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
InputStream stream_content=null;
try
{URL url=new URL("https://74.125.28.103/");
HttpsURLConnection conn=(HttpsURLConnection)url.openConnection();
conn.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER );
conn.setDoOutput(true);
conn.setRequestMethod("GET");
conn.setRequestProperty("Host", "www.google.com");
stream_content=conn.getInputStream();
}
catch (Exception e) {}
One can use the 'http.virtual-host' parameter in order to force an arbitrary (virtual) hostname and port as a value of the Host request header instead of those derived from the actual request URI. This works with the 4.x API only, though.

How do I use a local HTTPS URL in java?

Using the Java URL class, I can connect to an external HTTPS server (such as our production site), but using a local URL I get following exception.
"SunCertPathBuilderException: unable to find valid certification path to requested target".
How do I get a valid certification path?
EDIT: I'm not using this URL to directly create a connection, I am passing the URL to an itext PDFReader, which is then having the connection issue.
Here was my solution that incorporates some of the ideas in this thread and peiced together with code from around the net.
All I do call this function and it sets the default Trust Manager and HostName Verifier for HttpsURLConnection. This might be undesirable for some because it will effect all HttpsURLConnections but I'm just writing a simple proxy so it worked for me.
private void setTrustAllCerts() throws Exception
{
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType ) { }
public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType ) { }
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance( "SSL" );
sc.init( null, trustAllCerts, new java.security.SecureRandom() );
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(
new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
});
}
catch ( Exception e ) {
//We can not recover from this exception.
e.printStackTrace();
}
}
You probably need to setup a HostnameVerifier. Before connecting, you need add it to the connection object
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
// check hostname/session
return true;
}
});
conn.connect();
// read/write...
There certainly are some implementations out there, if you need one. You might want to look at HttpClient project too.
Another thing to look at is the TrustManager you are using. The error message suggests that the certificate presented by the server is not signed by a trusted root. Since you don't have direct control over the SSL socket that is created, I think your best bet is to initialize your own SSLContext with a TrustManager that's been setup with the root CA of the server's certificate chain. Then set this context as the default.
This is assuming you are using Java 6. The API is more limited in Java 5. You can get a default SSLSocketFactory, but there's no standard way to set it.
The problem it's complaining about is that when you create an SSL connection, the server must present a valid certificate to the client. You can write an appropriate endpoint in Java (HTTPServerSocket will do it I think) but it would require some hacking about to set it up. It's probably easier to set up a local web server with anything that handles SSL correctly --- Apache, lighttp, whatever --- and create a self-signed cert using the openssl tools.
Updated
Here's an example from the Java Almanac. http://www.exampledepot.com/egs/javax.net.ssl/Server.html
An SSL server socket requires certificates that it will send to clients for authentication. The certificates must be contained in a keystore whose location must be explicitly specified (there is no default). Following the example we describe how to create and specify a keystore for the SSL server socket to use.
try {
int port = 443;
ServerSocketFactory ssocketFactory = SSLServerSocketFactory.getDefault();
ServerSocket ssocket = ssocketFactory.createServerSocket(port);
// Listen for connections
Socket socket = ssocket.accept();
// Create streams to securely send and receive data to the client
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
// Read from in and write to out...
// Close the socket
in.close();
out.close();
} catch(IOException e) {
}
Specify the keystore of certificates using the javax.net.ssl.keyStore system property:
> java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax.net.ssl.keyStorePassword=123456 MyServer
It may also help you to add the certificate that the localhost server is using (I assume it's self-signed) to the JVM's keystore, using the "keytool" utility. This should have the effect of telling the JVM "you can trust this certificate".
I have ended up running a static method (only on dev) that installs a very trusting TrustManager (accepts everything), and also added a hostnameVerifier that always returns true (thanks sblundy).

Categories