Crypto Padding Invalid and cannot be removed - c#

i'm call the StringCipher Class from a userform encrpyting the value and using it as a string parameter into a method in another class. this class then decrypts the value and uses it. (userform gets the username and passsword encrypts and sends to another object to handle the request. that other object decrypts the value and uses it).
here's the problem when i call the encrypt and decrypt in the same class i have no issues. but if i encrypt in one class and decrypt in another i get a "Padding Invalid and cannot be removed" error.
thoughts?
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
/// <summary>
/// Adding a namespace comment to change something
/// </summary>
namespace SYF.AMSDev.Security.Encryption
{
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 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 prepended 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 salt bytes by extracting the first 32 bytes from the supplied cipherText btyes.
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();
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);
}
}
}
}
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
// 32 bytes will give us 256 bits.
var randomBytes = new byte[32];
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
/// <summary>
/// Converts a plain text string into pre-formated hash string. This is currently intended for use with FD Connections.
/// </summary>
/// <param name="itemToHash">Plain text string that needs to be converted to a hash.</param>
/// <returns>The resulting hash string representation of the original plain text string provided.</returns>
public static string ConvertToHash(string itemToHash)
{
SHA1CryptoServiceProvider sha1Object = new SHA1CryptoServiceProvider();
byte[] bytesToHash = Encoding.ASCII.GetBytes(itemToHash);
bytesToHash = sha1Object.ComputeHash(bytesToHash);
string strResult = null;
foreach (byte byteToHash in bytesToHash)
{
strResult += byteToHash.ToString("x");
}
strResult = strResult.Substring(0, 15);
return string.Format("SHA10437{0}", strResult);
}
}
}

Related

AES Encryption Invalid Padding

