Click here to Skip to main content
Click here to Skip to main content

Cryptographically Random Password / Data Set Creator using WPF, MVVM, and TPL

, 31 Jan 2013
Rate this:
Please Sign up or sign in to vote.
Random data/password generator using WPF and MVVM to accomplish clean separation of GUI logic and business logic using MVVM pattern.

Introduction

The purpose of this project is to demonstrate both proper generation of a random password (or large randomized data set) as well as showcase the usage of the incredibly useful Model-View-ViewModel architecture. MVVM is a pattern that affords separation of concerns, i.e., the user interface logic and the business logic are not interspersed. Instead, a class (the view-model) sits in between the UI code (view) and the business logic (model). WPF utilizes additional features, namely data binding, to facilitate the implementation of MVVM in a WPF application.

Background

To understand all the aspects of this article, it behooves the reader to have a strong understanding of core C# concepts, including collection classes, thread synchronization, and delegates and events. Moderate understanding of WPF and XAML is also beneficial.

To expedite development, the use of the ever useful WPF MVVM project template, by Ofir Shemesh, is also advised. It is available via the Extension Manager in Visual Studio 2010. Version 3.0 of the WPF MVVM project template was used to produce the folder and code structure for this project.

Using the Code

The code in this project is meant to be as self-explanatory as possible, given a strong understanding of general C# concepts and a willingness to learn WPF, XAML, and MVVM. The project delineated in this article is self-contained. Downloading the source code and running it will produce a window that looks like the following:

The project folder and file organization is structured as per the illustration below.

MVVM Components at a High Level

You will notice that there is one view, one view-model, and two models. The MainWindow view encapsulates the XAML code and the code behind (though the latter is essentially blank, meaning all view rendering is done via markup). The MainWindowViewModel inherits from a simple base class that inherits from NotificationObject to facilitate notifications flowing to and from the view and models. The two model classes, RandPasswordModel and StatsModel, handle the holding of in-memory data about the state of the underlying business objects for the random password generator settings, generated randomized data, as well as the statistics gleaned from the generated password/data strings. These statistics can be used to assess the randomness of the generated data.

The XAML

Constructing the View (again, all in XAML) made heavy use of the designer as well as direct edits of the XAML code. It is your preference as to whether you wish to rely on the designer or code by hand.

Crucially, you will notice the use of the view-model and the converters (more on converters later) in the window resources section of the XAML.

<Window.Resources>
    <localVM:MainWindowViewModel x:Key="Windows1ViewModel" />

    <localConverters:CharArrayToStrConverter x:Key="CharArrayToStrConverter" />
    <localConverters:StrListToStrConverter x:Key="StrListToStrConverter" />
    <localConverters:DictionaryToText x:Key="DictionaryToText" />
</Window.Resources>

These elements provide the view's link to the view-model and to converters used later in data conversion, as data flows to and from the view and model.

The capacity to bind WPF controls to both data and to commands gives us the ability to perform actions and to receive data. The following illustrates the XAML syntax to associate a button click with a command and to receive text back from the business layer.

<Button Content="Create passwords" Height="23" Name="button1" 
  Width="233" HorizontalAlignment="Left" Command="{Binding GeneratePassesCommand}" />

...

<TextBox Height="300" Name="textBox3" Width="Auto" 
  HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" 
  HorizontalScrollBarVisibility="Auto" 
  Text="{Binding Model.GeneratedPass, Mode=OneWay, Converter={StaticResource StrListToStrConverter} }" />

In the preceding example, you will note that the button is associated with a command, the GeneratePassesCommand. That command flows to a same-named method in the view-model that calls the relevant method in the model. Once generated, the TextBox control receives the generated randomized data set, converts that result to something that can be bound to the TextBox control (via the StrListToStrConverter), and is then rendered as the text in the control.

The View-Model - taking a look at the GeneratePassesCommand

GeneratePassesCommand is of type System.Windows.Input.ICommand and is declared as follows:

public ICommand GeneratePassesCommand { get { return new DelegateCommand(GeneratePasswords); } }

GeneratePassesCommand encapsulates an Action type that, in this scenario, calls the private GeneratePasswords method. See method code below, in full.

private void GeneratePasswords()
{
    AppStatus = "Generating passwords...";

    AsyncCallHelper.RunAync(() =>
    {
        List<string> passes = new List<string>(Model.NumPasses);
        Parallel.For(0, Model.NumPasses, i =>
        {
            int seed = RandomGenHelper.GetCryptographicallyRandomInt32();
            Random r = new Random(seed);
            string pass = RandomGenHelper.GeneratePassword(r, Model.Length, Model.AllowableChars);
            lock (passes)
            {
                passes.Add(pass);
            }
        } );

        Model.GeneratedPass = passes;

        StatsModel = new StatsModel(Model.GeneratedPass);

        AppStatus = string.Format("{0} passwords generated", Model.NumPasses);
    } );

}

