Showing posts with label security. Show all posts
Showing posts with label security. Show all posts

Wednesday, February 2, 2011

Encapsulate SSL TrustStore Configuration

Here I'll present the steps of a recent exercise I completed, around configuring SSL on the client side, encapsulating the configuration for transparency. For some background on SSL and the topics discussed in this article, here are some resources:

SSL Protocol Overview
SSL Certificates HOWTO
Transport Layer Security

The Security Now podcast series also has some excellent deep-dives into SSL and many other security-related topics.

Friday, January 28, 2011

Friday, July 16, 2010

JEE Authentication: Login Errors, Roles, Access Denied, and Logout

In a previous post around LDAP authentication using Jetty, I had some unfinished business. Here I'll deal with login errors, restrict the login to a given role, deal with subsequent access denied scenarios, display the currently logged-in user name, and provide log-out functionality.

The web.xml I'd started with already specified a login-error page, but I was simply pointing it to the same page as the login form. This results in that page simply refreshing without any indication to the user of why that happened. What we want is some kind of message displayed that indicates the given username or password was not valid.

Now, I could simply copy the login.jsp to another JSP, say login-error.jsp, add a message to that page and alter the web.xml to specify that new page on login error:
<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>ldap</realm-name>
    <form-login-config>
        <form-login-page>/faces/login/login.jsp</form-login-page>
        <form-error-page>/faces/login/login-error.jsp</form-error-page>
    </form-login-config>
