IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

CryptDecrypt and NTE_BAD_DATA

Tuesday 11 May, 2004, 09:54 AM

I've been struggling with a problem with the Windows CryptoAPI over the last few days. I thought I'd write about it in the hope that the next person to hit this problem has a slightly easier time googling for the solution than I did...

I had some data encrypted on a Windows 2003 box using the RC2 symmetric encryption algorithm. This data had to be decrypted by various other boxes. The machines running Windows 2003 were all able to decrypt the data without problems, but on any Windows 2000 box, the CryptDecrypt API returned the NTE_BAD_DATA error. As it happens, in my particular case the encryption in Windows 2003 was being done using the .NET RC2CryptoServiceProvider, but the problem can also be reproduced by encrypting the data on Windows 2003 with CryptEncrypt. (As you'd hope, regardless of whether you use the .NET classes or the Win32 crypto API to encrypt data, if you use the same algorithm, key, and cleartext, you get the same ciphertext.)

A little googling revealed that I was far from alone - this appears to be a common problem. Sadly, the solution was much less in evidence than the complaint... Both on the web and on newsgroup archives, unanswered pleas for help with this issue were the norm. And in many cases, well-meaning but unhelpful advice was given. (Most often, those trying to help had merely read the documentation for NTE_BAD_DATA and declared that the problem was almost certainly to do with padding. While that's what the documentation suggests, it's not actually the problem here.)

I did eventually find the solution. Daniel Sie at Microsoft described the root cause of the problem on the microsoft.public.platformsdk.security newsgroup here, back in 2002. But seeing how many times people seem to have had this problem since without being directed to that explanation, I guess I'm not the only one who had trouble finding it.

There were two problems with my code. The first didn't take long to discover. It was the second that is a whole lot less obvious.

Both problems were an upshot of the fact that the defaults chosen by the Crypto API change from one version of Windows to the next. For example, you don't have to specify which particular crypto service provider you require when you call CryptAcquireContext at startup, but if you don't, the provider you get is OS-dependent, and may even be user-dependent. (It is possible to configure user-specific settings for default providers.) Currently, the "Microsoft Strong Provider" is the default, but the documentation suggests that it hasn't always been. (Given the history of export regulations surrounding cryptography, it would be surprising if the 'Strong' provider was always the default in all locales - here in the UK, you couldn't get versions of Windows with 128-bit encryption a few years ago.)

As it happens, the provider name wasn't the problem in my case - I explicitly set the provider, and even checked that I was getting the one I asked for by calling CryptGetProvParam, passing in PP_NAME.

But the provider isn't the only default that can change. Even when you specify the provider explicitly, the defaults within that provider change from one OS to the next.

For example, the first problem in my code was that the default key length for RC2 keys is different in Windows 2000 and Windows 2003. When you create a key with CryptGenKey, or derive one from a password with CryptDeriveKey, you can specify a key length in the upper 16 bits of the dwFlags parameter. If you pass 0, it chooses a default key length. For RC2, Windows 2003 will give you a 128 bit key, but you'll only get 40 bits on Windows 2000. Even if you pass CryptDeriveKey a 128-bit HCRYPTHASH it'll only create a 40-bit key unless you tell it otherwise. I hadn't realised this. In fact I failed to spot the documentation that points out where to set the key length - I thought, wrongly, that passing in a 128 bit hash to CryptDeriveKey would be sufficient to get a 128-bit key. On Windows 2003, that's what I was getting, but on Windows 2000, I was only getting a 40-bit key. (Fortunately, I had some pre-generated ciphertext in my unit tests, rather than generating the ciphertext on the fly in the test harness, so the problem was evident from unit test failures on Windows 2000. Hoorah for unit tests!)

The second problem was similar, but not quite so obvious. At least not to me. It hadn't occurred to me to ask the following question:

How long is a 128-bit key?

On Windows 2000, it turns out that the default answer for RC2 keys is a rather surprising 40 bits.

More specifically, the default 'effective key length' of a 128-bit key is 40 bits. I'm not a cryptography expert, so I don't fully understand what that means, but according to the documentation, just because you have a 128-bit key, the RC2 implementation won't necessarily use all 128 bits! So having creating a 128-bit key, you have to set the effective key length. (Call CryptSetKeyParam passing KP_EFFECTIVE_KEYLEN.) At least you have to do this on some versions of Windows. On Windows 2003, and Windows XP sp1, if you ask for a 128-bit key you'll get one, but on older versions, you need to do this 'No, I really do mean 128 bits' call after creating the key.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk