1

I am using the following code to encrypt and decrypt passwords in asp.net. the encryption works perfectly but when decrypting it throws this error Invalid length for a Base-64 char array.

my password is 123 and i am using it as follow to encrypt: HttpUtility.UrlEncode(CryptorEngine.Encrypt(strpassword, true)); and to decrypt I am using it as follow :

CryptorEngine.Decrypt(HttpUtility.UrlDecode(strpassword), true));

and here is the code :

 public class CryptorEngine
    {
        /// <summary>
        /// Encrypt a string using dual encryption method. Return a encrypted cipher Text
        /// </summary>
        /// <param name="toEncrypt">string to be encrypted</param>
        /// <param name="useHashing">use hashing? send to for extra secirity</param>
        /// <returns></returns>
        public static string Encrypt(string toEncrypt, bool useHashing)
        {
            byte[] keyArray;
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

            System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
            // Get the key from config file
            string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));
            //System.Windows.Forms.MessageBox.Show(key);
            if (useHashing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                hashmd5.Clear();
            }
            else
                keyArray = UTF8Encoding.UTF8.GetBytes(key);

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            tdes.Key = keyArray;
            tdes.Mode = CipherMode.ECB;
            tdes.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = tdes.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            tdes.Clear();
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        /// <summary>
        /// DeCrypt a string using dual encryption method. Return a DeCrypted clear string
        /// </summary>
        /// <param name="cipherString">encrypted string</param>
        /// <param name="useHashing">Did you use hashing to encrypt this data? pass true is yes</param>
        /// <returns></returns>
        public static string Decrypt(string cipherString, bool useHashing)
        {
            byte[] keyArray;
            byte[] toEncryptArray = Convert.FromBase64String(cipherString);

            System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
            //Get your key from config file to open the lock!
            string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));

            if (useHashing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                hashmd5.Clear();
            }
            else
                keyArray = UTF8Encoding.UTF8.GetBytes(key);

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            tdes.Key = keyArray;
            tdes.Mode = CipherMode.ECB;
            tdes.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = tdes.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

            tdes.Clear();
            return UTF8Encoding.UTF8.GetString(resultArray);
        }
    }
Zaki
  • 5,540
  • 7
  • 54
  • 91
  • 4
    Have you considered not decrypting passwords but instead checking that the hashes match instead of decrypting a password to check that the input matches the encrypted password? If an encrypted password were to be decryptable I don't deem it to be safe, as sony found out. Just a suggestion. – LukeHennerley Oct 12 '12 at 09:35
  • I'm assumign you've debugged and checked the values going in and out of the various functions? How does the Base64 encoded string that the encrypt gives you compare to the one that you are passing into the decrypt function? The base64 string should be a multiple of four characters in length... – Chris Oct 12 '12 at 09:35
  • my encrypted password for 123 is VCZaNZapEXY%3d but cannot see it on login, when providing 123 as password it fails on this line yte[] toEncryptArray = Convert.FromBase64String(cipherString); of Decrpt method – Zaki Oct 12 '12 at 09:37
  • @Sam1: If you break on that line what is the value of cipherString? – Chris Oct 12 '12 at 09:46
  • can you tell me wat is your input for encrypt and Decrypt ? – rohan panchal Oct 12 '12 at 09:51
  • @rohanpanchal 123 to encrypt and 123 for decrypt – Zaki Oct 12 '12 at 09:53
  • @Sam1: It sounds like you are passing in the wrong thing then. cipherString should be your encrypted password (ie `VCZaNZapEXY=`). If you are passing in 123 then you are passing in the unencrypted password and asking it to decode it... – Chris Oct 12 '12 at 09:53
  • you can not use 123 for dycrypt you should use encrypt string of 123 to dycrypt. – rohan panchal Oct 12 '12 at 09:54
  • try my code.. but you will have to pass encrypted string to decode.. – rohan panchal Oct 12 '12 at 09:56

4 Answers4

2

In the URL there are come characters that conflict with the encrypted output, and at least on my code, this is what make the problem. So I use this two functions to change this characters and avoid that.

public static string ChangeSPChart(string sTheInput)
{
    StringBuilder sRetMe = new StringBuilder(sTheInput);

    sRetMe.Replace('+', '-');
    sRetMe.Replace('/', '*');
    sRetMe.Replace('=', '!');

    return sRetMe.ToString();
}

