Reputation: 127563
I have a program that uses System.DirectoryServices.AccountManagement.PrincipalContext
to verify that the information a user entered in a setup screen is a valid user on the domain (the computer itself is not on the domain) and do some operations on the users of the domain. The issue is I do not want the user to need to enter his or her password every time they run the program so I want to save it, but I do not feel comfortable storing the password as plain-text in their app.config file. PrincipalContext needs a plain-text password so I can not do a salted hash as everyone recommends for password storing.
This is what I did
const byte[] mySalt = //It's a secret to everybody.
[global::System.Configuration.UserScopedSettingAttribute()]
public global::System.Net.NetworkCredential ServerLogin
{
get
{
var tmp = ((global::System.Net.NetworkCredential)(this["ServerLogin"]));
if(tmp != null)
tmp.Password = new System.Text.ASCIIEncoding().GetString(ProtectedData.Unprotect(Convert.FromBase64String(tmp.Password), mySalt, DataProtectionScope.CurrentUser));
return tmp;
}
set
{
var tmp = value;
tmp.Password = Convert.ToBase64String(ProtectedData.Protect(new System.Text.ASCIIEncoding().GetBytes(tmp.Password), mySalt, DataProtectionScope.CurrentUser));
this["ServerLogin"] = value;
}
}
Was this the right thing to do or is there a better way?
EDIT -- Here is a updated version based on everyone's suggestions
private MD5 md5 = MD5.Create();
[global::System.Configuration.UserScopedSettingAttribute()]
public global::System.Net.NetworkCredential ServerLogin
{
get
{
var tmp = ((global::System.Net.NetworkCredential)(this["ServerLogin"]));
if(tmp != null)
tmp.Password = System.Text.Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(tmp.Password), md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(tmp.UserName.ToUpper())), DataProtectionScope.CurrentUser));
return tmp;
}
set
{
var tmp = value;
tmp.Password = Convert.ToBase64String(ProtectedData.Protect(System.Text.Encoding.UTF8.GetBytes(tmp.Password), md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(tmp.UserName.ToUpper())), DataProtectionScope.CurrentUser));
this["ServerLogin"] = tmp;
}
}
Upvotes: 8
Views: 974
Reputation: 9954
I like the JoelCoehoorn approach.
Use a value unique for the user machine as the password salt.
So it will be different in each deplyment ; ).
UPDATE: See this thread for ideas: How-To-Get-Unique-Machine-Signature
Upvotes: 0
Reputation: 415735
For the salt, I'd do a transformation on the username (hash it) rather than share the same salt for everyone.
For something like this, I'd also look for a way to keep the existing session alive longer rather than saving the password to create new sessions.
Upvotes: 4
Reputation: 887433
Instead of writing new System.Text.ASCIIEncoding()
, you should write System.Text.Encoding.ASCII
.
Also, I recommend using UTF8 instead.
Other than that, your code looks pretty good.
Upvotes: 2