In the first part of the tutorial, SSL is being configured for
server authentication.
In the second part, the SSL configuration is extended for also
providing client authentication.
SSL configuration is a two-step process. First the SSL certificates need to be created, and then the jigsaw server needs to be configured.
Make sure that jdk 1.4.x or higher is being
used.
When using jdk 1.2.x or 1.3.x, the JSSE jars must be available in
the class path.
Optional client authentication or generic trust stores require at
least jdk 1.5.x.
We are going to be using the SSL implementation from Sun. Mainly the keytool (http://java.sun.com/j2se/1.4/docs/tooldocs/solaris/keytool.html) is used to configure the SSL certificates. If keytool is not in your default PATH, you can find it in the "bin" directory of your JDK installation.
keytool -genkey -alias troi.example.com
-keypass example -keystore
/opt/jigsaw/dev/Jigsaw/keystore/troi.keystore -keyalg
RSA
keytool -certreq -alias troi.example.com
-keypass example -keystore
/opt/jigsaw/dev/Jigsaw/keystore/troi.keystore -file
troi.csr
-----BEGIN NEW CERTIFICATE REQUEST----- MIICgTCCAj4CAQAwezELMAkGA1UEBhMCVVMxETAPBgAAAAgTCElsbGlub2lzMRYwFAYDVQQHEw1E b3duZXJzIEdyb3ZlMRAwDgYDVQQKEwdQZXJzZWNvMRkwFwYDVQQLExBCdXNpbmVzcyBTeXN0ZW1z MRQwEgYDVQQDEwtCcmlhbiBMYWlyZDCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLf Spwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQ paSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd 0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6j fwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDc5Ki4 7dX93se92yzYjxJIi99R9EZYHu4sNUH9obMQYO7o5u/3AOkxzqHLx60wWbf9JoCAlMe8q2i28NNC hjsN6LN7V0fgA05k/CzM9pMxbgeA0dqwQrtroWkJnFyzzFLrxjv7Rrh5RDGV/+ZeR72ZpHwy1GOj yDB2Dz+NE98sgKAAMAsGByqGSM44BAMFAAMwADAtAhRumfMg6P1KJmstMYga74KxaPLBfgIVAIwB bVanNqQb898bqHBkRybHIFqW -----END NEW CERTIFICATE REQUEST-----
Now we will import the response from the certificate authority.
keytool -import -alias sis.example.com
-keystore sis.keystore -file sis.cer -keyalg RSA
-trustcacerts
and skip the next point (related to self-signature)
keytool -selfcert -alias troi.example.com
-keystore /opt/jigsaw/dev/Jigsaw/keystore/troi.keystore -keyalg
RSA
# Points to the key store (cf. 4. above, don't forget to put the full path to the keystore) org.w3c.jigsaw.ssl.keystore.path= # Supplies the password for accessing the key store ... org.w3c.jigsaw.ssl.keystore.password= # Finally, the socket client factory has to be set to the SSL factory ... org.w3c.jigsaw.http.ClientFactory=org.w3c.jigsaw.https.socket.SSLSocketClientFactory
Of course you may change the default port to use the default one for SSL: 443 (or any other you like and which is not yet used)
You should also change the propfile value to match the name of your configuration file.
org.w3c.jigsaw.daemon.handlers=https-server|... https-server.org.w3c.jigsaw.daemon.class=org.w3c.jigsaw.https.httpsd
This should cover the setup requirements. You should be able to start jigsaw and see something like the following when the start up occurs:
Jigsaw[2.2.4]: serving at
https://troi:8002/
You can also use the secure protocol for webdav, in which case the daemon class must be set to
org.w3c.jigsaw.webdavs.webdavsd
Server authentication enables a client to verify in a secure way, which server she or he is talking to.
The SUN provider uses the proprietary JKS default key store type. When using a suitable security provider, e.g. bouncy castle, the standard PKCS#12 key store type is available as an option. The SUN provider also offers PKCS#12 in jdk 1.5.x.security.provider.7=sun.security.pkcs11.SunPKCS11 ${java.home}/lib/pkcs11.properties
name = eToken library = /WINDOWS/SYSTEM32/eTpkcs11.dll
org.w3c.jigsaw.ssl.keystore.type=pkcs11 org.w3c.jigsaw.ssl.keystore.password=myPin1234
Jigsaw also provides for client authentication, which addresses the other way around: It enables a server (and a web application) to verify in a secure way, which client accesses the server. Although, client authentication is rarely used in the web, it is a powerful infrastructure for building secure web applications with distributed user management.
For clients to be verified, they must present a certificate,
which can be checked by the server using public key
infrastructure.
In order to do so, the server needs an additional trust
store for also keeping track of the CA certificates used to
sign client certificates.
Extending the SSL configuration for also verifying clients is a two-step process again. First the trust store has to be populated with CA certificates (at least one, the so-called "trust anchor"), and then the jigsaw server needs to be configured.
You can obtain a client certificate from an authority like verisign and store it in your browser for being presented to the server during the SSL handshake. In this case, you also need to import the authority's certificate used to sign yours into the server's trust store. The latter task can be performed using keytool again.
If you prefer acting as your own CA, keytool and the SUN JCE implementation are not sufficient, because they currently do not provide for signing certificates other than your own. However, you can download a free JCE extension from bouncy castle, which also provides for certificate signing features. You can modify the following sample code for playing CA:
Make sure that the bouncy castle bcprov-jdk14-121.jar file is in your JRE/lib/ext directory.
/** * Copyright (c) 2004 [email protected] * All rights reserved. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ import java.io.*; import java.util.*; import java.security.Security; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.*; import org.bouncycastle.jce.*; import org.bouncycastle.jce.provider.*; import org.bouncycastle.asn1.x509.*; /** * Sample signing utility developped for simplified interoperability with keytool. * * It assumes that the CA has a self-signed certificate with alias "ca" in the "ca.db" JKS type keystore. * In addition, it assumes that a self-signed client certificate with alias "foo" resides in * the "bar.db" JKS type keystore. The CA-signed version gets stored with alias "foo" again in * the "bar.p12" PKCS12 type keystore, so that it can easily be imported by web browsers. * * The ca uses the "manager" password for accessing its own store and the "example" * password for accessing the client stores and certificates. */ public class signtool { static { // add the bouncy castle provider Security.addProvider(new BouncyCastleProvider()); } public static void main(String[] args) { try { // The issuer certificate access KeyStore ksca = KeyStore.getInstance("JKS"); ksca.load(new FileInputStream("ca.db"), "manager".toCharArray()); X509Certificate cacert = (X509Certificate)ksca.getCertificate("ca"); PrivateKey caprivate = (PrivateKey)ksca.getKey("ca", "manager".toCharArray()); // The subject certificate access KeyStore ksbar = KeyStore.getInstance("JKS"); ksbar.load(new FileInputStream("bar.db"), "example".toCharArray()); X509Certificate foocert = (X509Certificate)ksbar.getCertificate("foo"); PrivateKey fooprivate = (PrivateKey)ksbar.getKey("foo", "example".toCharArray()); // The certificate chain building process X509V3CertificateGenerator engine = new X509V3CertificateGenerator(); engine.setSerialNumber(foocert.getSerialNumber()); engine.setSignatureAlgorithm(foocert.getSigAlgName()); engine.setNotBefore(foocert.getNotBefore()); engine.setNotAfter(foocert.getNotAfter()); engine.setPublicKey(foocert.getPublicKey()); engine.setSubjectDN(new X509Name(true, foocert.getSubjectX500Principal().getName())); engine.setIssuerDN(new X509Name(true, cacert.getSubjectX500Principal().getName())); // ... and a little signature ... X509Certificate foosigned = engine.generateX509Certificate(caprivate); X509Certificate[] signedchain = new X509Certificate[] { foosigned, cacert }; // The signed certificate update KeyStore ksbarca = KeyStore.getInstance("PKCS12", "BC"); ksbarca.load(null, null); // for initializing the keystore ksbarca.setKeyEntry("foo", fooprivate, "example".toCharArray(), signedchain); ksbarca.store(new FileOutputStream("bar.p12"), "example".toCharArray()); System.out.println(Arrays.asList(signedchain)); } catch (Exception ex) { ex.printStackTrace(); } } }
Now, being able to set up a server-side trust store keeping a CA certificate and to import a CA-signed certificate into your browser's certificate store, we can complete the client authentication configuration.
# Points to the trust store (cf. 1. above, don't forget to put the full path to the truststore) org.w3c.jigsaw.ssl.truststore.path= # Supplies the password for accessing the trust store ... org.w3c.jigsaw.ssl.truststore.password= # Optionally, you can decide, whether to force clients (right side set to: true) to authenticate or # also admit a client without successful authentication (right side set to: false), which is the default org.w3c.jigsaw.ssl.must.authenticate=
This should cover the setup requirements. When starting and
accessing a server url, you should (after server certificate
presentation) be asked to type your browser's certificate store
password, because the server wants to check your client
certificate. If this is successful or the
org.w3c.jigsaw.ssl.must.authenticate
option is set to
false
you are allowed to continue.
Note, client certificates and other SSL characteristics are also
transparent to a servlet via the getAuthType
,
getRemoteUser
, getUserPrincipal
methods
and the following request attributes of the servlet api:
javax.servlet.request.cipher_suite javax.servlet.request.key_size javax.servlet.request.X509Certificate
javax.net.ssl.ManagerFactoryParameters
parameter when
the generic key or trust store flag is set to
true
.org.w3c.jigsaw.ssl.truststore.type
property and with
the class implementing the following method:
public static ManagerFactoryParameters getInstance(String path, String password) throws InvalidAlgorithmParameterException;
foo.MyStore
implementation class.
org.w3c.jigsaw.ssl.truststore.generic=true org.w3c.jigsaw.ssl.truststore.type=foo.MyStore org.w3c.jigsaw.ssl.truststore.path=... org.w3c.jigsaw.ssl.truststore.password=...
foo.MyStore
class available in the class
path. The corresponding Java source might look like this:
/** * Copyright (c) 2004 [email protected] * All rights reserved. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ package foo; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.StringTokenizer; import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.Certificate; import java.security.cert.CertPathParameters; import java.security.cert.CertStore; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; import java.security.cert.X509CertSelector; import javax.net.ssl.ManagerFactoryParameters; /** * This class delegates trust store features to the jce certpath api. * It compiles under JDK 1.4.x, but requires at least JDK 1.5.x at runtime. * * In this example, a simple collection only is used for storing trust material. * For a more realistic scenario, you might choose to regularily refresh * the collection from an underlying relational database or to use an * LDAP certificate store instead. Refreshing could be performed by * implementing the MyStore class as a Runnable and using a periodically * running background thread that updates the collection. */ public class MyStore { public static PKIXBuilderParameters getParameters(String path, String password) throws InvalidAlgorithmParameterException { try { // path contains a list of ordinary key store paths with // identical passwords and with the first list element being // the trust anchor StringTokenizer list = new StringTokenizer(path, java.io.File.pathSeparator); // get a keystore with the CA trust anchor KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(new BufferedInputStream(new FileInputStream(list.nextToken())), (null != password) ? password.toCharArray() : new char[0]); // wrap it as a PKIX builder parameters instance PKIXBuilderParameters pkix = new PKIXBuilderParameters(ks, new X509CertSelector()); // set additional attributes, e.g no revocation list checking for simplicity. // Otherwise certores will have to be added using the addCertStore method for // adding additional certificate and crl material pkix.setRevocationEnabled(false); // the list of additional signer certificates for populating the trust store Collection untrusted = new ArrayList(); while (list.hasMoreTokens()) { KeyStore ks2 = KeyStore.getInstance(KeyStore.getDefaultType()); ks2.load(new BufferedInputStream(new FileInputStream(list.nextToken())), (null != password) ? password.toCharArray() : new char[0]); Enumeration keys = ks2.aliases(); while (keys.hasMoreElements()) { String alias = keys.nextElement().toString(); Certificate cert = ks2.getCertificate(alias); if (cert != null) { untrusted.add(cert); } } } // create an untrusted collection store and add it CollectionCertStoreParameters params = new CollectionCertStoreParameters(untrusted); CertStore store = CertStore.getInstance("Collection", params); pkix.addCertStore(store); return pkix; } catch (Exception ex) { InvalidAlgorithmParameterException sub = new InvalidAlgorithmParameterException(ex.toString()); sub.initCause(ex); throw sub; } } public static ManagerFactoryParameters getInstance(String path, String password) throws InvalidAlgorithmParameterException { try { PKIXBuilderParameters pkix = getParameters(path, password); // wrap the certification path resolution bundle // as a suitable trust manager wrapper instance Class wrapper = Class.forName("javax.net.ssl.CertPathTrustManagerParameters"); Constructor c = wrapper.getConstructors()[0]; ManagerFactoryParameters mfp = (ManagerFactoryParameters)c.newInstance(new Object[] {pkix}); return mfp; } catch (InvalidAlgorithmParameterException ex) { throw ex; } catch (Exception ex) { InvalidAlgorithmParameterException sub = new InvalidAlgorithmParameterException(ex.toString()); sub.initCause(ex); throw sub; } } }