Decrypting Apple Pay Payment Blob Using .NET – Part 4: Decrypt the data.

Series Intro: Decrypting Apple Pay Payment Blob Using .NET

Finally, we’ve arrived at Step 4 on Apple’s guide for decrypting the payment blob, which is the actual decryption. This is where all our efforts will finally bear fruit. What kind of fruit? Apples, of course. Our guides have instructed us as such:

For ECC (EC_v1), Decrypt the data key using AES–256 (id-aes256-GCM 2.16.840.1.101.3.4.1.46), with an initialization vector of 16 null bytes and no associated authentication data.

Sadly this is also where our journey to use only what is available in .NET & Windows ends.

AES-GCM is not supported in .NET Framework. .NET Core 3.0 has an object, AesGcm, but sadly it will not work for us. The reason is Apple’s 16 null bytes of the initialization vector. It took many hours over multiple days to figure that out. We discussed in “Part 3” of this series that .NET security on Windows really just calls into the native libraries. AesGcm in .NET Core 3 is actually calling BCryptDecrypt. Here’s the corefx source showing the call. We could do the same in .NET Framework, but the AES-GCM implementation on Windows is actually bugged. It will only work with 12 bytes of IV. Now I think this is more on Apple, than Microsoft. AES-GCM works best with 12 bytes of IV which reserves the last 4 bytes for its counter data, giving us 16 total (block size). The AES-GCM spec says that if you get any number of bytes other than 12, you have to hash it to equal 16. Those hashes can have collisions, which is actually less secure. I’m paraphrasing from this wonderful discussion: AES-GCM recommended IV size: Why 12 bytes?

Interesting to note, it also seems like 16 null bytes is pretty pointless from a security design perspective. That is if you believe these discussions, I have not taken the time to do the math and read the proofs to verify that myself. Nor will I ever!

From some other posts I saw it looks like Python defaults to 16 null bytes, for some reason. Apple are you using Python on your end? You owe me a week of my life back for that decision!

Anyway, this post mentions what I found through testing, that BCryptDecrypt throws a STATUS_INVALID_PARAMETER error if you give it anything other than 12 bytes of IV/Nonce. That’s why we can’t use what .NET/Microsoft provides.

The task then is to pick a library and use it to do the decryption. Here’s an example using BouncyCastle, which is available on NuGet for .NET Framework and .NET Standard:

        private static readonly byte[] s_ApplePayInitializationVector = new byte[16];

        public static byte[] DecryptCipherDataUsingAesGcmAlgorithm(byte[] keyMaterial, byte[] cipherData)
        {
            if (keyMaterial.Length != 32)
                throw new InvalidOperationException("KeyMaterial size was invalid.");

            IBufferedCipher Cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding");
            Cipher.Init(
                false,
                new ParametersWithIV(
                    ParameterUtilities.CreateKeyParameter("AES", keyMaterial),
                    s_ApplePayInitializationVector));
            return Cipher.DoFinal(cipherData);
        }

Once we have the key material, it’s actually very simple to do the decryption. Take the decrypted (plaintext) bytes you get from your AES-GCM library and run them through System.Text.Encoding.UTF8.GetString() to get your final JSON, which Apple calls “Payment Data Keys” on its guide. To be perfectly honest I’m unclear whether decrypted bytes are ASCII or UTF8 encoded. I emailed our Apple rep and will update this post when I know for sure. Update: Apple rep said UTF8 “should be fine” so take from that what you will.

Buyer beware: We do not use BouncyCastle in prod to do the decryption of our Apple Pay blobs. I’m not endorsing it whatsoever, please do your own investigation and vet it out before making a decision on what to use. We use another library which is really expensive and enterprise-grade, and also a total pain to develop against because the documentation is lacking. But that’s what our security team decided was best!

We’re done. Cheers mates!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.