RSA cryptography between a WinRT and a .Net app

In this blog post, I share how I managed to enable RSA cryptography between a .Net app (actually a WCF service hosted in Azure) and WinRT app. I found few entries on the Internet talking about this issue but none of them solved my issue directly. In particular, I had to use some options I wasn’t using before the interop need with WinRT. That’s the reason behind this blog post.

Context

The following diagram showcase the need I have in my scenario:

WinRT encryption

I need to securely transfer data from a WinRT app to a .Net server app. The transport mechanism is out the scope of this article but it worth noting I’m using this approach because I cannot use HTTPS.

In order to give you a complete implementation I will be sharing in the rest of this article this way (encrypted data from WinRT to the server) and the other way around. So for each platform, you have the creation of the key pair, the encryption and the description method.

WinRT

Create key pairs

public static Tuple WinRTCreateKeyPair()
{
    AsymmetricKeyAlgorithmProvider asym = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
    CryptographicKey key = asym.CreateKeyPair(1024);
            
    IBuffer privateKeyBuffer = key.Export(CryptographicPrivateKeyBlobType.Capi1PrivateKey);
    IBuffer publicKeyBuffer = key.ExportPublicKey(CryptographicPublicKeyBlobType.Capi1PublicKey);
            
    byte[] privateKeyBytes;
    byte[] publicKeyBytes;
            
    CryptographicBuffer.CopyToByteArray(privateKeyBuffer, out privateKeyBytes);
    CryptographicBuffer.CopyToByteArray(publicKeyBuffer, out publicKeyBytes);

    string privateKey = Convert.ToBase64String(privateKeyBytes);
    string publicKey = Convert.ToBase64String(publicKeyBytes);

    return new Tuple(privateKey, publicKey);
}

Encrypt

public static byte[] WinRTEncrypt(string publicKey, string data)
{
    IBuffer keyBuffer = CryptographicBuffer.DecodeFromBase64String(publicKey);

    AsymmetricKeyAlgorithmProvider asym = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
    CryptographicKey key = asym.ImportPublicKey(keyBuffer, CryptographicPublicKeyBlobType.Capi1PublicKey);

    IBuffer plainBuffer = CryptographicBuffer.ConvertStringToBinary(data, BinaryStringEncoding.Utf8);
    IBuffer encryptedBuffer = CryptographicEngine.Encrypt(key, plainBuffer, null);

    byte[] encryptedBytes;
    CryptographicBuffer.CopyToByteArray(encryptedBuffer, out encryptedBytes);

    return encryptedBytes;
}

Decrypt

public static string WinRTDecrypt(string privateKey, byte[] data)
{
    IBuffer keyBuffer = CryptographicBuffer.DecodeFromBase64String(privateKey);

    AsymmetricKeyAlgorithmProvider asym = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
    CryptographicKey key = asym.ImportKeyPair(keyBuffer, CryptographicPrivateKeyBlobType.Capi1PrivateKey);
            
    IBuffer plainBuffer = CryptographicEngine.Decrypt(key, data.AsBuffer(), null);

    byte[] plainBytes;
    CryptographicBuffer.CopyToByteArray(plainBuffer, out plainBytes);

    return Encoding.UTF8.GetString(plainBytes, 0, plainBytes.Length);
}

.Net

Create key pairs

public static Tuple DotNetCreateKeyPair()
{
    CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };

    RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024, cspParams);

    string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
    string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));

    return new Tuple(privateKey, publicKey);
}

Encrypt

public static byte[] DotNetEncrypt(string publicKey, string data)
{
    CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
    RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);

    rsaProvider.ImportCspBlob(Convert.FromBase64String(publicKey));

    byte[] plainBytes = Encoding.UTF8.GetBytes(data);
    byte[] encryptedBytes = rsaProvider.Encrypt(plainBytes, false);

    return encryptedBytes;
}

Decrypt

public static string DotNetDecrypt(string privateKey, byte[] encryptedBytes)
{
    CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
    RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);

    rsaProvider.ImportCspBlob(Convert.FromBase64String(privateKey));

    byte[] plainBytes = rsaProvider.Decrypt(encryptedBytes, false);

    string plainText = Encoding.UTF8.GetString(plainBytes, 0, plainBytes.Length);

    return plainText;
}

Hope it helps 🙂

Using PowerShell to automate the build process of your Windows Phone app

Another day another occasion to share a tip which comes with the development of my todo-list app 2Day. This time, I share a small PowerShell script I use in order to build the application. Of course the script will not work out of the box for you but it could be useful if you’re thinking about automating the generation of your application.

Background: managing multiple versions

Since release 1.5.0 there are two versions of 2Day: the lite version (free) and the standard version (paid). I switch from one configuration to another using two Build Configurations. When I want to build the Lite version I build using the Release Lite configuration while for the standard version I build in Release. The difference between the two is a conditional symbol.

