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)
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)
Comments
Post a Comment