Loop in Constructor c#

Asked

Viewed 97 times

2

I’m trying to set mine Senha using the md5, but there is a loop at settar.

public class Usuario
{
        public int ID { get; set; }
        [Required]
        public string Nome { get; set; }
        [Required]
    public string Senha {
        get { return Senha; }
        set { Console.WriteLine("valor"+value );
            this.Senha = CalculateMD5Hash(value); }
    }

    public static String CalculateMD5Hash(String input) {

            // step 1, calculate MD5 hash from input
            MD5 md5 = MD5.Create();
            byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);
            // step 2, convert byte array to hex string
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++) {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
    }
}

The class is calling the function CalculateMD5Hash several times as shown below. E.g.: value = 123

value1 = 202CB962AC59075B964B07152D234B70 (hash do value)

value2 = D9840773233FA6B19FDE8CAF765402F5 (hash do value1)

How do I stop the Loop and call the function of calculating the MD5 only once?

  • Why not create the hash inside the loop and use .Append() out of the loop?

2 answers

1

The properties were not set correctly. For the set and the get were calling themselves in order to cause overflow.

To solve it is necessary to create a field backing field to store the value of the property:

private string _senha;
public string Senha 
{
    get { return _senha; }
    set 
    { 
        Console.WriteLine("valor"+value );
        _senha = CalculateMD5Hash(value);
    }
}
  • 1

    Take and remove the method CalculateMD5Hash class Usuario. It is quite clear that it is not responsible for User in encrypting strings.

1

Vitor, I will completely evade 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 are, no matter how small the chance of collision, is safe enough to protect a password.

Below is an implementation I used for some time (Pbkdf2 + SHA512):

Model of the Database

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 above example I store the password as an array of 64 bytes, and Salt in an array of 16 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, while in my DTO I have only the field string Password.

User controller

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 above example I used the following Nuget package: Cryptsharp

When comparing passwords, you should recover the Salt and call the method Pbkdf2.ComputeDerivedKey again passing the same parameters.

  • I had actually seen about md5, but as the application is for learning I found it interesting to just encrypt basic anyway. The approach you presented is very interesting and I will apply it to you. Vlw.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.