2Day-Configurations

I also have a WP7 and a WP8. So for each official release, I must produce 4 XAPs:

  • WP7 Lite
  • WP7 Full
  • WP8 Lite
  • WP8 Full

This is the reason why I was thinking about automating this stuff !

PowerShell script

The script itself is very straightforward: I use MSBuild to build the Visual Studio Solution File (*.sln) using the 2 configurations I described earlier. Once a build is complete I move the generated XAPs to an output folder. As I said this script will NOT work out of the box for your but it could be used as an example. Here is the code:

# usage is ./ScripName -version 1.5.0
param([string]$version = "version")

$solution = "2Day.sln"
$msbuild = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe"
# get the location of the script
$rootDir = [System.Io.Path]::GetDirectoryName($MyInvocation.MyCommand.Path)
# build the location of the sln file. In my case this is under /Branches/WindowsPhone/versionunmber/
$baseDir = $rootDir + "\Branches\WindowsPhone\" + $version + "\"
# and the ouput is in a Build folder
$outputFolder = $rootDir + "\Build\"

# if the output folder exists, delete it
if ([System.IO.Directory]::Exists($outputFolder))
{
 [System.IO.Directory]::Delete($outputFolder, 1)
 [System.IO.Directory]::CreateDirectory($outputFolder) | Out-Null
}
else
{
 [System.IO.Directory]::CreateDirectory($outputFolder)| Out-Null
}

Write-Host "Building 2Day version: " $version
Write-Host "Solution is at: "  $baseDir$solution

# make sure our working directory is correct
cd $baseDir

$configurations = "Release", "Release Lite"
foreach($configuration in $configurations)
{
	# prepare build command line
	$options = "/noconsolelogger /m /p:Configuration=""" + $configuration + """ /p:Platform=""Any CPU"""

	# prepare output path
	$releasePath1 = $baseDir + "2Day.App.WP7\bin\" + $configuration + "\_2DayWP7.xap"
	$outFile1 = "2Day_" + $version + "_WP7_" + $configuration.Replace(" ", "_") + ".xap"
	$releasePath2 = $baseDir + "2Day.App.WP8\bin\" + $configuration + "\_2DayWP8.xap"
	$outFile2 = "2Day_" + $version + "_WP8_" + $configuration.Replace(" ", "_") + ".xap"
	
	# clean
	Write-Host Perform cleaning...	
	$clean = $msbuild + " " + $solution + " " + $options + " /t:Clean"
	Invoke-Expression $clean | Out-Null
	
	# build
	Write-Host "Building with configuration"$configuration"..."
	$build = $msbuild + " " + $solution + " " + $options + " /t:Build"
	Invoke-Expression $build | Out-Null
	
	# move the files that were built to the output folder
	$out1 = $outputFolder + $outFile1;
	$out2 = $outputFolder + $outFile2;
	[System.IO.File]::Copy($releasePath1, $out1)
	[System.IO.File]::Copy($releasePath2, $out2)
	
	Write-Host "Output: " $out1 $out2
}

cd $rootDir
Write-Host "Done"

A Reactive Extension (Rx) use case in a Windows Phone app

While working on my todo-list application 2Day, I encountered a situation where Rx came to the rescue. Rx (Reactive Extension) is a framework which has been available for a couple of years now. It is possible to use it on the phone very easily. In this blog post, I share a piece of code which use Rx to implement a specific feature in 2Day.

2Day’s users have requested a search feature. The idea is simple: give the user a new page where he can type some text which then filters his tasks. Here is the feature in action in 2Day:

2Day - global search

Even though it seems very basic, I wanted to add an extra feature: perform the search a couple of milliseconds after the user actually stops typing. This prevents the search result to blink while the user types. It turns out it’s very easy to implement this using Rx which is dedicated to manipulate stream of observable items. Here is the code:

var seq = Observable
    .FromEventPattern(this.textbox, "TextChanged")
    .Throttle(TimeSpan.FromMilliseconds(500))
    .ObserveOnDispatcher()
    .Subscribe(e =>
    {
        this.viewmodel.SearchText = this.textbox.Text;
    });

The first step is to create the observable sequence using the FromEventPattern method which can turn an event into an observable stream. Then the Throttle method allows to “calm down” the stream, by requiring the specified amount of time to be spent before sending the item in the output stream.

This can be shown using the following diagram:

RxExampleThrottle

If the user presses quickly the letters “p”, “a”, “i” and “r” we will start the search only after the specified amount of time and not for each new letter. Notice that the subscribe method does not do a lot of things: it only sets a property on the ViewModel layer which actually performs the search.

Here is a simple yet useful use case for Rx. It’s very nice to have the library available out of the box on the phone. Hope it helps 🙂