public static string FixSPChart(string sTheInput)
{
    StringBuilder sRetMe = new StringBuilder(sTheInput);

    sRetMe.Replace('-', '+');
    sRetMe.Replace('*', '/');
    sRetMe.Replace('!', '=');

    return sRetMe.ToString();
}

and the encryption/decryption code will be:

public static string Encrypt(string toEncrypt, bool useHashing)
{
    byte[] keyArray;
    byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

    System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
    // Get the key from config file
    string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));
    //System.Windows.Forms.MessageBox.Show(key);
    if (useHashing)
    {
        MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
        keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
        hashmd5.Clear();
    }
    else
        keyArray = UTF8Encoding.UTF8.GetBytes(key);

    TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
    tdes.Key = keyArray;
    tdes.Mode = CipherMode.ECB;
    tdes.Padding = PaddingMode.PKCS7;

    ICryptoTransform cTransform = tdes.CreateEncryptor();
    byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

    tdes.Clear();
    var encrypted = Convert.ToBase64String(resultArray, 0, resultArray.Length);

    // here I change it
    return  ChangeSPChart(encrypted);
}
/// <summary>
/// DeCrypt a string using dual encryption method. Return a DeCrypted clear string
/// </summary>
/// <param name="cipherString">encrypted string</param>
/// <param name="useHashing">Did you use hashing to encrypt this data? pass true is yes</param>
/// <returns></returns>
public static string Decrypt(string cipherString, bool useHashing)
{
    cipherString = FixSPChart(cipherString);

    byte[] keyArray;
    byte[] toEncryptArray = Convert.FromBase64String(cipherString);

    System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
    //Get your key from config file to open the lock!
    string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));

    if (useHashing)
    {
        MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
        keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
        hashmd5.Clear();
    }
    else
        keyArray = UTF8Encoding.UTF8.GetBytes(key);

    TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
    tdes.Key = keyArray;
    tdes.Mode = CipherMode.ECB;
    tdes.Padding = PaddingMode.PKCS7;

    ICryptoTransform cTransform = tdes.CreateDecryptor();
    byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

    tdes.Clear();
    return UTF8Encoding.UTF8.GetString(resultArray);
}
Aristos
  • 66,005
  • 16
  • 114
  • 150
  • @Sam1 Work with it a bit, debug it to locate and see if the string is break. – Aristos Oct 12 '12 at 09:45
  • Do I need to surround my password with HttpUtility.UrlDecode and HttpUtility.UrlEncode – Zaki Oct 12 '12 at 09:51
  • It occurs to me that this shouldn't be necessary. If you urlencode `+/=` then it will give you `%2b%2f%3d` which doesn't then need any magic replacing so I am not sure this is the problem in fact... – Chris Oct 12 '12 at 09:52
  • @Chris You may have right, I do not remember why I have done that way - is simple work on me, and at this moment I work on a procedure and I am too tired to remember, I just write this solution as a try to help. – Aristos Oct 12 '12 at 09:58
  • @Aristos: I personally use the replacing characters method myself, purely because its easy enough and doesn't bulk out the string at all (so the encoded string is the same length). Its more a case that you don't need two encoding methods to make it url safe. – Chris Oct 12 '12 at 10:01
  • @Chris No, I use the replacement, together with the UrlEncode/URLDecode. – Aristos Oct 12 '12 at 10:02
  • @Aristos: You certainly don't need both though there will be no harm in using both. If you replace the three characters as Aristos suggested then Urlencoding will do nothing. Similarly if you urlencode then the three characters that Aristos replaces will never be present. Whichever encoding you do second will basically do nothing. – Chris Oct 12 '12 at 10:04
  • @Sam1 This code help or not ? this code make it work or not ? – Aristos Oct 12 '12 at 10:05
  • 1
    @Sam1: If an answer doesn't actually contain the solution then you shouldn't mark it as the accepted answer. And I will admit that I say this in part because my answer does contain the suggestion that your password needs to be encrypted before beign passed into the decrypt function. – Chris Oct 12 '12 at 10:08
  • @Chris I agree with you, I will like to tell us if this code make it work or not. – Aristos Oct 12 '12 at 10:10
  • @Chris I accepted the answer as I am using it in my code with amendments that Aristos did. I have given your answer a vote up too :) – Zaki Oct 12 '12 at 10:31
  • i'm new to encryption. I want to know why do we call these functions: `hashmd5.clear()` and `tdes.clear()`? – Esha Nov 08 '12 at 07:18