I am trying to convert a previous working script that handled AES encryption using a 256byte key. I am trying to have the script use 128bye, but i am getting a padding invalid and cannot be removed error at line 110. Any help or tips would be helpful.
I have tried playing around with the keySize, or the blocksize but have yet to get it to work.
Here is the code.
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Response.Write(timeNow.ToString());
string str = #"{""dataset"":{""schema"" :{""manifest"":{""datasetname"":""/Apps/SM/Custom/Shoppers/ShopperMailbox_GetAssignedInstances"",""datafieldsmode"":""D"",""dbsqltype"":""P"",""schemaformat"":""JSON"",""dataformat"":""json"",""encoding"":""utf-8"",""security"":{},""cacheable"":{},""hasoutputparameters"":false,""meta"":{}},""parameters"":[{""name"":""SecurityObjectUserID"",""heading"":""SecurityObjectUserID"",""headingglobalizationenabled"":true,""sqlparamname"":""#SecurityObjectUserID"",""datatype"":""bigint"",""datatypecharlength"":null,""datatypenumericprecision"":19,""datatypenumericprecisionradix"":10,""datatypenumericscale"":0,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":1,""direction"":null,""lookup"":null},{""name"":""ShopperPhoneNumber"",""heading"":""ShopperPhoneNumber"",""headingglobalizationenabled"":true,""sqlparamname"":""#ShopperPhoneNumber"",""datatype"":""varchar"",""datatypecharlength"":63,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":2,""direction"":null,""lookup"":null},{""name"":""ShopperEmailAddress"",""heading"":""ShopperEmailAddress"",""headingglobalizationenabled"":true,""sqlparamname"":""#ShopperEmailAddress"",""datatype"":""nvarchar"",""datatypecharlength"":222,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":3,""direction"":null,""lookup"":null},{""name"":""MiscSettings"",""heading"":""MiscSettings"",""headingglobalizationenabled"":true,""sqlparamname"":""#MiscSettings"",""datatype"":""nvarchar"",""datatypecharlength"":-1,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":4,""direction"":null,""lookup"":null}],""columns"":[{""name"":""Shopper Email Address"",""heading"":""Shopper Email Address"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":222,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":0,""direction"":null,""lookup"":null},{""name"":""Instance ID"",""heading"":""Instance ID"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""bigint"",""datatypecharlength"":null,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":1,""direction"":null,""lookup"":null},{""name"":""Location Street Address"",""heading"":""Location Street Address"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":150,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":2,""direction"":null,""lookup"":null},{""name"":""Location City"",""heading"":""Location City"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":50,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":3,""direction"":null,""lookup"":null},{""name"":""Location State"",""heading"":""Location State"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":8,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":4,""direction"":null,""lookup"":null},{""name"":""Location Postal Code"",""heading"":""Location Postal Code"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":12,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":5,""direction"":null,""lookup"":null},{""name"":""Client Name"",""heading"":""Client Name"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":200,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":6,""direction"":null,""lookup"":null},{""name"":""Survey Title"",""heading"":""Survey Title"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":500,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":7,""direction"":null,""lookup"":null},{""name"":""Planned Date"",""heading"":""Planned Date"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""date"",""datatypecharlength"":null,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":8,""direction"":null,""lookup"":null},{""name"":""Due Date"",""heading"":""Due Date"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""datetime"",""datatypecharlength"":null,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":9,""direction"":null,""lookup"":null},{""name"":""Campaign"",""heading"":""Campaign"",""headingglobalizationenabled"":true,""sqlparamname"":null,""datatype"":""nvarchar"",""datatypecharlength"":50,""datatypenumericprecision"":null,""datatypenumericprecisionradix"":null,""datatypenumericscale"":null,""datatypedatetimeprecision"":null,""allownulls"":true,""defaultvalue"":null,""controlvisual"":{},""validator"":{},""ordinalposition"":10,""direction"":null,""lookup"":null}]},""data"":[{""Shopper Email Address"":""frecklegami8#gmail.com"",""Instance ID"":416722,""Location Street Address"":""2700 Potomac Mills Circle"",""Location City"":""Woodbridge"",""Location State"":""VA"",""Location Postal Code"":""22192"",""Client Name"":""Christmas Tree Shops "",""Survey Title"":""CTS In Store Mystery Shop"",""Planned Date"":null,""Due Date"":""2018-01-17 00:59:00"",""Campaign"":""2018-01""},{""Shopper Email Address"":""frecklegami8#gmail.com"",""Instance ID"":418529,""Location Street Address"":""4830 Crossings Court"",""Location City"":""Massaponax"",""Location State"":""VA"",""Location Postal Code"":""22407"",""Client Name"":""Steak 'N Shake - Virginia"",""Survey Title"":""Steak 'N Shake Dine In Evaluation v2"",""Planned Date"":null,""Due Date"":""2018-01-20 23:59:00"",""Campaign"":""2018-01""}]}}";
string encryptedString = AESEncryption.Encrypt(str, "ds!5da%-0sadg$!$2fDUC-51AHB)!"); //, "ASDdasdsa213DSA#4!##!##dsadsa", "SHA512", 10000, IV);
Response.Write(encryptedString);
//Response.Write("<br/>");
//Response.Write("<br/>");
//Response.Write("<br/>");
//Response.Write("<br/>");
encryptedString = #"dFmyMsfNxzWqVqfYG14ueWRrxA2EezLcatVo9127uRpFZEnYepm9yXv2SQpD+UtX5Fuag9mpJrQL1I0QX7KSGp87TrEH0y6PXlLsjbXSO8hIv0XybmaxAS0/xKmkCdxz";
Response.Write(AESEncryption.Decrypt(encryptedString, "ds!5da%-0sadg$!$2fDUC-51AHB)!")); //, "ASDdasdsa213DSA#4!##!##dsadsa", "SHA512", 10000, IV));
}
}
public static class AESEncryption
{
#region Static Functions
// 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 = 128;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 100000;
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 = 128;
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]
if (String.IsNullOrEmpty(cipherText))
{
return "Empty string";
}
if (!IsBase64String(cipherText))
{
return "Not a valid Base64 string";
}
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 16 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 16 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) * 5)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 128;
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);
}
}
}
}
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
private static bool IsBase64String(string value)
{
if (string.IsNullOrEmpty(value))
{
return false;
}
// if(value == null || value.Length == 0 || value.Length % 4 != 0 || value.Contains(" ") || value.Contains("\t") || value.Contains("\r") || value.Contains("\n"))
// {
// return false;
// }
try
{
Convert.FromBase64String(value);
return true;
//if (value.EndsWith("="))
//{
// value = value.Trim();
// int mod4 = value.Length % 4;
// if (mod4 != 0)
// {
// return false;
// }
// return true;
//}
//else
//{
// return false;
//}
}
catch (FormatException)
{
return false;
}
}
public static string Left(this string value, int maxLength)
{
if (string.IsNullOrEmpty(value)) return value;
maxLength = Math.Abs(maxLength);
return (value.Length <= maxLength
? value
: value.Substring(0, maxLength)
);
}
#endregion
}
You have the bug in this line:
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 5)).ToArray();
Why do you take only cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 5) bytes? As I can see in your encryption alogithm all bytes remaining after removing of salt and IV should be taken.
I.e. replace the line with
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).ToArray();
and decryption will work fine.
UPD: Remove your hardcoded encodedString and try decrypting previously generated string. It seems that your hardcoded string was encrypted using different alogrithm.

