Cryptography and Hashing - Programming Windows Store Apps with C# (2014)

Programming Windows Store Apps with C# (2014)

Appendix A. Cryptography and Hashing

In this appendix we’ll talk about SSL (Secure Sockets Layer), device security, and encryption in SQLite, and then look at practical examples of hashing, generating random data, and symmetric encryption.

SSL

I will touch on SSL first. It goes without saying that all communication with your server/servers should be done over SSL. There’s no excuse for this not to be the case.

Device Security

In a business context, what project sponsors are really worried about are devices getting lost with sensitive information on them. The way to square the circle is to rely on the device to be secure and for your apps to rely on that security. Baking your own security into the app may not actually get you very far, which is one of the reasons why this topic isn’t a central thrust of this book despite its importance in the problem domain. You shouldn’t need to think about it, or do anything special in order to make it happen.

Mobile devices are supposed to be managed via something called mobile device management (MDM). MDM is provided in two parts. One part is provided by the platform vendor and describes “policies” on the device that support device management and security. For example, the platform vendor may bake in a policy that can be turned on or off that indicates whether the user has a passcode on the device, another that specifies whether the device is wiped after n incorrect passcodes, and so on. The second part of MDM is a management tool that pushes down policy to those devices. Examples of MDM vendors at the time of writing include AirWatch, MobileIron, Good Technology, and Microsoft Intune. As an aside, MDM products also have “value adds,” things like secure document lockers and mobile application management (MAM), which is usually interpreted as private app stores operated by a company for its employees.

In enterprise environments, what customers are typically most worried about is data loss. The easiest way to handle this is to encrypt the device. On Windows 8 Pro and Enterprise you have BitLocker support, so all you have to do here is turn that on. (The baseline edition of Windows 8 intended for home use—that is, not the Professional or Enterprise editions—doesn’t have BitLocker support, but you’d rarely find that in business.) On the Windows RT side, there isn’t a thing called “BitLocker,” but the device encryption that you can activate is actually BitLocker under the hood.

With both of those technologies, you should be “safe” if a machine is lost in the field in terms of not leaking the data. What seems to be missing as of the time of writing is the ability to remote-wipe the entire machine. Although information is due to come out on this, it appears remote wipe on Windows RT only kills off messaging-related data and not application data generally. With Windows 8 the story is slightly different, as MDM products have more freedom in what they can do.

The upshot: always make sure you have encryption switched on so that if you do lose a device, it’s less of a problem than it could be.

SQLite

SQLite has no practical way built in to lock down access to data contained within.

NOTE

There is support for encrypting data within SQLite databases—if you search online for it, you can find a number of open source and commercial solutions.

However, this brings me back to my original point: you are better served relying on the security on the device to keep your data safe, rather than trying to bash your app into implementing security that will unavoidably be poorer.

Hashing

In the first of three practical examples that we’ll look at in these pages, we’re going to explore hashing.

I’m not going to try to cover the advantages or disadvantages of different hashing algorithms—I’m just going to explain the basics of how to call them. I’m also not going to get into how to apply them to common usage patterns, and particularly here I’m talking about hashing with regards to password security. The reason why I’m ducking this is a) it’s complicated, and b) if you’re reading this even just two years after I wrote it, industry best practice would likely not be what it is today.

The hash algorithms that you are given in WinRT are MD5, SHA-1, SHA-256, SHA-384, and SHA-512. In these pages, I’m going to show you SHA-512. In normal .NET, you get those just enumerated, and you also get RIPEMD160. To be honest, and I’m not sure this is necessarily valid logic, I’d never heard of RIPEMD160 until I wrote this paragraph, so I don’t think it’ll be sorely missed in Windows Store app development.

For this exercise, we’re going to create a new Windows Store app project that can accept some text to be hashed. We’ll then hash it using SHA-512, both to a base-64 string and a hex dump of the underlying bytes. In the project, we won’t create a complex app with MVVM abstraction, etc. We’ll just create a little proof-of-concept app.

Create a new Visual C# – Windows Store – Blank App project called HashScratch. To MainPage, add a StackPanel control, three TextBlock controls, a Button control, and three TextBox controls, as shown in Figure A-1. The TextBox controls need x:Name values of textInput,textBase64, and textHex.

The layout of our scratch project

Figure A-1. The layout of our scratch project

For clarity, here’s the XAML representing the layout of the controls.

<StackPanel Margin="10,10,10,10" Width="800"

HorizontalAlignment="Left">

<TextBlock Text="To hash"></TextBlock>

<TextBox x:Name="textInput" Text="Hello, world!" Height="100"

AcceptsReturn="true"></TextBox>

<Button Content="Hash!" Click="HandleHashClick"></Button>

<TextBlock Text="Base-64"></TextBlock>

<TextBox x:Name="textBase64" AcceptsReturn="true"></TextBox>

<TextBlock Text="Hex"></TextBlock>

<TextBox x:Name="textHex" AcceptsReturn="true"></TextBox>

</StackPanel>

