<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2820577476179403377</id><updated>2026-07-07T16:42:57.253-04:00</updated><category term="maven"/><category term="hadoop"/><category term="java"/><category term="jetty"/><category term="spring"/><category term="x509"/><category term="mapreduce"/><category term="trystack"/><category term="whirr"/><category term="zookeeper"/><title type='text'>Whitey&#39;s Coding Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>7</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2820577476179403377.post-4146567815526602431</id><published>2013-10-09T07:07:00.000-04:00</published><updated>2013-10-13T09:58:41.766-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="trystack"/><category scheme="http://www.blogger.com/atom/ns#" term="whirr"/><category scheme="http://www.blogger.com/atom/ns#" term="zookeeper"/><title type='text'>Apache Whirr with TryStack.org</title><content type='html'>I&#39;ve been recently playing with &lt;a href=&quot;http://whirr.apache.org/&quot;&gt;Apache Whirr&lt;/a&gt; to spin up clusters and wanted to try and write some of my own services - but didn&#39;t want to pay for testing against Amazon EC2 (I&#39;ve expired my 1 year / 750 hour trial period and Amazon charges by the rounded up hour).&lt;br /&gt;
&lt;br /&gt;
Whirr supports a number of providers (under the hood Whirr uses JCloud, so anything that JCloud can interface with), and I&#39;ve started to look into using &lt;a href=&quot;http://www.openstack.org/software/&quot;&gt;OpenStack&lt;/a&gt;. You can check out &lt;a href=&quot;http://devstack.org/&quot;&gt;DevStack&lt;/a&gt; (a single node OpenStack install) and run on your own system (assuming you have CPU hardware virtualization, enough memory etc) but another option is to sign up to &lt;a href=&quot;http://www.trystack.org/&quot;&gt;TryStack&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
TryStack is a free testing sandbox for OpenStack apps - signup on their web page to get a free account (you&#39;ll need a Facebook account to get on the x86 cluster). Once you have an account approved, you can log on to the x86 cluster. From there you can browse the images available and configure your default security group (much like you would on EC2).&lt;br /&gt;
&lt;br /&gt;
To test, I thought I&#39;d spin up a single ZooKeeper instance. As WHIRR takes care of the security groups for you, you won&#39;t need to this manually. You will need to create your own Key Pair (&lt;code&gt;ssh-keygen -t rsa -P &#39;&#39; -f id_rsa_trystack&lt;/code&gt;)&lt;br /&gt;
&lt;br /&gt;
Finally you&#39;ll need to generate an API Password Key. This is under the Settings link (top right), and the select the API Password link from the left menu. Click the Request API Password button and note the generated password. Now you&#39;ll need to play the waiting game as the passwords are sync&#39;d once an hour - if you keep refreshing the page every 10 minutes, eventually the displayed password will disappear, denoting that it&#39;s in sync.&lt;br /&gt;
&lt;br /&gt;
Now you can finally use Whirr to spin up that ZooKeeper instance. Download Apache Whirr and unpack to a directory. You&#39;ll need to create a configuration file which will store the cluster configuration information ($WHIRR_HOME/conf/trystack-zookeeper.conf for example):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;
whirr.provider=trystack-nova
whirr.identity=facebook01234567:facebook01234567
whirr.credential=aBCDeF0123455678
&lt;/pre&gt;
&lt;br /&gt;
&lt;div&gt;
Finally we can now use the whirr launch-cluster command to bring up a ZooKeeper node on TryStack:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;

&lt;script class=&quot;brush: bash&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
cswhite@x1-carbon:/opt/whirr/default$ ./bin/whirr launch-cluster \
    --config conf/trystack-zookeeper.conf \
    --private-key-file id_rsa_trystack \
    --cluster-name zookeeper-test \
    --instance-templates &quot;1 zookeeper&quot; \
    --endpoint http://8.21.28.222:5000/v2.0 \
    --image-id &quot;RegionOne/5e809aa3-c95e-4241-8e6a-1d807804c313&quot; \
    --location-id RegionOne
]]&gt;
&lt;/script&gt;

