Brainstorming REST Security Part I

4 Comments

If you went to my presentations at JUDCon/JBossWorld/RHS 2011 or read my recent blog posting you’ve probably noticed that I’m starting to focus on REST+Security.  This will be the start of a series of blogs that attempts to solidify a common vision around Security+REST and spec out what we’re going to do for RESTEasy and JBoss.

Internet Security is A Ghetto

One thing I’ve noticed is what a ghetto Internet security is, or even security in general.  There are old and new specifications, various industry collaborations efforts that succeed sort of (OpenID), start to succeed then have mutinies (OAuth), WS-* specs trying to bleed into the Web space (SAML), and promising specs that have had success in the email world (DKIM).  That’s just the small list of examples.  Its a freakin mess!  One common thread seems to be that most of them focus on providing security for the Internet (Internet with a capital ‘I’) and most have their roots in providing security for browser based apps.  Enterprise apps, while they can build off of security specs defined for the Internet, can often have very different requirements.  Web services can also have different requirements as well as a human (browser) may not be involved with client/server interactions.  In summary, I guess what I’m saying is that there are too many specs, no clear winners, too browser focused, and very little Enterprise focused.

What I’m trying to do with this and subsequent blogs is to brainstorm what high-level requirements for security enterprise apps should have, how can we make deployment of a security solution easier, what existing specs are applicable, what existing specs are open to input, what new specs have to be implemented, how can we make the protocols as easy to implement as possible in multiple languages, and finally, how can we design security services to make it as easy as possible to deploy to our Enterprise applications.

If I had to deploy a security solution…

A security solution I’d like to have would take enterprise as well as the difference between browser and non-browser clients in mind.  Its gotta balance strong security with ease of deployment, ease of use, and ease of implementation.  Many of these will be obvious, but I want to write it down.

  • For browser based clients I’d to authenticate using a user password and a one-time-password (OTP) generated by a soft or hard token generator.  Plain passwords are just not viable.   I myself have had both my GMail and World of Warcraft accounts hacked.  A combo of password + random key allows users to have simple to remember passwords yet be secure enough not to get hacked.  With smart phones like iPhone and Android, its easy to acquire a soft key generator (or implement one) without paying RSA an arm and a leg.
  • After authentication, the browser client should obtain an expirable cookie that it forwards with each request that contains authentication information the server will use to authenticate subsequent requests.
  • For non-browser clients,  I like the idea of digitally signed requests.  Verification of a digitally signed request would be the authentication mechanism.  What’s good about this (like the OTP of browser-based clients) is that credentials are different per request in that they are part of the attached signature.  A nonce and/or an expiration can be included within the digital signature to avoid replay attacks.
  • I foresee the need for non-browser clients to make requests on behalf of other services to other services.  Attaching multiple signatures to a request might be the way here.
  • It would be really cool to have a decentralized way to to both authenticate and authorize.  The hub and spoke approach that Picketlink STS uses creates a bit of a single point of failure and can require extra network round trips.  This decentralized mechanism should be able to work in an environment where services are making requests to other services on behalf of one or more identities.
  • A user had a really interesting case where they wanted to provide access to content through signed URLs.  The idea is that they would generate a signed URL and email it to a user to click on.  Very interesting.

Applicable Specs

Here’s some specs that I thought of off the top of my head that could be useful.  If anybody has ideas of others, let me know.

  • Time-based One Time Password Algorithm (TOTP).  Anil already did some work in Picketlink to implement this protocol.  We still need to integrate it as a Authenticator Valve in JBossWeb.  There’s also a nice iPhone app that supports TOTP.  I actually forked and re-implemented a lot of it on my own when I was learning Objective C a few months ago.  We’re looking at creating an Apple App Store account to distributed this forked implementation so we can brand it Red Hat.
  • SAML.  This may be what we need to do decentralized authorization.  I’m not fully versed in the spec, but I have read up on their HTTP bindings.  I’m not sure if there is any way to tunnel assertions through an HTTP header. (We don’t want to send SOAP requests).  If we can use SAML, we can piggyback off of a lot of the efforts already done in the Picketlink project.
  • Doseta.  I’ve already blogged about this protocol.  Using DNS to distribute keys is a little weird, but cool.  I’m asking that working group for this spec to break out Doseta into a few different specifications so that we can re-use the signature calculation algorithm in a standard way and to also make DNS public key publication optional and maybe also to provide an HTTP way to distribute keys.
  • Amazon REST Authentication.  Specs out how to sign URLs.  Maybe this could be standardized at IETF.
  • OpenID.  OpenID seems interesting for decentralized authentication, but I’m not sure if it can be used as a mechanism to do decentralized authorization.  OpenID is also more of a browser-based technology.
  • OAuth.  OAuth has both browser and non-browser bindings.  OAuth 2.0 pretty much leaves out what a token looks like.  I also don’t really want a token based system for non-browser clients