You’ll notice in the XAML that I’ve defined a Click method for the button. You can either do this, or just double-click on the button on the design surface to create a default handler.

In Chapter 6, when we were working with files, we saw that we worked directly with WinRT classes that replaced the .NET file I/O class that we’d been used to previously. In the .NET world, you’ll be familiar with the various stream classes that we were given to work with file data and also other forms of data. In WinRT there are new types for dealing with streams, which you can find in the Windows.Storage.Streams namespace. Also in this namespace you’ll find an interface called IBuffer. This is used to represent a “lightweight stream.” In .NET we would have often just used a byte[] value. (I’ll talk more about that in a moment.)

When we’re working with hashing, we use two classes. We use HashAlgorithmProvider to get a worker class that will actually do the hashing, and we use CryptographicBuffer to marshal data into and out of IBuffer instances. CryptographicBuffer isn’t a buffer in its own right—it’s a static class with a bunch of static helper methods. CryptographicBufferHelper would actually be a better name.

There are classes in CryptographicBuffer to convert strings to binary—specifically, they act to emit an IBuffer instance containing the binary representation of the string. You have to supply an encoding to this, and you have limited choice: UTF-8, UTF-16 little endian, and UTF-32 big endian. In all normal cases, you’d be looking to choose UTF-8.

Once you have your IBuffer-wrapped input, you can then pass it over to the algorithm of your choice. This will return another IBuffer instance containing the hashed output. Once you have that, you can do what you want with it. In our scenario we’re going to convert it into a base-64 string, and also into a string containing a dump of the hex values.

NOTE

One “top tip” for .NET developers is this. You may find that you need to convert your IBuffer into a byte[] array. From first principles this is pretty difficult, but there are extension methods that you can access that do all of this lifting for you. However, they are quite well buried; you need to include the System.Runtime.InteropServices.WindowsRuntimenamespace.

Here’s the code for our hashing operation:

// Add method to MainPage...

private void HandleHashClick(object sender, RoutedEventArgs e)

{

// get the text...

var inputText = this.textInput.Text;

// put the string in a buffer, UTF-8 encoded...

IBuffer input = CryptographicBuffer.ConvertStringToBinary(inputText,

BinaryStringEncoding.Utf8);

// hash it...

var hasher = HashAlgorithmProvider.OpenAlgorithm

(HashAlgorithmNames.Sha512);

IBuffer hashed = hasher.HashData(input);

// format it...

this.textBase64.Text = CryptographicBuffer.

EncodeToBase64String(hashed);

this.textHex.Text = CryptographicBuffer.EncodeToHexString(hashed);

}

Obviously in that method we’re processing the output twice—once to get a base-64 string, and then again to get a hex dump. You’d almost certainly never do this in the real world; it’s just for illustration so that we can see both types of output.

Run the project and click the button, and you’ll see a result like Figure A-2. You can compare the output by running the same input string through various websites that will compute hashes from arbitrary strings. Search online for “calculate sha-512 hash” to find one you like.

A successful run of the SHA-512 hash algorithm

Figure A-2. A successful run of the SHA-512 hash algorithm

Generating Random Data

I won’t take you through building this, as it’s very simple. You’ll find a RandomSpike project in the downloads for this appendix.

Random data is used frequently in cryptography, but there are also plenty of examples where having good-quality random data in your software is a good thing. The random-number generator provided by System.Random is a classic pseudorandom number generator and isn’t particularly random. It compromises randomness for speed. The random numbers used in cryptography have to be as close to “organically random” as possible, and generators used for this purpose will take longer to run and use more processor horsepower.

Generating a random number is easy—there’s a method in CryptographicBuffer that will give you an integer value. Here’s an example:

private void HandleRandomInteger(object sender, RoutedEventArgs e)

{

this.buttonInteger.Content = CryptographicBuffer.GenerateRandomNumber()

.ToString();

}

We can also create a random block of data using the GenerateRandom method. This will return back an IBuffer, which we can work with in the usual way. Here’s an example:

private void HandleRandomData(object sender, RoutedEventArgs e)

{

var buffer = CryptographicBuffer.GenerateRandom(1024);

this.textRandom.Text = CryptographicBuffer.

EncodeToHexString(buffer);

}

Now that we know how to generate arbitrary blocks of data, you can use this to generate other numeric primitives through the expedient of creating enough bytes to make one. For example, if you want a 64-bit integer, you can ask for eight bytes of random data and then convert it to a primitive type using the standard .NET BitConverter class. To get a byte[] value out of an IBuffer instance, you can use the extension methods that become available when you include the System.Runtime.Interop.WindowsRuntime namespace. Here’s an example:

private void HandleRandomLong(object sender, RoutedEventArgs e)

{

// get eight bytes of data...

var buffer = CryptographicBuffer.GenerateRandom(8);

// convert it...

ulong val = BitConverter.ToUInt64(buffer.ToArray(), 0);

this.buttonLong.Content = val.ToString();

}

