The new ASP.net Identity project has brought some useful code and interfaces for website security. To implement a custom system using the interfaces (instead of using the standard Entity Framework implementation included in the MVC 5 template) an IPasswordHasher is required.
IPasswordHasher interface in ASP.net Identity
namespace Microsoft.AspNet.Identity
{
public interface IPasswordHasher
{
string HashPassword(string password);
PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword);
}
}
Is it possible to use password salting for more secure encryption in ASP.net Identity and via this interface?
HEALTH WARNING for the below answer: Know which version of ASP.Net Identity you are using. You should refer to the source code directly if it is one of the newer versions from the github repository.
As I write this, the current version (3.0.0-rc1/.../PasswordHasher.cs) of the password handler is significantly different to the below answer. This newer version supports multiple hash algorithm versions and is documented as (and may change further by the time you read this):
Version 2:
PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
(See also: SDL crypto guidelines v5.1, Part III)
Format: { 0x00, salt, subkey }
Version 3:
PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
(All UInt32s are stored big-endian.)
The original answer is still valid for the original version of ASP.Net Identity, and is as follows:
#jd4u is correct, but to shed a little more light which wouldn't fit into a comment for his answer:
Microsoft.AspNet.Identity.PasswordHasher : IPasswordHasher already salts for you,
more importantly it uses Rfc2898DeriveBytes to generate the salt and the hash,
which uses industry standard PBKDF2 (SE discussion here, OWASP recommendation for PBKDF2 here).
The default Microsoft.AspNet.Identity.UserManager<TUser> implementation uses Microsoft.AspNet.Identity.PasswordHasher as a concrete IPasswordHasher
PasswordHasher in turn is a really simple wrapper for (ultimately)System.Security.Cryptography.Rfc2898DeriveBytes
So, if you are going to use Rfc2898DeriveBytes, just use PasswordHasher - all the heavy lifting is already done (hopefully correctly) for you.
Details
The full code that PasswordHasher (currently) ultimately uses does something very close to:
int saltSize = 16;
int bytesRequired = 32;
byte[] array = new byte[1 + saltSize + bytesRequired];
int iterations = SOME; // 1000, afaik, which is the min recommended for Rfc2898DeriveBytes
using (var pbkdf2 = new Rfc2898DeriveBytes(password, saltSize, iterations))
{
byte[] salt = pbkdf2.Salt;
Buffer.BlockCopy(salt, 0, array, 1, saltSize);
byte[] bytes = pbkdf2.GetBytes(bytesRequired);
Buffer.BlockCopy(bytes, 0, array, saltSize+1, bytesRequired);
}
return Convert.ToBase64String(array);
"Is it possible to use password salting for more secure encryption in
ASP.net Identity and via this interface?"
Yes, the interface is provided for the new implementation of PasswordHasher already present in Core framework.
Also note that the default implementation is already using Salt+Bytes.
After creating custom PasswordHasher (say MyPasswordHasher), you can assign it to UserManager instance like userManager.PasswordHasher=new MyPasswordHasher()
See one example of such IPasswordHasher
To implement a custom system using the interfaces (instead of using the standard Entity Framework implementation included in the MVC 5 template) an IPasswordHasher is required.
For implementing alternate system from EF,
- You shall implement all Core interfaces.
- IPasswordHasher implementation is not required. PasswordHasher is already provided in Core framework as it's implementation.
I ran into an issue while updating from Membership to AspNet.Identity. The Rfc2898 hashes are different from those used before. That's for good reason, but changing the hashes would require all users to reset their passwords. As a solution this custom implementation makes it backwards compatible:
public class MyPasswordHasher : PasswordHasher {
public FormsAuthPasswordFormat FormsAuthPasswordFormat { get; set; }
public MyPasswordHasher(FormsAuthPasswordFormat format) {
FormsAuthPasswordFormat = format;
}
public override string HashPassword(string password) {
return FormsAuthentication.HashPasswordForStoringInConfigFile(password, FormsAuthPasswordFormat.ToString());
}
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) {
var testHash = FormsAuthentication.HashPasswordForStoringInConfigFile(providedPassword, FormsAuthPasswordFormat.ToString());
return hashedPassword.Equals(testHash) ? PasswordVerificationResult.Success : PasswordVerificationResult.Failed;
}
}
Once you create your UserManager instance just set the hasher:
Usermanager.PasswordHasher = new MyPasswordHasher(FormsAuthPasswordFormat.SHA1);
The code complains that the HashPasswordForStoringInConfigFile method is deprecated, but that's fine as we know that the whole exercise is to get rid of the old technology.
Related
I just upgraded a project from .NET 4.0 to .NET 4.5.1 which produced the following warning:
Public Shared Function Encode(data() As Byte, protectionOption As
System.Web.Security.MachineKeyProtection) As String' is obsolete:
'This method is obsolete and is only provided for compatibility with
existing code. It is recommended that new code use the Protect and
Unprotect methods instead.'
I have lots of values floating around in cookies and emails that were encrypted with Encode. If I am going to replace Encode/Decode with Protect/Unprotect, I still need to be able to decrypt those old encrypted values. Is it possible to Unprotect a value that was encrypted with Encode?
In .NET 4.0 you can use MachineKey API to Protect/Unprotect data like this:
string Protect(byte[] data)
{
if (data == null || data.Length == 0) return null;
return MachineKey.Encode(data, MachineKeyProtection.All);
}
byte[] Unprotect(string value)
{
if (String.IsNullOrWhiteSpace(value)) return null;
return MachineKey.Decode(value, MachineKeyProtection.All);
}
MachineKey.Encode accepts a byte[] to protect and returns a string. The second parameter is an enum that indicates if you want encryption, validation or both. I’d typically suggest both (MachineKeyProtection.All). The returned string can then be used to pass back to the client as a cookie value or a query string value without concern for viewing or tampering. MachineKey.Decode simply reverses the process.
And here’s the 4.5 usage:
string Protect(byte[] data)
{
if (data == null || data.Length == 0) return null;
var value = MachineKey.Protect(data, "");
return Convert.ToBase64String(value);
}
byte[] Unprotect(string value)
{
if (String.IsNullOrWhiteSpace(value)) return null;
var bytes = Convert.FromBase64String(value);
return MachineKey.Unprotect(bytes, "");
}
In 4.5 the old APIs are deprecated in favor of these new Protect and Unprotect APIs. The new APIs no longer accept the level of protection (they always encrypt and MAC now [which is good]) and instead now accept a new parameter which is called purpose. This purpose parameter is intended to act somewhat as a validation mechanism. If we use a value that’s specific to the user (as we do above with the GetMachineKeyPurpose helper) we then are verifying that the value can only be unprotected by the same user. This is a nice addition in 4.5.
No - the process is different. Not to mention you'll be trying to Unprotect data that has no additional parameter specified (which won't work) and sometimes decoding data with the additional parameter specified (if you are ideally taking advantage of how protect works)
I'd refactor the code to be able to tell when the old data is present and write a new cookie/etc out using Protect();
On the first case I mentioned - you cannot use a blank parameter in Protect like
var unprotect = MachineKey.Unprotect(Encoding.UTF8.GetBytes(myOldEncryptedStuff), "");
and if you in turn have some other code there like "User 12345" to help protect that data - this is called the purpose string and helps keep that string encrypted in a more unique way tied to that user.
var unprotect = MachineKey.Unprotect(Encoding.UTF8.GetBytes(myOldEncryptedStuff), "User 12345")
If the purpose strings don't match you'll just get a generic exception here like:
System.Security.Cryptography.CryptographicException: Error occurred
during a cryptographic operation.
So it won't work for you - the two methods operate very differently. You'll need to figure out when to use one vs the other. You could always catch the exception and then try to fallback to the old tech - but test that out :)
I have an existing membership database where passwords are a hash of both username and a unique id. As I understand, ASP.NET Identity will take care of salting passwords for you.
However, I need my old hashed passwords to work until they are updated (i.e. they need to work on the first login at which point I'll update it).
The IPasswordHasher has method: VerifyHashedPassword(string hashedPassword, string providedPassword). This method doesn't allow me to pass in any sort of salt. I realize I don't need to provide a value for any new hashed passwords, but for my existing ones I need to do a legacy check.
public class CoolGuyPasswordHasher : PasswordHasher {
public IdentityContext DbContext { get; set; }
// Custom hashing used before migrating to Identity
public static string GetSHA1Hash(string password, string guid) {
string passWithSalt = String.Concat(password, guid);
return FormsAuthentication.HashPasswordForStoringInConfigFile(passWithSalt, "SHA1");
}
// Verify if the password is hashed using SHA1. If yes, rehash using ASP.NET Identity Crypto which is more secure
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) {
//I can't pass in my salt!
if (String.Equals(hashedPassword, GetSHA1Hash(providedPassword, wheresTheSalt), StringComparison.InvariantCultureIgnoreCase)) {
ReHashPassword(hashedPassword, providedPassword);
return PasswordVerificationResult.Success;
}
return base.VerifyHashedPassword(hashedPassword, providedPassword);
}
}
In the code snippet above, notice in the call to GetSHA1Hash that I don't have a second parameter to pass in.
How could I go about doing my legacy salted password check? In the new system I suppose I could make the hashed password stay as the result of the username + id. However, since the implementation of ASP.NET Identity doesn't seem to cater towards that, what would be my best option?
The way the modern salts are stored is that they're just added to the hashed password value (see this: http://brockallen.com/2012/10/19/password-management-made-easy-in-asp-net-with-the-crypto-api/). So you might have to modify (ahem hack up) the UserManager to load the salt from the separate column and pass it into the password hasher as part of the hashed password.
I found this article; it has code samples extending the UserManager and using a crafted PasswordHasher which does exactly what you want.
I need to do a GET/PUT/DELETE/POST message on httpwebrequest.
my request body contains XML.
I need to encrypt the content in body XML and decrypt back on the client/receiver side.
I see there are multiple ways to encrypt the XML.
one of it is here http://msdn.microsoft.com/en-us/library/sb7w85t6.aspx
But my concern is, receiver should be able to decrypt it. and receiver shoul dbe on different platform might not be on .NET framework.
Can any one suggest a best approach for this.
What i have tried so far:
// Create a new Rijndael key.
key = new RijndaelManaged();
// Load an XML document.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("test.xml");
// Encrypt the "creditcard" element.
Encrypt(xmlDoc, "creditcard", key);
Console.WriteLine("The element was encrypted");
Console.WriteLine(xmlDoc.InnerXml);
Decrypt(xmlDoc, key);
Console.WriteLine("The element was decrypted");
Console.WriteLine(xmlDoc.InnerXml);
This looks to be doing the job. But I have concerns about the key
key = new RijndaelManaged();
Decrypt(xmlDoc, key);
What is this Key, will client on different machine and different framework and different technology be able to decrypt this message?
Update
After my research on few encryption methods, I found X509Certificate2 is best encryption option and client can also able to decrypt it, if the same X509 cert is installed on their machine.
I could find a script to encrypt
public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, X509Certificate2 Cert)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
if (ElementToEncrypt == null)
throw new ArgumentNullException("ElementToEncrypt");
if (Cert == null)
throw new ArgumentNullException("Cert");
XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
// Throw an XmlException if the element was not found.
if (elementToEncrypt == null)
{
throw new XmlException("The specified element was not found");
}
EncryptedXml eXml = new EncryptedXml();
// Encrypt the element.
EncryptedData edElement = eXml.Encrypt(elementToEncrypt, Cert);
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
}
If found this code to decrypt
public static void Decrypt(XmlDocument Doc)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
// Create a new EncryptedXml object.
EncryptedXml exml = new EncryptedXml(Doc);
// Decrypt the XML document.
exml.DecryptDocument();
}
My question is this decrypt method is not asking for and X509 key. So how is it decrypting, doesn't it need and key to decrypt. Will this decryption works on other machines as well.
There exist several approaches to encryption.
Symmetric encryption uses the same key to encrypt and decrypt the data. AES encryption algorithm is an example of such encryption.
Asymmetric (public- and private-key based) encryption uses a pair of keys. In this mode you encrypt the data for someone using his public key. He uses his private key (which you don't have and should not have) to decrypt the data prepared for him. Asymmetric encryption is accomplished using certificate-based PKCS#7 / CMS standard or using OpenPGP.
Now about XML. You can encrypt it as if it were binary data using one of the above methods. Or you can encrypt it using XMLEnc standard.
The way to use depends on who decides or demands encryption format and method. If it's you that makes the decision, then the decision should be based on what capabilities (libraries, code) both sides can use AND how the keys are managed (PKI is a bit harder to manage than symmetric key, but in general PKI is more secure).
Just a note: our SecureBlackbox product supports both symmetric and certificate-based encryption (both binary, XMLEnc and also OpenPGP) on .NET, Java and other platforms.
Is there any good way of combining ASP.NET Windows Authentication with a custom IPrincipal/IIdentity object? I need to store the user's email address and have done so for Forms Authentication using a custom IIdentity/IPrincipal pair that I added to the Context.CurrentUser during the AuthenticateRequest event.
How would I best go by to accomplish this using WindowsAuthentication?
Maybe you could create your "ExtendedWindowsPrincipal" as a derived class based on WindowsPrincipal, and just add your extra data to the derived class?
That way, your ExtendedWindowsPrincipal would still be recognized anywhere where a WindowsPricinpal is needed.
OR: since you're talking about using Windows Authentication, you're probably in a Windows network - is there an Active Directory or a user database somewhere, where you could look up your e-mail address that you're interested in instead of storing it in the principal?
Marc
I ended up refactoring my initial solution into replacing the Principal instead of the Identity as I originally thought. Replacing the Identity proved troublesome, since i ran into security problems when creating an instance of a new extended WindowsPrincipal.
public class ExtendedWindowsPrincipal : WindowsPrincipal
{
private readonly string _email;
public ExtendedWindowsPrincipal(WindowsIdentity ntIdentity,
string email) : base(ntIdentity)
{
_email = email;
}
public string Email
{
get { return _email; }
}
}
In my Authentication module i replaced the principal on the HttpContext like this:
var currentUser = (WindowsIdentity)HttpContext.Current.User.Identity;
HttpContext.Current.User =
new ExtendedWindowsPrincipal(currentUser, userEmail);
Does anyone know of examples which show how to encrypt a dataset at the client side and send it over to a web service and have it decrypted there?
Another question:
What i need to do is a send hundreds of rows of data from a client to a web service and have the web service update the database with these records. I can't think of any other way to do this without using a dataset. Is there a better method?
Thanks in advance!
As far as the encryption is concerned, why try to reinvent the wheel? Just connect to the webservice over SSL - it'll most likely be much safer than a homegrown alternative.
I would probably create a custom struct/object and send an array of those to the webservice rather than a DataSet. It will mean (slightly) less network traffic; it will make the webservice's WSDL more descriptive; and it will make it easier for any non-Microsoft apps to talk to the webservice, if that becomes necessary in the future.
EDIT: An example...
At the server-side you can declare a custom type (eg, ExampleUser), and then setup your method to accept an array of that type instead of a DataSet:
[WebService(Namespace="http://example.yourdomain.com/ExampleWebService/")]
public class ExampleWebService : System.Web.Services.WebService
{
// this is your custom type
public class ExampleUser
{
public int UserID { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
// this is your method
// accepts an array of ExampleUser rather than a DataSet
[WebMethod]
public void UploadUsers(ExampleUser[] usersArray)
{
// do something...
}
}
In the client application you would add a reference to the webservice. This will enable you to use the ExampleUser type declared in the server-side code above.
You could then just convert your DataSet to an array of ExampleUser objects before sending it to the webservice:
// get the number of rows in the DataTable
int rowCount = yourDataSet.Tables[0].Rows.Count;
// create an array of ExampleUser with the correct capacity
ExampleWebService.ExampleUser[] usersArray =
new ExampleWebService.ExampleUser[rowCount];
// iterate through each row in the table
for (int i = 0; i < rowCount; i++)
{
DataRow dr = yourDataSet.Tables[0].Rows[i];
// create an ExampleUser object and populate it from the DataRow columns
ExampleWebService.ExampleUser eu = new ExampleWebService.ExampleUser();
eu.UserID = (int)dr["User_ID"];
eu.Name = (string)dr["Name"];
eu.DateOfBirth = (DateTime)dr["Date_Of_Birth"];
// add the ExampleUser object to the array
usersArray[i] = eu;
}
// the array is populated so let's call the webservice
ExampleWebService.UploadUsers(usersArray);
EDIT: Another example...
If you're using .NET 3.5 then you can get the client-side down to just a few lines of code by using LINQ and object initialisers to create your array:
// create and populate the array
ExampleWebService.ExampleUser[] usersArray =
yourDataSet.Tables[0].AsEnumerable().Select
(
s => new ExampleWebService.ExampleUser()
{
UserID = (int)s["User_ID"],
Name = (string)s["Name"],
DateOfBirth = (DateTime)s["Date_Of_Birth"]
}
).ToArray();
// the array is populated so let's call the webservice
ExampleWebService.UploadUsers(usersArray);
Well, there are a lot of approaches to this. Since you are sending this over the wire, you could: 1) Write the data to an XML stream (this is very much what the DataSet is meant to do) then 2) you could compress the XML (the compression ratio would be best at this stage) then 3) Encrypt using one of the .NET cryptographic schemes and finally 4) decrypt, unzip, and deserialize your XML into a DataSet object or whatever you want to do with it.
Note, you might need to Base64 the result of the encryption. Another alternative is to not encrypt and use the Web Service over SSL and just use that native encryption. Depending on the type of data a DataSet may not be the best choice in terms of performance. You could send a CSV or JSON style data block; this would potentially be smaller, especially if there is only one "DataTable" object in your DataSet. A lot of this is situation dependent as far as what the best method do use is.