Vitor, I will completely escape your problem, but I want to point out a basic error in your solution:
You are using MD5 to store a Password.
Understand, No hash algorithm, no matter how large the blocks, however small the chance of collision, is not secure enough to protect a password.
Below is an implementation I used at some time (Pbkdf2 + SHA512):
Database Template
CREATE TABLE [dbo].[Usuario](
[UsuarioGUID] [uniqueidentifier] NOT NULL,
[UserName] [varchar](50) NOT NULL,
[Password] [binary](64) NOT NULL,
[Salt] [binary](16) NOT NULL,
[Email] [varchar](50) NOT NULL,
[DataCriacao] [datetime2](7) NOT NULL CONSTRAINT [DF_Pessoa_DataCriacao] DEFAULT (sysdatetime()),
CONSTRAINT [PK_Pessoa] PRIMARY KEY CLUSTERED
(
[UsuarioGUID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Note that in the example above I store the password as a 64-byte array of bytes, and Salt in a 16-byte array of bytes.
The function of Salt here is to make the password unique, even if two users choose to use the same password.
Entity - Entity Framework
[Table("Usuario")]
public partial class Usuario
{
[Key]
public Guid UsuarioGUID { get; set; }
[Required]
[StringLength(50)]
public string UserName { get; set; }
[Required]
[MaxLength(64)]
public byte[] Password { get; set; }
[Required]
[MaxLength(16)]
public byte[] Salt { get; set; }
[Required]
[StringLength(50)]
public string Email { get; set; }
[Column(TypeName = "datetime2")]
public DateTime DataCriacao { get; set; }
}
DTO
[DataContract]
public partial class Usuario
{
[DataMember(EmitDefaultValue = false)]
public Guid UsuarioGUID { get; set; }
[DataMember(EmitDefaultValue = false)]
public string UserName { get; set; }
[DataMember(EmitDefaultValue = false)]
public string Password { get; set; }
[DataMember(EmitDefaultValue = false)]
public string Email { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime DataCriacao { get; set; }
}
Note that in my entity I have a property byte[] Password
and another call byte[] Salt
, whereas in my DTO I only have the string Password
field.
UserController
private byte[] CreateSalt()
{
var salt = new byte[16];
using (var provider = new System.Security.Cryptography.RNGCryptoServiceProvider())
{
provider.GetBytes(salt);
}
return salt;
}
[HttpPut]
public async Task<int> Inserir(DTO.Pessoa dto)
{
var entity = default(Models.Pessoa);
try
{
var password = new System.Security.Cryptography.HMACSHA512() {
Key = Encoding.UTF8.GetBytes(dto.Password)
};
entity = MapperConfig.Mapper.Map<Models.Pessoa>(dto);
entity.Salt = this.CreateSalt();
entity.Password = Pbkdf2.ComputeDerivedKey(password, entity.Salt, UInt16.MaxValue, password.HashSize / 8);
}
catch (Exception err)
{
return -1;
}
}
In the example above I used the following NuGet package: CryptSharp
When comparing passwords, you should retrieve Salt
and call the Pbkdf2.ComputeDerivedKey
method again by passing the same parameters.