Possible Middleware Services

Here’s some ideas for services/integration we would implement.

  • HTTP Identity Proxy.  While implementing just an HTTP Proxy Cache is boring what might make these feasible is applying Identity to the mix.  This would delegate authentication and even authorization to an outside service.  Requests would be authenticated/authorized through the proxy, digitally signed, then forwarded to the target service.  The target service then only need to verify the signed request using the public key of the proxy.  While there’s obvious performance drawbacks, what’s interesting about this is that the application doesn’t have to think much about security and it could possibly be added even after the service is deployed.
  • TOTP Authenticator Valve.  Nuff said…I tihnk Anil already has this.
  • Better Auth integration with JBossWeb and the JBoss Security Domain abstraction.  Right now there’s just too many steps to enable things.
  • Various auth plugins for JBossWeb to realize our vision here.

Resteasy 2.2 Released

2 Comments

After baking in the oven the last few months, Resteasy 2.2 has been released to the world and is available for download.  You can view our documentation here.  We fixed a lot of bugs since the 2.1 release which can be viewed in the release notes of previous beta and RC releases:

Features wise we’re starting to focus on security solutions for RESTful web services.  In this release we focused on a digital signature framework based on DOSETA and DKIM.  I wrote a blog a few months ago about some possible use cases for digital signatures.  It will be interesting to see how people use our digitial signature framework, but more importantly how and if they want to use the DOSETA and DKIM protocols for digital signature propagation.  We are extremely interested in feedback and suggestions for improving the protocol and how it might solve (or not solve) any security use cases you might have.

Beyond that, writing the digital signature framework also helped to flush out the Resteasy interceptor API.  For instance, we found that it was very useful to hold off marshalling header objects into string formats until the stream is written to.  This allowed us to pass information through header objects to the interceptors that are performing signing and verification.  Writing down these requirements will be very applicable to the JAX-RS 2.0 JSR as we’re currently focusing on interceptors there.

What’s Next?

Further 2.x releases will focus mainly on adding security features.  We’re also going to be developing Resteasy 3.0 in parallel.  Here’s some points:

  • message body encryption with both multipart/encrypted and develop a new Content-Encoding. This will also help us flush out interceptors more I think
  • SAML/Picketlink. I think we may be able to integrate with SAML, specifically Picketlink to provide some hub/spoke authentication/authorization.
  • Clean up our OAuth support.
  • JAX-RS 2.0 has started which we will implement in Resteasy 3.0. The client API is shaping out and I might deliver a prototype of it when the next revision is submitted by the JAX-RS spec leads.

Interceptors in JAX-RS 2.0

1 Comment

If you don’t know already, JAX-RS 2.0 JSR has started.  Right now things are focused on the Client API and also interceptor model.  The initial proposal for the client API and its corresponding interceptor model is based on Jersey:

I’ve submitted a counter proposal that tries to simplify the class hierarchy and model interceptors based more on what Resteasy has to offer.

Santiago Pericas-Geertsen, one of the spec leads, recently blogged about another proposed interceptor model.  He does a great job of setting some precedence by looking at EJB and CDI interception models.  I think there are some requirements he has overlooked though with his initial proposal that I’d like to address in this blog (and that is addressed in the Red Hat proposal linked above).

Interceptor Use Cases

Resteasy’s interceptor model was driven by use cases.  There were a bunch of features I, and others, wanted Resteasy to have and an interceptor model provided the needed abstractions to implement these features.  Specifically:

  • Server-side custom security
  • Client response caching
  • Sever response caching
  • content encoding: GZIP
  • Header decoration: i.e. annotations that add Cache-Control header
  • Digital Signature generation: the DKIM stuff I’ve been working on lately

All these features have been implemented using our interceptor model.  Another feature I want to add, that I also think might effect the requirements of an interceptor API is:

  • Message Body encryption and the ability to transparently handle it for the client or server.

Interceptor Requirements