Wrong algorithm: AES or Rijndael required on c#

I had andriod code and I tried to convert it to c#. It's a simple Encryption class. But when I try to decrypt data with it I catch: Wrong algorithm: AES or Rijndael required.
Here is my converted code:
public static string decrypt(string data)
{
byte[] dataBytes = Convert.FromBase64String(data);
SecretKey secretKey = getSecretKey(hashTheKey("ABCD"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, secretKey, new IvParameterSpec(new byte[16]),
SecureRandom.getInstance("SHA1PRNG"));
var x = cipher.doFinal(dataBytes);
return System.Text.Encoding.UTF8.GetString(x);
}
public static SecretKey getSecretKey(char[] key)
{
var secretKeyType = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
var secretkey = secretKeyType.generateSecret(new PBEKeySpec(key,
System.Text.Encoding.UTF8
.GetBytes("ABCD"),
100, 128)).getEncoded();
return new SecretKeySpec(secretkey, "AES/CBC/PKCS5Padding");
}
public static char[] hashTheKey(string key)
{
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(System.Text.Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(messageDigest.digest()).ToCharArray();
}
Here is my original android code:
private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(key.getBytes());
return Base64.encodeToString(messageDigest.digest(),
Base64.NO_PADDING).toCharArray();
}
private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
return new SecretKeySpec(
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
.generateSecret(new PBEKeySpec(key,
"ABCD".getBytes("UTF8"),
100, 128)).getEncoded(), "AES");
}
public String decrypt(String data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeySpecException {
byte[] dataBytes = Base64.decode(data, Base64.DEFAULT);
SecretKey secretKey = getSecretKey(hashTheKey("ABCD"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, secretKey, new IvParameterSpec(new byte[16]),
SecureRandom.getInstance("SHA1PRNG"));
return new String(cipher.doFinal(dataBytes));
}
c# and java are using the same well-estabilished cryptography algorithms, but differs in approach how to invoke them. It is still possible to convert the code though.
One key point is difference in base64 encoding - C# always use padding.
Converted code goes like:
const int KeySize = 128;
static string HashTheKey(string key) {
String hashKey;
using (var sha = new SHA1Managed()) {
hashKey = Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(key)));
}
// beware - you're on C# now so remove the padding and add the newline to match java
return hashKey.Replace("=", "") + "\n";
}
static byte[] GetSecretKey(string password) {
var salt = Encoding.UTF8.GetBytes("JVAaVhAiddKAaghraikhmaini");
using (var pass = new Rfc2898DeriveBytes(password, salt, 65536)) {
return pass.GetBytes(KeySize / 8);
}
}
static void Main(string[] args) {
string encrypted = "vtlkQHTz7/oz2weuAAkLz2Q5c2yj2LGukF7SHJjT+TA8oRLixTQSXQ7dG1O736hyT1HJxcz0P4DzzVaO5chWKKSJQ2uPEpDQJu/fZGguqDw=";
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
using (var aes = new AesManaged()) {
aes.KeySize = KeySize;
aes.Padding = PaddingMode.PKCS7;
aes.Key = GetSecretKey(HashTheKey("Android"));
// you're using the same init vector in your android code
aes.IV = new byte[16];
using (var decryptor = aes.CreateDecryptor()) {
// dumps {"barcode":"12345678","token":"cad603fc-1e53-4a95-9150-f1694baa07f9"}
Console.Out.WriteLine(Encoding.UTF8.GetString(decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length)));
}
}
}
C# does not handle the encryption algorithms as Android or java do you have to use either AES or Rijndael algorithm as you can see the error to covert to the simple text into Encrypted Base64 and vice versa you can use the following class in C#
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();
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);
}
}
}
}
}
}
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;
}
}

RijndaelManaged Specified key is not a valid size for this algorithm [duplicate]

