users@jersey.java.net

Re: [Jersey] client certificate based authentication and accounting with Jersey on GlassFish HOWTO

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Wed, 07 May 2008 11:45:04 -0700

Hi Gabor,

[Apologies for the delay i am at JavaOne this week so email responses
will be slow]

Many thanks for this write up.

Would you like to add this as a wiki page of the Jersey FAQ [1]? Then
it is easier to reference than the email.

Paul.

[1] http://wikis.sun.com/display/Jersey/FAQ

On May 5, 2008, at 6:10 AM, Gabor Szokoli wrote:

> Hi,
>
> My jersey web service prototype has come full circle, so here's a
> rough rundown on how I did it. This is of course a prototype only,
> useful for getting some hands on experience with the technology. To
> get any meaningful security out of it, you will have to invest some
> time into figuring out what's what, how to keep private keys secret,
> establish a human-in-the-loop protocol for signing certs, etc. Anyway,
> here it goes:
>
> Objective: a simple REST-style read-only web service using SSL
> certificates to authenticate clients and keep an audit log of who
> accessed what when.
>
> Design choices: Jersey as a Servlet on Glassfish, using java ee
> servlet authentication, but no audit module.
>
> Steps:
>
> 1, Create the web service .war file
>
> Implement your resources and converters as normal, or use the
> HelloWorld example.
>
> 2, Extend the web.xml and sun-web.xml, configure Glassfish
>
> 2.1,
> Here's my web.xml in full. Only login-config, security-role and
> security constraint are relevant.
>
> <web-app>
> <servlet>
> <servlet-name>Jersey Web Application</servlet-name>
> <servlet-class>
> com.sun.ws.rest.spi.container.servlet.ServletContainer
> </servlet-class>
> <init-param>
> <param-name>webresourceclass</param-name>
> <param-value>my.package.name.RootResources</param-value>
> </init-param>
>
> <load-on-startup>1</load-on-startup>
> </servlet>
>
> <servlet-mapping>
> <servlet-name>Jersey Web Application</servlet-name>
> <url-pattern>/*</url-pattern>
> </servlet-mapping>
>
> <persistence-context-ref>
> <persistence-context-ref-name>
> persistence/CCFEntityManager
> </persistence-context-ref-name>
> <persistence-unit-name>ccf-pu</persistence-unit-name>
> </persistence-context-ref>
>
> <login-config>
> <auth-method>CLIENT-CERT</auth-method>
> </login-config>
>
> <security-role>
> <description />
> <role-name>authorized</role-name>
> </security-role>
>
>
> <security-constraint>
> <display-name>CCF-REST</display-name>
> <web-resource-collection>
> <web-resource-name>CCF_REST</web-resource-name>
> <description></description>
> <url-pattern>/</url-pattern>
> <http-method>GET</http-method>
> <http-method>POST</http-method>
> <http-method>HEAD</http-method>
> <http-method>PUT</http-method>
> <http-method>OPTIONS</http-method>
> <http-method>TRACE</http-method>
> <http-method>DELETE</http-method>
> </web-resource-collection>
> <auth-constraint>
> <description/>
> <role-name>authorized</role-name>
> </auth-constraint>
> <user-data-constraint>
> <description/>
> <transport-guarantee>CONFIDENTIAL</transport-guarantee>
> </user-data-constraint>
> </security-constraint>
>
> </web-app>
>
> 2.2,
>
> And here is the corresponding sun-web.xml which defines the
> "authorized" role (it is not a keyword. "CLIENT-CERT" and
> "CONFIDENTAL" are keywords. "CCF" is the name of my application.)
>
> <?xml version="1.0" encoding="UTF-8"?>
> <!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD
> Application Server 9.0 Servlet 2.5//EN"
> "http://www.sun.com/software/appserver/dtds/sun-web-app_2_5-0.dtd">
> <sun-web-app error-url="">
> <context-root>CCF-REST</context-root>
> <class-loader delegate="true" />
>
> <security-role-mapping>
> <role-name>authorized</role-name>
> <group-name>authorized</group-name>
> </security-role-mapping>
>
> </sun-web-app>
>
> 2.3,
> To complete this step, configure the glassfish certificate realm to
> admint valid certificate holding clients into the "authorized" group.
> (still not a keyword.)
> Either in your domain.xml directly, or via the web admin console or
> asadmin:
> <auth-realm
> classname="com.sun.enterprise.security.auth.realm.certificate.Certific
> ateRealm"
> name="certificate">
> <property name="assign-groups" value="authorized"/>
> </auth-realm>
>
> You should be unable to access the web service at this point.
>
> 3, Create the client certificate, register the CA cert as trusted.
>
> As registering a new trusted client certifice into the truststore
> requires a Glassfish restart, we register a trusted Certificate
> Authority (CA) certificate instead, once and for all. Then each new
> client can get its certificate from the trusted CA, and become
> "transitively" trusted.
> To be exact, it is normaly the client who creates a public-private
> keypair, then submits the public part in the form of a Certificate
> Signing Request to the Certificate Authority, who then creates the
> certificate by signing it with its own private key. This certifies
> that tha CA trusts the certificate holder to be who he claims to be.
> Confusing at first, but rather straight forward once you get the hang
> of Public Key Infrastructure.
>
> 3.1, Establish a CA (assuming you don't have one already):
> source:
> http://www.madboa.com/geek/openssl/
>
> openssl req \
> -x509 -nodes -days 365 \
> -newkey rsa:1024 -keyout cakey.pem -out cacert.pem
>
> Edit openssl.cnf, create directories and files referenced there:
> mkdir demoCA
> echo '100001' > ./demoCA/serial
> mkdir ./demoCA/newcerts
> mkdir demoCA/private
> cp cacert.pem demoCA/
> cp cakey.pem demoCA/private/
>
> Enable signing foreign keys in openssl.cnf:
>
> policy = policy_anything
>
>
> 3.2, Sign the certificate of the client key:
>
> Create client key pair and certification request. This is normaly done
> by the client. The information provided here becomes part of the
> certificate, and will be included in the audit logs as "user
> principal":
>
> openssl genrsa -out somekey.pem 2048
> openssl req -new -key somekey.pem -out somekey.csr
>
> sign (done by the CA):
> openssl ca -config openssl.cnf -in somekey.csr -out somekey.crt
>
> 3.4, package key and cert for browser consumption:
> openssl pkcs12 -export -out somekey.pfx -in somekey.crt -inkey
> somekey.pem -name "Some Cert"
>
> 3.5, Add own CA cert to truststore:
>
> keytool -import -v -keystore
> /opt/glassfish/domains/domain1/config/cacerts.jks -storepass changeit
> -alias localCA -file cacert.pem
>
> Restart Glassfish.
> Importing somekey.pfx into a browser should enable it to access your
> web service again at this point.
>
> 4, Extend @GET methods with audit logging
>
> I have decided against using a custom Glassfish Audit module because I
> was unsure how I could automate shipping and deploying it. Please note
> however that since the application is RESTful, the user principal
> together with the URL accessed are sufficient for an audit trail.
>
> You need URIInfo and SecurityContext injected into your resource
> class:
>
> @Context private UriInfo context;
>
> @Context SecurityContext security;
>
> Then each @GET method can call this audit method:
>
> protected void doAudit() {
> StringBuffer auditMessage = new StringBuffer("AUDIT LOG for GET
> method. ");
> auditMessage.append("USER: ");
> if (security != null && security.getUserPrincipal() != null) {
> auditMessage.append(security.getUserPrincipal().toString());
> } else {
> logger.info("SecurityContext or UserPrincipal was null.");
> auditMessage.append("UNKNOWN.");
> }
> auditMessage.append(" URL: ");
> if (context !=null && context.getRequestUri() != null) {
> auditMessage.append( context.getRequestUri().toString());
> } else {
> logger.info("URIContext or RequestUri was null.");
> auditMessage.append("UNKNOWN");
> }
> logger.info(auditMessage.toString());
> }
>
> This is totally permissive of course, depending on your domain you
> probably don't want to allow the client to access anything unless the
> audit trail is fully descriptive. Keeping the audit entries in a
> database instead of intermixed with regular logs is also recommended.
>
>
> Share and enjoy!
> (Public domain, use or reproduce in any way you want)
>
> Gabor Szokoli
> Sirius Cybernetics Corporation Complaints division
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>