Interceptor APIs aren’t a new thing.  They have been implemented in many different frameworks over the years.  One thing that I think throws a wrench in JAX-RS is asynchronous invocations (both on client and server side).  Asynchronous HTTP has become pretty popular both on client and server side.  In this case, different threads may post a request and process the response.

An interceptor model much take into account asynchronous invocations

The Red Hat proposal has 4 different types of interceptors:  Pre, Post, Reader, and Writer.  They are invoked in the following way on the client (pseudo code):

public ClientResponse execute(...) {
  ClientResponse response = invokePreProcessors();

  if (res == null) {
     invokeWriterInterceptors();
     response = invokeHttpInvocation();
  }

  response = invokePostProcessors();
  return response;
}
// application code
ClientResponse response = execute(...);
Something Something = response.getEntity(Something.class); // application acquires entity

// getEntity() invokes ReaderInterceptors.

The server side pseudo code would be very similar.  Why the need for 4 interfaces? 4 interception points?  What is the purpose of each interception point?  Let’s look at our original list of use cases to see:

  • First and foremost, we need to be able to support an asynchronous model.  On the client, different threads may be sending and processing requests and responses.  This is the reason for the pre and post splits.
  • Notice that if a pre-processor returns a response object, no HTTP invocation is done.  Client cache use case needs this because it may have the requested entity cached.  In that scenario, HTTP invocation will want to be circumvented.
  • On the server, with custom security, a pre-processor needs to be able to abort an incoming invocation before it reaches the JAX-RS method if the request is not authenticated.
  • A pre-processor may want to decorate request headers.  The client cache implementation will want to set If-None-Match and If-Modified-Since headers if it believes a cached entry is stale (to perform a conditional GET).

So, thats all the things that might be done be a pre-processor.

What is a WriterInterceptor for? Why is a specific WriterInterceptor needed instead of just piggy backing off of the pre-processor (on client) or post processor (on server).

  • There are two separate use cases for WriterInterceptors.  GZIP encoding and Digital Signatures.  A GZIP WriterInterceptor needs to compress the outgoing response, so it needs to wrap the OutputStream in a GzipOutputStream.  For Digital Signatures (in the DKIM case), a hash of the body needs to be calculated and added to the DKIM-Signature request (client-side) or response (server-side) header.  THis means the outgoing body needs to be buffered as well as hashed so that the header can be set before the body is written.
  • Why a separate interface from pre-processor (client) post-processor (server)? The most compelling reason to have a separate WriterInterceptor is reusability on client and server.  Writer interception happens in different places from the client and server.  Client it happens during request pre-processing.  SErver it happens during response post-processing.
  • Another reason for a separate interface is that a WriterInterceptor has a clear order and interception point.  A client cache interceptor wants to avoid streaming an entity body altogether.  While a content-encoding interceptor wants to intercept stream output.

What are post-processors for?  Why the separation/distinction of a ReaderInterceptor compared to a PostProcessor?

  • On the client side, a cache interceptor will want to cache the raw bits of a response entity *BEFORE* it is unmarshalled.  Also, based on the status code (i.e. NOT MODIFIED), it may want to pull an entry from the cache itself and set the input stream and override some response headers.  A post processor would be used for this.
  • One of the problems on the client is that application code basically needs to decide when unmarshalling happens.  Application code may make decisions based on a status code and/or a response header before it decides how a entity body is unmarshalled, or even if it is unmarshalled.  Because a cross cutting concern (like caching) may need to modify a response code or header, you need this distinction between post processing of a response, and reader interception.
  • One last use case for post-processor is header decoration on the server side.  Think of a @CacheControl annotation that builds and sets a Cache-Control response header.

What are ReaderInterceptors used for?

  • decoding GZIP encoded streams.  Verifying digital signatures.
  • Like WriterInterceptors, it is nice to have the concept of a ReaderInterceptor as it can be used both on the client and server side.

Review of Requirements

Here’s a shorter list of requirements, without the explanation.  A few others are added in here without detailed explanation

  • Need to support both synchronous and asynchronous invocation styles seemlessy without a lot of redundant code.
  • ability to add/modify/remove headers from an outgoing or incoming request
  • ability to add/modify/remove headers from an outgoing or incoming response
  • Ability to abort/interrupt/bypass request processing and return a custom response
  • Ability to intercept before unmarshalling to add/modify/remove headers or change the status code.
  • You need to be able to pass information between interceptors.  Servlet API has request attributes.  Something similar is needed in a JAX-RS interceptor model
  • Interceptors need to be able to obtain metadata from the things they are intercepting.  They need to be able to introspect anntotations on the server side (on the client side too if we standardize Resteasy’s proxy framework).