I have with this code:
RijndaelManaged rijndaelCipher = new RijndaelManaged();
// Set key and IV
rijndaelCipher.Key = Convert.FromBase64String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678912");
rijndaelCipher.IV = Convert.FromBase64String("1234567890123456789012345678901234567890123456789012345678901234");
I get this exception thrown:
Specified key is not a valid size for this algorithm.
Specified initialization vector (IV) does not match the block size for this algorithm.
What's wrong with this strings ? Can I count at some examples strings from you?
The string "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678912" when base64-decoded yields 48 bytes (384 bits). RijndaelManaged supports 128, 192 and 256 bit keys.
A valid 128-bit key is new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } or if you need to get it from base64 : Convert.FromBase64String("AAECAwQFBgcICQoLDA0ODw==").
The default blocksize is 128 bits, so the same byte-array will work as the IV.
Use the random number generator class (RNGCryptoServiceProvider) to fill a specified buffer with random bytes as follows:
var numberOfBits = 256; // or 192 or 128, however using a larger bit size renders the encrypted data harder to decipher
var ivBytes = new byte[numberOfBits / 8]; // 8 bits per byte
new RNGCryptoServiceProvider().GetBytes(ivBytes);
var rijndaelManagedCipher = new RijndaelManaged();
//Don't forget to set the explicitly set the block size for the IV if you're not using the default of 128
rijndaelManagedCipher.BlockSize = 256;
rijndaelManagedCipher.IV = ivBytes;
Note the same process could be used to derive a key. Hope this helps.
The RijndaelManaged algorithm supports key lengths of 128, 192, or 256 bits. Is your key one of these sizes?
here is the class i created
public class ByteCipher
{
// 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 int _Keysize = (int)GlobalConfiguration.DataEncode_Key_Size;
private byte[] saltStringBytes;
private byte[] ivStringBytes;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
private string _passPhrase = GlobalConfiguration.DataEncode_Key;
private const string salt128 = "kljsdkkdlo4454GG";
private const string salt256 = "kljsdkkdlo4454GG00155sajuklmbkdl";
public ByteCipher(string passPhrase = null, DataCipherKeySize keySize = DataCipherKeySize.Key_128)
{
if (!string.IsNullOrEmpty(passPhrase?.Trim()))
_passPhrase = passPhrase;
_Keysize = keySize == DataCipherKeySize.Key_256 ? 256 : 128;
saltStringBytes = _Keysize == 256 ? Encoding.UTF8.GetBytes(salt256) : Encoding.UTF8.GetBytes(salt128);
ivStringBytes = _Keysize == 256 ? Encoding.UTF8.GetBytes("SSljsdkkdlo4454Maakikjhsd55GaRTP") : Encoding.UTF8.GetBytes("SSljsdkkdlo4454M");
}
public byte[] Encrypt(byte[] plainTextBytes)
{
if (plainTextBytes.Length <= 0)
return plainTextBytes;
using (var password = new Rfc2898DeriveBytes(_passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(_Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = _Keysize;
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 cipherTextBytes;
}
}
}
}
}
}
public byte[] Decrypt(byte[] cipherTextBytesWithSaltAndIv)
{
if (cipherTextBytesWithSaltAndIv.Length <= 0)
return cipherTextBytesWithSaltAndIv;
var v = Encoding.UTF8.GetString(cipherTextBytesWithSaltAndIv.Take(_Keysize / 8).ToArray());
if (v != salt256 && v != salt128)
return cipherTextBytesWithSaltAndIv;
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((_Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((_Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(_passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(_Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.BlockSize = _Keysize;
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 plainTextBytes;
}
}
}
}
}
}
}
I don't know the length of rijndaelCipher.Key
if it is 24, then rijndaelCipher.Key = s.SubString(0, 24);
So easy.

Encrypt string with Bouncy Castle AES/CBC/PKCS7

