Click here to Skip to main content
15,892,161 members
Articles / Desktop Programming / WPF

A (Mostly) Declarative Framework for Building Simple WPF-based Wizards

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
7 Mar 2011LGPL322 min read 19.3K   229   15  
A declarative framework for building WPF wizards.
/*
* Olbert.Utilities.CryptorLib
* a simple interface for encrypting and decrypting strings using the Rijndael algorithm
* Copyright (C) 2011  Mark A. Olbert
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published 
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Olbert.Utilities
{
    /// <summary>
    /// A class of static methods for encrypting and decrypting strings via the Rijndael algorithm.
    /// Key and salt management is included. Keys and salts can be generated as needed, or supplied
    /// by the user.
    /// </summary>
    public class Cryptor
    {
        /// <summary>
        /// Gets the required size of the encryption/decryption salt parameter
        /// </summary>
        public static int SaltSize
        {
            get
            {
                RijndaelManaged rijn = new RijndaelManaged();

                return rijn.BlockSize / 8;
            }
        }

        /// <summary>
        /// Gets the required size of the encryption/decryption key parameter
        /// </summary>
        public static int KeySize
        {
            get
            {
                RijndaelManaged rijn = new RijndaelManaged();

                return rijn.KeySize / 8;
            }
        }

        /// <summary>
        /// Generates a byte array of random bytes of the specified length
        /// </summary>
        /// <param name="numBytes">the number of bytes to return</param>
        /// <returns>a byte array of random bytes</returns>
        public static byte[] GenerateRandomBytes( int numBytes )
        {
            if( numBytes < 0 )
                throw new CryptorLibException("requested less than zero random bytes");

            byte[] data = new byte[numBytes];
            if( numBytes == 0 ) return data;

            RNGCryptoServiceProvider rngSalt = new RNGCryptoServiceProvider();
            rngSalt.GetBytes(data);

            return data;
        }

        /// <summary>
        /// Tests the supplied string to see if it is a valid salt
        /// </summary>
        /// <param name="salt">the string to test</param>
        /// <returns>true if the supplied string is a valid salt, false otherwise</returns>
        public static bool IsValidSalt( string salt )
        {
            RijndaelManaged rijn = new RijndaelManaged();

            EncodedString encString = new EncodedString(rijn.BlockSize / 8);

            return (encString.SetText(salt, false) == EncodedString.ConversionResult.Okay);
        }

        /// <summary>
        /// Tests the supplied byte array to see if it is a valid salt
        /// </summary>
        /// <param name="salt">the byte array to test</param>
        /// <returns>true if the supplied byte array is a valid salt, false otherwise</returns>
        public static bool IsValidSalt( byte[] salt )
        {
            RijndaelManaged rijn = new RijndaelManaged();

            EncodedString encString = new EncodedString(rijn.BlockSize / 8);

            return encString.SetBytes(salt, false);
        }

        /// <summary>
        /// Tests the supplied string to see if it is a valid key
        /// </summary>
        /// <param name="key">the string to test</param>
        /// <returns>true if the supplied string is a valid key, false otherwise</returns>
        public static bool IsValidKey( string key )
        {
            RijndaelManaged rijn = new RijndaelManaged();

            EncodedString encString = new EncodedString(rijn.KeySize / 8);

            return ( encString.SetText(key, false) == EncodedString.ConversionResult.Okay );
        }

        /// <summary>
        /// Tests the supplied byte array to see if it is a valid key
        /// </summary>
        /// <param name="key">the byte array to test</param>
        /// <returns>true if the supplied byte array is a valid key, false otherwise</returns>
        public static bool IsValidKey( byte[] key )
        {
            RijndaelManaged rijn = new RijndaelManaged();

            EncodedString encString = new EncodedString(rijn.KeySize / 8);

            return encString.SetBytes(key, false);
        }

        private RijndaelManaged theRijn = new RijndaelManaged();

        private EncodedString encSalt;
        private EncodedString encKey;

        /// <summary>
        /// Initializes an instance with empty salt and key parameters
        /// </summary>
        public Cryptor()
        {
            encSalt = new EncodedString(theRijn.BlockSize / 8);
            encKey = new EncodedString(theRijn.KeySize / 8);
        }

        /// <summary>
        /// Initializes an instance using the provided salt and key
        /// </summary>
        /// <param name="salt">the salt to use in encryption/decryption</param>
        /// <param name="key">the key to use in encryption/decryption</param>
        public Cryptor( string salt, string key )
            : this()
        {
            encSalt.SetText(salt);
            encKey.SetText(key);
        }

        /// <summary>
        /// Initializes an instance using the provided salt and key
        /// </summary>
        /// <param name="salt">the salt to use in encryption/decryption</param>
        /// <param name="key">the key to use in encryption/decryption</param>
        public Cryptor( byte[] salt, byte[] key )
            : this()
        {
            encSalt.SetBytes(salt);
            encKey.SetBytes(key);
        }

        /// <summary>
        /// Gets the current salt for the Rijndael method as an EncodedString, which can be
        /// accessed as either an array of bytes or a normal/base64 encoded string
        /// </summary>
        public EncodedString Salt
        {
            get { return encSalt; }
        }

        /// <summary>
        /// Gets the current key for the Rijndael method as an EncodedString, which can be
        /// accessed as either an array of bytes or a normal/base64 encoded string
        /// </summary>
        public EncodedString Key
        {
            get { return encKey; }
        }

        /// <summary>
        /// Generates a new random salt
        /// </summary>
        public void GenerateSalt()
        {
            Salt.SetBytes(GenerateRandomBytes(theRijn.BlockSize / 8));
        }

        /// <summary>
        /// Generates a new random key
        /// </summary>
        public void GenerateKey()
        {
            theRijn.GenerateKey();

            Key.SetBytes(theRijn.Key);
        }

        /// <summary>
        /// Generates a new random salt and key
        /// </summary>
        public void GenerateSaltAndKey()
        {
            GenerateSalt();
            GenerateKey();
        }

        /// <summary>
        /// Encrypts the provided string using the Rijndael method and the current 
        /// salt and key values
        /// </summary>
        /// <param name="input">the string to encrypt</param>
        /// <returns>the encrypted string</returns>
        public string Encrypt( string input )
        {
            if( !encSalt.HasEnoughBytes ) throw new CryptorLibException("invalid salt");
            if( !encKey.HasEnoughBytes ) throw new CryptorLibException("invalid key");

            UTF8Encoding textConverter = new UTF8Encoding();
            byte[] toEncrypt = textConverter.GetBytes(input);

            ICryptoTransform encryptor = theRijn.CreateEncryptor(encKey.GetBytes(), encSalt.GetBytes());
            MemoryStream msEncrypt = new MemoryStream();
            CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

            // Read the byteData out of the crypto stream.
            csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
            csEncrypt.FlushFinalBlock();

            // Convert the byte array into a base64 string.
            return Convert.ToBase64String(msEncrypt.ToArray());
        }

        ///// <summary>
        ///// Encrypts the provided string using Rijndael method and the provided salt and key strings. 
        ///// The supplied salt and key become the new default salt and key values if they are valid. 
        ///// A CryptoLibException is thrown if either the provided salt or key are invalid.
        ///// </summary>
        ///// <param name="salt">the salt to use in encrypting the string</param>
        ///// <param name="key">the key to use in encrypting the string</param>
        ///// <param name="input">the string to encrypt</param>
        ///// <returns>the encrypted string</returns>
        //public static string Encrypt( string salt, string key, string input )
        //{
        //    EncodedString newSalt = new EncodedString(encSalt.RequiredLength, false);
        //    newSalt.Text = salt;

        //    if( !newSalt.HasEnoughBytes )
        //        throw new CryptorLibException(String.Format("invalid salt value ({0})", newSalt.Status.ToString()));

        //    encSalt = newSalt;

        //    EncodedString newKey = new EncodedString(encKey.RequiredLength, false);
        //    newKey.Text = key;

        //    if( !newKey.HasEnoughBytes )
        //        throw new CryptorLibException(String.Format("invalid key value ({0})", newKey.Status.ToString()));

        //    encKey = newKey;

        //    return Encrypt(input);
        //}

        /// <summary>
        /// Decrypts the supplied string using the Rijndael method and the current salt and key.
        /// </summary>
        /// <param name="input">the string to decrypt</param>
        /// <returns>the decrypted string</returns>
        public string Decrypt( string input )
        {
            if( !encSalt.HasEnoughBytes ) throw new CryptorLibException("invalid salt");
            if( !encKey.HasEnoughBytes ) throw new CryptorLibException("invalid key");

            EncodedString encInput = new EncodedString(0);
            encInput.SetText(input);

            if( !encInput.HasEnoughBytes ) 
                throw new CryptorLibException("unable to parse the supplied input string; it should be base 64 encoded");

            ICryptoTransform decryptor = theRijn.CreateDecryptor(encKey.GetBytes(), encSalt.GetBytes());
            MemoryStream msDecrypt = new MemoryStream(encInput.GetBytes());
            CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

            // Read the byteData out of the crypto stream.
            byte[] bufDecrypt = new byte[encInput.BytesAvailable];
            csDecrypt.Read(bufDecrypt, 0, bufDecrypt.Length);

            //Convert the byte array back into a string, removing any padding characters
            UTF8Encoding textConverter = new UTF8Encoding();
            StringBuilder sb = new StringBuilder(textConverter.GetString(bufDecrypt));

            int charIdx = sb.Length - 1;
            while( ( charIdx > 0 ) && ( sb[charIdx] == 0x0 ) )
            {
                charIdx--;
            }

            if( charIdx < ( sb.Length - 1 ) ) sb.Remove(charIdx + 1, sb.Length - charIdx - 1);

            return sb.ToString();
        }

        ///// <summary>
        ///// Decrypts the provided string using Rijndael method and the provided salt and key strings. 
        ///// The supplied salt and key become the new default salt and key values if they are valid. 
        ///// A CryptoLibException is thrown if either the provided salt or key are invalid.
        ///// </summary>
        ///// <param name="salt">the salt to use in decrypting the string</param>
        ///// <param name="key">the key to use in decrypting the string</param>
        ///// <param name="input">the string to decrypt</param>
        ///// <returns>the decrypted string</returns>
        //public static string Decrypt( string salt, string key, string input )
        //{
        //    EncodedString newSalt = new EncodedString(encSalt.RequiredLength, false);
        //    newSalt.Text = salt;

        //    if( !newSalt.HasEnoughBytes )
        //        throw new CryptorLibException(String.Format("invalid salt value ({0})", newSalt.Status.ToString()));

        //    encSalt = newSalt;

        //    EncodedString newKey = new EncodedString(encKey.RequiredLength, false);
        //    newKey.Text = key;

        //    if( !newKey.HasEnoughBytes )
        //        throw new CryptorLibException(String.Format("invalid key value ({0})", newKey.Status.ToString()));

        //    encKey = newKey;

        //    return Decrypt(input);
        //}
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Jump for Joy Software
United States United States
Some people like to do crossword puzzles to hone their problem-solving skills. Me, I like to write software for the same reason.

A few years back I passed my 50th anniversary of programming. I believe that means it's officially more than a hobby or pastime. In fact, it may qualify as an addiction Smile | :) .

I mostly work in C# and Windows. But I also play around with Linux (mostly Debian on Raspberry Pis) and Python.

Comments and Discussions