-1

I'm a fairly new developer so bear with me, this has given me a headache.

I have been trying to store the passwords for a Winforms application I'm developing in an encrypted format in the database. There is a login and registration interface when the application is first opened.

I have managed to encrypt the password that is given during registration, and it is not in plaintext in the database, and I used a class Cryptography for this. However, when I try to decrypt the password to grant the user access to the application I get the exception unhandled error:

The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.

Is there a way to solve this? I cannot figure it out.

The code that does the encryption:

public static string Encrypt(string encryptString)
{
        string EncryptionKey = "djknh46hdkkjsdvvjjsijeykskerfubb1906234575";      
        byte[] clearBytes = Encoding.Unicode.GetBytes(encryptString);

        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
            0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);

            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), 
                CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }

                encryptString = Convert.ToBase64String(ms.ToArray());
            }
        }

        return encryptString;
}

The code that is supposed to do the decryption:

public static string Decrypt(string cipherText)
{
        string EncryptionKey = "djknh46hdkkjsdvvjjsijeykskerfubb1906234575";      
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);

        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {
            0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);

            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), 
                CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }

                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }

        return cipherText;
}

The code that does is supposed to verify the password from the database:

string Password = "";
bool IsExist = false;           

SqlCommand command = new SqlCommand("select * from LibraryUser where UserName='" + 
        txtUsernameLogin.Text + "'", connection1);

SqlDataReader dataReader = command.ExecuteReader();

if (dataReader.Read())
{
    Password = dataReader.GetString(4);   
    IsExist = true;
}

connection1.Close();

if (IsExist)   
{
    if (Cryptography.Decrypt(Password).Equals(txtPasswordLogin.Text))
    {
        this.Hide();
        new LibraryForm().Show();                 
    }
    else
    {
        MessageBox.Show("The password you have entered is incorrect, please try again.", 
                        "Incorrect Password", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}
else  
{
    MessageBox.Show("Please enter the valid credentials.", "Error", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
}

I appreciate any help anyone has to offer, thanks in advance.

marc_s
  • 675,133
  • 158
  • 1,253
  • 1,388
  • 10
    you shouldn't need to decrypt passwords, because they should only be stored as salted hashes. – Mitch Wheat May 29 '21 at 07:14
  • 4
    This is not best practice. Always use one way encryption as described above then encrypt the user input in the same way for a match. – Peter Smith May 29 '21 at 07:20
  • Forgive me, what do you mean? Does this mean I should delete the code that decrypts? What are salted hashes? How do I use them? – Chibuye Mwenya May 29 '21 at 07:21
  • 1
    Yes, never decrypt. Google will help with your second question. There must be many good examples around of password management. – Peter Smith May 29 '21 at 07:23
  • 6
    [SQL Injection alert](http://msdn.microsoft.com/en-us/library/ms161953%28v=sql.105%29.aspx) - you should **not** concatenate together your SQL statements - use **parametrized queries** instead to avoid SQL injection - check out [Little Bobby Tables](http://bobby-tables.com/) – marc_s May 29 '21 at 07:29
  • Thanks a lot Peter, I will look into salted hashes and replace the code! – Chibuye Mwenya May 29 '21 at 07:36
  • Will look into parametrized queries! Thanks! I have lots of queries in the application so I will definitely be looking into fixing them. – Chibuye Mwenya May 29 '21 at 07:37
  • Here's the latest on hashed passwords. https://stackoverflow.com/questions/4181198/how-to-hash-a-password/10402129#10402129 **Pro tip** [Read this](https://www.php.net/manual/en/faq.passwords.php). Even though it's written for php, it contains a good explanation of password security. – O. Jones May 29 '21 at 10:15
  • Hopefully you haven't posted your actual encryption keys here. Even if you're _sure_ they're not real, you should be putting nonsense in place of any secrets posted online. :) – Craig Eddy May 29 '21 at 13:27

2 Answers2

0

The above comments on best practice (one-way encryption, salted hashes, SQL injection) are valid and I fully endorse them.

However, the actual question is: why is the Base64-decoding failing?

The answer is that the value you are reading from the database is not a valid Base64-encoding. This line is suspicious:

cipherText = cipherText.Replace(" ", "+");

A space is not a valid character in Base64-encoding. What data type are you using to store the string? Is it being returned as a fixed-length string with space padding? If so, you may need to call .Trim() on cipherText instead of the above line.

If this doesn't work, please post an example of cipherText from your Decrypt() method which is causing this exception so that we can try to answer your question.

Again, this is not best practice. Please note the comments above by Mitch Wheat, Peter and marc_s.

Mitch Wheat
  • 280,588
  • 41
  • 444
  • 526
Neil T
  • 1,039
  • 7
  • 15
0

For passwords, you don't decrypt them, you compare only the encrypted ones.

New user:

var hash = Encrypt(password);
DbInsert(userId, hash);

Authenticating a user:

var hash = Encrypt(password);
var dbHash = GetPasswordFromDb(userId);
if (dbHash == hash) {
    // authenticated
} else {
    // wrong password
}

You compare the hashed values because passwords should be impossible to decrypt, such that the only way to discover a password is guessing.

Usually I use BCrypt.Net-Next NuGet. Its simple to use and secure enough.

marcos
  • 121
  • 5