I have been looking everywhere for some sample code on how to encrypt a simple string with the encryption in the title using the Bouncy Castle Framework.
This code will run on a Windows Universal project.
My previous attempts to encrypt using the build in API's failed to decrypt on the server.
I tried this: which gives me a string like:
4pQUfomwVVsl68oQqWoWYNRmRM+Cp+vNFXBNdkN6dZPQ34VZ35vsKn9Q7QGTDVOj+w5mqVYHnGuAOFOgdgl8kA==
s = String.Format("{0}_{1}", s, DateTime.Now.ToString("ddMMyyyyHmmss"));
SymmetricKeyAlgorithmProvider algorithm = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
IBuffer keymaterial = CryptographicBuffer.ConvertStringToBinary("[Key]", BinaryStringEncoding.Utf8);
CryptographicKey KEY = algorithm.CreateSymmetricKey(keymaterial);
IBuffer IV = CryptographicBuffer.ConvertStringToBinary("[IV]", BinaryStringEncoding.Utf8);
IBuffer data = CryptographicBuffer.ConvertStringToBinary(s, BinaryStringEncoding.Utf8);
IBuffer output = CryptographicEngine.Encrypt(KEY, data, IV);
return CryptographicBuffer.EncodeToBase64String(output);
The server does encryption/decryption with
public static string Encrypt(string text, byte[] key, byte[] iv, int keysize = 128, int blocksize = 128, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = blocksize;
aes.KeySize = keysize;
aes.Mode = cipher;
aes.Padding = padding;
byte[] src = Encoding.UTF8.GetBytes(text);
using (ICryptoTransform encrypt = aes.CreateEncryptor(key, iv))
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
encrypt.Dispose();
return Convert.ToBase64String(dest);
}
}
public static string Decrypt(string text, byte[] key, byte[] iv, int keysize = 128, int blocksize = 128, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = blocksize;
aes.KeySize = keysize;
aes.Mode = cipher;
aes.Padding = padding;
byte[] src = Convert.FromBase64String(text);
using (ICryptoTransform decrypt = aes.CreateDecryptor(key, iv))
{
byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length);
decrypt.Dispose();
return Encoding.UTF8.GetString(dest); //Padding is invalid and cannot be removed.
}
}
But it fails becasue:
Padding is invalid and cannot be removed.
That's why I want to try Bouncy Castle, but I can't find any suitable example code.
EDIT
I tried using Bouncy Castle with the code provided in the answer.
Now I'm getting the error:
initialisation vector must be the same length as block size
byte[] inputBytes = Encoding.UTF8.GetBytes(s);
byte[] IV = Encoding.UTF8.GetBytes("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
byte[] key = Encoding.UTF8.GetBytes("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
//Set up
AesEngine engine = new AesEngine();
CbcBlockCipher blockCipher = new CbcBlockCipher(engine);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
Debug.WriteLine(IV.Length); //32
Debug.WriteLine(cipher.GetBlockSize()); //16
KeyParameter keyParam = new KeyParameter(key);
ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParam, IV);
cipher.Init(true, keyParamWithIv); //Error Message thrown
byte[] outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)]; //cip
int length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
cipher.DoFinal(outputBytes, length); //Do the final block
string encryptedInput = Convert.ToBase64String(outputBytes);
The length on the server is 128. How can I force it to be equal and same length?
Here are snippets I use. It uses the default built-in System.Security.Cryptography. It doesn't need to be BC
/// <summary>
/// Encrypt a byte array using AES 128
/// </summary>
/// <param name="key">128 bit key</param>
/// <param name="secret">byte array that need to be encrypted</param>
/// <returns>Encrypted array</returns>
public static byte[] EncryptByteArray(byte[] key, byte[] secret)
{
using (MemoryStream ms = new MemoryStream())
{
using (AesManaged cryptor = new AesManaged())
{
cryptor.Mode = CipherMode.CBC;
cryptor.Padding = PaddingMode.PKCS7;
cryptor.KeySize = 128;
cryptor.BlockSize = 128;
//We use the random generated iv created by AesManaged
byte[] iv = cryptor.IV;
using (CryptoStream cs = new CryptoStream(ms, cryptor.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
cs.Write(secret, 0, secret.Length);
}
byte[] encryptedContent = ms.ToArray();
//Create new byte array that should contain both unencrypted iv and encrypted data
byte[] result = new byte[iv.Length + encryptedContent.Length];
//copy our 2 array into one
System.Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
System.Buffer.BlockCopy(encryptedContent, 0, result, iv.Length, encryptedContent.Length);
return result;
}
}
}
/// <summary>
/// Decrypt a byte array using AES 128
/// </summary>
/// <param name="key">key in bytes</param>
/// <param name="secret">the encrypted bytes</param>
/// <returns>decrypted bytes</returns>
public static byte[] DecryptByteArray(byte[] key, byte[] secret)
{
byte[] iv = new byte[16]; //initial vector is 16 bytes
byte[] encryptedContent = new byte[secret.Length - 16]; //the rest should be encryptedcontent
//Copy data to byte array
System.Buffer.BlockCopy(secret, 0, iv, 0, iv.Length);
System.Buffer.BlockCopy(secret, iv.Length, encryptedContent, 0, encryptedContent.Length);
using (MemoryStream ms = new MemoryStream())
{
using (AesManaged cryptor = new AesManaged())
{
cryptor.Mode = CipherMode.CBC;
cryptor.Padding = PaddingMode.PKCS7;
cryptor.KeySize = 128;
cryptor.BlockSize = 128;
using (CryptoStream cs = new CryptoStream(ms, cryptor.CreateDecryptor(key, iv), CryptoStreamMode.Write))
{
cs.Write(encryptedContent, 0, encryptedContent.Length);
}
return ms.ToArray();
}
}
}
If you really need BC, here is a quick test I manage to write based on the test suit from https://github.com/bcgit/bc-csharp/blob/master/crypto/test/src/crypto/test/AESFastTest.cs
You can tailor it for your need
private static void TestBC()
{
//Demo params
string keyString = "jDxESdRrcYKmSZi7IOW4lw==";
string input = "abc";
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
byte[] iv = new byte[16]; //for the sake of demo
//Set up
AesEngine engine = new AesEngine();
CbcBlockCipher blockCipher = new CbcBlockCipher(engine); //CBC
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher); //Default scheme is PKCS5/PKCS7
KeyParameter keyParam = new KeyParameter(Convert.FromBase64String(keyString));
ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, iv, 0, 16);
// Encrypt
cipher.Init(true, keyParamWithIV);
byte[] outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)];
int length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
cipher.DoFinal(outputBytes, length); //Do the final block
string encryptedInput = Convert.ToBase64String(outputBytes);
Console.WriteLine("Encrypted string: {0}", encryptedInput);
//Decrypt
cipher.Init(false, keyParamWithIV);
byte[] comparisonBytes = new byte[cipher.GetOutputSize(outputBytes.Length)];
length = cipher.ProcessBytes(outputBytes, comparisonBytes, 0);
cipher.DoFinal(comparisonBytes, length); //Do the final block
Console.WriteLine("Decrypted string: {0}",Encoding.UTF8.GetString(comparisonBytes)); //Should be abc
}
enter link description here
byte[] k; //32 byte
string para; // plaintext
string msgRefNo; // 16byte
byte[] inputBytes = Encoding.UTF8.GetBytes(para);
byte[] IV = Encoding.UTF8.GetBytes(msgRefNo);
byte[] key = k;
AesEngine engine = new AesEngine();
CbcBlockCipher blockCipher = new CbcBlockCipher(engine);
PaddedBufferedBlockCipher cipher1 = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
KeyParameter keyParam = new KeyParameter(key);
ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParam, IV);
cipher1.Init(true, keyParamWithIv); //Error Message thrown
byte[] outputBytes = new byte[cipher1.GetOutputSize(inputBytes.Length)]; //cip
int length = cipher1.ProcessBytes(inputBytes, outputBytes, 0);
cipher1.DoFinal(outputBytes, length); //Do the final block
string encryptedInput = Convert.ToBase64String(outputBytes);
return encryptedInput;

