Getting client certificate in restlet
Asked Answered
C

2

0

I managed to implement an https restlet with bot client and server certificated. I can prove it works since if I call the server with an untrusted certification communication fails. Unfortunately I can't find the certificate of the client on the server. I'm using this code:

List<Certificate> certs = request.getClientInfo().getCertificates();

but list is empty. What I'm doing wrong?

EDIT:

version is Restlet-Framework/2.3m2

Cecilececiley answered 8/9, 2014 at 12:56 Comment(3)
this guy upgraded to 2.1 and had the same problem. No answer :( #13049355Mellissamellitz
does request.getAttributes()..get("org.restlet.https.clientCertificates"); work?Mellissamellitz
@Mellissamellitz Unfortunately it does not work too. AFAIK under the hood the call done by getCerificates rely on that attribute.Cecilececiley
D
1

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.

Deluxe answered 17/9, 2015 at 9:6 Comment(0)
R
-1

This method should provide what you are looking for: request.getClientInfo().getCertificates() http://restlet.com/learn/javadocs/2.3/jse/api/org/restlet/data/ClientInfo.html

You can also retrieve the cipher suites

Rectifier answered 9/9, 2014 at 23:20 Comment(4)
as you see in the OP the code you post is exactly the one I use, but does not work. I'm self hosting the restlet, could be this a problem?Cecilececiley
which container are you using, is it jetty, tomcat or what?Mellissamellitz
@Mellissamellitz I'm self hosting the http listening: ie use restlet for listening.Cecilececiley
@JeromeLouvel Can you take a look at my answer https://mcmap.net/q/1009082/-getting-client-certificate-in-restlet and maybe update Restlet's source code?Deluxe

© 2022 - 2024 — McMap. All rights reserved.