|
HTTPsec-Java is an implementation of the HTTPsec/1.0 specification in Java.
HTTPsec-Java includes a wrapper for java.net.HttpURLConnection that adds HTTPsec authentication to client applications and a servlet filter that can add HTTPsec authentication to any servlet webapp.
httpsec.jar |
The HTTPsec-Java library. |
httpsec-tools.jar |
Command line tools. |
web/ |
Sample webapp |
docs/ |
Documentation |
httpsec-tools.jar ) requires the Bouncy Castle libraries.
Include httpsec.jar in your classpath.
If you are using the com.secarta.httpsec.tools package include httpsec-tools.jar.
Copy the web/ directory of the distribution to your servlet containers webapps/ directory.
Copy httpsec.jar to your webapps WEB-INF/lib/ directory. Use the sample web.xml from the sample webapp as a guide to edit your web.xml.
You must configure the Java Cryptography Extension ( JCE ) before these examples will work. Unless you have specfic JCE configuration requirements try The Quick Method That Always Works.
Make simple http "GET" request to the HTTPsec test server at http://server3.clinksystems.com:8080/httpsec/ The test servlet "echo" will return our request in the response body ( in the manner of the http method "TRACE" ).
java -jar httpsec-tools.jar client -id test http://server3.clinksystems.com:8080/httpsec/echo
Should produce output like this:
no private key specified - creating a self-signed CA...
local principal: test#aa4242443a3c4445e2af5a8b29077835b6f3b532491f19f97398991cbefa3a44
remote principal: httpsec_sample_webapp#c9a7f07c16f7dd04f2e735dff616a9b6fe575d5a50d4a0c80faf2368ce8f7b89
200 OK
Server: Apache-Coyote/1.1
Content-Encoding: x-httpsec/1.0-cipher
Expires: Thu, 26 Oct 2006 10:49:37 GMT
Cache-Control: no-transform
WWW-Authenticate: httpsec/1.0 continue; count=2; mac=AUatMW+yNuSW24MFYgjfuVaJ5hxUDI6gc7IQ0PTcSFk=; digest=P+ziUUKo6rkKjtYUdmDQxHI/PNyO5gb2RWIpXjVIoy8=
Content-Type: message/http
Content-Length: 496
Date: Thu, 26 Oct 2006 10:49:37 GMT
HTTP/1.1 GET /httpsec/echo
accept-encoding: x-httpsec/1.0-cipher
authorization: httpsec/1.0 continue; count=1; mac=b3nxfxuolaiNKWRjfKFOq/9B4JTR9++aPVHds+aMBrM=; digest=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=; url=http://server3.clinksystems.com:8080/httpsec/echo; token=/UV1euebPt8=
user-agent: Java/1.5.0_04
host: server3.clinksystems.com:8080
accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
connection: keep-alive
content-type: application/x-www-form-urlencoded
Import the HTTPsec-Java classes:
import com.secarta.httpsec.HttpsecException;
import com.secarta.httpsec.HttpsecURLConnection;
import com.secarta.httpsec.HttpsecURLConnectionFactory;
Choose an id for the local principal or "requester":
String id = "test";
We need a certificate url and a private key. We could use the url of a certificate we've published on the a web server and load a private key from a file. This time we'll create a self-signed certificate authority that contains a certificate and private key.
com.secarta.httpsec.tools.tools.CA ca = new com.secarta.httpsec.tools.CA( id );
Then we'll initialize an HttpsecURLConnectionFactory with the id, certificate and private key:
HttpsecURLConnectionFactory cf = new HttpsecURLConnectionFactory(
id,
ca.getCertificate(),
ca.getPrivateKey
);
Now we'll create an ordinary java.net.HttpURLConnection:
HttpURLConnection c = ( HttpURLConnection )new URL(
"http://server3.clinksystems.com:8080/httpsec/echo"
);
Then we'll use the HttpsecURLConnectionFactory to wrap it and add HTTPsec authentication:
HttpsecURLConnection hc = cf.wrap( c );
We can use the HttpsecURLConnection just like an ordinary java.net.HttpURLConnection:
try {
hc.setRequestMethod( "POST" );
hc.setRequestProperty( "Content-Type", "text/plain; charset=utf-8" );
hc.setDoOutput( true );
hc.getOutputStream().write( "hello world".getBytes() );
System.out.println( hc.getResponseCode() + " " + hc.getResponseMessage() );
} catch ( IOException e ) {
// handle exceptions
} finally {
hc.close();
}
Note the finally { hc.close(); } block. HTTPsec-Java might create a temporary file to
buffer large messages. Calling HttpsecURLConnection.close() in a "finally" block ensures that if a file
has been created it will be deleted regardless of whether exceptions are thrown.
HttpsecURLConnection reports the remote principal or "responder" as an HttpsecPrincipal object:
HttpsecPrincipal p = hc.getPrincipal();
System.out.println( p );
> httpsec_sample_webapp#c9a7f07c16f7dd04f2e735dff616a9b6fe575d5a50d4a0c80faf2368ce8f7b89
System.out.printnln( p.getID() );
> httpsec_sample_webapp
System.out.println( p.getFingerprint() );
> c9a7f07c16f7dd04f2e735dff616a9b6fe575d5a50d4a0c80faf2368ce8f7b89
Here is an annotated version of the web.xml from the sample webapp.
<?xml version="1.0" encoding="ISO-8859-1"?><web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"><!-- This filter does httpsec/1.0 authentication. see the javadocs for com.secarta.httpsec.servlet.HttpsecFilter for more info. --><filter><filter-name>httpsec</filter-name><filter-class>com.secarta.httpsec.servlet.HttpsecFilter</filter-class><!-- Out of the box the filter only supports a single local identity. This is what appears in the "id" field of challenge and initialize headers. --><init-param><param-name>local-id</param-name><param-value></init-param>httpsec_sample_webapp</param-value><!-- The filename of the private key associated with local-id. For obvious reasons you must make sure this is not accessible from the web. --><init-param><param-name>private-key</param-name><param-value></init-param>WEB-INF/httpsec_sample_webapp.key</param-value><!-- The url of the certificate associated with local-id. This url must be accessible from the web. See the httpsec documentation for more about how to communicate your certificate to other users. --><init-param><param-name>certificate</param-name><param-value></init-param>http://secarta.com/products/httpsec-java/httpsec_sample_webapp.cert</param-value><!-- If set to "yes" ( or "true" ) the filter will send a 401 Authorization Required response with an httpsec/1.0 challenge header. to unauthenticated requests. Otherwise it will ignore them. --><init-param><param-name>challenge</param-name><param-value></init-param>yes</param-value><!-- Should the filter expect a valid digest with each authenticated request. --><init-param><param-name>request-digest</param-name><param-value></init-param>yes</param-value><!-- Should the filter add a digest to authenticated responses. --><init-param><param-name>response-digest</param-name><param-value></init-param>yes</param-value><!-- Should the filter cipher responses for clients that have sent Accept-Encoding: x-httpsec/1.0-cipher --><init-param><param-name>cipher</param-name><param-value></init-param>yes</param-value><!-- Delete httpsec sessions ( *NOT* servlet sessions ) that have not been used for this many milliseconds. --><init-param><param-name>session-timeout</param-name><param-value></init-param>600000</param-value><!-- The filter will examine it's table of sessions every so many milliseconds and delete expired sessions. --><init-param><param-name>session-cleanup</param-name><param-value></init-param>60000</param-value><!-- Log filter activity. --><init-param></filter><param-name>verbose</param-name><param-value></init-param>yes</param-value><!-- This servlet returns the request it receives as message/http in the manner of the http TRACE method. --><servlet><servlet-name>echo</servlet-name><servlet-class></servlet>com.secarta.httpsec.servlet.Echo</servlet-class><servlet-mapping><servlet-name>echo</servlet-name><url-pattern></servlet-mapping>/echo</url-pattern><filter-mapping></web-app><filter-name>httpsec</filter-name><servlet-name></filter-mapping>echo</servlet-name>
This section is about how to configure the Java Cryptography Environment ( "JCE" ) to work with HTTPsec-Java and how to configure HTTPsec-Java to work with the JCE.
If you are unfamilliar with the JCE this might help.
You MUST install the Java Cryptography Extension Unlimited Strength Jurisdiction Policy Files 1.4.2. Sun has to cripple its crypto libraries to export them legally. These "policy files" de-restrict them.
Do not touch Primitives.conf
Download the provider package from the Legion of the Bouncy Castle.
For client applications put it in the same directory as httpsec.jar and, if you're using it
httpsec-tools.jar.
For servlet applications ( with Apache Tomcat anyway ) you can't put the provider library in
your webapps WEB-INF/lib/ directory because if you do the webapp will refuse to reload.
Instead put it in your servlet containers common libs directory ( $TOMCAT/common/lib/ with
Tomcat ).
If you have a more complicated application, or you want to use another provider,
or a combination of providers you might need to make some changes to Primitives.conf
to make HTTPsec-Java work for your application.
Primitives.conf is a java properties file which should be included at the bottom level of
the classpath where httpsec.jar is installed.
The first entry in Primitives.conf is a space-separated list of JCE provider classes
that HTTPsec-Java will attempt to install.
providers = org.bouncycastle.jce.provider.BouncyCastleProvider:2 com.rsa.jsafe.provider.JsafeJCE
Each provider in the list can have an optional index after a colon at the end:
...BouncyCastleProvider:2
Which if present will make HTTPsec-Java use
java.security.Security.insertProviderAt( provider, index )
rather than the default
java.security.Security.addProvider( provider ).
If a provider class cannot be found HTTPsec-Java will print a warning, but not fail.
If any exception happens whilst instantiating the provider HTTPsec-Java will fail with a
java.lang.IllegalStateException.
HTTPsec-Java uses symbolic names to map JCE algorithm names to it's cryptographic services.
Primitives.conf allows you to override the defaults.
The following properties are shown with their default values.
Hash = SHA-256
Hmac = HmacSHA256
PublicKeyCipher = RSA/NONE/OAEPwithSHA1andMGF1padding
BlockCipher = AES/ECB/NoPadding
StreamCipher = AES/CBC/PKCS5padding
PublicKeyGenerator = RSA
PublicKeyFactory = RSA
DHGenerator = DiffieHellman
DHAgreement = DiffieHellman
CertificateFactory = X.509
Signature = SHA256withRSAandMGf1
SecureRandom = SHA1PRNG
Note that you can completely mess up HTTPsec-Java by substituting different algorithms.
For each of the above properties you can also specify a provider. For example:
BlockCipher.provider = BC
Would force HTTPsec-Java to use the Bouncy Castle ( BC ) provider to create
java.security.Cipher instances using the BlockCipher algorithm.
By default HTTPsec-Java uses whatever provider provides the algorithm it's after.
The HTTPsec specification defines Diffie-Hellman parameter groups ( MODP groups ) that implementations must support. You can use Primitives.conf to set the default MODP group that HTTPsec-Java uses.
DHGroup = rfc3526#14
HTTPsec uses X.509 certificates. For many applications self-signed certificates are useful. The sample webapp contains a test certificate and private key.
To generate a self-signed certificate and private key ( a "certificate authority" ) first define the HTTPsec "peer identifier" to use:
ID=test
To create the certificate authority with HTTPsec-Java do:
java -jar httpsec-tools.jar ca -cert certificate.pem -private private.pem $ID
Alternatively you can generate the certificate authority with openssl:
openssl genrsa 2048 > test.key
openssl req -new -x509 -nodes -sha1 -days 365 -key test.key -subj "/CN=$ID" > certificate.pem
openssl pkcs8 -nocrypt -topk8 < test.key > private.pem
You can use openssl to view the contents of a certificate by using the command:
openssl x509 -text -noout < certificate.pem
The HTTPsec specification defines several ways to communicate certificates. The simplest is to publish certificate.pem on a web server.
The private key private.pem should always be stored somewhere that is not accessible via external http.
Using HTTPsec in your application adds overheads and constraints to its ordinary http conversations.
Messages authenticated with HTTPsec are not cachable.
Initializing an HTTPsec session involves an extra request / response exchange. Both peers may need to make a subsequent database / http request to locate a certificate for the other.
The http protocol allows for messages of arbitrary length with the end of the message delimited
by the sender closing the stream ( Connection: close ) or by sending a final chunk
( Transfer-Encoding: chunked ).
This, in combination with HTTPsec message digests can lead to an arbitrarily long wait as the receiver has to reach the end of the message to calculate the message digest.
Both client and server components of HTTPsec-Java allow the user to switch off message digest processing at the expense of establishing message integrity.
Cryptographic algorithm performance in Java is not as predictable as native implementations. Alright, it's crap. Then again no sometimes it's not. It will depend on which provider you use for which algorithm.
certificate=this:entity-body not supported
This version of HTTPsec-Java does not recognise the this:entity-body URI in
the "certificate" field of initialize headers.
If a peer receives a message with the certificate specified like this it will be unable to
retrieve the certificate and will fail.
The optional certificate=... field is never sent in challenge responses.
Since the field is optional this has no effect on protocol operation.
The default formats for private keys produced by HTTPsec-Java and openssl differ.
To convert an existing private key created with openssl
( that wasn't created with the -topk8 option ) to HTTPsec-Java ( PKCS#8 ) format do:
openssl pkcs8 -nocrypt -topk8 < openssl-key
To convert a key created with HTTPsec-Java to the default openssl format ( PKCS#12 ) do:
openssl pkcs8 -nocrypt < httpsec-java-key
This product uses strong cryptography and may be illegal or subject to import / export restrictions in your country or jurisdiction.
index