Good AES Initialization Vector practice

per my question Aes Encryption... missing an important piece, I have now learned that my assumption for creating a reversible encryption on a string was a bit off. I now have
public static byte[] EncryptString(string toEncrypt, byte[] encryptionKey)
{
var toEncryptBytes = Encoding.UTF8.GetBytes(toEncrypt);
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return ms.ToArray();
}
}
}
}
and this produces consistent results; however, I will not be able to decrypt without knowing/ setting the initialization vector. I really do not want to pass three values into this method (on for the IV), which leaves me with hardcoding the IV or deriving it from the key. I'd like to know if this is a good practice, or if it will render the encrypted value vulnerable to attack somehow... or am I really overthinking this and should just hardcode the IV?
UPDATE
Per Iridium's suggestion, I tried something like this instead:
public static byte[] EncryptString(string toEncrypt, byte[] encryptionKey)
{
if (string.IsNullOrEmpty(toEncrypt)) throw new ArgumentException("toEncrypt");
if (encryptionKey == null || encryptionKey.Length == 0) throw new ArgumentException("encryptionKey");
var toEncryptBytes = Encoding.UTF8.GetBytes(toEncrypt);
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
{
using (var ms = new MemoryStream())
{
ms.Write(provider.IV, 0, 16);
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return ms.ToArray();
}
}
}
}
public static string DecryptString(byte[] encryptedString, byte[] encryptionKey)
{
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var ms = new MemoryStream(encryptedString))
{
byte[] buffer;
ms.Read(buffer, 0, 16);
provider.IV = buffer;
using (var decryptor = provider.CreateDecryptor(provider.Key, provider.IV))
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
byte[] decrypted = new byte[encryptedString.Length];
var byteCount = cs.Read(decrypted, 0, encryptedString.Length);
return Encoding.UTF8.GetString(decrypted, 0, byteCount);
}
}
}
}
}
however, this shows something odd in my unit test:
[TestMethod]
public void EncryptionClosedLoopTest()
{
var roundtrip = "This is the data I am encrypting. There are many like it but this is my encryption.";
var encrypted = Encryption.EncryptString(roundtrip, encryptionKey);
var decrypted = Encryption.DecryptString(encrypted, encryptionKey);
Assert.IsTrue(roundtrip == decrypted);
}
my decrypted text shows up as "92ʪ�F"�,hpv0�� I am encrypting. There are many like it but this is my encryption." which seems almost right but of course completely wrong. It looks like I'm close though. Am I missing an offset on the memory stream?
The IV should be random and unique for every run of your encryption method. Deriving it from the key/message or hard-coding it is not sufficiently secure. The IV can be generated within this method, instead of passed into it, and written to the output stream prior to the encrypted data.
When decrypting, the IV can then be read from the input before the encrypted data.
When Encrypting, generate your IV and pre-pend it to the cipher text (something like this)
using (var aes= new AesCryptoServiceProvider()
{
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
var input = Encoding.UTF8.GetBytes(originalPayload);
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
{
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
//Prepend IV to data
//tBinaryWriter.Write(iv); This is the original broken code, it encrypts the iv
cipherStream.Write(iv); //Write iv to the plain stream (not tested though)
tBinaryWriter.Write(input);
tCryptoStream.FlushFinalBlock();
}
string encryptedPayload = Convert.ToBase64String(cipherStream.ToArray());
}
}
When decrypting this back, get first 16 bytes out and use it in crypto stream
var aes= new AesCryptoServiceProvider()
{
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
//get first 16 bytes of IV and use it to decrypt
var iv = new byte[16];
Array.Copy(input, 0, iv, 0, iv.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cs))
{
//Decrypt Cipher Text from Message
binaryWriter.Write(
input,
iv.Length,
input.Length - iv.Length
);
}
return Encoding.Default.GetString(ms.ToArray());
}
Great input from folks. I took the combined answers from ankurpatel and Konstantin and cleaned it up and added some convenient method overrides. This works as of June 2019 in .NET Core 2.2.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
private const int AesKeySize = 16;
public static void Main()
{
// the data to encrypt
var message = "Here is some data to encrypt!";
// create KeySize character key
var key = "g(KMDu(EEw63.*V`";
// encrypt the string to a string
var encrypted = AesEncrypt(message, key);
// decrypt the string to a string.
var decrypted = AesDecrypt(encrypted, key);
// display the original data and the decrypted data
Console.WriteLine($"Original: text: {encrypted}");
Console.WriteLine($"Round Trip: text: {decrypted}");
}
static string AesEncrypt(string data, string key)
{
return AesEncrypt(data, Encoding.UTF8.GetBytes(key));
}
static string AesDecrypt(string data, string key)
{
return AesDecrypt(data, Encoding.UTF8.GetBytes(key));
}
static string AesEncrypt(string data, byte[] key)
{
return Convert.ToBase64String(AesEncrypt(Encoding.UTF8.GetBytes(data), key));
}
static string AesDecrypt(string data, byte[] key)
{
return Encoding.UTF8.GetString(AesDecrypt(Convert.FromBase64String(data), key));
}
static byte[] AesEncrypt(byte[] data, byte[] key)
{
if (data == null || data.Length <= 0)
{
throw new ArgumentNullException($"{nameof(data)} cannot be empty");
}
if (key == null || key.Length != AesKeySize)
{
throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
}
using (var aes = new AesCryptoServiceProvider
{
Key = key,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
{
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
// prepend IV to data
cipherStream.Write(iv);
tBinaryWriter.Write(data);
tCryptoStream.FlushFinalBlock();
}
var cipherBytes = cipherStream.ToArray();
return cipherBytes;
}
}
}
static byte[] AesDecrypt(byte[] data, byte[] key)
{
if (data == null || data.Length <= 0)
{
throw new ArgumentNullException($"{nameof(data)} cannot be empty");
}
if (key == null || key.Length != AesKeySize)
{
throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
}
using (var aes = new AesCryptoServiceProvider
{
Key = key,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
// get first KeySize bytes of IV and use it to decrypt
var iv = new byte[AesKeySize];
Array.Copy(data, 0, iv, 0, iv.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cs))
{
// decrypt cipher text from data, starting just past the IV
binaryWriter.Write(
data,
iv.Length,
data.Length - iv.Length
);
}
var dataBytes = ms.ToArray();
return dataBytes;
}
}
}
I modified your decryption method as follows and it works:
public static string DecryptString(byte[] encryptedString, byte[] encryptionKey)
{
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
using (var ms = new MemoryStream(encryptedString))
{
// Read the first 16 bytes which is the IV.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
using (var decryptor = provider.CreateDecryptor())
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
return sr.ReadToEnd();
}
}
}
}
}
}
The problem with your implementation is that you are reading too many bytes into the CryptoStream. You really need to read encryptedText.Length - 16. Using a StreamReader simplifies this, since you don't need to worry about offsets anywhere anymore.
The accepted answer is correct, but doesn't provide a good example of how to get a random IV.
It turns out that this is a lot easier than people are trying to make it. The AesCryptoServiceProvider in .NET automatically generates a cryptographically random IV every time you construct one. And if you need to use the same instance for multiple encryptions, you can call GenerateIV()
You can also prepend the IV to the encrypted value before returning it and have the decrypting end pull it off
private static void Main(string[] args) {
var rnd = new Random();
var key = new byte[32]; // For this example, I'll use a random 32-byte key.
rnd.NextBytes(key);
var message = "This is a test";
// Looping to encrypt the same thing twice just to show that the IV changes.
for (var i = 0; i < 2; ++i) {
var encrypted = EncryptString(message, key);
Console.WriteLine(encrypted);
Console.WriteLine(DecryptString(encrypted, key));
}
}
public static string EncryptString(string message, byte[] key) {
var aes = new AesCryptoServiceProvider();
var iv = aes.IV;
using (var memStream = new System.IO.MemoryStream()) {
memStream.Write(iv, 0, iv.Length); // Add the IV to the first 16 bytes of the encrypted value
using (var cryptStream = new CryptoStream(memStream, aes.CreateEncryptor(key, aes.IV), CryptoStreamMode.Write)) {
using (var writer = new System.IO.StreamWriter(cryptStream)) {
writer.Write(message);
}
}
var buf = memStream.ToArray();
return Convert.ToBase64String(buf, 0, buf.Length);
}
}
public static string DecryptString(string encryptedValue, byte[] key) {
var bytes = Convert.FromBase64String(encryptedValue);
var aes = new AesCryptoServiceProvider();
using (var memStream = new System.IO.MemoryStream(bytes)) {
var iv = new byte[16];
memStream.Read(iv, 0, 16); // Pull the IV from the first 16 bytes of the encrypted value
using (var cryptStream = new CryptoStream(memStream, aes.CreateDecryptor(key, iv), CryptoStreamMode.Read)) {
using (var reader = new System.IO.StreamReader(cryptStream)) {
return reader.ReadToEnd();
}
}
}
}
[EDIT: I modified my answer to include how to pass the IV in the encrypted value and get it when decrypting. I also refactored the example a bit]
In order to resolve the setting of IV on the provider (As Iridium pointed out):
ms.Read(provider.IV, 0, 16);
I added the following to your code:
var iv = new byte[provider.IV.Length];
memoryStream.Read(iv, 0, provider.IV.Length);
using (var decryptor = provider.CreateDecryptor(key, iv);
granted, my key is not set by the provider on each run. I generated it once and then stored it. The IV is randomly generated off of the provider for each encryption.
In my case, to generate the IV, I use something like this
/// <summary>
/// Derives password bytes
/// </summary>
/// <param name="Password">password</param>
/// <returns>derived bytes</returns>
private Rfc2898DeriveBytes DerivePass(string Password)
{
byte[] hash = CalcHash(Password);
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Password, hash, _KEY_ITER);
return pdb;
}
/// <summary>
/// calculates the hash of the given string
/// </summary>
/// <param name="buffer">string to hash</param>
/// <returns>hash value (byte array)</returns>
private byte[] CalcHash(string buffer)
{
RIPEMD160 hasher = RIPEMD160.Create();
byte[] data = Encoding.UTF8.GetBytes(buffer);
return hasher.ComputeHash(data);
}
that is, I calculate the password hash using RIPEMD160 and use it to generate the derived bytes, at that point, when it comes to intializing the encryption/decryption I just use something like this
Rfc2898DeriveBytes pdb = DerivePass(Password);
SymmetricAlgorithm alg = _engine;
alg.Key = pdb.GetBytes(_keySize);
alg.IV = pdb.GetBytes(_IVSize);
I don't know if it's "correct" (probably crypto gurus here will shoot at me :D), but, at least, it gives me a decent IV and I don't have to store it "somewhere" since just entering the correct password will give back the needed IV value; as a note, the _engine in the above example is declared as "SymmetricAlgorithm" and initialized using something like this
_engine = Rijndael.Create();
_keySize = (_engine.KeySize / 8);
_IVSize = (_engine.BlockSize / 8);
which creates the desired crypto objects and initializes the key and IV sizes
To generate random IV you would need a truly random number. Whichever language specific API you use for generating the random number, should generate true random number. Both android and ios have apis which generate random numbers based on sensor data.
I recently implemented AES 256 with random IV (Generated using really random numbers) and hashed key. For more secure(random IV + hashed key) cross platform (android, ios, c#) implementation of AES see my answer here - https://stackoverflow.com/a/24561148/2480840

Categories