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:
- Remove the Byte Order Mark, and parse the XML file, extracting the Salt, and the CipherText
- Base64 decode the salt, and set it aside
- Base64 decode the ciphertext
- Copy the first 16 bytes of the ciphertext, and set it aside as your IV
- Copy the next 32 bytes of the ciphertext, and set it aside as your integrity check
- Concatenate the remaining bytes together with the IV. ( IV + remaining bytes ) Set this aside as your data
- 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.
- 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
- 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
- Decrypt the data using AES/CBC/OAEP with the encryption key you generated
- 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:
- 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)
- 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