Hopefully I didn’t miss anything here.

Interceptor ordering

Another thing to talk about is how should interceptors be ordered?  While interceptor developers should try to make their implementations as order independent as possible, this isn’t always possible.  If you are writing a library of interceptors you want to be usable by a wide variety of applications (like the ones we have in Resteasy), you don’t want to require any extra configuration by the user to specify interceptor ordering.  You want them to just be able to pick up interceptors just as they would automatically have their services scanned for and deployed.

To help mitigate this problem, Resteasy has the concept of logical ordering, or “named” precedence.  Resteasy defines a default set of precedence catagories:  SECURITY, HEADER_DECORATOR, DECODER, ENCODER.  If an application interceptor falls into one of these catagories, they just annotate their interceptor with the precendence catagory desired.  New catagories can be created and defined as coming before or after a preconfigured precedence catagory.

It probably doesn’t need to be that complicated.  In Santiago’s blog he suggested a numeric ordering.  What an application could do is define constants that represent a catagory.  Much easier to plug things in this way than the Resteasy model. :)

Anyways, this blog is getting quite long.  Hopefully I’ve articulated the use cases and requirements of interceptors good enough so that you can see that the Red Hat proposal is a sound one based on extensive experience using the model.  I also want to say that the JAX-RS 2.0 process seems to be moving along pretty smoothly.  With Paul and Roberto leaving so abruptly I was a little worried at first, but I think Santiago and Marek have things in hand.

Investigating DOSETA(DKIM) For Signatures

1 Comment

Recently I blogged about my proposed Content-Signature header for transmitting digital signatures.  I created a Internet Draft and submitted it to the IETF.  After a bunch of discussions with some helpful folks on the IETF HTTP WG list, I found that email already has such a system called Domain Keys Identified Mail (DKIM).  Its designed specifically for email messages, but some work is being done by David Crocker  and friends to make it applicable to other protocols via the DOSETA specification.

One particular interesting feature is how public keys are discovered.  Basically DNS names are used for identity and acquiring public keys for verification is just a matter of getting a text record from a particular domain.  It sounds exciting because even in an IT organization you could have distributed non-centralized authentication and authorization. DNS gives you a structure so that you could authorize a whole domain of users or one user at a time.  It would be interesting to be able to see how this structure could be mapped onto a URI instead too.

So, my short lived support for Content-Signature in Resteasy 2.2-beta-1 will be retired and I’m going to look into using DOSETA instead for 2.2.Final.

HornetQ 2.2.2 Released (Has latest REST interface)

3 Comments

HornetQ 2.2.2 has been released.  The HornetQ REST interface is now distributed and bundled with it.  The source code has also moved to the HornetQ SVN.  Visit hornetq.org for more details.

Resteasy 2.2-beta-1 released with new digital signature framework

2 Comments

Fixed a lot of bugs check out jira.  Also some notable new features, specifically:

- Our new digital signature framework inspired by Greg Totsline.  This is the implementation and JAX-RS integration I was talking about the last few blogs.
- Improved interceptors a little bit by allowing attribute passing.

Hopefuly an RC release in April (about a month, I”m traveling a little bit the next month) followed by a quick GA release very soon after.  As always go to our main resteasy page for download and documentation links.

Exchanging digital signatures with Python and Java

3 Comments

I’ve been testing my Content-Signature framework discussed earlier and wanted to see if I could exchange digital signatures generated and verified from both Python and Java code.   After a bit of research here’s what I’ve found so far on how to do this.

Generate keys with openssl

The first step is to generate a private key and a certificate using the openssl program.  This is a common utility.  Do a search if it is not available on your computer and you’ll find support and instructions to install on various platforms.  It came with my macbook pro (I think maybe with Darwin tools).  You’ll have to generate the keys in both .pem format (for Python) and .der format (for Java).

# generate pems
$ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert-private.pem -out mycert.pem

# create private key .der file
$ openssl pkcs8 -topk8 -nocrypt -in mycert-private.pem -out mycert-private.der -outform der

# create certificate .der file
$ openssl x509 -in mycert.pem -out mycert.der -outform der