1

The code you have provided works fine. I tested it with this little program:

void Main()
{
    var cryptB64 =CryptorEngine.Encrypt("123", true);
    var encoded = HttpUtility.UrlEncode(cryptB64);
    var decoded = HttpUtility.UrlDecode(encoded);
    var decrypted = CryptorEngine.Decrypt(decoded, true);

    bool matches = (decrypted=="123");
    Console.WriteLine(matches);
}

And it returned true as expected.

The problem is presumably that you are mangling your encrypted value at some point. I would guess it is being transmitted via HTTP which I would guess is where the problem happens.

Additional Note:

When calling CryptorEngine.Decrypt(HttpUtility.UrlDecode(strpassword), true)); then you need to make sure that strpassword is the encrypted form of the password, not the plaintext password you are comparing against.

Chris
  • 27,210
  • 6
  • 71
  • 92
  • When you put that on the url, and then get the result from the url, if its contains symbols like the `+`, '=` is break. Your test is work because you do not actually place it on url. – Aristos Oct 12 '12 at 09:44
  • @Aristos: Yup. Totally right. My point was that the code in the question is nothing to do with the problem and that he is looking in the wrong place. Your answer addresses the transmission problems and I'm addressing the misassumptions in the question and trying to encourage him to learn how to debug this sort of problem himself (by examining what is going in and out of methods). (and a +1 to you because I think your technique is probably the key to the solution). – Chris Oct 12 '12 at 09:49
  • @Aristos: I take that back actually about your answer addressing the problem. The urlencoding should already take care of the trasmission problems you are talking about. – Chris Oct 12 '12 at 09:56
1

hiii please use this code this works good

//Encryption method for credit card

public string EncryptTripleDES(string Plaintext, string Key)
{

    System.Security.Cryptography.TripleDESCryptoServiceProvider DES =

    new System.Security.Cryptography.TripleDESCryptoServiceProvider();

    System.Security.Cryptography.MD5CryptoServiceProvider hashMD5 =

    new System.Security.Cryptography.MD5CryptoServiceProvider();

    DES.Key = hashMD5.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(Key));

    DES.Mode = System.Security.Cryptography.CipherMode.ECB;

    System.Security.Cryptography.ICryptoTransform DESEncrypt = DES.CreateEncryptor();

    Buffer = System.Text.ASCIIEncoding.ASCII.GetBytes(Plaintext);
   string TripleDES = Convert.ToBase64String(DESEncrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));

    return TripleDES;

}
//Decryption Method 

public string DecryptTripleDES(string base64Text, string Key)
{

    System.Security.Cryptography.TripleDESCryptoServiceProvider DES =

    new System.Security.Cryptography.TripleDESCryptoServiceProvider();

    System.Security.Cryptography.MD5CryptoServiceProvider hashMD5 =

    new System.Security.Cryptography.MD5CryptoServiceProvider();
    DES.Key = hashMD5.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(Key));
    DES.Mode = System.Security.Cryptography.CipherMode.ECB;
    System.Security.Cryptography.ICryptoTransform DESDecrypt = DES.CreateDecryptor();
    Buffer = Convert.FromBase64String(base64Text);

    string DecTripleDES = System.Text.ASCIIEncoding.ASCII.GetString(DESDecrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));
   return DecTripleDES;

}
rohan panchal
  • 881
  • 3
  • 15
  • 32
0

A little late here but the problem could be something else (it was for me).

Request.Querystring does some decoding already. In my case I was essentially decoding twice. Adding a second "HttpUtility.UrlEncode" did the trick (at least after 20 different tests).

I haven't exactly found firm documentation from Microsoft on this behavior, and the old books are stored away (I try not to leave my chair), but these links were helpful:

does Request.Querystring automatically url decode a string?

http://forums.asp.net/t/1354726.aspx?Request+Querystring+without+decoding+possible+

Community
  • 1
  • 1
MichaelBolton
  • 78
  • 2
  • 7