Back in the view, and by virtue of the fact that the TextBox control previously described is bound to the GeneratedPass property in the model, the generated passwords (or randomized data) will be fed back into the control after generation and upon execution of the property setter, called by this line: Model.GeneratedPass = passes;.

It is also worth mentioning the use of helper methods that can be used elsewhere, including across models. For example, the entire block in the GeneratePasswords() method above is executed via a .NET Task via the call to RunAsync. For purposes of illustration, let's take a look at that code.

public class AsyncCallHelper
{
    static public void RunAync(Action action)
    {
        Task.Factory.StartNew(action);
    }
}

In fact, the entire class is just a few lines! The asynchronous helper method utilizes the exceptionally helpful TPL, task parallel library, to execute as an asynchronous operation. By utilizing this technique, we are following the recommended guidelines for increasing the responsiveness of the application, as the execution of the expensive business logic is done outside the UI thread.

The Model - A closer look at RandPasswordModel

The model's GeneratedPass is just a regular property, with one caveat. Once that property is set, a notification must be sent so that the view can be updated.

private List<string> _GeneratedPass;
public List<string> GeneratedPass
{
    get { return this._GeneratedPass; }
    set
    {
        if (this._GeneratedPass != value)
        {
            this._GeneratedPass = value;
            RaisePropertyChanged(() => GeneratedPass);
        }
    }
}

Recall that the base class of both models in this project inherit from NotificationObject. Once RaisePropertyChanged is called in the setter of a property of the model, the PropertyChangedEventHandler event is fired, alerting the underlying view's (bound) control that a property change has occurred.

Before we leave this topic, you will recall in the View-Model section above that we dived into the GeneratePasswords method. If you look over that method again, you will see that the logic to fetch the random seed and the randomized data are done via calls to helper methods in the RandomGenHelper class. Let's take a look at this code and examine it.

public static class RandomGenHelper
{
    static public Int32 GetCryptographicallyRandomInt32()
    {
        byte[] randomBytes = new byte[4];
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        rng.GetBytes(randomBytes);
        Int32 randomInt = BitConverter.ToInt32(randomBytes, 0);
        return randomInt;
    }

    static public string GeneratePassword(Random r, int length, char[] allowableChars)
    {
        StringBuilder passwordBuilder = new StringBuilder((int)length);

        for (int i = 0; i < length; i++)
        {
            int nextInt = r.Next(allowableChars.Length);
            char c = allowableChars[nextInt];
            passwordBuilder.Append(c);
        }

        return passwordBuilder.ToString();
    }

}

Above, you will see the call to GetCryptographicallyRandomInt32. This method utilizes the System.Security.Cryptography namespace. Here, we are using a well known library to aid in the generation of highly random 32-bit integers. These cryptographically random numeric seeds are fed into the Random class, which is then used by the GeneratePassword method above to generate random passwords (i.e., a randomized data set), using the character set specified by the user (with a reasonable default one provided). This is the crux of the code from a business logic standpoint.

Converters - When and how to use them

A converter will need to be used in cases where data coming from or routed to the model must be translated to be understood. In the case of the GeneratedPass property, we can use a simple converter to translate the List<string> to a regular string. Note that since the binding is one way, we only need to convert in one direction: from the model and to the control. Let's take a look at the StrListToStrConverter class in full.

public class StrListToStrConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
            return string.Empty;

        List<string> passes = (List<string>)value;
        string ret=string.Join(Environment.NewLine, passes.ToArray<string>() );
        return ret;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

And there you have it, a conversion of a list of strings to a regular string to allow for binding to the TextBox control.

Conclusion

In this article, we have covered the high level concepts of the MVVM pattern implemented in the context of a WPF desktop application in C# for .NET 4.0, as well as ancillary topics including the task parallel library for parallelized work and asynchronous operations, the use of ICommand objects for command delegation, and the use of the cryptography library for random number seeding.

Points of interest

I thoroughly enjoyed writing this application to demonstrate the power and flexibility of WPF coupled with MVVM. It is my hope that others find it equally useful and inspiring.

History

Version 1.0 - Operational MVVM application.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Garrett M. Groff
Software Developer (Senior)
United States United States
Sr software developer with experience in various languages and platforms, namely windows development using C#, C++, and VB, as well as various database platforms. At present, working in financial services industry in the United States.

Comments and Discussions

 
QuestionOk, I like your idea but... Pinmemberdb7uk31-Jan-13 23:31 
AnswerRe: Ok, I like your idea but... PinmemberGarrett M. Groff17-Feb-13 18:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 31 Jan 2013
Article Copyright 2013 by Garrett M. Groff
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid