Our recent work to add coverage of the Microsoft .NET API to Cryptosense Analyzer has led us into a dark and dangerous part of the internet: C# crypto tutorials.
It’s extremely dangerous to treat a cryptography API like just another interface where all you need to do is figure out how to “make it work”. It is theoretically possible to make, say, encryption and decryption “work” using a cryptography API while leaving a slew of terrible security holes. The C# Crypto API is no exception.
Let’s look at some concrete examples. Everyone gets different results on Google, but for us, the first result for “C# cryptography tutorial” is on the official Microsoft site, and dates from March 2017.
The sample code for encryption includes a hard-coded key and a hard-coded IV. Of course sample code often does this, but unfortunately, inexperienced programmers might just copy-paste this code and change (or not) the key values, without ever reading the tutorial. It’s wise to add a comment in the code, at least, that this is insecure and both the IV and key should be correctly randomly generated, and stored securely separately from the application code.
Next, the method of encryption is described as “using the Rijndael symmetric algorithm”. Rijndael was the name of the submission that eventually became the Advanced Encryption Standard back in 2000, and 17 years on you could be forgiven for never having heard of its original name. However, the Microsoft API happens to still refer to AES as Rijndael so perhaps it’s understandable here.
The real problem is that no mention is made of what encryption mode will be used if you encrypt following the sample code. In fact this code will use CBC mode, which requires a random IV for each encryption, and additionally requires some kind of authentication of the ciphertext to avoid a whole bunch of chosen-ciphertext attacks such as padding oracle attacks. This isn’t mentioned anywhere in the tutorial.
The post also describes a kind of hand-rolled hybrid encryption protocol, where the symmetric key and the IV are encrypted separately using an RSA key. Again there’s no discussion of the encryption mode that will be used in this case, which is PKCS#1v1.5. This mode is known to be weak and is a continual source of real-world crypto attacks (this year’s Pwmnie award for best crypto attack went to ROBOT, which is an attack on precisely this mode). Apart from the mode error, the protocol proposed has a whole set of flaws. There’s no way to tell the difference between the key and the IV – they are both 128-bit strings, encrypted in separate RSA blocks with no tags or identifiers, and they are not tied to the encrypted message or associated to particular sender.
Essentially, the protocol breaks all the Robustness Principles for Public Key protocols. The message the tutorial should be giving is that you shouldn’t roll your own hybrid encryption protocol any more than you should roll your own block cipher.
The second hit we get on Google is for C# Corner. Dated 2004, this article starts off with a discussion of the differences between public and private key cryptography. In fact, to avoid confusion, it’s better to talk about asymmetric and symmetric-key cryptography, or public and private keys (in asymmetric cryptography) and secret keys (in symmetric cryptography).
After this inauspicious start, the article goes on to offer a whole host of errant crypto advice. For example, “Asymmetric, or public key, algorithms are not as fast as symmetric, but are much harder codes to break.”, and “Hash values are used when you do not wish to ever recover the original value and you especially wish for no one else to discover the original value as well.” This is more or less just nonsense. The tutorial concludes by giving some sample code for streaming encryption, with again no discussion of the encryption mode that will be used.
The third tutorial Google finds is called Encrypt and Decrypt Data with C#. This tutorial does at least discuss encryption modes, but unfortunately decides to use Electronic Code Book (ECB) mode, bizarrely after having discussed why this is an extremely bad idea (“Also it is possible for an active adversary to substitute and exchange individual blocks without detection.” etc.). The sample code also appears to be proposing to derive a key from a password using a single MD5 hash, another very bad idea.
Writing a good crypto tutorial is difficult. However, a lot of the top hits for C# cryptography on major search engines are for tutorials that don’t even seem to be aware of the risks of insecure code resulting from their examples. As subject matter experts, it’s easy enough for us to spot the dangers, but consider that these tutorials will often be read by time-pressed programmers looking to quickly resolve a problem and move on. It’s no wonder that we find so many crypto-related vulnerabilities in applications.
On the good news side, we’ll be announcing .NET support for Analyzer in the next few weeks!