Thursday, July 2, 2015

Microsoft 70-486: Manage data integrity

Exam Objectives


Apply encryption to application data, apply encryption to the configuration sections of an application, sign application data to prevent tampering

Quick Overview of Training Materials


MSDN - Encrypting and Decrypting Configuration Sections
Using CryptoStream in C#
Signing and verifying signatures with RSA C#
RSA Encryption of large data in C#
MSDN - Generating Keys for Encryption and Decryption


Encrypt application data


The exam ref has some general overview type info on encryption to start the chapter.  It's useful enough, but I went over pretty much everything he covered in my post for Objective 5.5.

Symetric Encryption


So I gotta say, the Exam Ref was pretty weak on this front overall.  He lists a handful of encryption algorithms in the System.Security.Cryptography namespace, throws in a couple incomplete snippets of code, and just kind of leaves it there.  I knew if I was going to give this topic a proper treatment, I would have to see some data encrypted and decrypted again, so off to Google land I went.  The first resources I found were the MSDN articles on encrypting and decrypting a stream. These had some examples that were targeted at network streams, but I knew you could put a string in a stream, so it should be simple to apply this to text, right?

I think part of my problem is the fact that I haven't played around with streams much. When I wasn't throwing CryptographicExceptions I was getting empty strings, so I went to look for some help, and found a gold mine on a Stack Overflow thread called Simple two-way encryption for C#.  Multiple implementations taking different approaches to the problem, along with all the typical discussion of pro's and con's, really was quite enlightening.  To at least get some the feel-goods that go with working code, I stole one of the top answers and dropped it wholesale into a class in my project.  Couple calls later and boom, it works... so this is possible at least.  

namespace Encryption
{
    //Lifted, verbatim, straight from Mud's answer on Stack Overflow
    //Zero creative credit goes to me on this one lol
    //http://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp/212707#5518092
    public class SimplerAES
    {
        private static byte[] key = { 123217191124268545114184271623711222220924124175144173531962924261721813123653209 };
        private static byte[] vector = { 146641911112331131192311212211127932114156 };
        private ICryptoTransform encryptor, decryptor;
        private UTF8Encoding encoder;
 
        public SimplerAES()
        {
            RijndaelManaged rm = new RijndaelManaged();
            encryptor = rm.CreateEncryptor(key, vector);
            decryptor = rm.CreateDecryptor(key, vector);
            encoder = new UTF8Encoding();
        }
 
        public string Encrypt(string unencrypted)
        {
            return Convert.ToBase64String(Encrypt(encoder.GetBytes(unencrypted)));
        }
 
        public string Decrypt(string encrypted)
        {
            return encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
        }
 
        public byte[] Encrypt(byte[] buffer)
        {
            return Transform(buffer, encryptor);
        }
 
        public byte[] Decrypt(byte[] buffer)
        {
            return Transform(buffer, decryptor);
        }
 
        protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
        {
            MemoryStream stream = new MemoryStream();
            using (CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
            {
                cs.Write(buffer, 0, buffer.Length);
            }
            return stream.ToArray();
        }
    }
}

This is the call from my HomeController class:

public String Encrypt(String plainText)
{
    SimplerAES saes = new SimplerAES();
    var cipherText = saes.Encrypt(plainText);
    return cipherText + " | " + saes.Decrypt(cipherText);
}

And finally, the result:



But I still wanted to figure out what I was doing wrong with the MSDN stream code, so I kept hammering away at it.  An answer toward the bottom, though it was downvoted (probably for using DES and not giving any kind of context or explanation, it was literally just a code snippet), actually was quite helpful.  Of course I had to read up on reading streams, reading CryptoStreams, using StreamWriter correctly, and revising the gold mine post more than once.  After flailing about for a while, I did, eventually, manage to at least get it to encrypt, but damn if I could reverse it.  I needed another shot of endorphins, so I copied the downvoted code, made a few adjustments, and BANG that one worked too.  So I really and truly knew that this could be done with streams... what was I missing?

namespace Encryption
{
    //Adapted from user3016854's answer on Stack Overflow
    //http://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp/212707#22780423
    public class AESCrypto
    {
        byte[] Key = { 123217191124268545114184271623711222220924124175144173531962924261721813123653209 };
        byte[] IV = { 146641911112331131192311212211127932114156 };
 
 
        public string Encrypt(string userID)
        {
            using (var cryptoProvider = new AesCryptoServiceProvider())
            using (var memoryStream = new MemoryStream())
            using (var cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(Key, IV), CryptoStreamMode.Write))
            using (var writer = new StreamWriter(cryptoStream))
            {
                writer.Write(userID);
                writer.Flush();
                cryptoStream.FlushFinalBlock();
                //writer.Flush();
                return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
            }
        }
 
 
        public string Decrypt(string userID)
        {
            using (var cryptoProvider = new AesCryptoServiceProvider())
            using (var memoryStream = new MemoryStream(Convert.FromBase64String(userID)))
            using (var cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
            using (var reader = new StreamReader(cryptoStream))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

The call in the HomeController class looks a lot like the SimplerAes call:

public String EncryptToo(String plainText)
{
    AESCrypto aes = new AESCrypto();
    var cipherText = aes.Encrypt(plainText);
    return cipherText + " | " + aes.Decrypt(cipherText);
}

Finally, these are the results.  Look familiar? It's the exact same result that I got with the SimplerAes function:


I knew I was soooooo close.  Bit more flailing and hacking and frustration and finally I had a breakthrough.  Mine isn't nearly as concise or elegant as either of the other two, but this ugly deformed bit of code is (mostly) mine at least:

public String StreamEncrypt(String plainText)
{
    
    //Create the crypto provider, set the mode and padding, 
    //create a key and insertion vector
    RijndaelManaged RMCrypto = new RijndaelManaged();
    RMCrypto.Mode = CipherMode.CBC;
    RMCrypto.Padding = PaddingMode.ISO10126;
    byte[] Key = { 123217191124268545114184271623711222220924124175144173531962924261721813123653209 };
    byte[] IV = { 146641911112331131192311212211127932114156 };
 
    //Create a CryptoStream, passing in the new MemoryStream 
    //and using the RijndaelManaged object to create a decryptor
    //finally setting the stream mode to write (you can't read AND write, it's either/or)
    var textStream = new MemoryStream();
    CryptoStream CryptStream = new CryptoStream(textStream,
        RMCrypto.CreateEncryptor(Key, IV),
        CryptoStreamMode.Write);
 
    //Create a StreamWrite for the CryptoStream, write the plainText
    //call Flush on the writer to clear the buffers and write the text to the CryptoStream
    //(which does the encryption) and then call FlushFinalBlock to clear the CryptoStream
    //buffers and write the encrypted bytes to the underlying MemoryStream
    StreamWriter SWriter = new StreamWriter(CryptStream);
    SWriter.WriteLine(plainText);
    SWriter.Flush();
    CryptStream.FlushFinalBlock();
 
    //Here I'm getting the underlying byte[] directly and converting it
    //to a Base64String, then I'm converting the string back into a byte[]
    //and passing it into a MemoryStream
    var encrypted = Convert.ToBase64String(textStream.GetBuffer(), 0, (int)textStream.Length);
    var encStream = new MemoryStream(Convert.FromBase64String(encrypted));
 
    //THIS WORKS... Create a decryption stream from the newly created stream of encrypted data
    //CryptoStream DecryptStream = new CryptoStream(encStream, 
    //    RMCrypto.CreateDecryptor(Key, IV), 
    //    CryptoStreamMode.Read);
 
    //THIS ALSO WORKS... Here I'm just recycling the existing stream of encrypted data.  Position
    //has to be set to 0 so you can read from the beginning again (writing puts the position at the end)
    textStream.Position = 0;
    CryptoStream DecryptStream = new CryptoStream(textStream,
        RMCrypto.CreateDecryptor(Key, IV),
        CryptoStreamMode.Read);
 
    //Create a StreamReader for the CryptoStream (however it was created)
    //and read back the string. Bytes flow from the underlying memory stream, 
    //decrypted by the CryptoStream, and returned as a string by the StreamReader
    StreamReader SReader = new StreamReader(DecryptStream);
    string result = SReader.ReadToEnd();
 
    return encrypted + "  |  " + result;
}

While mine seems to work, I got a different base64 string back.  There is probably a simple reason why but at this point I don't see much value in chasing it down:




Asymmetric Encryption


Unlike symetric encryption, asymmetric (e.g. public key encryption) is pretty straightforward. The actual encryption and decryption is done in one method call: rsa.Decrypt() or rsa.Encrypt(), where rsa is an instance of the RSACryptoServiceProvider.  Most of the code in the examples in the Encrypting Data and Walkthrough articles on MSDN related to this class revolves around creating, storing, or retrieving the key.  There is a blurb commented out in the code below for digital signatures that is just a quick encrypt and decrypt using an RSA key pair.

Because public key encryption is relatively slow, it is generally used to encrypt a shared secret that can be used as the key for symmetric encryption.  Basically, you transport a key with RSA, then use this key to communicate securely using AES (Rijndael).

There is plenty more information available on MSDN,  under the Cryptography Model heading.  If you REALLY wanted to invest some time, there is also the Cryptography 1 course at Coursera (from Stanford University), which looks as though it gets pretty deep on the subject.  Maybe someday...

Encrypt configuration


The web.config file of a web application may contain sensitive information (such as connection strings to a database), and for peace of mind it may be desirable to encrypt this data.  MSDN offers a walkthrough on how to accomplish this (which I am not going to recreate here...).  It boils down to using a command line tool called aspnet_regiis.  Using an RSA key stored in the "NetFrameConfigurationKey" file, you specify which elements of the configuration file will be encrypted.  You can use the same tool to decrypt those elements, however you do not need to do this for .NET to use the elements, as the framework decrypts them automatically.

An alternative provider can be used for the encryption provider: DPAPIProtectedConfigurationProvider (Windows Data Protection API).  One drawback of this provider is that you can't export the private key (and thus use the application on more than one server)

Here is an example of what the encrypted data looks like:

<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
      <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
         xmlns="http://www.w3.org/2001/04/xmlenc#">
         <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
         <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
               <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
               <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                  <KeyName>RSA Key
                  </KeyName>
               </KeyInfo>
               <CipherData>
                  <CipherValue>WcFEbDX8VyLfAsVK8g6hZVAG1674ZFc1kWH0BoazgOwdBfinhcAmQmnIn0oHtZ5tO2EXGl+dyh10giEmO9NemH4YZk+iMIln+ItcEay9CGWMXSen9UQLpcQHQqMJErZiPK4qPZaRWwqckLqriCl9X8x9OE7jKIsO2Ibapwj+1Jo=
                  </CipherValue>
               </CipherData>
            </EncryptedKey>
         </KeyInfo>
         <CipherData>
            <CipherValue>OpWQgQbq2wBZEGYAeV8WF82yz6q5WNFIj3rcuQ8gT0MP97aO9SHIZWwNggSEi2Ywi4oMaHX9p0NaJXG76aoMR9L/WasAxEwzQz3fexFgFSrGPful/5txSPTAGcqUb1PEBVlB9CA71UXIGVCPTiwF7zYDu8sSHhWa0fNXqVHHdLQYy1DfhXS3cO61vW5e/KYmKOGA4mjqT0VZaXgb9tVeGBDhjPh5ZlrLMNfYSozeJ+m2Lsm7hnF6VvFm3fFMXa6+h0JTHeCXBdmzg/vQb0u3oejSGzB4ly+V9O0T4Yxkwn9KVDW58PHOeRT2//3iZfJfWV2NZ4e6vj4Byjf81o3JVNgRjmm9hr9blVbbT3Q8/j5zJ+TElCn6zPHvnuB70iG2KPJXqAj2GBzBk6cHq+WNebOQNWIb7dTPumuZK0yW1XDZ5gkfBuqgn8hmosTE7mCvieP9rgATf6qgLgdA6zYyVV6WDjo1qbCV807lczxa3bF5KzKaVUSq5FS1SpdZKAE6/kkr0Ps++CE=
            </CipherValue>
         </CipherData>
      </EncryptedData>
   </connectionStrings>


Sign application data

.
Digital signatures are used to authenticate the source of a the data.  This is how it works:

  • The data is hashed with a hash function (SHA256, etc.)
  • The hash is then encrypted with the senders private key
  • The receiver can decrypt the message with the senders public key
  • The decrypted hash is compared against the hash the receiver computes against the send data
  • If they match, then the sender is the only one who could have sent the data (or someone with their private key...)
The exam ref has a couple code snippets using DSA.  The MSDN docs on the DSA class note that newer algorithms, such as RSA and ECDsa are available and should be used instead, with DSA more appropriate for legacy applications.


I managed to find some code on Stack Overflow to play with.  I had to go hunting a bit (he didn't have all of it in one question), but I've put it all in one place here:

namespace Encryption
{
    //copied this completely from Simon Langhoff on Stack Overflow
    // Decrypt and Encrypt came from this question : 
    // http://stackoverflow.com/questions/8417993/rsa-encryption-of-large-data-in-c-sharp
    // Signing and Verifying came from this one :
    // http://stackoverflow.com/questions/8437288/signing-and-verifying-signatures-with-rsa-c-sharp
    public class Cryptograph
    {
        public static string Encrypt(string dataToEncrypt, RSAParameters publicKeyInfo)
        {
            //// Our bytearray to hold all of our data after the encryption
            byte[] encryptedBytes = new byte[0];
            using (var RSA = new RSACryptoServiceProvider())
            {
                try
                {
                    //Create a new instance of RSACryptoServiceProvider.
                    UTF8Encoding encoder = new UTF8Encoding();
 
                    byte[] encryptThis = encoder.GetBytes(dataToEncrypt);
 
                    //// Importing the public key
                    RSA.ImportParameters(publicKeyInfo);
 
                    int blockSize = (RSA.KeySize / 8- 32;
 
                    //// buffer to write byte sequence of the given block_size
                    byte[] buffer = new byte[blockSize];
 
                    byte[] encryptedBuffer = new byte[blockSize];
 
                    //// Initializing our encryptedBytes array to a suitable size, depending on the size of data to be encrypted
                    encryptedBytes = new byte[encryptThis.Length + blockSize - (encryptThis.Length % blockSize) + 32];
 
                    for (int i = 0; i < encryptThis.Length; i += blockSize)
                    {
                        //// If there is extra info to be parsed, but not enough to fill out a complete bytearray, fit array for last bit of data
                        if (2 * i > encryptThis.Length && ((encryptThis.Length - i) % blockSize != 0))
                        {
                            buffer = new byte[encryptThis.Length - i];
                            blockSize = encryptThis.Length - i;
                        }
 
                        //// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it
                        if (encryptThis.Length < blockSize)
                        {
                            buffer = new byte[encryptThis.Length];
                            blockSize = encryptThis.Length;
                        }
 
                        //// encrypt the specified size of data, then add to final array.
                        Buffer.BlockCopy(encryptThis, i, buffer, 0, blockSize);
                        encryptedBuffer = RSA.Encrypt(buffer, false);
                        encryptedBuffer.CopyTo(encryptedBytes, i);
                    }
                }
                catch (CryptographicException e)
                {
                    Console.Write(e);
                }
                finally
                {
                    //// Clear the RSA key container, deleting generated keys.
                    RSA.PersistKeyInCsp = false;
                }
            }
            //// Convert the byteArray using Base64 and returns as an encrypted string
            return Convert.ToBase64String(encryptedBytes);
        }
 
        /// <summary>
        /// Decrypt this message using this key
        /// </summary>
        /// <param name="dataToDecrypt">
        /// The data To decrypt.
        /// </param>
        /// <param name="privateKeyInfo">
        /// The private Key Info.
        /// </param>
        /// <returns>
        /// The decrypted data.
        /// </returns>
        public static string Decrypt(string dataToDecrypt, RSAParameters privateKeyInfo)
        {
            //// The bytearray to hold all of our data after decryption
            byte[] decryptedBytes;
 
            //Create a new instance of RSACryptoServiceProvider.
            using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
            {
                try
                {
                    byte[] bytesToDecrypt = Convert.FromBase64String(dataToDecrypt);
 
                    //// Import the private key info
                    RSA.ImportParameters(privateKeyInfo);
 
                    //// No need to subtract padding size when decrypting (OR do I?)
                    int blockSize = RSA.KeySize / 8;
 
                    //// buffer to write byte sequence of the given block_size
                    byte[] buffer = new byte[blockSize];
 
                    //// buffer containing decrypted information
                    byte[] decryptedBuffer = new byte[blockSize];
 
                    //// Initializes our array to make sure it can hold at least the amount needed to decrypt.
                    decryptedBytes = new byte[dataToDecrypt.Length];
 
                    for (int i = 0; i < bytesToDecrypt.Length; i += blockSize)
                    {
                        if (2 * i > bytesToDecrypt.Length && ((bytesToDecrypt.Length - i) % blockSize != 0))
                        {
                            buffer = new byte[bytesToDecrypt.Length - i];
                            blockSize = bytesToDecrypt.Length - i;
                        }
 
                        //// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it
                        if (bytesToDecrypt.Length < blockSize)
                        {
                            buffer = new byte[bytesToDecrypt.Length];
                            blockSize = bytesToDecrypt.Length;
                        }
 
                        Buffer.BlockCopy(bytesToDecrypt, i, buffer, 0, blockSize);
                        decryptedBuffer = RSA.Decrypt(buffer, false);
                        decryptedBuffer.CopyTo(decryptedBytes, i);
                    }
                }
                finally
                {
                    //// Clear the RSA key container, deleting generated keys.
                    RSA.PersistKeyInCsp = false;
                }
            }
 
            //// We encode each byte with UTF8 and then write to a string while trimming off the extra empty data created by the overhead.
            var encoder = new UTF8Encoding();
            return encoder.GetString(decryptedBytes).TrimEnd(new[] { '\0' });
 
        }
 
        public static string SignData(string message, RSAParameters privateKey)
        {
            //// The array to store the signed message in bytes
            byte[] signedBytes;
            using (var rsa = new RSACryptoServiceProvider())
            {
                //// Write the message to a byte array using UTF8 as the encoding.
                var encoder = new UTF8Encoding();
                byte[] originalData = encoder.GetBytes(message);
 
                try
                {
                    //// Import the private key used for signing the message
                    rsa.ImportParameters(privateKey);
 
                    //// Sign the data, using SHA512 as the hashing algorithm 
                    signedBytes = rsa.SignData(originalData, CryptoConfig.MapNameToOID("SHA512"));
                }
                catch (CryptographicException e)
                {
                    Console.WriteLine(e.Message);
                    return null;
                }
                finally
                {
                    //// Set the keycontainer to be cleared when rsa is garbage collected.
                    rsa.PersistKeyInCsp = false;
                }
            }
            //// Convert the a base64 string before returning
            return Convert.ToBase64String(signedBytes);
        }
 
        public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
        {
            bool success = false;
            using (var rsa = new RSACryptoServiceProvider())
            {
                //fix per SO comment
                //byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
                var encoder = new UTF8Encoding();
                byte[] bytesToVerify = encoder.GetBytes(originalMessage);
 
                byte[] signedBytes = Convert.FromBase64String(signedMessage);
                try
                {
                    rsa.ImportParameters(publicKey);
 
                    SHA512Managed Hash = new SHA512Managed();
 
                    byte[] hashedData = Hash.ComputeHash(signedBytes);
 
                    success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signedBytes);
                }
                catch (CryptographicException e)
                {
                    Console.WriteLine(e.Message);
                }
                finally
                {
                    rsa.PersistKeyInCsp = false;
                }
            }
            return success;
        }
    }
}

The signing and verifying thread included a test client, which I heavily modified to make it more amenable to test application.  Plus the fact that his key fetching code was nowhere to be found and, ultimately, unnecessary; and the message encryption/decryption isn't really about signing, but simply using RSA key pairs.  Interesting, but not relevant in this context:

public String SignData(String plainText)
        {
            string message = "";
            //PublicKeyInfrastructure pki = new PublicKeyInfrastructure();
            Cryptograph crypto = new Cryptograph();
            //RSAParameters privateKey = crypto.GenerateKeys("simlanghoff@gmail.com");
            //RSAParameters publicKey = crypto.GetPublicKey("simlanghoff@gmail.com");
            //const string PlainText = "This is really sent by me, really!";
 
            //copied from MSDN article on generating keys
            //Generate a public/private key pair.
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            //Save the public key information to an RSAParameters structure.
            RSAParameters publicKey = RSA.ExportParameters(false);
            RSAParameters privateKey = RSA.ExportParameters(true);
 
            //This is an interesting exercise in asymmetric encryption, but really isn't 
            //needed for signing...
            //string encryptedText = Cryptograph.Encrypt(plainText, publicKey);
            //message += "<br/>This is the encrypted text: " + encryptedText + "<br/>";
            //string decryptedText = Cryptograph.Decrypt(encryptedText, privateKey);
            //message += "<br/>This is the decrypted text: " + decryptedText + "<br/>";
            //string messageToSign = encryptedText;
 
            string messageToSign = plainText;
            message += "<br/>This is the message: " + messageToSign + "<br/>";
 
            string signedMessage = Cryptograph.SignData(messageToSign, privateKey);
 
            message += "<br/>This is the signed message: " + signedMessage + "<br/>";
 
            //// Is this message really, really, REALLY sent by me?
            bool success = Cryptograph.VerifyData(messageToSign, signedMessage, publicKey);
 
            message += "<br/>Is this message really, really, REALLY sent by me? " + success;
            return message;
        }

And the results.  It was nice to have this one work pretty much right out of the gate:



No comments:

Post a Comment