From this you should have 2 sets of files: mycert-private.pem, mycert-private.der and mycert.pem and mycert.der

Import private key sign in Java

Here’s a nice tool for loading in the .der files created into a Java KeyStore.  I’ve extracted some of the code so that you can see the whole manual, programmatic process of importing a private key and signing a message.

import org.jboss.resteasy.util.Hex;
import java.io.DataInputStream;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.security.spec.PKCS8EncodedKeySpec;

public class ExampleSignTest
{
   @Test
   public void testDerFile() throws Exception
   {
      // import private key
      InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mycert-private.der");
      DataInputStream dis = new DataInputStream(is);
      byte[] derFile = new byte[dis.available()];
      dis.readFully(derFile);
      KeyFactory kf = KeyFactory.getInstance("RSA");
      PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(derFile);
      PrivateKey privateKey = kf.generatePrivate(spec);

      Signature instance = Signature.getInstance("SHA256withRSA");
      instance.initSign(privateKey);
      instance.update("from-java".getBytes());
      byte[] signatureBytes = instance.sign();
      System.out.println("Signature: ");
      System.out.println(Hex.encodeHex(signatureBytes));
   }
}

The code prints out the signature in hex using a simple routine from Resteasy.

Import certificate and verify in Java

Here’s an example of verifying:

@Test
public void testDerFile() throws Exception
{
   CertificateFactory cf = CertificateFactory.getInstance("X.509");
   is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mycert.der");
   Certificate cert = cf.generateCertificate(is);
   PublicKey publicKey = cert.getPublicKey();

   String hexSignature = "4e3014a3a0ff296c07927e846221ee68f70e0b06ed54a1fe974944ea17b836b92279635a7e0bb6b8923df94f4023de95ef07fa76506888897a88ac440eb185b6b117f4c906cba989ffb4e1f81c6677db12e7dc22d51d9369df92165709817792dc3e647dae6b70a0d84c386b0228c2442c9a6a0107381aac8e4cb4c367435d52";
   // loading CertificateChain
   Signature verify = Signature.getInstance("SHA256withRSA");
   verify.initVerify(publicKey);
   verify.update("from-python".getBytes());
   Assert.assertTrue(verify.verify(Hex.decodeHex(pythonHexSignature)));
}

The code has hardcoded a generated signature produced from signing the “from-python” string.

Import private key and sign in Python

The Python code requires the M2Crypto library.  I tried PyCrypto, but I could get it to work.  My code was tested on macbook pro with Python 2.6.1 M2Crypto version   0.21.1.  Also notice that the .pem files are used instead of .der.  I couldn’t figure out if M2Crypto fully supported .der so I just used the .pems.

from M2Crypto import EVP, RSA, X509
import binascii

key = EVP.load_key("mycert-private.pem")
key.reset_context(md='sha256')
key.sign_init()
key.sign_update("from-python")
signature = key.sign_final()
print "Signature:"
print binascii.b2a_hex(signature)

Importing certificate and verifying in Python

Here’s the verification:

rom M2Crypto import EVP, RSA, X509
import binascii

hexSignature = "0a11ab4ebcd2b0803d6e280a1d45b5b5d5d53688949f5a4f2d6436f15df3b10633c79760b9fe3b64eb9d84371c35e8b7d946052dfdd99ebb5cf7f3092762e1a91b261117e6675f2d28afe2ec4\
d90abfe3559a1259d2c66f3dc42ca3bfce7498705833445170bd8c293d60448b6c599abfe2d06882d3fff9ef887379eb7da3fe0"
java_sig = binascii.a2b_hex(hexSignature)

cert = X509.load_cert("mycert.pem")
pubkey = cert.get_pubkey()
pubkey.reset_context(md="sha256")
pubkey.verify_init()
pubkey.verify_update("from-java")
assert pubkey.verify_final(java_sig) == 1

Hope you enjoy.  If you know a better way to set up the certs and key files, let me know.  Using openssl was the best way I could find.

Adding objects that are @Context injectable

Leave a comment

One thing I’ve forgotten to document thoroughly is how to add objects that are injectable via the @javax.ws.rs.core.Context.  Usually you’ll want to use CDI or Spring to inject your dependencies or configuration into a provider or a service, but you may have situations where you cannot depend on these facilities being available to you.

import org.jboss.resteasy.core.Dispatcher;

import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;

public class MyApplication extends Application
{
   public MyApplication(@Context Dispatcher dispatcher)
   {
      MyClass myInstance = new MyClass();
      dispatcher.getDefaultContextObjects().put(MyClass.class, myInstance);
   }

}

The myInstance variable is now available for injection via the @Context annotation.

Multiple uses for Content-Signature

4 Comments

After describing Content-Signature in my last blog, it was picked up by InfoQ.  Also had a great private email exchange with Jean-Jacques Dubray in which we discussed various usecases for signature protocols.  Firstly, before I dive in, a disclaimer.  I am not a security expert and don’t pretend to be one.  While I have used various authentication and authorization protocols over the years, I have not been a designer or implementer of them.  So, here’s some use cases for Content-Signature:

The NULL Use Case

I think one of the most important aspects of something like Content-Signature is that this information can be ignored by any party in the request/response chain.  The signature becomes just another thing that describes the entity being passed around.  Why is this important?  I’ll give a simple example first, then later in the blog a more complex one.

Consider a simple blog.  Let’s say I posted some really stupid comment on somebody’s blog.  Its actually very easy to impersonate somebody in the comments section of anyone’s blog.  So, if a reader read my stupid comment and thought “Did Bill Burke really say that?!?”, how would they know if I really did post or not?  While not that practical in reality, what I could do is sign each comment I made to a blog.  That way, a reader could verify my signature if they so desired.

What’s interesting about this use case is that the blog itself doesn’t care about the signature.  Nor do most comment readers care about the signature.  Only a specific party cares about the signature.  With a header based approach like Content-Signature, renderers can completely ignore the signature applied to the comment if they do not care or understand how to process it.  This is why something like Content-Signature is better than multipart/signed, IMO.  Another interesting thing is that if the blog moved, lets say from Blogspot to WordPress, the import could take along the comment signature with it.  Even though the comment is served under a different URL, the signature is still valid.

Authentication, Authorization, and Message Integrity All In One

Another use for Content-Signature is that it could be used for authentication, authorization, and message integrity, all at the same time.  When a server received a request signed with Content-Signature, it could look into the metadata of Content-Signature to determine the signer.  (This assumes a asymetric key-pair solution)  Look up the public key of the signer in private registry.  Verify the signature with this public key.  If it is successful, the server knows a) that it is the signer that sent the message, and b)that the integrity of the message is good as well.  Now that the identity of the signer is known and valid, the server can determine internally whether the signer is authorized to make the request.  Because Content-Signature is flexible and allows you to add as much metadata as you wish to the signature, additional information like the request URL, a timestampe, a NONCE, whatever could be added to create a more secure process.

Approval Process

Consider a vacation request application.  An employee creates a vacation request form.  Signs it by adding a Content-Signature header and posts it to his manager.  The manager reads the request form, signs it, forwards the document and appends his signature to the Content-Signature header.  Forwards the doc and the new Content-Signature header to HR.  HR knows both parties approved of the document and processes the vacation.

Workflow

Consider a simple order entry workflow where each phase of order fulfillment needs to happen in a specific order.  Each phase also needs to know that the previous phases really happened.  i.e. don’t ship the product until it has been payed for.  It could work like this:

  1. Customer posts order to order-entry system.  Signing it with his information.
  2. Order entry verifies signature.  It also adds an additional signature “order-entry” which is customer-sig+message body.
  3. Billing gets the order next.  It verifies the customer signature and that the “order-entry” signature.  Because “order-entry” was created with the customer-sig and message body, the billing system knows that the order is valid and that the exact order was looked at by the order-entry system.  The Billing system signs the message with customer-sig+message body.
  4. Shipping gets the order next.  It verifies the customer and billing signatures and ships the product.

Ignorant Gateways and Authorization of Actions

Another use case that JJ talked to me about is the ignorant gateway scenario.  Imagine an application that would listen to your twitter messages and forward these messages, via SMS, to your friends’ mobile.  You would automatically be billed instead of the application forwarding the tweets.  In this case, Twitter is the ignorant, pass-through, gateway.   It knows nothing about the whole authorization process.  In an imaginary world, this is how it could work:

  1. You post a twitter message.  You sign (“AT&T Auth Code” + “timestamp” + “message-id” + message body) and attach it as a signature to the method.
  2. The App is listening to twitter.  Does an SMS of message and sends along signature too.
  3. AT&T gets the SMS, looks at the signature.  Verifies it came from the user.  Because the “AT&T Auth Code” is part of the signature, AT&T knows that “Bill Burke” sent the SMS.  Since the “timestamp” and “Message-id” are part of the signature, AT&T can check to see if the SMS is a duplicate.  If all of these pass, then AT&T can bill “Bill Burke” instead of the App for the SMS.