</login-config>
But now both login.jsp and login-error.jsp contain the same FORM snippet. Now if I next want to specify yet another login page to which the user is directed on an "access denied" error (which we'll deal with momentarily), and I'm still afflicted with copy-paste fever, I'll have the same login form in three places. Let's factor it out instead into a JSP snippet named loginform.jsp:
<form method=post action="j_security_check">
    <label for="j_username" style="font-weight:bold">Username</label>
    <input type="text" name="j_username" id="j_username" style="margin-left:10px"/>
    <label for="j_password" style="font-weight:bold">Password</label>
    <input type="password" name="j_password" id="j_password" style="margin-left:10px"/>
    <input type="submit" value="Log In"/>
</form>
The usual login page now looks like this:
<div style="margin-top:25px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Please Log In</h3>
    <%@include file="loginform.jsp"%>
</div>
And I'll provide an "access denied" page that looks like this:
<div style="margin-top:25px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Please Log In</h3>
    <div style="color:red;font-weight:bold;">Authentication failed. User is not in Required Role.</div>
    <%@include file="loginform.jsp"%>
</div>
Configuring HTTP-403 responses (i.e. access denied) to navigate to this page is done like so:
<error-page>
    <error-code>403</error-code>
    <location>/login/accessDenied.jsp</location>
</error-page>
Access denied problems will occur if a given user is not in the expected role. So far, the web.xml has granted authorization to all roles by virtue of the wild-card for the role-name. We can restrict that by naming a role instead:
<security-constraint>
    <web-resource-collection>
        <web-resource-name>Protected Resources</web-resource-name>
        <url-pattern>*.iface</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>
            CONFIDENTIAL
        </transport-guarantee>
    </user-data-constraint>
</security-constraint>
Now, once a user provides his/her credentials at the login form, these are first checked by the LDAP module (configured as a Jetty realm, as per the previous post); if those are valid, the user is next confirmed to be assigned the admin role. If that is the case, all is well and navigation will proceed as configured by the JSF navigation rule (again, please see the previous post). If the credentials are not valid, the user will be redirected to the login-error page, this time with an informative error message about the login problem. If the credentials are good but the user is not assigned the admin role, the user will be redirected to the access-denied page, again with a informative message.

Displaying the current username is a simple matter of leveraging the built-in getRemoteUser(), provided by HttpServletRequest. Since, after logging in, I've transitioned into a JSF application - and because I'm adverse to using JSP scriptlets to accomplish use of that getter once I'm in JSF - I simply provide a getter in one of my JSF managed beans that fetches the HTTP request and returns the user name:
public String getUserName() {
    return getServletRequest().getRemoteUser();
}
...referencing it, as usual, with the JSF expression language:

User: #{svh.userName}

Finally, I'll provide log-out functionality. First, a command link (done with the IceFaces framework):
<ice:commandLink 
    action="logout" immediate="true" value="Logout" 
    style="margin-left:5px;color:blue;font-size:medium"/>
The logout action is mapped with a navigation rule:
<navigation-rule>
    <description>Logout</description>
    <from-view-id>/*</from-view-id>
    <navigation-case>
        <from-outcome>logout</from-outcome>
        <to-view-id>/login/logout.jsp</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>
...taking us to the logout.jsp page, which invalidates the session and invites the user to log back in:
<% session.invalidate(); %>

<div style="margin-top:5px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Logout Succeeded</h3>
    <p>
        You are now logged out of the Management UI.
    </p>
    <a href="/index.jsp" style="text-decoration:underline">Return to Login page.</a>
</div>
The index.jsp redirects to the application's JSF-based home page:
<html>
    <head>
        <title>Management UI</title>
    </head>
    <body>
    <%
        String redirectURL = "./index.iface";
        response.sendRedirect(redirectURL);
    %>
    </body>
</html>
And, as mentioned in the first post, all iface resources are protected by a security constraint, so this will redirect to the login page.

Wednesday, July 14, 2010

LDAP Authentication with Jetty

If you noticed nothing else but the titles of my last two posts, you might suspect that I went down the Spring-Security road and back-pedaled to a standard JAAS approach. You would be correct.

As it turned out - to my surprise and with great disappointment - I found Spring Security to be impenetrable. Now, Spring makes a lot of things in my life easier, and in fact that's the only reason I use it. But when it becomes a tangled snarl of undocumented opaqueness, and especially when I read that even an expert in Acegi had trouble with it (see the Wrap-Up in that article), I decide to find another way.

Here are my basic building blocks for a JAAS approach with a JSF application in Jetty. First, I configure my web.xml with an authorization constraint:
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected Resources</web-resource-name>
            <url-pattern>*.iface</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>
                CONFIDENTIAL
            </transport-guarantee>
        </user-data-constraint>
    </security-constraint>
My web.xml has already mapped an IceFaces PersistentFacesServlet to *.iface, so that's what the URL pattern is about. My initial naive attempt was to use a URL pattern of /*, but that's much too broad - it will preclude e.g. loading image resources as part of your login page. That login page is also configured in the web.xml:
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>ldap</realm-name>
        <form-login-config>
            <form-login-page>/faces/login/login.jsp</form-login-page>
            <form-error-page>/faces/login/login.jsp</form-error-page>
        </form-login-config>
    </login-config>
The login page (./login/login.jsp) is dirt-simple so far. I'm only working on basic functionality at this point, and there's nothing pretty about it:
    <form method=post action="j_security_check">
        <label for="j_username">Username</label>
        <input type="text" name="j_username" id="j_username"/>
        <br/>
        <label for="j_password">Password</label>
        <input type="password" name="j_password" id="j_password"/>
        <br/>
        <input type="submit" value="Login"/>
    </form>
The realm-name portion in the web.xml references the container environment; configuring the realm is delegated to the container in JEE. My container is (embedded) Jetty, and I configure the Jetty realm via a simple entry in my jetty.xml file:
    <Call name="addUserRealm">
        <Arg>
            <New class="org.mortbay.jetty.plus.jaas.JAASUserRealm">
                <Set name="name">ldap</Set>
                <Set name="LoginModuleName">ldapmodule</Set>
            </New>
        </Arg>
    </Call>
By the way, I followed the Jetty tutorial on JAAS to make all of this happen. My previous post mentioned a gotcha in that article around the LDAP Login Module package name. Depending on which version of Jetty you're using, you may need to make the change discussed there.

In either event, the Jetty realm configuration references a LoginModuleName of "ldapmodule", and as per standard JAAS, this configuration is captured in a file (in my case, a file named ./etc/ldap.conf) referenced by the JVM argument -Djava.security.auth.login.config=etc/ldap.conf. That file is basically a replica of the ldaploginmodule example in the Jetty tutorial (again, except for the package name of the LdapLoginModule class), configured of course with the proper schema references and credentials for my LDAP environment.

Finally, I configure a navigation rule so JSF will take me to my target page after successful login:
    <navigation-rule>
        <description>After Login</description>
        <from-view-id>/login/login.jsp</from-view-id>
        <navigation-case>
            <to-view-id>/index.jsp</to-view-id>
            <redirect/>
        </navigation-case>
    </navigation-rule>
Note that this a bare-bones scaffold for authentication. I have yet to deal with login failures, roles, JSF-messaging for the user, and the like.

Tip: Package Name Correction for Jetty LdapLoginModule

If you're following the Jetty tutorial for establishing JAAS authentication, and you have copied the sample configuration file for the LDAP module, you may come across this error message after you enter your username and password on your login page:

javax.security.auth.login.LoginException: unable to find LoginModule class: org.mortbay.jetty.plus.jaas.spi.LdapLoginModule

The reason is that, at least for the version of Jetty I'm using (6.1.21), the package name has changed to org.mortbay.jetty.plus.jaas.ldap.LdapLoginModule. Make that change in the ldaploginmodule sample in that tutorial and you'll solve this problem (assuming of course you have all the needed dependencies).

For your reference, here's the complete dependency list I'm using for a JSF-based app running with embedded Jetty 6.1.21:
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jsp-2.1-jetty</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-plus</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-ldap-jaas</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

Tip: Provide the Spring Security NamespaceHandler Explicitly

If you're working with Spring Security, you might start your pom with an entry like so:
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.0.0.RELEASE</version>
</dependency>
This will bring in the spring-web, spring-security-core and commons-logging artifacts, and I would not fault you for thinking you're good to go. You're possibly following this tutorial or another among the many out there, and are providing a Spring config that references the http://www.springframework.org/schema/security namespace - and, at least for me, here's where things did not go as expected. At runtime, the error message I received was this:

Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/security]

After a fair amount of Google'ing and Stack-Overflow'ing, I found the problem - and I reproduce it here to save myself (and hopefully you) the headache next time around: there is a dependency missing that looks like this:
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>3.0.0.RELEASE</version>
 </dependency>

Now you should be good to go.

Friday, July 2, 2010

Using JDK keytool To Generate Keys and Certs

In a previous post, I stepped through use of IBM's KeyMan GUI to generate SSL keys and certificates, placing them in KeyMan's "token", or as more commonly known, a keystore. I then attempted to use this keystore in both a Windows and Linux deployment of my webapp, and met with mixed success - since I used the Windows version of the KeyMan tool, this worked out OK for the Windows webapp, but not so much for the Linux deployment.

My subsequent attempts to use KeyMan's Unix shell script (km) instead - under a Cygwin environment - were much the same: on deployment, an "Invalid keystore format" exception was issued. Clearly, this is about the difference between DOS and Unix file formats. The next obvious step was to simply execute the km script directly under Linux, foregoing any file encoding or translation issues that might be happening with Cygwin. Here, however, after appearing to generate the key pair, the KeyMan GUI went into some kind of blocking wait - or maybe an infinite loop? a deadlock? There was no way to tell; the GUI simply became unresponsive. Followup exercises to include setting KM_HOME in the environment, unpacking the native library support ZIP file and setting the LD_LIBRARY_PATH to point to them, and etc. all proved fruitless.

Finally, I reverted to using the JDK keytool utility, and - no surprise - this works out just fine in both Windows and Linux (i.e. in terms of generating a keystore that is recognized by the webserver). Here is the script I use to generate things, in both Linux and Cygwin:

######################################
#
# generate-keystore.sh - Generate key and certificate
#
######################################

CN=MyKeystore
OU='Web - Development'
ORG='My Biz Inc.'
COUNTRY=US
ALIAS=MyBizKeystore
PASS=password
KEYSTORE=keystore
CERTFILE=cert
EXPIRY=730

# remove it if it's there
[ -f "$KEYSTORE" ] && /bin/rm $KEYSTORE

# generate the keystore with a self-signed cert and an RSA keypair
$JAVA_HOME/jre/bin/keytool -genkeypair -keyalg RSA \
-dname "cn=$CN, ou=$OU, o=$ORG, c=$COUNTRY" \
-alias $ALIAS -keypass $PASS -keystore $KEYSTORE \
-storepass $PASS -validity $EXPIRY

# export the certificate so we can look at it
$JAVA_HOME/jre/bin/keytool -exportcert -alias $ALIAS -file $CERTFILE -keystore $KEYSTORE -storepass $PASS

# print the certificate
$JAVA_HOME/jre/bin/keytool -printcert -file $CERTFILE

If you bump into this error message in the generate step:

Incorrect AVA format

...you'll want to make sure you didn't embed any commas or other special characters in the values you provide. For example, I started out with an Organizational Unit (OU) of 'My Biz, Inc.' - but that provoked the error message. Embedded dashes and periods are apparently OK, but note that I've enclosed any values with embedded spaces in single quotes. That's more a shell issue than a keytool problem.

If you bump into an error message something like this, in the print-certificate step:

lengthTag=109, too big

...you might be trying to pass in the entire keystore to the printcert command; that's why I export the certificate first in the script above, using just that piece as the argument to print it out.

Wednesday, June 30, 2010

Using KeyMan To Generate Keys and Certs

This post continues my series around Jetty. Previous posts discuss setting up a basic embedded Jetty application, and then some adjustments to repeat that exercise in a Cygwin environment. My next step is to configure SSL...now, I recall earlier adventures using Java's keytool to manage this, and have hoped for something better. Jetty's documentation pointed me to KeyMan, which is by far a nicer way to go - it provides a decent intuitive GUI to help create, delete, and otherwise manage keys and certificates, among other things. Here's an outline of how to use KeyMan to create a PKCS#12 keystore with a self-signed certificate and public-private key pair:

  • Download, install (unpack zip, etc.), read the README.txt. I did nothing with the km.setup file, but did edit the km.bat as instructed. Turns out that, since I'm on cygwin, that wasn't needed; instead, I execute the km program. Click on the "New" icon to create a new "token" (i.e. repository for keys, certs, etc.):



  • Choose the PKCS#12 Token from the next dialog, and hit the checkmark ("Complete Dialog") to proceed:



  • Next, you need to store a key and a certificate in this token. Select "Actions -> Generate Key" from the token management window that appears:



  • The default algorithm is RSA-1024; that's strong enough for my needs. Click the Complete Dialog checkmark...this takes a second to complete, offering a cool little progress bar while you wait. The new key shows up in the All Certificate Items viewport of the token management window; now we need a certificate to go with it. Click "Actions -> Create Certificate...". Self-signed is good enough for my needs. Click checkmark and fill in the fields as needed (only "Your name" is required):




  • A verification appears when you check "Complete Dialog" here, with the option to label this certificate. Enter a label if you wish, and again move on with the checkmark:



  • Save the token to a file by selecting File -> Save. This first prompts you for a passphrase, then a file location.
Prove to yourself that the keystore (token, repository, whatever) is really there and that you can view it in human-friendly form by first exiting the program, restarting it and selecting the "Open existing..." icon, then "Local resource..." and "Open a file...". Browse to the file location you just saved to, enter the passphrase, and you should see your token listed in the Private Certificates category (in the dropdown). Click right on that item and you'll see all the informational details entered when you created the certificate.

Next, I'll see about using that keystore for my Jetty SSL setup. Meanwhile, here are some useful links around KeyMan, SSL and Jetty's SSL instructions:

Solaris Keytoolhttp://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/keytool.html
Windows Keytoolhttp://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html
KeyManhttp://www.alphaworks.ibm.com/tech/keyman
OpenSSLhttp://www.openssl.org/docs/HOWTO/
OpenSSL FAQhttp://www.openssl.org/support/faq.html
Jetty SSLhttp://docs.codehaus.org/display/JETTY/How+to+configure+SSL