$Id: ssl,v 1.1 2004/04/23 12:50:02 pjungwir Exp $
This document describes how to do SSL in Java. It does not discuss configuring Tomcat or Apache, but rather the application itself. Setting up Tomcat for SSL is very easy; a nice description is here: http://jakarta.apache.org/tomcat/tomcat-4.0-doc/ssl-howto.html.
We have several requirements related to SSL:
* Pages should have links and forms that use https rather than http.
* Actions and JSPs should be "protected" so that if someone goes there via http, they get redirected to the same URL under https.
Currently, the first requirement is not met by either html:link or c:url. If you want an https link, you have to piece it together yourself. Moreover, if you want an https form, you can't use Struts forms. The second requirement is also not met by Struts or anything in JSP. You'd have to write your own Filter to get this functionality.
Fortunately, a Struts add-on exists, sslext, which provides all this functionality. The author of this utility expects it to be rolled into Struts in the near future. Here is the sslext project page: http://struts.ditlinger.com/. Unforunately, there is almost zero documentation. There is a sample app, but that's it. So I describe below the changes I made to enable sslext for BantaSP.
Under sslext, you specify whether Actions are http-only, https-only, or either. Then, you use alternate link and form tags to write links appropriately. Those links will check the nature of the Action and output the appropriate URL. If an action can take either http or https, the link will be whatever is the current scheme. So in your JSP, you'd do this:
<%@ taglib uri="/WEB-INF/sslext.tld" prefix="ssl" %>
...
go here
...
...
...
These tags use a plugin to get the correct port numbers for http and https. Add this plugin to your app by adding the following snippet to your merge-files/struts-plugins.xml:
Those ports should be whatever is appropriate. For production, that's probably 80 and 443.
Again, note that these jsp tags don't specify whether the destination should be http or https. They query the Action to find out which scheme it wants. You specify this in the struts-config.xml file:
As you can see, the set-property tag has a property name of "secure." Values can be "true" (https), "false" (http), or "any." The trouble is, we don't write the struts-config.xml file ourselves; we let XDoclet do it. And unfortunately, XDoclet does not support tags. Fortunately, adding this tag was an easy hack to the struts .xdt file. Here is the XDoclet patch:
? patch.diff
Index: modules/apache/src/META-INF/xtags.xml
===================================================================
RCS file: /cvsroot/xdoclet/xdoclet/modules/apache/src/META-INF/xtags.xml,v
retrieving revision 1.16
diff -b -u -r1.16 xtags.xml
--- modules/apache/src/META-INF/xtags.xml 16 May 2003 20:17:38 -0000 1.16
+++ modules/apache/src/META-INF/xtags.xml 19 Dec 2003 21:17:41 -0000
@@ -325,6 +325,29 @@
class
+ struts.action-property
+ Adds a set-property element to the action element in struts-config.xml.
+ false
+ Struts Action
+
+
+
+ org.apache.struts.action.Action
+
+
+
+ name
+ The name of the property
+ true
+
+
+ value
+ The value of the property
+ true
+
+
+
+ class
struts.action-forward
Defines local forwards for a Struts action class
false
Index: modules/apache/src/xdoclet/modules/apache/struts/resources/struts_config_xml.xdt
===================================================================
RCS file: /cvsroot/xdoclet/xdoclet/modules/apache/src/xdoclet/modules/apache/struts/resources/struts_config_xml.xdt,v
retrieving revision 1.12
diff -b -u -r1.12 struts_config_xml.xdt
--- modules/apache/src/xdoclet/modules/apache/struts/resources/struts_config_xml.xdt 13 Nov 2003 04:31:30 -0000 1.12
+++ modules/apache/src/xdoclet/modules/apache/struts/resources/struts_config_xml.xdt 19 Dec 2003 21:17:42 -0000
@@ -86,6 +86,10 @@
unknown=""
validate=""
>
+
+ "
+ value=""/>
+
"
The first half of this is just for documentation. The important part is the patch to modules/apache/src/xdoclet/modules/apache/struts/resources/struts_config_xml.xdt. If you're using our internal XDoclet distro, you should already have this patch.
Once you've got an XDoclet that supports tags, you can use @struts.action-property in your Action class:
/**
* Login Action class.
*
* @struts.action path="/LogIn"
* name="LogInForm"
*
* @struts.action-property name="secure"
* value="true"
* ...
*/
public final class LogInAction extends AbstractAction { ... }
So far, so good. However, Struts will not be able to accept your "secure" property unless you use an ActionMapping with a "Secure" getter/setter. Sslext provides such a class, and you tell Struts to use it by making it the ActionMapping class. Once again, this is a change to the struts-config.xml. Usually, the file looks like this:
...
...
...
You want to make it look like this:
...
...
...
Since this parameter doesn't apply to a single Action, you tell XDoclet about it when you run the ant task. Here's the snippet from your build.xml file:
...
Finally, add a copy of the sslext.jar file in webapp/lib. This gets deployed with the webapp and handles all the real work.
The above changes will cause and tags to output the correct scheme. They will not "protect" Actions be automatically redirecting the user if he manually types an incorrect scheme. To get that functionality, add this line to your merge-files/struts-controller.xml file:
In addition, you may want to protect not just Actions, but JSPs, too. In that case, just add this tag to your JSP (preferably at the top):
Note that protecting Actions and JSPs is a bad idea if you're running behind an Apache proxy that is managing the SSL! If that's the case, the user hits Apache with a URL like https://--:443/Login.do, and Apache forwards to Tomcat at http://--:8080/Login.do. The sslext library then sees that the request is not in SSL, so it redirects the browser to https://--:443/Login.do. The browser, of course, says, "I just went there!" and gives the user an error message.
And that is it!
If you want to run java-based webapp tests against an SSL server, you may have some extra work. If the certificate is self-signed, you'll need to tell Java you can trust it. Also, Java will only accept the certificate if its Common Name (CN) is the fully-qualified machine name in the URL.
Then first step, then, is importing the certificate into your keystore. Do that like so:
keytool -import -alias some_tomcat -file some_tomcat.cer -keystore truststore
Now run your Java class like this:
java -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=changeit -Djavax.net.debug=ssl JavaClassName
-Djavax.net.debug=help will show you how to use the debug property.
If you're running JUnit via Ant, you can add elements to the tag like this: