The problem is related to the use of the default server implementation via com.sun.httpserver.
The class org.restlet.engine.connector.HttpExchangeCall should return the certificates in the getCertificates() method,
but it always returns null. This class is used in org.restlet.engine.connector.HttpsServerHelper
which in turn is the helper for the Restlet framework when using the server implementation com.sun.httpserver.
To fix this, a couple of things are needed.
First, a new class HttpsExchangeCall:
package org.restlet.engine.connector;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import org.restlet.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpsExchange;
/**
* The default {@link HttpExchangeCall} fails to extract certificates from the SSL connection.
* This class implements {@link #getCertificates()} to extract certificates.
*/
@SuppressWarnings("restriction")
public class HttpsExchangeCall extends HttpExchangeCall {
private static final Logger log = LoggerFactory.getLogger(HttpsExchangeCall.class);
private final HttpsExchange sexchange;
public HttpsExchangeCall(Server server, HttpExchange exchange) {
this(server, exchange, true);
}
public HttpsExchangeCall(Server server, HttpExchange exchange, boolean confidential) {
super(server, exchange, confidential);
if (exchange instanceof HttpsExchange) {
sexchange = (HttpsExchange) exchange;
} else {
sexchange = null;
}
}
@Override
public List<Certificate> getCertificates() {
if (sexchange == null) {
log.debug("Cannot extract peer certificates from unsecure connection.");
return null;
}
Certificate[] certs = null;
try {
certs = sexchange.getSSLSession().getPeerCertificates();
if (log.isDebugEnabled()) {
log.debug("Found " + (certs == null ? "no" : Integer.toString(certs.length)) + " peer certificate(s).");
}
} catch (Exception e) {
log.debug("Unable to find peer certificates - " + e);
}
List<Certificate> lcerts = null;
if (certs != null) {
lcerts = new ArrayList<Certificate>();
for (int i = 0; i < certs.length; i++) {
lcerts.add(certs[i]);
}
}
return lcerts;
}
}
Then a copy of HttpsServerHelper
renamed to HttpsServerHelper2 with one line modified. Replace the line
HttpsServerHelper.this.handle(new HttpExchangeCall(getHelped(),
with the line:
HttpsServerHelper2.this.handle(new HttpsExchangeCall(getHelped(),
This helper needs to be registered:
Engine.getInstance().getRegisteredServers().add(new HttpsServerHelper2(null));
and creating a Server now becomes very explicit:
Component component = new Component();
Server server = new Server(
(Context) null, Arrays.asList(Protocol.HTTPS),
(String) null, Constants.PORT_TEST, component.getServers().getNext(),
HttpsServerHelper2.class.getName()
);
component.getServers().add(server);
I'm hoping Restlet's own HttpExchangeCall will be updated to extract the certificates:
it is a minor fix and saves a lot of unneeded code required to work around the issue.
In the mean time, you can find all the source code (using Restlet 2.3.4) and a working example in the
restlet-clientcert Github project.