5

I have model like below:

public class User 
{
    private string password;
    public string Password
    {
        get { return Decrypt(password); }
        set { password = Encrypt(value); }
    }
}

I want from user code to use just insert and select password easily and see in clear text and want the logic layer to handle the encryption and decryption.

But when selecting, EF will set the value again so the encrypted password gotten from database will be encrypted again for me to get from the client code which is a problem. I also cannot distinguish between an Insert and Select to set conditionals.

I can very well do this:

//Insert
user.Password = Encrypt("123"); //Encrypt or Hash does not matter
context.Users.Add(user);

//Select
var hashedpassword = context.Users.Find(1).Select(u => u.Password).Single();
var unhashed = Decrypt(hashedpassword);

Instead I would like it not to be apparent from client code:

//Insert
user.Password = "123"; //Inserts encrypted password in database
context.Users.Add(user);

//Select
var cleartextpassword = context.Users.Find(1).Select(u => u.Password).Single();

Hope I was able to explain the problem and someone can point me to the right direction. If it is even possible or not.

lbrahim
  • 3,710
  • 12
  • 57
  • 95
  • 6
    Any particular reason you don't use hashing instead of encryption? [Why passwords should be hashed](http://security.blogoverflow.com/2011/11/why-passwords-should-be-hashed/). [Difference between Hashing a Password and Encrypting it](http://stackoverflow.com/q/326699/706456) – oleksii Apr 08 '14 at 11:48
  • @oleksii There is hashing and salting present but my concern is regarding how to hash/unhash or encrypt/decrypt without making it apparent from the client code. Hope I was able to explain. – lbrahim Apr 08 '14 at 11:51
  • 3
    I think oleksii`s point is that you should never under any circumstances be able to decrypt a password. Once a user has selected and stored a password, it should for ever be unreadable. You should only be able to compare a hash to it, to check a provided password - not read the stored password (and if a user needs to change a password, generate a new random one for him). – Kjartan Apr 08 '14 at 11:55
  • There's no such thing as unhash. Hash is a lossy one-way conversion. – GeekyMonkey Feb 23 '18 at 13:07

3 Answers3

10

The better solution is indeed to use a Hash.

But a general pattern for injecting some logic:

public class User 
{   
    // the mapped-to-column property 
    protected virtual string PasswordStored
    {
        get ;
        set ;
    }


    [NotMapped]
    public string Password
    {
        get { return Decrypt(PasswordStored); }
        set { PasswordStored = Encrypt(value); }
    }


}

Instead of [NotMapped] you can also use the fluent API to keep EF from storing it directly.

H H
  • 263,252
  • 30
  • 330
  • 514
  • 2
    Unmapped is not a real attribute, but [NotMapped](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.schema.notmappedattribute(v=vs.110).aspx) is. – Richard Apr 08 '14 at 12:20
  • @HenkHolterman Thanks. This route seems very nice but my DataContext is in another layer (Project) so it won't see the `PasswordStored` and ask for migration that will remove the column. Is there a workaround? – lbrahim Apr 08 '14 at 13:57
  • Then make it `public` instead of `protected`, although I can't see the exact issue here. – H H Apr 08 '14 at 14:01
  • @HenkHolterman I can very well do that. But then two *Password* related properties would be exposed to the client code. For now making `private set;` seems reasonable but maybe if there was an elegant way. – lbrahim Apr 08 '14 at 17:43
  • 1
    You can always use a wrapper class, or apply an architectural layer with DAOs (Data Access Objects). Or maybe you can make the problem go away in your DTOs. – H H Apr 08 '14 at 20:36
2

Read and write your entities only from a repository, i.e., through a data access class that you write:

var myEntities = userRepository.GetUserById(id);

then your GetUserById() method can perform the decryption in-place. Then when you do

userRepository.UpdateUser(myUser);

you can encrypt the field again in the UpdateUser() method.

Roy Dictus
  • 32,551
  • 8
  • 60
  • 76
1

Please find following method which will get you a password hash:

    public static string GetPasswordHash(this string password)
    {
        using (var sha1 = new SHA1Managed())
        {
            var hash = Encoding.UTF8.GetBytes(password);
            var generatedHash = sha1.ComputeHash(hash);
            var generatedHashString = Convert.ToBase64String(generatedHash);
            return generatedHashString;
        }
    }
Palak.Maheria
  • 1,497
  • 2
  • 15
  • 32