Hi, Bryan here.
For any of you that might not have seen the movie Sneakers, I’ll try to not spoil the plot completely for you, but the main storyline revolves around a “little black box” that a scientist has developed that can automatically defeat asymmetric encryption. It’s a fun movie, but what if this happened in real life? After all, crypto algorithms are broken all the time – either through little black box cleverness or simply through improved access to brute force processing power. If RSA, AES, SHA-2 or any of the other current SDL crypto standard algorithms were to be broken tomorrow, what kind of changes would you have to make to your applications? If you’ve hardcoded that algorithm into your apps, you’ll probably have to make some emergency code changes to use a new algorithm, issue patches to all of your users, and then hope that the new algorithm you chose doesn’t also get compromised. However, there is a better way to handle this scenario.
The SDL requires all applications to be able to upgrade the algorithms they use over time. This is usually referred to as the SDL crypto agility requirement. Both the .NET framework and the Cryptography API: Next Generation (CNG) include some useful features that can help you make your code more cryptographically agile; in fact, if you write your application the right way, you’ll be able to change algorithms with simple configuration file edits – no code changes required.
For .NET code, the first step in crypto agility is to avoid hardcoding any particular algorithm or algorithm implementation into your code, and instead refer only to the abstract class of algorithm you need. Instead of instantiating a SHA256CryptoServiceProvider object (for example), you would declare an abstract HashAlgorithm. Instead of instantiating an AesManaged object, you would declare an abstract SymmetricAlgorithm. You can’t instantiate an abstract class, but System.Security.Cryptography abstract algorithm classes expose static Create methods. So instead of this code:
SHA512Cng sha = new SHA512Cng(); // not agile!
You would use this code:
HashAlgorithm hash = HashAlgorithm.Create(“SHA512”); // more agile
This new code doesn’t look any better than the old code – it looks like we’ve still hardcoded SHA512 into the application. But we actually haven’t; we’ve only hardcoded the string “SHA512” into the application, and we can edit the machine.config file to redefine which class actually gets instantiated when an application tries to create a “SHA512” object.
An even better alternative is to define application-specific configuration strings and write these both into the code and into the machine.config files of systems where the app is deployed. This is safer than redefining a common algorithm string like “SHA512”, because you can upgrade the algorithm used by an individual application without affecting any other apps:
HashAlgorithm hash = HashAlgorithm.Create(“MyApplication_PreferredHash”); // most agile
If you’re writing native C++ code, you can accomplish the same goal with CNG. Again, the first step is to avoid hardcoding algorithm names into your code:
BCRYPT_ALG_HANDLE hAlg = NULL;
NTSTATUS ret = BCryptOpenAlgorithmProvider(&hAlg, L"SHA256", NULL, 0); // not agile!
Instead, load the desired algorithm provider string from a configuration file, or the registry, or some other location. Just be sure to apply an appropriate ACL to the resource where you’re storing your setting! You don’t want unauthorized users reducing the security strength of the application by reconfiguring it to use a weaker algorithm.
LPCWSTR algName = NULL;
// load desired algName from the registry
NTSTATUS ret = BCryptOpenAlgorithmProvider(&hAlg, algName, NULL, 0); // more agile
In most cases, some additional work will be required in order to make crypto agility code work correctly. For example, if you’re storing password hashes for an authentication system, you can’t change just the comparing algorithm and expect the system to work. If the stored hashes are MD5 and you start comparing them to computed SHA-2 hashes, no one will be able to log in. In this case, you’ll need to store metadata about the algorithm used along with the stored password hash. When authenticating the user, instantiate the exact algorithm they originally used to create the password hash. Once they’ve authenticated, check whether the algorithm used to create their hash is out of date. If so, prompt them to create a new password, then create and store the new password hash using the new preferred algorithm.
Of course, this example is just the tip of the iceberg. If you’re interested in reading more about crypto agility, including more code and configuration samples, be sure to check out the Security Briefs column of the current MSDN Magazine, the Cryptographic Enhancements chapter of Writing Secure Code for Windows Vista, or Shawn Farkas’ (of the CLR Security team) blog. As always, questions and comments are welcome.