That’s all there is to generating random data. Let’s round off this chapter by talking about symmetric encryption.

Symmetric Encryption

Symmetric encryption is the process whereby you can take a block of data, encrypt it using a key, and then decrypt it back to the original data using that same key.

NOTE

I’ve made an assumption in this chapter that asymmetric encryption is less interesting to readers of this book. Asymmetric encryption tends to have very specific use cases, and as a developer you generally have to abide by the implementation rules in order to do anything with it. Symmetric encryption is much more of a helpful, ad hoc concept to have around.

In this section we’re going to create another scratch project, EncryptionScratch, and add a TextBox control containing data that we can encrypt.

Figure A-3 shows the layout that we’re aiming for.

Encryption scratch app layout

Figure A-3. Encryption scratch app layout

Here’s the XAML for the form:

<StackPanel Margin="10,10,10,10" HorizontalAlignment="Left" Width="400">

<TextBlock Text="Text to encrypt"></TextBlock>

<TextBox AcceptsReturn="true" Height="150" Text="Hello, world."

x:Name="textData"></TextBox>

<StackPanel Orientation="Horizontal">

<Button Content="Encrypt" Click="HandleEncryptClick"></Button>

<Button Content="Decrypt" Click="HandleDecryptClick"></Button>

</StackPanel>

</StackPanel>

When we do asymmetric encryption, we need to provide both a key and an initialization vector (IV). Although it looks like that’s just a password in two parts, they are very different. The key is supposed to be your super-secret; only you know its value. The IV is “less secret”—its purpose is to randomize the input stream so that the same key yields different output. (And thus it’s a little similar to the concept of a salted hash—it makes it harder to discover the underlying values used as part of the data-hiding process.)

In the sample you can download, I created two random values using the CryptographicBufffer.GenerateRandom method that we saw before. One of these we’ll use for the key, and the other we’ll use for the IV.

The approach to using the symmetric encryption calls is similar to that used in hashing: we create a buffer containing the input data, get the algorithm that we want, create a key from the “key material,” and then call CryptographicEngine, passing everything in.

Here’s the code to encrypt the text:

private const string keyAsHex =

"d1ee5548bae50c6c52e785dbee523f022a1f39eb316dad2d2d50cc72957da4ef";

private const string ivAsHex =

"b2ba13011d845de7be1a246331a46f5d56ceea4bb6e81fde547b54440ad6d415";

private void HandleEncryptClick(object sender, RoutedEventArgs e)

{

// input...

var input = CryptographicBuffer.ConvertStringToBinary(this.textData

.Text, BinaryStringEncoding.Utf8);

// create...

var keyMaterial = CryptographicBuffer.DecodeFromHexString(keyAsHex);

var iv = CryptographicBuffer.DecodeFromHexString(ivAsHex);

// encrypt...

var encryptor = SymmetricKeyAlgorithmProvider.OpenAlgorithm(

SymmetricAlgorithmNames.AesCbcPkcs7);

var key = encryptor.CreateSymmetricKey(keyMaterial);

var encrypted = CryptographicEngine.Encrypt(key, input, iv);

// show...

this.textData.Text = CryptographicBuffer.EncodeToHexString(

encrypted);

}

private void HandleDecryptClick(object sender, RoutedEventArgs e)

{

// we'll do this in a moment...

}

In that code, the algorithm name that I’ve proposed using is AesCbcPkcs7. In .NET we used to just ask for the RijandaelManaged encryption class (which does AES encryption, like the one we’ve selected here). You could then set properties on this instance to devise the cipher model (CBC in our example) and the padding mode (PKCS #7 in this example). In WinRT, you don’t get to set properties on the encryption worker as we did before; you have to ask for a qualified object that matches what you want.

If you run the preceding code, you can try encrypting some text. Figure A-4 shows a result.

A successful encryption call

Figure A-4. A successful encryption call

The decryption routine is the reverse of all that. Here’s the code:

private void HandleDecryptClick(object sender, RoutedEventArgs e)

{

// input...

var input = CryptographicBuffer.DecodeFromHexString(this.textData.Text);

// create...

var keyMaterial = CryptographicBuffer.DecodeFromHexString(keyAsHex);

var iv = CryptographicBuffer.DecodeFromHexString(ivAsHex);

// decrypt...

var decryptor = SymmetricKeyAlgorithmProvider.OpenAlgorithm(

SymmetricAlgorithmNames.AesCbcPkcs7);

var key = decryptor.CreateSymmetricKey(keyMaterial);

var decrypted = CryptographicEngine.Decrypt(key, input, iv);

// show...

this.textData.Text = CryptographicBuffer.ConvertBinaryToString

(BinaryStringEncoding.Utf8, decrypted);

}

Now you can run the project and both encrypt and decrypt text. To test this properly, enter some plain text to encrypt and press the Encrypt button a multiple, but known, number of times. The string will get longer and longer as you do this. Once you’re satisfied, click the Decrypt button the same number of times, and you’ll eventually get back to the plain-text value that you entered originally.