&lt;div&gt;
Of course, i didn&#39;t get to the magical set of arguments straight off the bat, and encountered several error messages along the way.&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Security credentials were the first problem, i didn&#39;t realize that your user name and tenantName were the same (hence the whirr.identity value being the same, delimited by a single colon). I had to fall back to the OpenStack Web API to validate i had the right credentials:
&lt;script class=&quot;brush: bash&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
cswhite@x1-carbon:/opt/whirr/default$ curl -k -X &#39;POST&#39; \
  -v http://8.21.28.222:5000/v2.0/tokens \
  -d &#39;{&quot;auth&quot;:{&quot;passwordCredentials&quot;:{&quot;username&quot;: &quot;facebook01234567&quot;, &quot;password&quot;:&quot;aBCDeF0123455678&quot;}, &quot;tenantName&quot;:&quot;facebook01234567&quot;}}&#39; \
  -H &#39;Content-type: application/json&#39;
]]&gt;
&lt;/script&gt;&lt;/li&gt;
&lt;li&gt;Image Id&#39;s were the next problem - not obviously documented anywhere, but the image id must be prefixed with the region or zone, in this case the magical &quot;RegionOne&quot; prefix&lt;/li&gt;
&lt;li&gt;Lastly the location-id is again the &quot;RegionOne&quot; string (see &lt;a href=&quot;https://issues.apache.org/jira/browse/WHIRR-728&quot;&gt;WHIRR-728&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/4146567815526602431/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whiteycode.blogspot.com/2013/10/apache-whirr-with-trystackorg.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/4146567815526602431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/4146567815526602431'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/2013/10/apache-whirr-with-trystackorg.html' title='Apache Whirr with TryStack.org'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2820577476179403377.post-3071673496263592427</id><published>2012-06-01T19:12:00.001-04:00</published><updated>2012-06-01T19:12:35.473-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hadoop"/><category scheme="http://www.blogger.com/atom/ns#" term="mapreduce"/><title type='text'>Hadoop - Removing Empty Output Files</title><content type='html'>Sometimes you have a Map Reduce job that runs 100/1000&#39;s of mappers or reducers where a number of tasks don&#39;t output any K/V pairs. Unfortunately Hadoop still creates the part-m-xxxxx or part-r-xxxxx files, but the contents of those files are empty.
&lt;p&gt;
With a little code however, you can extend most &lt;code&gt;FileOutputFormat&lt;/code&gt; sub-classes to avoid committing these files to HDFS when the tasks complete. You need to override two methods from the &lt;code&gt;OutputFormat&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getRecordWriter&lt;/code&gt; - Wrap the writer to track that something was output via the &lt;code&gt;Context.write(K, V)&lt;/code&gt; method
&lt;li&gt;&lt;code&gt;getOutputCommitter&lt;/code&gt; - Extend the &lt;code&gt;OutputCommitter&lt;/code&gt; to override the &lt;code&gt;needsTaskCommit(Context)&lt;/code&gt; method
&lt;/ul&gt;
&lt;p&gt;
Here&#39;s an example for extending SequenceFileOutputFormat:

&lt;script class=&quot;brush: java&quot; type=&quot;syntaxhighlighter&quot;&gt;

&lt;![CDATA[
package org.apache.hadoop.mapreduce.lib.output;

import java.io.IOException;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.apache.hadoop.mapreduce.OutputCommitter;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.hadoop.mapreduce.TaskAttemptContext;

/**
 * Extension to {@link SequenceFileOutputFormat} which avoids committing files
 * that have no output
 * 
 * @author Chris White
 * 
 * @param &lt;K&gt;
 * @param &lt;V&gt;
 */
public class NonEmptySeqFileOutputFormat&lt;K, V&gt; extends
        SequenceFileOutputFormat&lt;K, V&gt; {
    /** Output committer */
    protected FileOutputCommitter committer;

    /** Flag to track whether anything was output */
    protected boolean outputWritten = false;

    /**
     * Wrap the parent {@link RecordWriter} to allow us to track whether
     * {@link Context#write(Object, Object)} was actually called
     */
    @Override
    public RecordWriter&lt;K, V&gt; getRecordWriter(TaskAttemptContext context)
            throws IOException, InterruptedException {
        // get underlying record writer
        final RecordWriter&lt;K, V&gt; recWriter = super.getRecordWriter(context);

        // wrap writer to track that something was output
        return new RecordWriter&lt;K, V&gt;() {
            @Override
            public void write(K key, V value) throws IOException,
                    InterruptedException {
                outputWritten = true;
                recWriter.write(key, value);
            }

            @Override
            public void close(TaskAttemptContext context) throws IOException,
                    InterruptedException {
                recWriter.close(context);
            }
        };
    }

    /**
     * Implementation copied from the parent but amended to override the
     * {@link OutputCommitter#needsTaskCommit(TaskAttemptContext)} method
     */
    @Override
    public synchronized OutputCommitter getOutputCommitter(
            TaskAttemptContext context) throws IOException {
        // annoyingly committer is private in the parent class
        if (committer == null) {
            Path output = getOutputPath(context);
            committer = new FileOutputCommitter(output, context) {
                @Override
                public boolean needsTaskCommit(TaskAttemptContext context)
                        throws IOException {
                    return outputWritten &amp;&amp; super.needsTaskCommit(context);
                }
            };
        }
        return committer;
    }
}
]]&gt;
&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/3071673496263592427/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whiteycode.blogspot.com/2012/06/hadoop-removing-empty-output-files.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/3071673496263592427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/3071673496263592427'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/2012/06/hadoop-removing-empty-output-files.html' title='Hadoop - Removing Empty Output Files'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2820577476179403377.post-1351711419964171709</id><published>2012-05-24T06:42:00.001-04:00</published><updated>2012-05-24T06:42:48.334-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hadoop"/><category scheme="http://www.blogger.com/atom/ns#" term="maven"/><title type='text'>Building Hadoop Source Jars</title><content type='html'>Script to build yourself the source jars for hadoop core:

&lt;ul&gt;
&lt;li&gt;Download a hadoop release from &lt;a href=&quot;http://www.apache.org/dyn/closer.cgi/hadoop/common/&quot;&gt;http://www.apache.org/dyn/closer.cgi/hadoop/common/&lt;/a&gt;
&lt;li&gt;Unpack the tar.gz to a folder (in my case /opt/hadoop/)
&lt;li&gt;Copy the script below into a file names mk-source.sh (Amend the &lt;b&gt;VERSION&lt;/b&gt; and &lt;b&gt;HADOOP_SRC&lt;/b&gt; variables for your environment)
&lt;script class=&quot;brush: java&quot; type=&quot;syntaxhighlighter&quot;&gt;

&lt;![CDATA[
#!/bin/bash

VERSION=0.20.2
HADOOP_SRC=&quot;/opt/hadoop/hadoop-$VERSION/src&quot;

DIRS=&quot;core hdfs mapred&quot;

OUTPUT=$HADOOP_SRC/hadoop-core-${VERSION}-sources.jar

rm $OUTPUT

echo &quot;Manifest-Version: 1.0&quot; &gt; MANIFEST.MF
echo &quot;Bundle-ManifestVersion: 2&quot; &gt;&gt; MANIFEST.MF 
echo &quot;Bundle-Name: Hadoop $VERSION sources&quot; &gt;&gt; MANIFEST.MF

jar -cvfm $OUTPUT MANIFEST.MF

for DIR in $DIRS; do
 pushd $HADOOP_SRC/$DIR

 jar -uvf $OUTPUT *

 popd
done;

]]&gt;
&lt;/script&gt;
&lt;li&gt;Optionally install the sources into your local maven repository&lt;br/&gt;
&lt;pre&gt;mvn install:install-file \
    -Dfile=/opt/hadoop/hadoop-0.20.2/src/hadoop-core-0.20.2-sources.jar \
    -DgroupId=org.apache.hadoop \
    -DartifactId=hadoop-core \
    -Dversion=0.20.2 \
    -Dpackaging=java-sources \
    -DgeneratePom=false&lt;/pre&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/1351711419964171709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whiteycode.blogspot.com/2012/05/building-hadoop-source-jars.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/1351711419964171709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/1351711419964171709'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/2012/05/building-hadoop-source-jars.html' title='Building Hadoop Source Jars'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2820577476179403377.post-4921939319302599358</id><published>2012-05-22T06:38:00.000-04:00</published><updated>2012-05-22T06:38:23.850-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hadoop"/><title type='text'>Hadoop Local Map Aggregation</title><content type='html'>The much cited &#39;word-count&#39; Hadoop map-reduce example has (in my opinion) some fundamental bad-practices with regards to map-reduce processing. In it, the mapper outputs a single &lt;text, intwritable=&quot;&quot;&gt; for each word processed in the input file. 

As a simple example on a small dataset this is fine to teach the principals, but too often i see people apply this method of element &#39;counting&#39; to huge datasets.&lt;/text,&gt;&lt;br /&gt;
&lt;br /&gt;
There are a number of improvements that can be made, and in this post, I outline a map-aggregation library i&#39;ve put together (available on &lt;a href=&quot;https://github.com/chriswhite199/map-aggregation-lib&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;github&lt;/a&gt;) to address these problems&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Limited domain keys&lt;/h2&gt;
&lt;div&gt;
Given a body of text, you can expect the vast majority of words to be contained within a small set or domain of words. A number of words such as &#39;a, the, and&#39; etc are used over and over again in bodies, and your mapper can be designed to account for this by using a concept of local map-aggregation.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Rather than outputting a &lt;text, intwritable=&quot;&quot;&gt; pair for each word observed, you can maintain a local map of &lt;text, intwritable=&quot;&quot;&gt; and update the IntWritable value as each word is discovered. You can easily store a map of some 60,000 word-frequency pairs in a in-memory map, and at the end of your mapper, dump the contents of the map to the output context. This will greatly reduce the volume of data sent between the mappers and reducers. Of course you could also implement this using a Combiner, but you&#39;re still hitting the local disk, and paying the cost of a map-side sort of this data.&lt;/text,&gt;&lt;/text,&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
map-aggregation-lib - amended word count example&lt;/h2&gt;
&lt;div&gt;
Amending the word count mapper to use the map-aggregation-lib is easy - using an instance of the IntWritableAccumulateOutputAggregator class, we can aggregate the word counts in memory rather than delegating all the work to the combiner:&lt;/div&gt;
&lt;div&gt;
&lt;script class=&quot;brush: java&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.mapagglib.impl.IntWritableAccumulateOutputAggregator;

/**
 * Amended word count mapper to utilize the
 * {@link IntWritableAccumulateOutputAggregator}
 */
public class WordCountMapper extends
        Mapper&lt;LongWritable, Text, Text, IntWritable&gt; {

    protected IntWritableAccumulateOutputAggregator&lt;Text&gt; wordFreqMap;

    protected IntWritable one = new IntWritable(1);

    protected Text word = new Text();

    @Override
    protected void cleanup(Context context) throws IOException,
            InterruptedException {
        // flush any residual word frequency counts to the output context
        wordFreqMap.reduceBuffers();
    }

    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
        String line = value.toString();
        StringTokenizer tokenizer = new StringTokenizer(line);
        while (tokenizer.hasMoreTokens()) {
            word.set(tokenizer.nextToken());

            // aggregate the word count
            wordFreqMap.aggregate(word, one);
        }
    }

    @Override
    protected void setup(Context context) throws IOException,
            InterruptedException {
        // configure the local value aggregator
        wordFreqMap = new IntWritableAccumulateOutputAggregator&lt;Text&gt;(context,
                new Text.Comparator(), Text.class, 10000);
    }
}
]]&gt;
&lt;/script&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/4921939319302599358/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whiteycode.blogspot.com/2012/05/hadoop-local-map-aggregation.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/4921939319302599358'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/4921939319302599358'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/2012/05/hadoop-local-map-aggregation.html' title='Hadoop Local Map Aggregation'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2820577476179403377.post-609944598203938163</id><published>2012-04-09T21:47:00.000-04:00</published><updated>2012-04-09T21:47:21.793-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="jetty"/><category scheme="http://www.blogger.com/atom/ns#" term="maven"/><category scheme="http://www.blogger.com/atom/ns#" term="spring"/><category scheme="http://www.blogger.com/atom/ns#" term="x509"/><title type='text'>Part 2 - x509 Authentication with Spring, Eclipse, Jetty and Maven</title><content type='html'>&lt;h2&gt;







Introduction&lt;/h2&gt;
I&#39;ve seen a few posts to &lt;a href=&quot;http://www.stackoverflow.com/&quot; target=&quot;_blank&quot;&gt;Stack Overflow&lt;/a&gt; recently regarding x509 authentication using Spring. I know from my own experience that finding a single tutorial that has everything in one place is difficult, so I&#39;d thought I would put one together that covers pretty much everything you need to get a simple web application going&lt;br /&gt;
&lt;br /&gt;
I&#39;m going to break this up into 4 parts:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Part 1 : Generation of a client &amp;amp; server self-signed certificates (with common self-signed CA root certificate)&lt;/li&gt;
&lt;li&gt;Part 2 : Maven web application archetype generation and maven-jetty-plugin configuration&lt;/li&gt;
&lt;li&gt;Part 3 : Using a simple in-memory authorization provider&lt;/li&gt;
&lt;li&gt;Part 4 : Web application debugging using Eclipse&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;





Part 2 - Maven web application&lt;/h3&gt;
I am going to present the generation of a simple Java web application using a Maven archetype, and configuration of the maven-jetty-plugin to allow local testing&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;





Maven archetype generation&lt;/h4&gt;
I&#39;m using a Maven archetype to build a template for my web application project, which can be easily accomplished with the following command:

