Monday, March 2, 2009

DPAPI Encrypted Password in Database

XIP SDK need password to login. So to use DPAPI encryption we need the scheme with the following properties:
(1) Have to allow encryption per machine
(2) easy to change password once in database to trigger change accross all machines

PASSWORD Schema
CREATE TABLE [dbo].JQD_PASSWORD(
[USER_ID] [varchar](50) NOT NULL,
[PASSWORD] [varchar](1000) NOT NULL,
[NEW_PASSWORD] [varchar](500) NOT NULL,
[INSTALL_MACHINE] [varchar](50) NOT NULL,
[PASSWORD_HASH] [varchar](100) NOT NULL,
CONSTRAINT [PK_JQD_PASSWORD] PRIMARY KEY CLUSTERED
(
[USER_ID] ASC,
[INSTALL_MACHINE] ASC
)

DPAPI Decrypt and use Old-New Password Chain

using (JQD.XIPCommon.Entity.ImportExportXIP.ImportExportXIPDataContext ctx = new JQD.XIPCommon.Entity.ImportExportXIP.ImportExportXIPDataContext(_csImportExportXIPIntSec))
{
var q = from r in ctx.JQD_PASSWORDs
where r.INSTALL_MACHINE == Environment.MachineName &&
r.USER_ID == _TraderName
select new { r.PASSWORD, r.NEW_PASSWORD };
string nPWD=q.First().NEW_PASSWORD;
string oPWD=Encoding.Unicode.GetString(ProtectedData.Unprotect(Convert.FromBase64String(q.First().PASSWORD ), null, DataProtectionScope.LocalMachine));
if (nPWD == string.Empty)
{
LoggingHelper.DoMsmqLogging("Trader Password retrieved successfully", "JQDAllocationService::GetTraderPassword", "Normal", "Info");
return oPWD;
}
else
{
LoggingHelper.DoMsmqLogging("Password change detected, updating password...", "JQDAllocationService::GetTraderPassword", "Normal", "Info");
nPWD=RijndaelSimple.Decrypt(nPWD, oPWD, oPWD, "SHA1", 1, "@1B2c3D4e5F6g7H8", 256);
JQD.XIPCommon.Entity.ImportExportXIP.JQD_PASSWORD pwd = new JQD.XIPCommon.Entity.ImportExportXIP.JQD_PASSWORD();
pwd.USER_ID = _TraderName;
pwd.PASSWORD =Convert.ToBase64String(ProtectedData.Protect(Encoding.Unicode.GetBytes(nPWD), null, DataProtectionScope.LocalMachine));
pwd.INSTALL_MACHINE = Environment.MachineName ;
pwd.NEW_PASSWORD = "";
HashAlgorithm hash = new SHA1Managed();
pwd.PASSWORD_HASH = Convert.ToBase64String((hash.ComputeHash(Encoding.UTF8.GetBytes(pwd.PASSWORD))));

var q2 = (from a in ctx.JQD_PASSWORDs
where a.USER_ID == _TraderName &&
a.INSTALL_MACHINE == Environment.MachineName
select a);
ctx.JQD_PASSWORDs.DeleteAllOnSubmit(q2);
ctx.SubmitChanges();

ctx.JQD_PASSWORDs.InsertOnSubmit(pwd);
ctx.SubmitChanges();
LoggingHelper.DoMsmqLogging("Password update completed. will try login", "JQDAllocationService::GetTraderPassword", "Normal", "INfo");

return nPWD;
}
}

DPAPI encrypt post installation

string DPAPIEncrypt(string clearText)
{
return Convert.ToBase64String(ProtectedData.Protect(Encoding.Unicode.GetBytes(clearText ), null, DataProtectionScope.LocalMachine));
}

private void SavePassword()
{
string MachineName = Environment.MachineName;
string cs = ConfigurationManager.ConnectionStrings["PasswordManagement.Properties.Settings.ImportExportXIPConnectionString"].ConnectionString;

using (ImportExportXIPDataContext ctx = new ImportExportXIPDataContext(cs)) // use integrated security
{
JQD_PASSWORD pwd = new JQD_PASSWORD();
pwd.USER_ID = tbUserID_Installation.Text;
pwd.PASSWORD = DPAPIEncrypt(tbPassword_Installation.Text);
pwd.INSTALL_MACHINE = MachineName;
pwd.NEW_PASSWORD = "";
HashAlgorithm hash = new SHA1Managed();
pwd.PASSWORD_HASH = Convert.ToBase64String((hash.ComputeHash(Encoding.UTF8.GetBytes(tbPassword_Installation.Text))));

var q = (from a in ctx.JQD_PASSWORDs
where a.USER_ID == tbUserID_Installation.Text &&
a.INSTALL_MACHINE == MachineName
select a);
ctx.JQD_PASSWORDs.DeleteAllOnSubmit(q);
ctx.SubmitChanges();

ctx.JQD_PASSWORDs.InsertOnSubmit(pwd);
ctx.SubmitChanges();
}
}

Change Password
private void btnChange_Click(object sender, EventArgs e)
{
string MachineName = Environment.MachineName;
string cs = ConfigurationManager.ConnectionStrings["PasswordManagement.Properties.Settings.ImportExportXIPConnectionString"].ConnectionString;

HashAlgorithm hash = new SHA1Managed();
string entered_pwd_hash = Convert.ToBase64String((hash.ComputeHash(Encoding.UTF8.GetBytes(tbOldPassword.Text))));

using (ImportExportXIPDataContext ctx = new ImportExportXIPDataContext(cs)) // use integrated security
{
var q = (from a in ctx.JQD_PASSWORDs
where a.USER_ID == cbUserID.SelectedValue.ToString() &&
a.PASSWORD_HASH==entered_pwd_hash
select a);

if (q.Count()==0)
{
MessageBox.Show("No machines use the old password entered. You may try to enter another old password");
WriteToLog("No machines use the old password entered, Therefore no change of password");
return;
}
if (!TestPassword(cbUserID.SelectedValue.ToString(), tbNewPassword.Text))
{
WriteToLog("New password is invalid. No change will be made.");
return;
}
foreach (JQD_PASSWORD pwd in q)
{
pwd.NEW_PASSWORD = RijndaelSimple.Encrypt(tbNewPassword.Text, tbOldPassword.Text, tbOldPassword.Text, "SHA1", 1, "@1B2c3D4e5F6g7H8", 256);
WriteToLog("New password saved for "+pwd.USER_ID+", "+pwd.INSTALL_MACHINE+" and will be picked up by installed services" );
}
ctx.SubmitChanges();
}
}


Rijndael Symmetric Encryption

public class RijndaelSimple
{
public static string Encrypt(string plainText,
string passPhrase,
string saltValue,
string hashAlgorithm,
int passwordIterations,
string initVector,
int keySize)
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password = new PasswordDeriveBytes(
passPhrase,
saltValueBytes,
hashAlgorithm,
passwordIterations);

byte[] keyBytes = password.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
keyBytes,
initVectorBytes);

MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string cipherText = Convert.ToBase64String(cipherTextBytes);
return cipherText;
}

public static string Decrypt(string cipherText,
string passPhrase,
string saltValue,
string hashAlgorithm,
int passwordIterations,
string initVector,
int keySize)
{

byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes password = new PasswordDeriveBytes(
passPhrase,
saltValueBytes,
hashAlgorithm,
passwordIterations);


byte[] keyBytes = password.GetBytes(keySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
keyBytes,
initVectorBytes);

MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream,
decryptor,
CryptoStreamMode.Read);

byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes,
0,
plainTextBytes.Length);

memoryStream.Close();
cryptoStream.Close();
string plainText = Encoding.UTF8.GetString(plainTextBytes,
0,
decryptedByteCount);

return plainText;
}
}

No comments: