Security Stuff
So the thing folks are talking about this week is security in REST. I figured it would be a good opportunity to once again highlight one of the cool features of Abdera… the fact that you can Digitally sign and/or Encrypt Atom feeds and entries with generally very little effort.
Update: Someone asked for a sample of the output from the listings…
Listing 1: Digitally Signing an Atom Entry:
public class DSig {
private static final String keystoreFile = "/key.jks";
private static final String keystoreType = "JKS";
private static final String keystorePass = "testing";
private static final String privateKeyAlias = "James";
private static final String privateKeyPass = "testing";
private static final String certificateAlias = "James";
public static void main(String[] args) throws Exception {
// Initialize the keystore
KeyStore ks = KeyStore.getInstance(keystoreType);
InputStream in = DSig.class.getResourceAsStream(keystoreFile);
ks.load(in, keystorePass.toCharArray());
PrivateKey signingKey =
(PrivateKey) ks.getKey(
privateKeyAlias,
privateKeyPass.toCharArray());
X509Certificate cert =
(X509Certificate) ks.getCertificate(
certificateAlias);
// Create the entry to sign
Abdera abdera = new Abdera();
AbderaSecurity absec = new AbderaSecurity(abdera);
Factory factory = abdera.getFactory();
Entry entry = factory.newEntry();
entry.setId(”http://example.org/foo/entry”);
entry.setUpdated(new java.util.Date());
entry.setTitle(”This is an entry”);
entry.setContentAsXhtml(”This <b>is</b> <i>markup</i>”);
entry.addAuthor(”James”);
entry.addLink(”http://www.example.org”);
// Prepare the digital signature options
Signature sig = absec.getSignature();
SignatureOptions options = sig.getDefaultSignatureOptions();
options.setCertificate(cert);
options.setSigningKey(signingKey);
// Sign the entry
entry = sig.sign(entry, options);
// Check the round trip
ByteArrayOutputStream out = new ByteArrayOutputStream();
entry.writeTo(out); // do not use the pretty writer, it will break the signature
ByteArrayInputStream bais = new ByteArrayInputStream(out.toByteArray());
Document<Entry> entry_doc = abdera.getParser().parse(bais);
entry = entry_doc.getRoot();
System.out.println(”Valid? ” + sig.verify(entry,null));
entry.setTitle(”Change the title”);
System.out.println(”Valid after changing the title? ” + sig.verify(entry,null));
}
}
Listing 2: Encrypting and Decrypting an Atom document
public class Enc {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
// Prepare the crypto provider
try {
Class.forName("org.bouncycastle.LICENSE");
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (Exception e) {
throw new RuntimeException("The Bouncy Castle JCE Provider is not available");
}
// Generate Encryption Key
String jceAlgorithmName = “AES”;
KeyGenerator keyGenerator =
KeyGenerator.getInstance(jceAlgorithmName);
keyGenerator.init(128);
SecretKey key = keyGenerator.generateKey();
// Create the entry to encrypt
Abdera abdera = new Abdera();
AbderaSecurity absec = new AbderaSecurity(abdera);
Factory factory = abdera.getFactory();
Entry entry = factory.newEntry();
entry.setId(”http://example.org/foo/entry”);
entry.setUpdated(new java.util.Date());
entry.setTitle(”This is an entry”);
entry.setContentAsXhtml(”This <b>is</b> <i>markup</i>”);
entry.addAuthor(”James”);
entry.addLink(”http://www.example.org”);
// Prepare the encryption options
Encryption enc = absec.getEncryption();
EncryptionOptions options = enc.getDefaultEncryptionOptions();
options.setDataEncryptionKey(key);
// Encrypt the document using the generated key
Document enc_doc = enc.encrypt(entry.getDocument(), options);
enc_doc.writeTo(System.out);
System.out.println(”nn”);
// Decrypt the document using the generated key
Document<Entry> entry_doc = enc.decrypt(enc_doc, options);
entry_doc.writeTo(System.out);
}
}
Listing 3: Abdera also provides (mostly as an example) a HTTP Servlet Filter that will digitally sign any Atom document served up by a server. Just add the following to your web.xml:
<filter>
<filter-name>signing filter</filter-name>
<filter-class>org.apache.abdera.security.util.servlet.SignedResponseFilter</filter-class>
<init-param>
<param-name>org.apache.abdera.security.util.servlet.Keystore</param-name>
<param-value>/key.jks</param-value>
</init-param>
<init-param>
<param-name>org.apache.abdera.security.util.servlet.KeystorePassword</param-name>
<param-value>testing</param-value>
</init-param>
<init-param>
<param-name>org.apache.abdera.security.util.servlet.PrivateKeyAlias</param-name>
<param-value>James</param-value>
</init-param>
<init-param>
<param-name>org.apache.abdera.security.util.servlet.PrivateKeyPassword</param-name>
<param-value>testing</param-value>
</init-param>
<init-param>
<param-name>org.apache.abdera.security.util.servlet.CertificateAlias</param-name>
<param-value>James</param-value>
</init-param>
</filter>
<filter-mapping id="signing-filter">
<filter-name>signing filter</filter-name>
<servlet-name>Abdera</servlet-name>
</filter-mapping>
December 2nd, 2006 at 3:51 pm
Question: In the encryption example you’re using a session key. In the real world, how would you get this key out to the consuming application? More accurately, since session keys are ephemeral, how would you get the consumer’s public key with which to encrypt the message (or encrypt the session key, if the encrypted key can be kept in the message somewhere)? If we assume the feed is delivered via HTTPS, can Abdera access the consumer’s public key from an SSL mutual-auth key exchange or does the producer have to have a copy already? Or am I not making sense?
Pete
December 2nd, 2006 at 5:44 pm
Good question. I suppose key feed can be introduced but that needs to be protected somehow. It can be protected with password but Atom client support for password-protected feed is rather sparse which means a custom client is needed. But if a custom RSS client is used, it is essentially a proprietary solution so you are back to square one.
I put a lot of thought and work into secure feed syndication and, frankly, it’s pretty slippery because traditional solutions tend to unravel the whole premise of feed syndication. It’s a shame that RSS developer community is practically ignorant about the subject and Atom developer community is still nibbling at the lower end of the slippery slope. I think the only way to break out of the situation is for a major vendor to introduce a product. That would either light the fire under everybody’s feed or establish a de facto standard.
December 2nd, 2006 at 6:36 pm
I’m still stewing over the key exchange for the encryption stuff. As soon as I figure it out I’ll be putting together a HTTP Servlet Filter in Abdera that will encrypt Atom documents as they are served up. I’ll have to stew on this a bit and see if I can come up with a simple workable solution.
December 2nd, 2006 at 7:23 pm
In that signed entry I see both a public key and a certificate which should also contain a copy of the key, so the key appears to be included twice. I’m too lazy to look at the XML-DSig spec, but this seems wrong or at least suboptimal. The obvious concern here is crypto bloat.
As for encryption, I think it is more realistic to encrypt with a public key (e.g. PGP-style or S/MIME-style).
I agree with Don that mixing crypto and syndication is tricky; in many situations it’s probably more appropriate to sign/encrypt individual entries so that slicing and dicing the feed won’t invalidate the signatures. Short of using some sort of must-understand, it’s not clear what to do about intermediaries who munge the contents of entries though.
December 2nd, 2006 at 7:32 pm
Yeah, I’m quite sure the examples could use a good tweak or two. I’ll take a look at the double key thing in the dsig example. For the encryption, absolutely. Encryption with a public or shared key is most likely. I’ll work up a few better examples this weekend.
The dsig support described in the Atom spec and implemented by Abdera allows entries to be signed independently of their containing feeds making it possible to move entries around without invalidating the signature. Any messing around with the entry content, however, definitely breaks the signature.
Encrypting individual entries within an Atom feed doesn’t really seem to make much sense to me. That is, if you’re serving up encrypted entries you’re likely not intending those to be syndicated arbitrarily. Encrypting the Atom document as a whole seems to be far more realistic (which is precisely why the Atom spec only addresses encrypting entire Atom documents).
December 2nd, 2006 at 8:54 pm
Ok, I checked in a fix that removes the extra keyinfo in the dsig and I’m getting ready to post another entry that shows encryption using a public key pair. I’ll touch on key exchange a bit later.