&lt;script class=&quot;brush: bash&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
cswhite@Dell-1555:~/workspace/x509WebApp$ mvn archetype:generate -DgroupId=com.whiteycorp.webapp -DartifactId=whitey-webapp -DarchetypeArtifactId=maven-archetype-webapp
]]&gt;
&lt;/script&gt;

After which you&#39;ll have a Maven managed Java project, with templated folders and files for a simple web application:

&lt;br /&gt;
&lt;pre&gt;./whitey-webapp
./whitey-webapp/src
./whitey-webapp/src/main
./whitey-webapp/src/main/resources
./whitey-webapp/src/main/webapp
./whitey-webapp/src/main/webapp/WEB-INF
./whitey-webapp/src/main/webapp/WEB-INF/web.xml
./whitey-webapp/src/main/webapp/index.jsp
./whitey-webapp/pom.xml
&lt;/pre&gt;
&lt;h4&gt;




Maven-jetty-plugin configuration&lt;/h4&gt;
We now want to configure the &lt;a href=&quot;http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin&quot;&gt;maven-jetty-plugin&lt;/a&gt;, which will allow us to bring up a jetty web container instance and host our web application.&lt;br /&gt;
&lt;br /&gt;
Open up the pom.xml and add the following plugin definition into your project-&amp;gt;build-&amp;gt;plugins section:

&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;plugin&gt;
    &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
    &lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;
    &lt;version&gt;6.1.26&lt;/version&gt;
    &lt;configuration&gt;
        &lt;stopKey&gt;foo&lt;/stopKey&gt;
        &lt;stopPort&gt;9999&lt;/stopPort&gt;
        &lt;reload&gt;manual&lt;/reload&gt;

        &lt;connectors&gt;
            &lt;connector
                implementation=&quot;org.mortbay.jetty.nio.SelectChannelConnector&quot;&gt;
                &lt;port&gt;8080&lt;/port&gt;
                &lt;maxIdleTime&gt;60000&lt;/maxIdleTime&gt;
            &lt;/connector&gt;
            &lt;connector
                implementation=&quot;org.mortbay.jetty.security.SslSocketConnector&quot;&gt;
                &lt;port&gt;8443&lt;/port&gt;
                &lt;maxIdleTime&gt;60000&lt;/maxIdleTime&gt;

                &lt;keystore&gt;${basedir}/src/test/certs/server.p12&lt;/keystore&gt;
                &lt;keystoreType&gt;PKCS12&lt;/keystoreType&gt;
                &lt;password&gt;password&lt;/password&gt;
                &lt;keyPassword&gt;password&lt;/keyPassword&gt;

                &lt;truststore&gt;${basedir}/src/test/certs/truststore.jks&lt;/truststore&gt;
                &lt;trustPassword&gt;password&lt;/trustPassword&gt;

                &lt;needClientAuth&gt;true&lt;/needClientAuth&gt;
            &lt;/connector&gt;
        &lt;/connectors&gt;
    &lt;/configuration&gt;
