Hi,
Ben Johnson wrote:
Hi
I am new to Restlet and web programming, HTTP and SSL certificates in
general, but hopefully my recent experiences will help. I spent the
last several days trying to find a Restlet example using HTTPS (there
isn't one), and eventually pieced together the following (using Windows
XP SP2 with Java 6, Eclipse, Restlet 1.1.1):
1) Create your keys and certificate. I tried both 'keytool' and IBM's
KeyMan to do this (KeyMan is easier, but more work to obtain, as you
need to register, etc...). Using keytool from a command prompt you need
to enter two commands - the first one creates the keystore file with the
keys, the second certifies it (self-certification, ok for testing). The
most important thing is that the name of the machine you will be using
the certificate on matches what you specify (in the example below, my
machine is called 'serverX'):
keytool -genkey -dname "CN=serverX, OU=IT, O=JPC, C=GB" -alias serverX
-keypass password -keystore serverX.cer -storepass password -keyalg
"RSA" -storetype "PKCS12" -provider sun.security.provider.Sun
keytool -selfcert -alias serverX -keystore serverX.cer -storepass
password -storetype "PKCS12"
The keystore file has now been created and self-certified: in this
example it is called 'serverX.cer' and was saved in the current
directory. There are two passwords: one for the keys and one to access
the keystore. I set them both to 'password' for testing. The name of
the keystore file ('serverX.cer') is not important, I just used that for
consistency.
Usually, the extension for JKS keystores is "jks" and the extension for
PKCS#12 keystores is "p12". "cer" tends to be used for X.509
certificates in DER format (application/x-x509-ca-cert), which are files
containing just the certificate: this is the format produced by "keytool
-export".
It looks from the example below that you've used "jks" for that, whereas
it's not actually a JKS file but a cer file.
2) To prevent warnings in a browser, add the keystore to the 'Trusted
Root Certification Authorities' on your computer. In Windows XP, I just
used Internet Options (via IE7 or Control Panel - Internet Options). On
the 'Content' tab, click 'Certificates', then go to 'Trusted Root
Certification Authorities' tab, click 'Import...' and follow the steps
to import your keystore file (in my example, 'serverX.cer'). It will
give warnings about not being verified, which is ok for testing (but
it must be properly signed for production).
Since your .cer is in fact a .p12, this might import the private key as
well (that's what you would do if you wanted to use client-certificate
authentication). In fact, the actual .cer exported by keytool should be
sufficient. Just to clarify the extensions:
keytool -export -alias serverX -file serverX.cer -storetype "PKCS12"
-keystore serverX.p12 -keypass password
(Shouldn't this be "-storepass" rather than "-keypass" by the way?)
3) In order for Java security to recognise the certificate, it needs to
be added to <JRE>\lib\security\cacerts, which is the Java certificates
file. This is important when you use a Restlet client to connect to the
server via HTTPS (but it did not seem to be needed by my browser - it
needed the IE options update described in point 2). On my
system, 'cacerts' is "C:\Program Files\Java\jre6\lib\security\cacerts".
I had some trouble adding my 'serverX' certificate to it, but the
following keytool commands work if you know the password for cacerts
('changeit' is the default I believe):
keytool -export -alias serverX -file serverX.jks -storetype "PKCS12"
-keystore serverX.cer -keypass password
keytool -import -alias serverX -file serverX.jks -noprompt -trustcacerts
-keystore "C:\Program Files\Java\jre6\lib\security\cacerts"
The first command exports the certificate from PKCS12 format into X.509
(JKS) format, which is what cacerts needs. In my case, I had to use
KeyMan to set the password for the 'cacerts' file (I set it back to the
default of 'changeit'), so when I ran 'keytool -import ...' I could
enter the correct password. There may be a better/easier way to do this.
If for some reason, you can write to that file (which is likely to be
shared by all users on that system), you can specify your own truststore
using -Djavax.net.ssl.trustStore=mytruststore.jks
-Djavax.net.ssl.trustStore=JKS
-Djavax.net.ssl.trustStorePassword=ABCDEFGH as JVM parameters.
If you're requiring client-authentication (in which case it makes sense
to set up a trust store on the server), you can used the truststorePath,
truststorePassword and truststoreType parameters of the Restlet Server
when you're using the sslContextFactory (as you've done below).
There's currently no SSLContextFactory support in the client connectors
of Restlet, so you'll have either to modify the central cacerts as you
did, or use the SSL system properties.
As a side note, the "C:\Program Files\Java\jre6\lib\security\cacerts" is
the set of defaults CA certificates your JVM will trust. However, I
remember seeing somewhere on the Sun documentation that it's up to the
user to make sure they're up to date. (It sounds normal, but I'm not
sure all users do so.)
4) In your Java Restlet server program, in addition to the standard
Restlet jar files, you also need jar files for HTTPS. The only HTTPS
connector I could get to work correctly was 'Simple', which uses these
jar files:
lib/com.noelios.restlet.ext.simple_3.1.jar
lib/org.simpleframework_3.1/org.simpleframework.jar
lib/com.noelios.restlet.ext.ssl.jar
lib/org.jsslutils_0.5/org.jsslutils.jar
(Grizzly compiled and ran, but gave inconsistent results - appeared to
be missing requests; Jetty threw an error saying it couldn't register
'AjpServerHelper').
5) Your Restlet server code should then look something like this:
package com.jpc.samples;
import org.restlet.Component;
import org.restlet.Server;
import org.restlet.data.Parameter;
import org.restlet.data.Protocol;
import org.restlet.util.Series;
public class SampleServer {
public static void main(String[] args) throws Exception {
// Create a new Component.
Component component = new Component();
// Add a new HTTPS server listening on port 8183
Server server = component.getServers().add(Protocol.HTTPS, 8183);
Series<Parameter> parameters = server.getContext().getParameters();
parameters.add("sslContextFactory",
"com.noelios.restlet.ext.ssl.PkixSslContextFactory");
parameters.add("keystorePath", "<path>serverX.cer");
parameters.add("keystorePassword", "password");
parameters.add("keyPassword", "password");
parameters.add("keystoreType", "PKCS12");
// Attach the sample application.
component.getDefaultHost().attach("", new SampleApplication());
// Start the component.
component.start();
}
}
The HTTP examples all show
'component.getContext().getParameters().add(...) but this doesn't seem
to work for any HTTPS connectors. Using the
server.getContext().getParameters().add(...) does work but this doesn't
seem to be clearly documented anywhere.
The reason it doesn't work when setting the component context is due to
a change between Restlet 1.0 and 1.1: the contexts are now separate and
SSL parameters are specific to the server connector. It's documented in
the wiki page "Migration guide from Restlet 1.0 to 1.1", but indeed, it
doesn't clearly say how it impacts the SSL configuration.
You can also find some documentation about SSL on the wiki:
http://wiki.restlet.org/docs_1.1/13-restlet/28-restlet/153-restlet.html
Admittedly, this could be improved, especially with some background on
what the keystores and truststores are, etc.
Best wishes,
Bruno.