MRK MRK - 29 days ago 12
C# Question

How to encrypt strings that may have non-base 64 characters

I am not familiar with encryption/decryption algorithms, Therefor I search the net and found this class:

Encrypting & Decrypting a String in C#

the code is :
(I add a Try/Catch in

Decrypt
method in case the password was wrong it will
return "";
)

using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Linq;

namespace EncryptStringSample
{
public static class StringCipher
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 256;

// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;

public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}

public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
try
{
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
catch (Exception)
{
return "";
}
}

private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
}


I used that class at my application like this:

string plaintext = "InsertedPasswordByUserToEncrypt";
string password = plaintext; // use its own password as encryption key
string encryptedstring = StringCipher.Encrypt(plaintext, password);


and I like that class be cause it give me different encryption results if I repeat last line with same data.

But now, I found out if a string have any characters other than base64 characters it would rise this exception:
"The input is not a valid Base-64 string as it contains a non-base 64 character" I search the net and I found many answers to this problem. Like these:

The input is not a valid Base-64 string as it contains a non-base 64 character

How can I solver an "base64 invalid characters" error?

In all those questions, answers was the same:


Remove non-base64 characters from your string!!!


But what if I or my application users wants to insert a string like this: "A@S#D$?" or "CanYouGu3$$Me?" or .... to be encrypted?

My Question:

A1. Is there any way to solve the above Class problem (that I mention at above) without replacing or removing any characters that users may insert to be encrypted?

A2. If there are no fix, So what are the other best ways? what method I can use that It can encrypt/decrypt any string with any characters in it.

PS: I even found this Class to replace my old class: http://codereview.stackexchange.com/questions/14892/simplified-secure-encryption-of-a-string but it give me the same problem cause it also use base64!

Thanks for your time

Answer

Convert your input string to a byte array and then convert that to base64. Now your input string is valid base64 and can still be encrypted.

byte[] data = Encoding.UTF8.GetBytes(inputString);
string b64 = Convert.ToBase64String(data);

You might want to invest some time into understanding why base64 is required. Encryption algorithms operate over byte arrays, raw data, not strings.

Comments