Reputation: 4387
I'm using a website as a frontend and all users are authenticated with the standard ASP.NET Membership-Provider. Passwords are saved "hashed" within a SQL-Database.
Now I want to write a desktop-client with administrative functions. Among other things there should be a method to reset a users password. I can access the database with the saved membership-data, but how can I manually create the password-salt and -hash? Using the System.Web.Membership Namespace seems to be inappropriate so I need to know how to create the salt and hash of the new password manually.
Experts step up! :)
Upvotes: 14
Views: 19493
Reputation: 14100
You can absolutely use System.Web.Security
within a console or winforms app.
Here's a simple console application:
static void Main(string[] args)
{
MembershipProvider provider = Membership.Provider;
MembershipUser myUser = provider.GetUser("myUser", false);
if( myUser != null ) provider.DeleteUser("myUser", true);
MembershipCreateStatus status;
myUser = provider.CreateUser("myUser", "password", "[email protected]", null, null, true, null, out status);
if (status != MembershipCreateStatus.Success)
{
Console.WriteLine("Could not create user. Reason: " + status.ToString());
Console.ReadLine();
return;
}
Console.WriteLine("Authenticating with \"password\": " + provider.ValidateUser("myUser", "password").ToString());
string newPassword = myUser.ResetPassword();
Console.WriteLine("Authenticating with \"password\": " + provider.ValidateUser("myUser", "password").ToString());
Console.WriteLine("Authenticating with new password: " + provider.ValidateUser("myUser", newPassword).ToString());
Console.ReadLine();
}
And the app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="MyConnectionString" connectionString="Data Source=localhost;Initial Catalog=MyDatabase;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="MyConnectionString"
applicationName="MyApplication"
minRequiredPasswordLength="5"
minRequiredNonalphanumericCharacters="0"
requiresQuestionAndAnswer="false" />
</providers>
</membership>
</system.web>
</configuration>
Upvotes: 13
Reputation: 4387
I used reflector to take a look at those methods the .NET-Framework is using internal. Maybe there are public methods available for this but I did not find them - if you know how to query those internal methods as a user please left a comment! :)
Here is the simplified source-code without unnecessary conditions because I only want to encode the password as a SHA1-Hash:
private string GenerateSalt() {
var buf = new byte[16];
(new RNGCryptoServiceProvider()).GetBytes(buf);
return Convert.ToBase64String(buf);
}
private string EncodePassword(string pass, string salt) {
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(salt);
byte[] dst = new byte[src.Length + bytes.Length];
byte[] inArray = null;
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
inArray = algorithm.ComputeHash(dst);
return Convert.ToBase64String(inArray);
}
Upvotes: 18
Reputation: 12351
It's been some time since I've tinkered with ASP.Net membership, but do remember dealing with something a bit related to it (needed to customize things due to an existing database of users). In that effort I overrode the methods (the existing user db had md5 hashed pwds).
So in the same "line of thought":
Expose the Membership API via a web service that your desktop app can reference. This way, you're not "re-creating" things, you're re-using them. You don't have to override anything, you're just exposing the existing methods via a web service for your desktop app.
Goes without saying that you'd have to secure this endpoint....
If the above is too sketchy for your taste, here's a link to the asp.net forums regarding some attempts to recreate the hashing....I can't confirm the accuracy, but it should be easy to test it out:
http://forums.asp.net/p/1336657/2899172.aspx
Upvotes: 1
Reputation: 2825
Quick dirty method
Public Shared Function GetSaltKey() As String
Dim saltBytes() As Byte
Dim minSaltSize As Integer = 4
Dim maxSaltSize As Integer = 8
' Generate a random number for the size of the salt.
Dim random As Random
random = New Random()
Dim saltSize As Integer
saltSize = random.Next(minSaltSize, maxSaltSize)
' Allocate a byte array, which will hold the salt.
saltBytes = New Byte(saltSize - 1) {}
' Initialize a random number generator.
Dim rng As RNGCryptoServiceProvider
rng = New RNGCryptoServiceProvider()
' Fill the salt with cryptographically strong byte values.
rng.GetNonZeroBytes(saltBytes)
' Convert plain text into a byte array.
Return Convert.ToBase64String(saltBytes)
End Function
Public Shared Function ComputeHash(ByVal password As String, ByVal salt As String) As String
Return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(salt & password, _
System.Web.Configuration.FormsAuthPasswordFormat.SHA1.ToString)
End Function
Although, the membership namespace has stuff built in for this as well, as stated by Forgotten Semicolon
Upvotes: 1