&lt;/plugin&gt;
]]&gt;
&lt;/script&gt;

You&#39;ll notice we&#39;re referencing the server certificate and trust store created in part 1 - be sure to copy them into the src/test/certs folder before continuing (you&#39;ll need to create the directory).

You should now be able to use Maven to run the jetty:run goal and test your jetty configuration:

&lt;script class=&quot;brush: bash&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
cswhite@Dell-1555:~/workspace/x509WebApp/whitey-webapp$ mvn jetty:run
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building whitey-webapp Maven Webapp
[INFO]    task-segment: [jetty:run]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing jetty:run
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] [compiler:compile {execution: default-compile}]
[INFO] No sources to compile
[INFO] [resources:testResources {execution: default-testResources}]
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/cswhite/workspace/x509WebApp/whitey-webapp/src/test/resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] No sources to compile
[INFO] [jetty:run {execution: default-cli}]
[INFO] Configuring Jetty for project: whitey-webapp Maven Webapp
[INFO] Webapp source directory = /home/cswhite/workspace/x509WebApp/whitey-webapp/src/main/webapp
[INFO] Reload Mechanic: manual
[INFO] Classes = /home/cswhite/workspace/x509WebApp/whitey-webapp/target/classes
2012-04-04 19:56:04.910:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Context path = /whitey-webapp
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] web.xml file = /home/cswhite/workspace/x509WebApp/whitey-webapp/src/main/webapp/WEB-INF/web.xml
[INFO] Webapp directory = /home/cswhite/workspace/x509WebApp/whitey-webapp/src/main/webapp
[INFO] Starting jetty 6.1.26 ...
2012-04-04 19:56:05.017:INFO::jetty-6.1.26
2012-04-04 19:56:05.247:INFO::No Transaction manager found - if your webapp requires one, please configure one.
2012-04-04 19:56:05.812:INFO::Started SelectChannelConnector@0.0.0.0:8080
2012-04-04 19:56:06.581:INFO::Started SslSocketConnector@0.0.0.0:8443
[INFO] Started Jetty Server
[INFO] Console reloading is ENABLED. Hit ENTER on the console to restart the context.
]]&gt;
&lt;/script&gt;

Note: If you get an error about the org.maven.plugins:maven-jetty-plugin not existing then you&#39;ll need to add a section to your ~/.m2/settings.xml file:

&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;settings xmlns=&quot;http://maven.apache.org/SETTINGS/1.0.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd&quot;&gt;

  &lt;pluginGroups&gt;
    &lt;pluginGroup&gt;org.mortbay.jetty&lt;/pluginGroup&gt;
  &lt;/pluginGroups&gt;
&lt;/settings&gt;
]]&gt;
&lt;/script&gt;

&lt;br /&gt;
&lt;h4&gt;



Browser validation&lt;/h4&gt;

So now you&#39;ve got the jetty server up and running, open up your browser and go to https://localhost:8443/whitey-webapp, you should get an error to the effect of ssl_error_bad_cert_alert (Firefox). This is for a number of reasons:
&lt;ul&gt;
&lt;li&gt;Your browser doesn&#39;t trust the server, and doesn&#39;t have a client certificate compatible for the server&#39;s trust store
&lt;li&gt;The server requires a client certificate (that&#39;s the needClientAuth=true portion of the maven-jetty-plugin configuration)
&lt;/ul&gt;

To enable us to successfully connect to the server, you&#39;ll now need to configure your browser. Without going into details for each and every browser out there you need to complete two things:
&lt;ol&gt;
&lt;li&gt;Import the CA root certificate (ca.crt) generated in part 1 into your browsers list of trusted Authorities&lt;/li&gt;
&lt;li&gt;Import the client certificate (client.p12) generated in part 1 into your browsers list of user certificates&lt;/li&gt;
&lt;/ol&gt;
With this complete, point your browser at https://localhost:8443/whitey-webapp. You&#39;ll most probably get a warning message - because the server certificate&#39;s common name (Test Server) doesn&#39;t match the name of the server (localhost). You should be able to convince your browser to ignore this message and continue.&lt;br /&gt;
&lt;br /&gt;
With any luck, you should see a Hello World web page, which has been delivered over SSL&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/609944598203938163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whiteycode.blogspot.com/2012/04/part-2-x509-authentication-with-spring.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/609944598203938163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/609944598203938163'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/2012/04/part-2-x509-authentication-with-spring.html' title='Part 2 - x509 Authentication with Spring, Eclipse, Jetty and Maven'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2820577476179403377.post-1502050277033942582</id><published>2012-04-09T21:46:00.001-04:00</published><updated>2012-04-09T21:46:59.654-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="jetty"/><category scheme="http://www.blogger.com/atom/ns#" term="maven"/><category scheme="http://www.blogger.com/atom/ns#" term="spring"/><category scheme="http://www.blogger.com/atom/ns#" term="x509"/><title type='text'>Part 3 - x509 Authentication with Spring, Eclipse, Jetty and Maven</title><content type='html'>&lt;h2&gt;
Introduction&lt;/h2&gt;
I&#39;ve seen a few posts to &lt;a href=&quot;http://www.stackoverflow.com/&quot; target=&quot;_blank&quot;&gt;Stack Overflow&lt;/a&gt; recently regarding x509 authentication using Spring. I know from my own experience that finding a single tutorial that has everything in one place is difficult, so I&#39;d thought I would put one together that covers pretty much everything you need to get a simple web application going&lt;br /&gt;
&lt;br /&gt;
I&#39;m going to break this up into 4 parts:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Part 1 : Generation of a client &amp;amp; server self-signed certificates (with common self-signed CA root certificate)&lt;/li&gt;
&lt;li&gt;Part 2 : Maven web application archetype generation and maven-jetty-plugin configuration&lt;/li&gt;
&lt;li&gt;Part 3 : Using a simple in-memory authorization provider&lt;/li&gt;
&lt;li&gt;Part 4 : Web application debugging using Eclipse&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
Part 3 - Using a simple authorization provider&lt;/h3&gt;
In this installment I&#39;m going to configure spring security for x509 Pre authentication, extract the users name from their client certificate and look up their credentials in an in-memory authorization provider&lt;br /&gt;

&lt;h4&gt;
Maven Dependencies&lt;/h4&gt;

We need to add a Spring filter to our web application, this filter will be responsible for providing the security layer for the web app. Firstly we need to add some Spring dependencies to our Maven pom.xml:

&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
        &lt;artifactId&gt;spring-security-web&lt;/artifactId&gt;
        &lt;version&gt;3.1.0.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- Required for the http namespace in the spring configuration xml files --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
        &lt;artifactId&gt;spring-security-config&lt;/artifactId&gt;
        &lt;version&gt;3.1.0.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;commons-logging&lt;/groupId&gt;
        &lt;artifactId&gt;commons-logging&lt;/artifactId&gt;
        &lt;version&gt;1.1.1&lt;/version&gt;
    &lt;/dependency&gt;
]]&gt;
&lt;/script&gt;

&lt;h4&gt;
Web Application Deployment Descriptor&lt;/h4&gt;

Next we need to amend the deployment descriptor (web.xml) to add in a Spring DelegatingProxyFilter filter and a listener:

&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
    &lt;!-- Base spring configuration file --&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
        &lt;param-value&gt;/WEB-INF/applicationContext.xml&lt;/param-value&gt;
    &lt;/context-param&gt;

    &lt;!-- Filter to ensure spring gets to handle requests and enforce security --&gt;
    &lt;filter&gt;
        &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
        &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
    &lt;/filter&gt;
    &lt;filter-mapping&gt;
        &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;

    &lt;!-- Web App listener to bootstrap the spring configuration bean factory --&gt;
    &lt;listener&gt;
        &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
    &lt;/listener&gt;
]]&gt;
&lt;/script&gt;

&lt;h4&gt;
Base Spring Configuration&lt;/h4&gt;

Now we need to create the base Spring configuration XML files. For this we need to create the file referenced in the web.xml: /WEB-INF/applicationContext.xml:

&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd&quot;&gt;
           
    &lt;!-- Delegate security configuration to another xml file - by using the 
        classpath prefix, we can override the confguration for testing vs production --&gt;
    &lt;import resource=&quot;classpath:/config/security.xml&quot; /&gt;
&lt;/beans&gt;
]]&gt;
&lt;/script&gt;

This configuration file simply calls out to another configuration file, this time via the classpath prefix. This allows us to amend the classpath order during testing, allowing for different settings for test vs production environments.

&lt;h4&gt;
Security Configuration&lt;/h4&gt;

Now for the security configuration file: src/main/webapp/WEB-INF/classes/config/security.xml:

&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
    xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.1.xsd&quot;&gt;

    &lt;http&gt;
        &lt;!-- Every user request needs to be authenticated and authorized --&gt;
        &lt;intercept-url pattern=&quot;/**&quot; access=&quot;ROLE_USER&quot; /&gt;

        &lt;!-- x509 Pre authentication - pull username from the Common Name 
            field of the client certificate and extract the last &#39;word&#39;, in our client 
            cert example, &#39;John Smith jsmith&#39;, the username &#39;jsmith&#39; --&gt;
        &lt;x509 subject-principal-regex=&quot;CN=.*?\s(\w+),&quot; /&gt;

        &lt;logout /&gt;
    &lt;/http&gt;

    &lt;authentication-manager&gt;
        &lt;authentication-provider&gt;
            &lt;user-service&gt;
                &lt;!-- hard code some user and their roles --&gt;
                &lt;user name=&quot;jsmith&quot; password=&quot;password&quot;
                    authorities=&quot;ROLE_USER, ROLE_ADMIN&quot; /&gt;
            &lt;/user-service&gt;
        &lt;/authentication-provider&gt;
    &lt;/authentication-manager&gt;
&lt;/beans:beans&gt;
]]&gt;
&lt;/script&gt;

&lt;h4&gt;
Displaying User Information&lt;/h4&gt;

To display the user information, we can amend the index.jsp to dump the contents of the request&#39;s user Principal and the user name:

&lt;script class=&quot;brush: html&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
&lt;html&gt;
&lt;body&gt;
 &lt;h2&gt;Spring Security&lt;/h2&gt;

 User principal:
 &lt;%=request.getUserPrincipal()%&gt;
 &lt;br /&gt; User name:
 &lt;%=request.getRemoteUser()%&gt;
 &lt;br /&gt;
&lt;/body&gt;
&lt;/html&gt;
]]&gt;
&lt;/script&gt;


&lt;h4&gt;
Testing It All Works&lt;/h4&gt;

To test everything we just configured, restart your jetty test server

&lt;script class=&quot;brush: xml&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
cswhite@Dell-1555:~/workspace/x509WebApp/whitey-webapp$ mvn jetty:run
]]&gt;
&lt;/script&gt;

Point your browser to &lt;a href=&quot;https://localhost:8443/whitey-webapp/&quot;&gt;https://localhost:8443/whitey-webapp/&lt;/a&gt; and again, with some luck, you should get the following:

&lt;br/&gt;
&lt;br/&gt;

&lt;div style=&quot;border: 1px solid black; margin: 5px; padding: 5px;&quot;&gt;
&lt;b&gt;Spring Security&lt;/b&gt;

&lt;br/&gt;&lt;br/&gt;

User principal: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@4032854a: Principal: org.springframework.security.core.userdetails.User@bb6965d9: Username: jsmith; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER &lt;br/&gt;
User name: jsmith &lt;br/&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/1502050277033942582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whiteycode.blogspot.com/2012/04/part-3-x509-authentication-with-spring.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/1502050277033942582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/1502050277033942582'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/2012/04/part-3-x509-authentication-with-spring.html' title='Part 3 - x509 Authentication with Spring, Eclipse, Jetty and Maven'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2820577476179403377.post-1449523222735613991</id><published>2012-04-04T22:43:00.001-04:00</published><updated>2012-12-27T14:46:30.886-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="jetty"/><category scheme="http://www.blogger.com/atom/ns#" term="maven"/><category scheme="http://www.blogger.com/atom/ns#" term="spring"/><category scheme="http://www.blogger.com/atom/ns#" term="x509"/><title type='text'>Part 1 - x509 Authentication with Spring, Eclipse, Jetty and Maven</title><content type='html'>&lt;h2&gt;



Introduction&lt;/h2&gt;
I&#39;ve seen a few posts to &lt;a href=&quot;http://www.stackoverflow.com/&quot; target=&quot;_blank&quot;&gt;Stack Overflow&lt;/a&gt; recently regarding x509 authentication using Spring. I know from my own experience that finding a single tutorial that has everything in one place is difficult, so I&#39;d thought I would put one together that covers pretty much everything you need to get a simple web application going&lt;br /&gt;
&lt;br /&gt;
I&#39;m going to break this up into 4 parts:
&lt;ul&gt;
&lt;li&gt;Part 1 : Generation of a client &amp;amp; server self-signed certificates (with common self-signed CA root certificate)&lt;/li&gt;
&lt;li&gt;Part 2 : Maven web application archetype generation and maven-jetty-plugin confguration&lt;/li&gt;
&lt;li&gt;Part 3 : Using a simple in-memory authorization provider&lt;/li&gt;
&lt;li&gt;Part 4 : Web application debugging using Eclipse&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;

Part 1 - Certificate generation&lt;/h3&gt;
I am going to present the generation of the client and server certificates signed by a common CA root certificate. Using a common root certificate is more like what you would have in a production environment and makes the creation of a simple trust stores easy.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;

Automated Script&lt;/h4&gt;

&lt;script class=&quot;brush: bash&quot; type=&quot;syntaxhighlighter&quot;&gt;
&lt;![CDATA[
#!/bin/bash

# default password for keys
PASSWORD=password

OUT_DIR=certs

# Subject items
C=&quot;US&quot;
ST=&quot;My State&quot;
L=&quot;My City&quot;
O=&quot;My Corp&quot;

CN_CA=&quot;My CA Root&quot;
CN_SERVER=&quot;Test Server&quot;
CN_CLIENT=&quot;John Smith jsmith&quot;

###############################

# Create output directory
mkdir -p ${OUT_DIR}

###############################

# create CA key
openssl genrsa -des3 -out ${OUT_DIR}/ca.key -passout pass:$PASSWORD 4096

# create CA cert
openssl req -new -x509 -days 365 -key ${OUT_DIR}/ca.key -out ${OUT_DIR}/ca.crt \
 -passin pass:$PASSWORD -subj &quot;/C=${C}/ST=${ST}/L=${L}/O=${O}/CN=${CN_CA}/&quot;

# create truststore
keytool -import -trustcacerts -alias caroot -file ${OUT_DIR}/ca.crt \
 -keystore ${OUT_DIR}/truststore.jks -storepass ${PASSWORD} -noprompt

###############################

# create server key
openssl genrsa -des3 -out ${OUT_DIR}/server.key -passout pass:$PASSWORD 4096

# create server cert request
openssl req -new -key ${OUT_DIR}/server.key -out ${OUT_DIR}/server.csr \
 -passin pass:$PASSWORD -subj &quot;/C=${C}/ST=${ST}/L=${L}/O=${O}/CN=${CN_SERVER}/&quot;

# create server cert
openssl x509 -req -days 365 -in ${OUT_DIR}/server.csr -CA ${OUT_DIR}/ca.crt \
 -CAkey ${OUT_DIR}/ca.key -set_serial 01 -out ${OUT_DIR}/server.crt \
 -passin pass:${PASSWORD}

# convert server cert to PKCS12 format, including key
openssl pkcs12 -export -out ${OUT_DIR}/server.p12 -inkey ${OUT_DIR}/server.key \
 -in ${OUT_DIR}/server.crt -passin pass:${PASSWORD} -passout pass:${PASSWORD}

###############################

# create client key
openssl genrsa -des3 -out ${OUT_DIR}/client.key -passout pass:${PASSWORD} 4096

# create client cert request
openssl req -new -key ${OUT_DIR}/client.key -out ${OUT_DIR}/client.csr \
 -passin pass:$PASSWORD -subj &quot;/C=${C}/ST=${ST}/L=${L}/O=${O}/CN=${CN_CLIENT}/&quot;


# create client cert
openssl x509 -req -days 365 -in ${OUT_DIR}/client.csr -CA ${OUT_DIR}/ca.crt \
 -CAkey ${OUT_DIR}/ca.key -set_serial 02 -out ${OUT_DIR}/client.crt \
 -passin pass:${PASSWORD}

# convert client cert to PKCS12, including key
openssl pkcs12 -export -out ${OUT_DIR}/client.p12 -inkey ${OUT_DIR}/client.key \
 -in ${OUT_DIR}/client.crt -passin pass:${PASSWORD} -passout pass:${PASSWORD}
]]&gt;
&lt;/script&gt;

So time for a bit of an explanation. We need to create 4 components:

&lt;ul&gt;
&lt;li&gt;CA root certificate - This is our Certificate Authority (CA). Using this certificate, we can create certificates for the client and server which are issued by the CA.
&lt;ul&gt;
&lt;li&gt;CA.key - Private key for the CA
&lt;li&gt;CA.crt - Public key for the CA
&lt;/ul&gt;
&lt;li&gt;Server certificate - This is our Server certificate - used by the web server to provide identity information about itself to clients.
&lt;ul&gt;
&lt;li&gt;server.key - Private key for the server
&lt;li&gt;server.csr - Certificate request for the server (used by the CA when creating the server public key)
&lt;li&gt;server.crt - Public key for the server
&lt;/ul&gt;
&lt;li&gt;Client certificate - This is our Client certificate - used by the browser or another client (java) to provide identity to the server.
&lt;ul&gt;
&lt;li&gt;client.key - Private key for the client
&lt;li&gt;client.csr - Certificate request for the client (used by the CA when creating the client public key)
&lt;li&gt;client.crt - Public key for the client
&lt;/ul&gt;
&lt;li&gt;Java trust store - This is our store of certificates that should be trusted. It&#39;s used by the web server or java client to validate whether the certificate for the other side of the link can be used to communicate. By using a common CA root certificate, we can create and use a single truststore on both the web server and java client.
&lt;/ul&gt;

&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://whiteycode.blogspot.com/feeds/1449523222735613991/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://whiteycode.blogspot.com/2012/04/x509-authentication-with-spring-eclipse.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/1449523222735613991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2820577476179403377/posts/default/1449523222735613991'/><link rel='alternate' type='text/html' href='http://whiteycode.blogspot.com/2012/04/x509-authentication-with-spring-eclipse.html' title='Part 1 - x509 Authentication with Spring, Eclipse, Jetty and Maven'/><author><name>Chris White</name><uri>http://www.blogger.com/profile/01453229954143442458</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibILPKEIySgAJOx9KkKAwfonDs-zJ7JLsh0hTLvYxu6j414PFZrRPQmQPgDgO48Rr1Y7g8K1Tr0fUEO-adKISx47MUPRbPNfLttlkHpoaUOp-zNWzGYAw9qtW5e3xRtA/s220/189304_6047830148_7888_n.jpg'/></author><thr:total>2</thr:total></entry></feed>