This is also an example of authorization of a specific action via a signature.  I dont think you need separate signatures for each action you want to authorize.  It can just be a matter of concatenating multiple auth-codes within the same signature.  The hole in this approach is that hostile apps could trick users into adding an authorization to their signatures. i.e. “pay-me-$20-from-your-bank-account”.  This is why it is important for providers be involved in authorization code creation.

Complex Workflow

A complex workflow could combine some or all of these use cases together with the coordination of many different applications.

Conclusion

What it boils down to, is that, IMO, something like Content-Signature gives you a lot of flexibility when defining a distributed interface.  It allows you to combine metadata about a representation to the signing of a representation.  Because it is a header, it can be ignored if desired.  Since it is a set of simple name value pairs, it is very easy to create and parse.  (Well, depending on your platform, actually signing the message might be difficult, but, hey…).  Personally, I’m very interested in applying signatures to the RESTful interface we’re creating for our workflow engine.  Signatures just seem like a simpler way to manage multi-tier authentication and authorization.  Who knows, maybe I’m wrong here…

Proposed HTTP digital signature protocol and API

27 Comments

4/5/11: After a lot of feedback from the IETF HTTP WG, I found some work is already being done in this area in the DOSETA specification.  I’ll be retiring Content-Signature for the time being.

3/23/11: I’ve been encouraged to bring this to the IETF and have submitted an Internet-Draft on the subject.  Please go there to see further iterations on this specification.


Recently a RESTEasy user asked for the ability to digitally sign requests and responses.  They were pushing HTTP requests through one or more intermediaries and wanted to make sure that the integrity of the message was maintained as it hopped around the network.  They needed digital signatures.

There’s always been multipart/signed, but I never really liked the data format.  One, what if some clients support the format and some don’t?  Two, signature data seems really to belong in the HTTP header rather than enclosed within an envelope.  I found a nice blog that shared and added a bunch more to the conversation.  So, without finding a match by doing a google search, I decided to define our own protocol. (FYI, OAuth does have signatures as part of its protocol, but I wanted something that could be orthogonal to authentication as the client and server may not be using OAuth for authentication.)

Protocol Goals

The protocol goals and features we wanted to have were:

  • Metadata defining exactly how the message was signed
  • Ability to specify application metadata about the signature and have that metadata be a part of the signature
  • Simplicity of headers.  Have all signature information be stored within HTTP request or response headers.  This makes it easier for frameworks and client and server code in general to handle signature verification.
  • Expiration.  We wanted the option to expire signatures.
  • Signer information.  We wanted the ability to know who signed the message.  This would allow receivers to look up verification keys within internal registries.
  • Ability to ignore the signature if you don’t care about that information or if the client or server doesn’t know how to process it.
  • Ability to forward representation/message to multiple endpoints/receivers
  • Allow multiple different URLs to publish the same signed message
  • Although it could be used as an authorization mechanism, it is not meant to replace existing OAuth or Digest protocols that ensure message integrity

The Content-Signature Header

The Content-Signature header contains all signature information.  It is an entity header that is transmitted along with a request or response.  It is a semicolon ‘;’ delimited list of name value pairs.  Values must be enclosed within quotes if they use any delimiting character within their name or value.  These attributes are metadata describing the signature as well as the signature itself.  Also, the Content-Signature may have more than one value, in other words, more than one signature may be included with the Content-Signature header.  Multiple signatures are delimited by the ‘,’ character.

These are the core attributes of the Content-Signature header:

signature – (required) This is the hex encoded signature of the message.  Hex encoding was chosen over Base64 because Base64 inserts cr/lf characters after 76 bytes which screws up HTTP header parsing.

values – (optional) this is a colon “:” delimited list of attributes that are included within Content-Signature header that are used to calculate the signature.  The order of these listed attributes defines how they are combined to calculate the signature.  The message body is always last when calculating the signature.  If this attribute is omitted, then no Content-Signature attribute is used within the calculation of the signature.

headers -(optional) List of colon “:” delimited  HTTP request or response headers that were included within the signature calculation.  The order of these listed headers defines how they are combined to calculate the signature.

