Over the last week, we've made a couple of updates to our Codeplex projects to add authenticated symmetric encryption to the managed cryptography surface area for the first time. Since we've never supported authenticated symmetric algorithms in managed code before, I thought I'd run though some basics about what they are and how to use them.
For starters, in order to use the authenticated symmetric encryption classes, you'll need a few prerequisites:
- Windows Vista SP 1, Windows Server 2008, or higher
- .NET framework v3.5 SP 1 or higher
- Security.Cryptography.dll 1.3 or higher (from http://codeplex.com/clrsecurity)
Authenticated symmetric cryptography differs from the symmetric cryptography that the .NET framework has traditionally supported in that it produces an authentication tag in addition to ciphertext when encrypting data. This authentication tag can be used to verify that the ciphertext has not been tampered with between when it was encrypted and decrypted.
For example, imagine you encrypted a message using the AES class:
While an attacker cannot read the data encrypted by this operation without knowing the encryption key, they can modify the ciphertext bytes themselves which will result in corruption of the decrypted message on the receiving end:
Which produces output such as:
This can be solved by signing the ciphertext, and having the receiving party verify the signature before decrypting the secret message.
Authenticated symmetric algorithms solve this problem by creating an authentication tag which can be used to verify that the ciphertext has not been tampered with since it was generated.
In addition to verifying that the ciphertext has not been modified, the authenticated symmetric algorithms that we have in managed code also take additional authenticated data as an input. This data is not included in the ciphertext itself - so when decrypting a message that was created with additional authenticated data, the output will not contain the authenticated data. Instead, the authenticated data is only used in generating the authentication tag. This means that much like the key and IV, both the encrypting and decrypting parties need to know what the additional authenticated data is otherwise the authentication tag will not verify.
Authenticated Symmetric Algorithm Type Hierarchy
This functionality is exposed in managed code via the AuthenticatedSymmetricAlgorithm base class. Much like the SymmetricAlgorithm base class, AuthenticatedSymmetricAlgorithm is an abstract class for actual authenticated symmetric algorithms to derive from.
Currently, the only authenticated symmetric algorithm is an authenticated version of AES, which is represented by the AuthenticatedAes abstract base class. Again, mirroring the symmetric algorithm type hierarchy, AuthenticatedAes is an abstract base class that concrete authenticated AES implementations derive from. (Much like Aes serves as the base class for AesManaged, AesCryptoServiceProvider and AesCng).
The concrete implementation of AuthenticatedAesCng which is in Security.Cryptography.dll 1.3 is built on top of CNG, and follows our traditional naming scheme: AuthenticatedAesCng.
Setting up an Authenticated AES Encryptor
The authentication tag is generated by an authenticated chaining algorithm, which is used in place of the standard chaining modes that AES can use (such as CBC or ECB). Currently CNG supports two algorithms for generating an authentication tag with AES:
- Galois/Counter Mode - this is the default, and is represented by CngChainingMode.Gcm. (http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf)
- Coutner with CBC-MAC - this is selected by using CngChainingMode.Ccm. (http://www.ietf.org/rfc/rfc3610.txt)
Since neither of these chaining modes are supported by the standard CipherMode enumeration, AuthenticatedAesCng adds a new property called CngMode which allows you to specify a CngChainingMode rather than a standard CipherMode. Trying to use the traditional Mode property when the CngChainingMode is set to one of these new values will result in an exception.
The IV property is used to setup what the two algorithm specifications above refer to as a nonce. Unlike traditional AES, the IV size does not match the block size, but is instead specified by the chaining mode. For instance, both GCM and CCM can work with an IV of 12 bytes.
AuthenticatedSymmetricAlgorithms also have an AuthenticatedData byte array property which is used to setup the additional authentication data being used in the tag generation. This property is optional - leaving the value null means that the authentication tag is generated only from the plaintext.
The last interesting property on the encryption side is the TagSize property. This property specifies the size (in bits) of the authentication tag to generate. The LegalTagSizes property contains information about which sizes are valid for the current chaining mode (and the ValidTagSize method allows you to quickly test to see if a tag size is valid).
Once the AuthenticatedAesCng object is setup, we'll need to create an encryptor to do the actual encryption operation. This can be done by calling the CreateAuthenticatedEncryptor method. CreateAuthenticatedEncryptor returns an IAuthenticatdCryptoTransform rather than an ICryptoTransform since IAuthenticatedCryptoTransform allows us access to the authentication tag after the encryption is done. The CreateEncryptor overloads also return IAuthenticatedCryptoTransforms, however they are typed as ICryptoTransform because they're defined on the SymmetricAlgorithm base type. If you call one of the these methods, then you'll have to manually cast to IAuthenticatedCryptoTransform.
Putting this all together, code to encrypt and generate an authentication tag using AuthenticatedAesCng would look something like this:
Notice how the tag is accessed by calling GetTag on the IAuthenticatedCryptoTransform after we've completed all encryption and flushed the final block. If GetTag is called before this, it will throw an InvalidOperaitonException as AuthenticatedAesCng does not support generating partial tags for partially encrypted data. Also, encryption will notably not set the Tag property of the AuthenticatedAesCng object which was used to create the encryptor. (The tag is an output, not an input, and therefore does not get propagated back to the AuthenticatedAesCng object which acts like a crypto transform factory).
Setting up an Authenticated AES Decryptor
Setting up an authenticated AES object to do decryption is very similar to setting one up to do encryption. The Key, IV, AuthenticationData, and CngMode all need to be setup to match the parameters in place when the ciphertext being decrypted was encrypted. The only additional property that needs to be set is the Tag property. Unsurprisingly, this should be set to be the output of the GetTag call on the encryptor.
We'll end up with decryption code along the lines of:
If the authentication tag generated while decrypting the ciphertext (taking into account any optional authenticated data provided), then an exception will be thrown when decryption is completed. The decryptor will also not produce any plaintext until the authentication tag is verified - this way partial plaintext cannoot be used if the authentication tag does not match.
Now that we're using AuthenticatedAesCng to do our encryption, the scenario where someone tampers the ciphertext no longer works. While we won't be able to access the corrupted plaintext anymore, we also will be unable to mistake it for valid plaintext. If the authentication tag does not match while decrypting (most commonly because the ciphertext was tampered with or the authenticated data was not correct), the following exception is thrown:
The Security.Cryptography.Debug library has also been updated on the Codeplex in order to support the same type of debugging of AuthenicatedSymmetricAlgorithm objects that was already supported for SymmetricAlgorithm objects. This support is enabled in the v1.1 release and higher of Security.Cryptography.Debug.dll - but since it requires a dependency on the Security.Cryptography.dll library, it is not enabled in the FxOnly builds. Instead, you'll need to download the full binary package or build the sources manually to get the AuthenticatedSymmetricAlgorithm debugging support.