Tuesday, 18 November 2014

C#: How do I store a secure password?

The eternal problems of encryption and data security is an ever present headache for all developers.
This post provides a great introduction to the problem.

Fortunately, C# provides a very powerful Windows Data Protection API to resolve this issue.

Read more about it here and find the sample code here.

Here is a simple extension method to achieve the required result:

using System;
using System.Security.Cryptography;
using System.Text;

namespace MyProject.Security
{
 public static class SecurityExtensions
    {
        private const DataProtectionScope Scope = DataProtectionScope.CurrentUser;
        static readonly byte[] AdditionalEntropy = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

        public static string Encrypt(this string plainText)
        {
            if (plainText == null) throw new ArgumentNullException("plainText");

            var data = Encoding.Unicode.GetBytes(plainText);
            var encrypted = ProtectedData.Protect(data, AdditionalEntropy, Scope);

            return Convert.ToBase64String(encrypted);
        }

        public static string Decrypt(this string cipher)
        {
            if (cipher == null) throw new ArgumentNullException("cipher");

            var data = Convert.FromBase64String(cipher);

            var decrypted = ProtectedData.Unprotect(data, AdditionalEntropy, Scope);
            return Encoding.Unicode.GetString(decrypted);
        }
    }
}

The usage would be as follows:

var plainPassword = "MyPassword";
var encryptedPassword = plainPassword.Encrypt();
var plainPasswordAgain = encryptedPassword.Decrypt();

This can also be moved to a PowerShell script:

namespace MyProject.Security
{
    // This class is used by the EncryptionHelper class to expose the encryptions methods

    public class SecurityHelper
    {
        public static string Encrypt(string plainText)
        {
            return plainText.Encrypt();
        }

        public static string Decrypt(string cipher)
        {
            return cipher.Decrypt();
        }
    }
}

function Get-EncryptedPassword
{        
[CmdletBinding()]        
Param(        
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]        
[string]$PlainPassword,
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]        
[string]$DllDirectory
)    

$bytesCommon = [System.IO.File]::ReadAllBytes("$DllDirectory\MyProject.Security.dll")
$loadResultCommon = [System.Reflection.Assembly]::Load($bytesCommon)

return [MyProject.Security.SecurityHelper]::Encrypt($PlainPassword)
}

function Get-DecryptedPassword
{        
[CmdletBinding()]        
Param(        
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]        
[string]$EncryptedPassword,
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]        
[string]$DllDirectory
)    

$bytesCommon = [System.IO.File]::ReadAllBytes("$DllDirectory\MyProject.Security.dll")
$loadResultCommon = [System.Reflection.Assembly]::Load($bytesCommon)

return [MyProject.Security.SecurityHelper]::Decrypt($EncryptedPassword)
}

This would be invoked as:

# get the directory of the dll.
$dir = Get-Location
# include the commandlets so they can be used
. .\<scriptName>.ps1
$cipher = Get-EncryptedPassword("P@$$W0rD")
$plainText = Get-EncryptedPassword($cipher)

No comments:

Post a comment