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);
}
}
}