algorithm – (optional) The algorithm used to sign the message.  The allowable values here are the same as those allowed by java.security.Signature.getInstance().  If there is a W3C RFC registry of signing algorithms we could use those instead.

signer - (optional) This is the identity of the signer of the message.  It allows the receiver to look up verification keys within an internal registry.  It also allows applications to know who sent the message.

id - (optional) This is the identity of the signature.  It could be used to describe the purpose of a particular signature included with the Content-Signature header.

timestamp – (optional) The time and date the message was signed.  This gives the receiver the option to refuse old signed messages.  The format of this timestamp is the Date format described in RFC 2616.

expiration – (optional) The time and date the message should be expired.  This gives the sender the option to set an expiration date on the message.  The format of this attribute is the Date format described in RFC 2616.

signature-refs – This is a ‘:’ delimited list referencing other signatures by their id attribute within the Content-Signature header.  This means that these referenced signature values will be included within the calculation of the current signature.  The hex-encoded value of the referenced signature will be used .

Other attributes may be added later depending on user requirements and interest.   URI and query parameters were specifically left out of the protocol as integrity between two parties should be handled by HTTPS/SSL, the Digest authentication scheme discussed in RFC 2617, or OAuth.  Remember, the point of writing this protocol is so that representations can be signed and exchanged between multiple parties on multiple machines and URLs.

Signing and Verifying a message

The signer of a message decides which Content-Signature attributes and HTTP headers it wants to include within the full signature.  The signature is calculated by signing the concatenation of

attribute-values + header-values + signature-refs + message-body

Attribute-values pertain to the list of attribute names defined within the ‘values’ attribute of the Content-Signature element.  Header-values pertain to the list of header names defined within the ‘headers’ attribute of the Content-Signature element.  Signature-refs pertains to referenced signatures that also appear in the Content-Signature header.  Attributes must always precede headers.  Headers must precede signature refs.  The message-body always comes last.  For example, if the signer decides to include the signer, expiration attributes and Content-Type and Date headers with a text/plain message of “hello world”, the base for the signature would look like this:

billSunday, 06-Nov-11 08:49:37 GMTtext/plainFriday, 11-Feb-11 07:49:37 GMThello world

The Content-Signature header transmitted would look like:

Content-Signature: values=signer:expiration;
                   headers=Content-Type:Date;
                   signer=bill;
                   expiration="Sunday, 06-Nov-11 08:49:37 GMT";
                   signature=0f341265ffa32211333f6ab2d1

To verify a signature, the verifier would recreate the signature string by concatenating the attributes specified in the “values” attribute, HTTP headers defined in “headers” attribute, and finally the message body. Then apply the verification algorithm.

If there is an attribute declared within the “values” attribute that isn’t specified in the Content-Signature header, it is assumed it is a secret held between the signer and verifier.  i.e. the signer.  The value of this attribute must be determined in an undefined way.

If there is a header declared within the “headers” attribute that doesn’t exist, the server may choose to abort if it cannot figure out how to reproduce this value.

Here’s an example of multiple signatures.  Let’s say the Content-Signature header is initially set up like this with a message body of “hello”:

Content-Signature: id=husband;
                   signature=0001,
                   id=wife;
                   signature=0002

Here, we have two initial signatures signed by two different entities, husband and wife (found by their id attribute).  We want to define a third signature, marriage, that includes those signatures.

Content-Signature: id=husband;
                   signature=0001,
                   id=wife;
                   signature=0002,
                   id=marriage;
                   signature-refs=husband:wife
                  signature=00033

The marriage signature would be calculate by the signing of this string:

00010002hello

Which is:

husband’s signature + wife’s signature + message body

If there is a signature reference declared within the signature-refs attribute that doesn’t exist, the server may choose to abort if it cannot figure out how to reproduce this value.

Other similar protocols out there?

I only spent about an hour looking to see if there were similar protocols out there.  If somebody knows, let me know.  It would be cool to get feedback on this proposal as well.

Edited:

People in the comments section of this entry keep mentioning two-legged OAuth, but I don’t see how they describe anything other than in the Authorization header.  This is something we don’t want as we want to be able to use traditional authentication mechanisms so that signing can be supported on servers or clients that don’t understand OAuth (or don’t want to use it).

Older Entries Newer Entries

Follow

Get every new post delivered to your Inbox.

Join 480 other followers