Wednesday, December 06, 2006

How-To Decrypt a CardSpace backup file

Having posted a utility that decrypts CardSpace backup files, I thought I'd take a moment to explain how it works. The backup file format is rather obscure, so hopefully this should help serve as a guide to people looking to import and export cards with non-Windows selectors.

The first thing to do is take a look at the CardSpace backup file format (edited for brevity):

<?xml version="1.0" encoding="utf-8"?>
<EncryptedStore xmlns="http://schemas.xmlsoap.org/ws/2005/05/identity">
<StoreSalt>3BprRlJ6LpWvvLvuGS6hXQ==</StoreSalt>
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#">
<CipherData>
<CipherValue>...Base64 Encoded Ciphertext...</CipherValue>
</CipherData>
</EncryptedData>
</EncryptedStore>


Here we have the file format. There are really just two things we care about...the Salt, and the CipherValue. The first thing to do is extract these 2 values.

At this point, it's probably good to get an overview of what you'll be doing. The ciphertext is actually a 16 byte Initialization Vector, 32 bytes of signature data for validating integrity, and then a CardSpace RoamingStore xml document encrypted using a PBE. Specifically, it's a PKCS5v1 derived key and AES with CBC.

As you get started, one thing to be aware of is the byte order mark. Regardless of what it claims, this xml in this file is actually encoded using UTF-16LE. So...it's prefixed with 3 bytes of data. Here's what you should expect for the byte order mark:

byte[] bom = {(byte)0xEF, (byte)0xBB, (byte)0xBF};

You'll also want to know about a couple pieces of static entropy used in the algorithm:

byte[] encKeyEntropy = { (byte)0xd9, (byte)0x59, (byte)0x7b, (byte)0x26, (byte)0x1e, (byte)0xd8, (byte)0xb3, (byte)0x44, (byte)0x93, (byte)0x23, (byte)0xb3, (byte)0x96, (byte)0x85, (byte)0xde, (byte)0x95, (byte)0xfc };

byte[] integrityKeyEntropy = {(byte)0xc4, (byte)0x01, (byte)0x7b, (byte)0xf1, (byte)0x6b, (byte)0xad, (byte)0x2f, (byte)0x42, (byte)0xaf, (byte)0xf4, (byte)0x97, (byte)0x7d, (byte)0x4, (byte)0x68, (byte)0x3, (byte)0xdb};


So - back to business. Once you've extracted the salt and the ciphertext, you can begin to decrypt the backup file. Here are the steps you'll want to take:

  1. Remove the Byte Order Mark, and parse the XML file, extracting the Salt, and the CipherText
  2. Base64 decode the salt, and set it aside
  3. Base64 decode the ciphertext
  4. Copy the first 16 bytes of the ciphertext, and set it aside as your IV
  5. Copy the next 32 bytes of the ciphertext, and set it aside as your integrity check
  6. Concatenate the remaining bytes together with the IV. ( IV + remaining bytes ) Set this aside as your data
  7. Derive your keys using PKCS5v1. Take the bytes of the user's password used to encrypt the data, and concatenate it with the salt bytes. Take a SHA256 hash of those bytes, and then SHA256 hash the output another 999 times.
  8. Generate the encryption key by concatenating the static encryption entropy together with the derived key. Generate a SHA256 byte hash of these bytes, and that is your encryption key
  9. Generate the integrity key by concatenating the static entrgrity entropy together with the derived key. Generate a SHA256 byte hash of these bytes, and that is your integrity key
  10. Decrypt the data using AES/CBC/OAEP with the encryption key you generated
  11. Remove the byte order mark, and you have the decrypted RoamingStore.

So - that's the basics of decryption. If you'd like to encrypt a store, you simply perform that process in reverse.

Next, you'll want to validate the integrity of the data. Here's how:
  1. Concatenate the IV, the integrity check data ( bytes 12-48 that you set aside earlier), and the last block of data (the last 16 bytes)
  2. Sha 256 hash this, and you have the computed integrity check. Compare your computed value to the integrity check...if they match byte for byte, you're in luck.
That's it. Be wary of character encoding, but if you follow these steps, you should be able to encrypt/decrypt backup files

4 comments:

Anonymous said...

Hello,

I’ve tried following your instructions to decrypt a card backup file, and I’m not having any success. All I get is “Random” data back from the decryption, which means I have done something wrong.

Can you explain why you have added the IV to the beginning of the Cipher text in step #6? (This doesn’t make any sense to me).

Also, you’ve mentioned to be careful of encodings, is there something specific I’m missing here that might be causing my trouble?

I’m using an XML reader to get the salt and ciphervalue from the XML file and base64 decoding both values.

I create my decryption key by using PKCS#5 with the salt value and my password, then further hash the derived key with the hard coded Encryption Key Salt.

Finally, I’m decrypting the (base64) decoded ciphertext buffer, starting at offset 48 using the first 16 bytes of the buffer as the IV, using AES –CBC.

No luck so far. Any thoughts on what has gone wrong? Or any chance we could see your sample code?

Thanks.

Anonymous said...

Hello,

I’ve tried following your instructions to decrypt a card backup file, and I’m not having any success. All I get is “Random” data back from the decryption, which means I have done something wrong.

Can you explain why you have added the IV to the beginning of the Cipher text in step #6? (This doesn’t make any sense to me).

Also, you’ve mentioned to be careful of encodings, is there something specific I’m missing here that might be causing my trouble?

I’m using an XML reader to get the salt and ciphervalue from the XML file and base64 decoding both values.

I create my decryption key by using PKCS#5 with the salt value and my password, then further hash the derived key with the hard coded Encryption Key Salt.

Finally, I’m decrypting the (base64) decoded ciphertext buffer, starting at offset 48 using the first 16 bytes of the buffer as the IV, using AES –CBC.

No luck so far. Any thoughts on what has gone wrong? Or any chance we could see your sample code?

Thanks.

cmort said...

Here's a link to the code:

http://openinfocard.googlecode.com/svn/trunk/src/org/xmldap/infocard/roaming/EncryptedStore.java

The encrypted store this generates might not work at the moment due to the namespaces being out of date. I've fixed this locally, but waiting on some merge issues to sort out. In the meantime if you fix the namespaces to the correct values in the Main method, than the sample encrypted store should work as well.

As far as why I added the IV to the ciphertext...I can only answer that this is what happens during the encrypt phase, so that's what's required to decrypt. In terms of reasoning, you'd likely want to check with the folks in Redmond; I assume its just used as entropy to confound bulk attacks against the cipher.

Let me know if you have any questions.

Anonymous said...

I found my issue. The password that is passed into the PKCS#5 key derivation must be UTF-16LE (Unicode), mine was not Unicode. Once I made that change to my code, everything worked fine.

Your source code was a great help. Thanks very much.