Encrypt a file in c# and decrypt in flutter - c#

I have encrypted a file in c# code using RijndaelManaged which is available in System.Security.Cryptography. This file needs to be transferred to a mobile app developed using dart/flutter and I need it to be decrypted using dart code and present it to the user. How can this be done?
Below shown is the code to do the encryption in c#:
string password = keyPhrase; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
string cryptFile = outputFile;
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateEncryptor(key, key),
CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
Thank you

I ran into the same problem. After many hours, a solution was found. My code is based on this question1 and question2 Code on C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var m_strPassPhrase = "YYYYYYYYYYYYYYYYYYY";
var p_strSaltValue = "XXXXXXXXXXXXXXXXX";
var m_strPasswordIterations = 2;
var m_strInitVector = "ZZZZZZZZZZZZZZZZ";
var plainText = "myPassword";
var blockSize = 32;
var saltValueBytes = Encoding.ASCII.GetBytes(p_strSaltValue);
var password = new Rfc2898DeriveBytes(m_strPassPhrase, saltValueBytes, m_strPasswordIterations);
var keyBytes = password.GetBytes(blockSize);
var symmetricKey = new RijndaelManaged();
var initVectorBytes = Encoding.ASCII.GetBytes(m_strInitVector);
var encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
var memoryStream = new System.IO.MemoryStream();
var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
var cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
var cipherText = Convert.ToBase64String(cipherTextBytes);
Console.WriteLine(cipherText);
Console.WriteLine("\n end");
}
}
}
For flutter you can use pointycastle
Code on Dart(use decryptString and cryptString methods):
import 'dart:convert';
import 'package:pointycastle/block/aes_fast.dart';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:pointycastle/key_derivators/pbkdf2.dart';
import 'package:pointycastle/paddings/pkcs7.dart';
import 'package:pointycastle/pointycastle.dart';
const KEY_SIZE = 32; // 32 byte key for AES-256
const ITERATION_COUNT = 2;
const SALT = "XXXXXXXXXXXXXXXXX";
const INITIAL_VECTOR = "ZZZZZZZZZZZZZZZZ";
const PASS_PHRASE = "YYYYYYYYYYYYYYYYYYY";
Future<String> cryptString(String text) async {
String encryptedString = "";
final mStrPassPhrase = toUtf8(PASS_PHRASE);
encryptedString =
AesHelper.encrypt(mStrPassPhrase, toUtf8(text), mode: AesHelper.CBC_MODE);
return encryptedString;
}
Future<String> decryptString(String text) async {
String decryptedString = "";
final mStrPassPhrase = toUtf8(PASS_PHRASE);
decryptedString =
AesHelper.decrypt(mStrPassPhrase, toUtf8(text), mode: AesHelper.CBC_MODE);
return decryptedString;
}
///MARK: AesHelper class
class AesHelper {
static const CBC_MODE = 'CBC';
static const CFB_MODE = 'CFB';
static Uint8List deriveKey(dynamic password,
{String salt = '',
int iterationCount = ITERATION_COUNT,
int derivedKeyLength = KEY_SIZE}) {
if (password == null || password.isEmpty) {
throw new ArgumentError('password must not be empty');
}
if (password is String) {
password = createUint8ListFromString(password);
}
Uint8List saltBytes = createUint8ListFromString(salt);
Pbkdf2Parameters params =
new Pbkdf2Parameters(saltBytes, iterationCount, derivedKeyLength);
KeyDerivator keyDerivator =
new PBKDF2KeyDerivator(new HMac(new SHA1Digest(), 64));
keyDerivator.init(params);
return keyDerivator.process(password);
}
static Uint8List pad(Uint8List src, int blockSize) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = blockSize - (src.length % blockSize);
var out = new Uint8List(src.length + padLength)..setAll(0, src);
pad.addPadding(out, src.length);
return out;
}
static Uint8List unpad(Uint8List src) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = pad.padCount(src);
int len = src.length - padLength;
return new Uint8List(len)..setRange(0, len, src);
}
static String encrypt(String password, String plaintext,
{String mode = CBC_MODE}) {
String salt = toASCII(SALT);
Uint8List derivedKey = deriveKey(password, salt: salt);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var ivStr = toASCII(INITIAL_VECTOR);
Uint8List iv =
createUint8ListFromString(ivStr);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
cipher.init(true, params);
Uint8List textBytes = createUint8ListFromString(plaintext);
Uint8List paddedText = pad(textBytes, aes.blockSize);
Uint8List cipherBytes = _processBlocks(cipher, paddedText);
return base64.encode(cipherBytes);
}
static String decrypt(String password, String ciphertext,
{String mode = CBC_MODE}) {
String salt = toASCII(SALT);
Uint8List derivedKey = deriveKey(password, salt: salt);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var ivStr = toASCII(INITIAL_VECTOR);
Uint8List iv = createUint8ListFromString(ivStr);
Uint8List cipherBytesFromEncode = base64.decode(ciphertext);
Uint8List cipherIvBytes =
new Uint8List(cipherBytesFromEncode.length + iv.length)
..setAll(0, iv)
..setAll(iv.length, cipherBytesFromEncode);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
cipher.init(false, params);
int cipherLen = cipherIvBytes.length - aes.blockSize;
Uint8List cipherBytes = new Uint8List(cipherLen)
..setRange(0, cipherLen, cipherIvBytes, aes.blockSize);
Uint8List paddedText = _processBlocks(cipher, cipherBytes);
Uint8List textBytes = unpad(paddedText);
return new String.fromCharCodes(textBytes);
}
static Uint8List _processBlocks(BlockCipher cipher, Uint8List inp) {
var out = new Uint8List(inp.lengthInBytes);
for (var offset = 0; offset < inp.lengthInBytes;) {
var len = cipher.processBlock(inp, offset, out, offset);
offset += len;
}
return out;
}
}
///MARK: HELPERS
Uint8List createUint8ListFromString(String s) {
Uint8List ret = Uint8List.fromList(s.codeUnits);
return ret;
}
String toUtf8(value) {
var encoded = utf8.encode(value);
var decoded = utf8.decode(encoded);
return decoded;
}
String toASCII(value) {
var encoded = ascii.encode(value);
var decoded = ascii.decode(encoded);
return decoded;
}

The default mode of Rijndael in .Net is 128 bit block size - compatible with AES. Unless you are using a non-standard block size, prefer .Net's AesManaged.
You haven't specified which padding or mode you are using. The .Net default seems to be CBC, so we'll assume that. It's not clear whether it defaults to a certain padding mode.
(Note that you are using the key both as the IV and the key. The IV should be unique for each invocation of the encryption routine. TLDR - the way you are using AesManaged is insecure - don't use this code in real life.)
Also, you are decoding the key from a string. The key length of AES must be exactly 128 or 256 bits (or one of the more unusual ones). Unless you have chosen your string well, it is unlikely to UTF-8 encode to an exact key length. Also, by using a string you are only using bytes in the key that happen to be characters. Typically, to use a string as a password you would convert it to a key using a key derivation algorithm (e.g. PBKDF2) rather than just UTF-8 encoding it.
With all that said, if your password is exactly 16 (or 32 long) and your file is an exact multiple of 16 bytes (if it is not, you need to decide how to pad it) you should be able to decrypt it like this:
import 'dart:convert';
import 'dart:io';
import 'package:pointycastle/export.dart';
main() async {
var key = utf8.encode('abcdefghijklmnop');
var cipher = CBCBlockCipher(AESFastEngine())
..init(false, ParametersWithIV<KeyParameter>(KeyParameter(key), key));
var cipherText = await File('encryptedFile').readAsBytes();
var plainText = cipher.process(cipherText);
await File('decryptedFile').writeAsBytes(plainText, flush: true);
}

Related

Issues implementing SHA256 hash, then pbkdf2Sync and finally cipher with AES-256-cbc - c# to Nodejs

I have been given some c# code which implements SHA256 hashing and then AES-256-cbc. Now I have to translate the same to NodeJS. I tried several option, documentation and question/answers here, but none helped. As I am working with encryption for the first time, possibly something going wrong with encoding - but can't figure out exactly what.
Here is c# implementation:
using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;
public class HelloWorld
{
public static void Main(string[] args)
{
HelloWorld h1 = new HelloWorld();
Console.WriteLine(h1.EncryptText("Vitthal", "Vitthal"));
}
public string EncryptText(string pInput, string password)
{
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(GenerateSHA256String(pInput));
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
// method name GenerateSHA256String and code
public string GenerateSHA256String(string inputString)
{
StringBuilder stringBuilder = new StringBuilder();
try
{
SHA256 sha256 = SHA256Managed.Create();
byte[] bytes = Encoding.UTF8.GetBytes(inputString);
byte[] hash = sha256.ComputeHash(bytes);
for (int i = 0; i <= hash.Length - 1; i++)
stringBuilder.Append(hash[i].ToString("X2"));
return stringBuilder.ToString();
}
catch (Exception ex)
{
}
return stringBuilder.ToString();
}
// method name AES_Encrypt and code
private byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new System.Security.Cryptography.Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
}
And here is the NodeJS implementation using crypto:
const GenerateSHA256String = (object) => {
const buff = Buffer.from(object.toString());
const hash = createHash('sha256');
hash.update(buff);
const hashed = hash.digest('hex');
return hashed;
}
const getEncryptedChecksum = (object) => {
const payload = GenerateSHA256String(object);
console.log(Buffer.from(payload));
const passKey = Buffer.from('Vitthal');
const saltString = [1,2,3,4,5,6,7,8];
const key = pbkdf2Sync(GenerateSHA256String(passKey), Buffer.from(saltString), 1000, 100, 'sha1');
const encKey = key.subarray(0, 32);
const encIV = key.subarray(32, 48);
const cipher = createCipheriv('aes-256-cbc', encKey, encIV);
let encrypted = cipher.update(Buffer.from(payload), 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
console.log(getEncryptedChecksum('Vitthal'));
Any help in this regard is highly appreciated.
Finally solved. It was encoding issue. Some strange behaviors different between c# & nodejs. Anyways, here is the final nodejs code that works!
const GenerateSHA256String = (object, encoding) => {
const buff = Buffer.from(object.toString());
const hash = createHash('sha256');
hash.update(buff);
const hashed = hash.digest(encoding ? encoding : null);
return hashed;
}
const getEncryptedChecksum = (object) => {
const payload = GenerateSHA256String(object, 'hex');
const payBuff = Buffer.from(payload.toUpperCase());
const passKey = Buffer.from('NDSICDM');
const saltString = [1,2,3,4,5,6,7,8];
const key = pbkdf2Sync(GenerateSHA256String(passKey), Buffer.from(saltString), 1000, 64, 'sha1');
const encKey = key.subarray(0, 32);
const encIV = key.subarray(32, 48);
const cipher = createCipheriv('aes-256-cbc', encKey, encIV);
let encrypted = cipher.update(payBuff, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}

Extra null characters when decrypting AES-CBC-PKCS7 with BouncyCastle

I need to implement AES encryption in 2 different projects, but one must use the .NET standard crypto libraries and the other must use BouncyCastle. Both are C# code. Relevant methods are as follows:
.NET:
internal class NETAesCryptor : IAesCryptor
{
public Tuple<byte[], byte[]> Encrypt(string plaintext, byte[] key)
{
byte[] ciphertext, iv;
using (var aes_provider = new AesCryptoServiceProvider())
{
aes_provider.Padding = PaddingMode.PKCS7;
aes_provider.GenerateIV();
iv = aes_provider.IV;
var encryptor = aes_provider.CreateEncryptor(key, iv);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (var sw = new StreamWriter(cs))
{
sw.Write(plaintext);
}
ciphertext = ms.ToArray();
}
}
}
var result = new Tuple<byte[], byte[](ciphertext, iv);
return result;
}
public string Decrypt(byte[] ciphertext, byte[] iv, byte[] key)
{
string plaintext;
using (var aes_provider = new AesCryptoServiceProvider())
{
aes_provider.Padding = PaddingMode.PKCS7;
aes_provider.IV = iv;
var decryptor = aes_provider.CreateDecryptor(key, iv);
using (var ms = new MemoryStream(ciphertext))
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
plaintext = sr.ReadToEnd();
}
}
}
}
return plaintext;
}
}
Bouncycastle:
internal class BCAesCryptor : IAesCryptor
{
private SecureRandom _r;
public BCAesCryptor()
{
_r = new SecureRandom();
}
public Tuple<byte[], byte[]> Encrypt(string plaintext, byte[] key)
{
var plaintext_bytes = Encoding.UTF8.GetBytes(plaintext);
var iv = GenerateRandomBytes(16);
var engine = new AesEngine();
var cbc_cipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(cbc_cipher, new Pkcs7Padding());
var key_param = new KeyParameter(key);
var key_param_with_iv = new ParametersWithIV(key_param, iv);
cipher.Init(true, key_param_with_iv);
var ciphertext = new byte[cipher.GetOutputSize(plaintext_bytes.Length)];
var length = cipher.ProcessBytes(plaintext_bytes, ciphertext, 0);
cipher.DoFinal(ciphertext, length);
var result = new Tuple<byte[], byte[]>(ciphertext, iv);
return result;
}
public string Decrypt(byte[] ciphertext, byte[] iv, byte[] key)
{
var engine = new AesEngine();
var cbc_cipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(cbc_cipher, new Pkcs7Padding());
var key_param = new KeyParameter(key);
var key_param_with_iv = new ParametersWithIV(key_param, iv);
cipher.Init(false, key_param_with_iv);
var plaintext = new byte[cipher.GetOutputSize(ciphertext.Length)];
var length = cipher.ProcessBytes(ciphertext, plaintext, 0);
cipher.DoFinal(plaintext, length);
var result = Encoding.UTF8.GetString(plaintext);
return result;
}
private byte[] GenerateRandomBytes(int length = 16)
{
var result = new byte[length];
_r.NextBytes(result);
return result;
}
}
Encryption/decryption between .NET methods works OK, and Bouncycastle encryption/.NET decryption also works OK. But for some reason, Bouncycastle decryption adds a variable number of \0 characters at the end of the plaintext, and I don't know why is this happening.
Test code I'm using:
[TestClass]
public class AesCryptorTests
{
private byte[] _key;
private string _plaintext;
public AesCryptorTests()
{
_key = GenerateRandomBytes();
_plaintext = "Lorem ipsum dolor sit amet";
}
[TestMethod]
public void TestMethod2()
{
var bc = new BCAesCryptor();
var net = new NETAesCryptor();
var result = net.Encrypt(_plaintext, _key);
var new_plaintext = bc.Decrypt(result.Ciphertext, result.IV, _key);
Assert.AreEqual(_plaintext, new_plaintext);
}
private byte[] GenerateRandomBytes(int cantidad = 16)
{
var result = new byte[cantidad];
using (var r = new RNGCryptoServiceProvider())
{
r.GetBytes(result);
}
return result;
}
}
In the previous test, the decryption returns Lorem ipsum dolor sit amet\0\0\0\0\0\0 instead of the plaintext.
Any advice/comment would be greatly appreciated.
The Bouncy Castle can only guess the output size of the plaintext message in advance during the call to GetOutputSize. It cannot know how many padding bytes are used, because those are only available after decryption. So they would have to partially decrypt the ciphertext to know the amount of padding, and that's taking it a step too far. Therefore you get just an estimate on the high side so that the maximum number of bytes can still fit in your newly created buffer.
You'll need the return value of the ProcessBytes and DoFinal to see the actual number of bytes that are decrypted from the ciphertext (in the input buffer and internal buffer) when the methods are called. DoFinal decrypts the last block(s) and then removes the padding from the final block, so only at that time is the size of the (remaining) plaintext known.
What you're currently seeing as zero valued bytes are just the unused bytes of the buffer, as the plaintext size is smaller than the value returned by GetOutputSize.
Of course, this is all hidden in the streaming code of the .NET sample, where ReadToEnd is required to doing some advanced buffering (probably using a MemoryStream internally itself).
Following instructions from Maarten Bodewes, the final working code is as follows:
public string Decrypt(byte[] ciphertext, byte[] iv, byte[] key)
{
var engine = new AesEngine();
var cbc_cipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(cbc_cipher, new Pkcs7Padding());
var key_param = new KeyParameter(key);
var key_param_with_iv = new ParametersWithIV(key_param, iv);
cipher.Init(false, key_param_with_iv);
var decryption_buffer = new byte[cipher.GetOutputSize(ciphertext.Length)];
var initial_length = cipher.ProcessBytes(ciphertext, decryption_buffer, 0);
var last_bytes = cipher.DoFinal(decryption_buffer, initial_length);
var total_bytes = initial_length + last_bytes;
var plaintext = new byte[total_bytes];
Array.Copy(decryption_buffer, plaintext, total_bytes);
var result = Encoding.UTF8.GetString(plaintext);
return result;
}
Note that the length of the plaintext is now calculated with the integer outputs of the decryption methods, and a simple array copy is able to create a plaintext without extra characters.

Why is only one half of my encrypted string not decrypting properly?

Can't seem to figure this one out... I am using DESCryptoServiceProvider to do a quick little two way encryption (not security related, and security is not the purpose of this question).
Anyways it's weird because the string that goes in and then comes back out is only decrypting properly for one half of the string. I can't seem to notice the bug so maybe someone will have some fun with this...
I am combining the two strings with a colon as the separator so 'abc12345:xyz56789' is the input. Then notice in the output only the first part of the string is getting screwed up, not the second part. I would expect that if I was doing it totally wrong then the whole thing wouldn't decrypt properly.
Here is all the code:
class Program
{
static void Main(string[] args)
{
var userId = "abc12345";
var appId = "xyz56789";
Console.WriteLine($"UserId: {userId}, AppId: {appId}");
var code = QuickEncode(userId, appId);
Console.WriteLine(code);
var result = QuickDecode(code);
var uId = result.Item1;
var aId = result.Item2;
Console.WriteLine($"UserId: {uId}, AppId: {aId}");
Console.ReadKey();
}
private static string QuickEncode(string userId, string appId)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
ICryptoTransform encryptor = des.CreateEncryptor();
var encryptMe = $"{userId}:{appId}";
Console.WriteLine($"Input String: {encryptMe}");
byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(encryptMe);
byte[] enc = encryptor.TransformFinalBlock(stringBytes, 0, stringBytes.Length);
var encryptedBytesString = Convert.ToBase64String(enc);
return encryptedBytesString;
}
private static Tuple<string, string> QuickDecode(string code)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
ICryptoTransform decryptor = des.CreateDecryptor();
var codeBytes = Convert.FromBase64String(code);
byte[] originalAgain = decryptor.TransformFinalBlock(codeBytes, 0, codeBytes.Length);
var decryptMe = System.Text.Encoding.UTF8.GetString(originalAgain);
Console.WriteLine($"Output String: {decryptMe}");
var ids = decryptMe.Split(':');
return new Tuple<string, string>(ids[0], ids[1]);
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
public static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
}
You must set initialization vector (IV) to the same value for encryption as well as for decryption. Because new IV is automatically generated for each new instance of DESCryptoServiceProvider, your IV differs and decryption is not successfull.
The reason that half of the message is decrypted correctly results from usage of CBC mode (which is default mode), which has one really nasty property, that only first block of encrypted message actually depends on value of IV, so potential attacker can decode all message, except first block, without knowing correct IV (of course, correct Key is still needed). So it is not recommended to use this mode. See Block cipher mode of operation for more info about this.
So solution is easy - store somewhere IV used for encryption and use the same IV for decryption. If possible, use another cypher mode too. Somthing like this:
using System;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main(string[] args)
{
var userId = "abc12345";
var appId = "xyz56789";
Console.WriteLine($"UserId: {userId}, AppId: {appId}");
byte[] IV;
var code = QuickEncode(userId, appId, out IV);
Console.WriteLine(code);
var result = QuickDecode(code, IV);
var uId = result.Item1;
var aId = result.Item2;
Console.WriteLine($"UserId: {uId}, AppId: {aId}");
Console.ReadKey();
}
private static string QuickEncode(string userId, string appId, out byte[] IV)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
des.GenerateIV();
IV = des.IV;
ICryptoTransform encryptor = des.CreateEncryptor();
var encryptMe = $"{userId}:{appId}";
Console.WriteLine($"Input String: {encryptMe}");
byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(encryptMe);
byte[] enc = encryptor.TransformFinalBlock(stringBytes, 0, stringBytes.Length);
var encryptedBytesString = Convert.ToBase64String(enc);
return encryptedBytesString;
}
private static Tuple<string, string> QuickDecode(string code, byte[] IV)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
des.IV = IV;
ICryptoTransform decryptor = des.CreateDecryptor();
var codeBytes = Convert.FromBase64String(code);
byte[] originalAgain = decryptor.TransformFinalBlock(codeBytes, 0, codeBytes.Length);
var decryptMe = System.Text.Encoding.UTF8.GetString(originalAgain);
Console.WriteLine($"Output String: {decryptMe}");
var ids = decryptMe.Split(':');
return new Tuple<string, string>(ids[0], ids[1]);
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
public static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
}

How do you use AES to Encrypt in One Program, Decrypt in Another

I was told not to use RSA to encrypt simple text but to use AES. I found a simple piece of code to implement AES:
public static class Crypto
{
#region Settings
private static int _iterations = 2;
private static int _keySize = 256;
private static string _hash = "SHA1";
private static string _salt = "aselrias38490a32"; // Random
private static string _vector = "8947az34awl34kjq"; // Random
#endregion
public static string Encrypt(string value, string password)
{
return Encrypt<AesManaged>(value, password);
}
public static string Encrypt<T>(string value, string password)
where T : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
byte[] valueBytes = Encoding.UTF8.GetBytes(value);
byte[] encrypted;
using (T cipher = new T())
{
PasswordDeriveBytes _passwordBytes =
new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);
cipher.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = cipher.CreateEncryptor(keyBytes, vectorBytes))
{
using (MemoryStream to = new MemoryStream())
{
using (CryptoStream writer = new CryptoStream(to, encryptor, CryptoStreamMode.Write))
{
writer.Write(valueBytes, 0, valueBytes.Length);
writer.FlushFinalBlock();
encrypted = to.ToArray();
}
}
}
cipher.Clear();
}
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string value, string password)
{
return Decrypt<AesManaged>(value, password);
}
public static string Decrypt<T>(string value, string password) where T : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
byte[] valueBytes = Convert.FromBase64String(value);
byte[] decrypted;
int decryptedByteCount = 0;
using (T cipher = new T())
{
PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);
cipher.Mode = CipherMode.CBC;
try
{
using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes))
{
using (MemoryStream from = new MemoryStream(valueBytes))
{
using (CryptoStream reader = new CryptoStream(from, decryptor, CryptoStreamMode.Read))
{
decrypted = new byte[valueBytes.Length];
decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length);
}
}
}
}
catch (Exception ex)
{
return String.Empty;
}
cipher.Clear();
}
return Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount);
}
}
However, this is based on a string coming back and then used to decrypt in the same program. I need to encrypt the following data in a WinForms program and the decrypt in a whole separate Windows Service program:
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
doc.Save(fileName);
// Convert XML doc to byte stream
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
// byte[] fileBytes = Encoding.Default.GetBytes(xmlDoc.OuterXml);
string encrypted = Crypto.Encrypt(xmlDoc.OuterXml, "testpass");
How can I do it? Please show sample code.
EDIT: Kevin, I have implemented your algorithm but the problem is I want to generate the key once and save it for use in the other program to decrypt but I need to pass the byte[] to the encrypt function. So I tried converting using System.Text.Encoding.ASCII.GetBytes(key); and it doesn't do it correctly. I have the wrong number of bytes for byte[] for the key.
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
doc.Save(fileName);
// Read file to a string
string contents = File.ReadAllText(fileName);
string key = String.Empty;
byte[] aesKey;
using (var aes = Aes.Create())
{
// aesKey = aes.Key;
key = Convert.ToBase64String(aes.Key);
}
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = System.Text.Encoding.UTF8.GetBytes(sKey);
string encyptedText = EncryptDecrpt.EncryptStringToBase64String(contents, aesKey);
File.WriteAllText(fileName, encyptedText);
EDIT2: Here's both parts as they stand now. The encrypting side:
private void SaveForm()
{
try
{
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
// doc.Save(fileName);
// Read file to a string
// string contents = File.ReadAllText(fileName);
string key = String.Empty;
byte[] aesKey;
//using (var aes = Aes.Create())
//{
// aesKey = aes.Key;
// key = Convert.ToBase64String(aes.Key);
//}
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = Convert.FromBase64String(sKey);
string encyptedText = EncryptDecrpt.EncryptStringToBase64String(doc.ToString(), aesKey);
File.WriteAllText(fileName, encyptedText);
//doc.Save(fileName);
The Windows Service side that tries to decrypt:
try
{
string path = AppDomain.CurrentDomain.BaseDirectory;
eventLog1.WriteEntry(path);
string fileName = System.IO.Path.Combine(path, "alphaService.xml");
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
Byte[] keyBytes = Convert.FromBase64String(sKey);
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);
eventLog1.WriteEntry(xmlStr);
using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
{
reader.ReadToFollowing("DatabaseServerName");
DatabaseServerName = reader.ReadElementContentAsString();
reader.ReadToFollowing("DatabaseUserName");
DatabaseUserName = reader.ReadElementContentAsString();
reader.ReadToFollowing("DatabasePassword");
DatabasePassword = reader.ReadElementContentAsString();
reader.ReadToFollowing("RegistrationCode");
RegistrationCode = reader.ReadElementContentAsString();
}
eventLog1.WriteEntry("Configuration data loaded successfully");
}
catch (Exception ex)
{
eventLog1.WriteEntry("Unable to load configuration data. " + ex.Message);
}
The algorithm I wrote below uses a random Initialization Vector that it puts at the beginning of the encrypted value so you can encrypt the same value twice and not get the same encrypted output. This is fairly normal and lets you only pass a single "secret" back and forth.
You will need to share your secret key by some out of bounds process because both encryption and decryption need to know the key. That is a seperate topic of key exchange that is documented in other places. Here is an SO link to get you started if you need some help on it.
Also if you are "making up" random values I recommend that you don't. Use something to help you like the following which generates random bytes and then converts them into a base64 string which is easier for human usage or some types of key exchange. Note that this is just an example of how you could generate random key's... in practice this may be based on some user input that is recreatable or you use the users hash value to lookup your random key that you generate. In any event here is the code for the key...
byte[] key;
string base64Key;
using (var aes = Aes.Create())
{
// key as byte[]
key = aes.Key;
// key as base64string - which one you use depends on how you store your keys
base64Key= Convert.ToBase64String(aes.Key);
}
Usage is as follows...
// you get the base64 encoded key from somewhere
var base64Key = "+CffHxKmykUvCrrCILd4rZDBcrIoe3w89jnPNXYi0rU=";
// convert it to byte[] or alternatively you could store your key as a byte[]
// but that depends on how you set things up.
var key = Convert.FromBase64String(base64Key);
var plainText = "EncryptThis";
var encryptedText = EncryptStringToBase64String(plainText, key);
var decryptedText = DecryptStringFromBase64String(encryptedText, key);
Here are the encryption methods... EncryptStringToBase64String and DecryptStringFromBase64String.
EDIT: Great point owlstead about using Aes.BlockSize for the IV size. I've also cleaned up the arguement checks.
private const int KeySize = 256; // in bits
static string EncryptStringToBase64String(string plainText, byte[] Key)
{
// Check arguments.
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
byte[] returnValue;
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.GenerateIV();
aes.Mode = CipherMode.CBC;
var iv = aes.IV;
if (string.IsNullOrEmpty(plainText))
return Convert.ToBase64String(iv);
var encryptor = aes.CreateEncryptor(Key, iv);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
// this is just our encrypted data
var encrypted = msEncrypt.ToArray();
returnValue = new byte[encrypted.Length + iv.Length];
// append our IV so our decrypt can get it
Array.Copy(iv, returnValue, iv.Length);
// append our encrypted data
Array.Copy(encrypted, 0, returnValue, iv.Length, encrypted.Length);
}
}
}
// return encrypted bytes converted to Base64String
return Convert.ToBase64String(returnValue);
}
static string DecryptStringFromBase64String(string cipherText, byte[] Key)
{
// Check arguments.
if (string.IsNullOrEmpty(cipherText))
return string.Empty;
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
string plaintext = null;
// this is all of the bytes
var allBytes = Convert.FromBase64String(cipherText);
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.Mode = CipherMode.CBC;
// get our IV that we pre-pended to the data
byte[] iv = new byte[aes.BlockSize/8];
if (allBytes.Length < iv.Length)
throw new ArgumentException("Message was less than IV size.");
Array.Copy(allBytes, iv, iv.Length);
// get the data we need to decrypt
byte[] cipherBytes = new byte[allBytes.Length - iv.Length];
Array.Copy(allBytes, iv.Length, cipherBytes, 0, cipherBytes.Length);
// Create a decrytor to perform the stream transform.
var decryptor = aes.CreateDecryptor(Key, iv);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherBytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
EDIT 2: Never convert actual binary data (like a random key) into a string using a TextEncoding. If data starts life as a string and you convert into binary using an encoding then and ONLY then can you convert it from binary into a string using the proper encoding. Otherwise you will have code that works sometimes which is a recipe for torturing yourself.
// This is base64 not UTF8, unicode, ASCII or anything else!!!
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = Convert.FromBase64String(sKey);
Edit 3:
Why use File.WriteAllText to write the file but use File.ReadAllBytes when you read it? You can write it and read it as text and use ASCII encoding since base64 is guaranteed to be ASCII. Also Decrypt returns a decrypted string which you are not storing or using. The decrypted string is what you need to parse because it's your xml.
You can use this for saving the file...
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
In your decrypt you should do this...
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedStr , keyBytes);
EDIT 4: I've attempted to duplicate your exception and I can't make it happen... here is my test code that I'm running in a console app and it works.
public static void EncryptMethod()
{
var fileName = #"c:/text.xml";
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", "txtServerName.Text"),
new XElement("DatabaseUserName", "txtDatabaseUserName.Text"),
new XElement("DatabasePassword", "txtDatabasePassword.Text"),
new XElement("ServiceAccount", "txtAccount.Text"),
new XElement("ServicePassword", "txtServicePassword.Text"),
new XElement("RegistrationCode", "txtRegistrationCode.Text"));
doc.Add(xml);
var sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
var aesKey = Convert.FromBase64String(sKey);
string encyptedText = EncryptStringToBase64String(doc.ToString(), aesKey);
File.WriteAllText(fileName, encyptedText);
}
public static void DecryptMethod()
{
var fileName = #"c:/text.xml";
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
Byte[] keyBytes = Convert.FromBase64String(sKey);
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);
using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
{
reader.ReadToFollowing("DatabaseServerName");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("DatabaseUserName");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("DatabasePassword");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("RegistrationCode");
Console.WriteLine(reader.ReadElementContentAsString());
}
}
Usage from the console app...
EncryptMethod();
DecryptMethod();

Rijndael Padding Error

Hello I am trying to encrypt / decrypt a string via Rijaendal.
I simply can't figure out why the decryption blows up. I always end up with an incorrect padding error. One thing that throws me off is the result of my encryption which I return as HEX array. It has a length of 14 bytes. In my decryption function, the same byte array ends up having 16 bytes upon conversion from HEX.
Any help would be appreciated:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace rjandal
{
class Program
{
static void Main(string[] args)
{
string DataForEncrypting = "this is a test";
string key = string.Empty;
string iv = string.Empty;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
rmt.GenerateKey();
rmt.GenerateIV();
key = Convert.ToBase64String(rmt.Key);
iv = Convert.ToBase64String(rmt.IV);
}
string encryptedData = _encrypt(DataForEncrypting, key, iv);
string unencryptedData = _decrypt(key, iv, HexString2Ascii(encryptedData));
Console.WriteLine(unencryptedData);
Console.WriteLine(encryptedData);
Console.ReadKey();
}
private static string _encrypt(string value, string key, string initVector)
{
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(value);
byte[] encBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length);
}
string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer));
return encryptValue;
}
private static string _decrypt(string key, string initVector, string value)
{
byte[] hexBuffer = ASCIIEncoding.ASCII.GetBytes(value);
byte[] decBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length);
}
return System.Text.ASCIIEncoding.ASCII.GetString(decBuffer);
}
private static string ConvertToHex(string asciiString)
{
string hex = "";
foreach (char c in asciiString)
{
int tmp = c;
hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
}
return hex;
}
private static string HexString2Ascii(string hexString)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i <= hexString.Length - 2; i += 2)
{
sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hexString.Substring(i, 2), System.Globalization.NumberStyles.HexNumber))));
}
return sb.ToString();
}
}
}
You're doing way too much conversion between text and data, basically. Look at this, for example:
string encryptValue = ConvertToHex(ASCIIEncoding.ASCII.GetString(encBuffer));
Once you've got an ASCII string, why would you need to convert that into hex? It's already text! But by then you'll already have lost the data. Unless you really need it in hex (in which case follow Adam's suggestion and change your HexToAscii method to take a byte[] instead of a string) you should just use Convert.ToBase64String:
string encryptValue = Convert.ToBase64String(encBuffer);
Use Convert.FromBase64String at the other end when decrypting. You can then get rid of your hex methods completely.
Oh, and in general I wouldn't use Encoding.ASCII to start with... I'd almost always use Encoding.UTF8 instead. Currently you'll fail to encrypt (correctly) any strings containing non-ASCII characters such as accents.
Here's a rejigged version of your test program, with a few of those changes made. Note that the names "cipher text" and "plain text" are in terms of encryption... they're still binary data rather than text!
using System;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main(string[] args)
{
string DataForEncrypting = "this is a test";
string key = string.Empty;
string iv = string.Empty;
using (RijndaelManaged rmt = new RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = CipherMode.CBC;
rmt.Padding = PaddingMode.ISO10126;
rmt.GenerateKey();
rmt.GenerateIV();
key = Convert.ToBase64String(rmt.Key);
iv = Convert.ToBase64String(rmt.IV);
}
string encryptedData = _encrypt(DataForEncrypting, key, iv);
string unencryptedData = _decrypt(key, iv, encryptedData);
Console.WriteLine(unencryptedData);
Console.WriteLine(encryptedData);
Console.ReadKey();
}
private static string _encrypt(string value, string key, string initVector)
{
using (RijndaelManaged rmt = new RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = CipherMode.CBC;
rmt.Padding = PaddingMode.ISO10126;
byte[] plainText = Encoding.UTF8.GetBytes(value);
byte[] cipherText = rmt.CreateEncryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector))
.TransformFinalBlock(plainText, 0, plainText.Length);
return Convert.ToBase64String(cipherText);
}
}
private static string _decrypt(string key, string initVector, string value)
{
using (RijndaelManaged rmt = new RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = CipherMode.CBC;
rmt.Padding = PaddingMode.ISO10126;
byte[] cipherText = Convert.FromBase64String(value);
byte[] plainText = rmt.CreateDecryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector))
.TransformFinalBlock(cipherText, 0, cipherText.Length);
return Encoding.UTF8.GetString(plainText);
}
}
}
You shouldn't be using ASCII character encoding as an intermediate step; you should change your functions that go from hex to ASCII (and back again) to go from a byte[] to hex (and back again) instead.
private static string ConvertToHex(byte[] data)
{
string hex = "";
foreach (byte b in data)
{
hex += b.ToString("X2");
}
return hex;
}
private static byte[] HexString2ByteArray(string hexString)
{
byte[] output = new byte[hexString.Length / 2];
for (int i = 0; i <= hexString.Length - 2; i += 2)
{
output[i/2] = Convert.ToByte(hexString.Substring(i, 2), 16);
}
return output;
}
As a side note, is there a reason that you're looking for a hex representation of the array versus something more compact like Base64? You're using Base64 in your example to transfer the key and IV, so I'm just curious about what makes you want to return the encrypted data as hex here.
In any case, here's something that should work for you:
private static string _encrypt(string value, string key, string initVector)
{
byte[] buffer = Encoding.Unicode.GetBytes(value);
byte[] encBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
encBuffer = rmt.CreateEncryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(buffer, 0, buffer.Length);
}
string encryptValue = ConvertToHex(encBuffer);
return encryptValue;
}
private static string _decrypt(string key, string initVector, string value)
{
byte[] hexBuffer = HexString2ByteArray(value);
byte[] decBuffer;
using (System.Security.Cryptography.RijndaelManaged rmt = new System.Security.Cryptography.RijndaelManaged())
{
rmt.KeySize = 256;
rmt.BlockSize = 128;
rmt.Mode = System.Security.Cryptography.CipherMode.CBC;
rmt.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
decBuffer = rmt.CreateDecryptor(Convert.FromBase64String(key),
Convert.FromBase64String(initVector)).TransformFinalBlock(hexBuffer, 0, hexBuffer.Length);
}
return Encoding.Unicode.GetString(decBuffer);
}
You may avoid the issues with Decypting/Encrypting and usign System.Text.Encoding and avoid using Base64 encoding work around, by adding a few methods that completely bypass microsoft's mismatched conversions in the System.Text.Encoding, by allowing you to encrypt the real bytes in memory without any translations.
Since using these I have avoided padding errors caused by System.Text.Encoding methods, without using the Base64 conversions either.
private static Byte[] GetBytes(String SomeString)
{
Char[] SomeChars = SomeString.ToCharArray();
Int32 Size = SomeChars.Length * 2;
List<Byte> TempList = new List<Byte>(Size);
foreach (Char Character in SomeChars)
{
TempList.AddRange(BitConverter.GetBytes(Character));
}
return TempList.ToArray();
}
private static String GetString(Byte[] ByteArray)
{
Int32 Size = ByteArray.Length / 2;
List<Char> TempList = new List<Char>(Size);
for (Int32 i = 0; i < ByteArray.Length; i += 2)
{
TempList.Add(BitConverter.ToChar(ByteArray, i));
}
return new String(TempList.ToArray());
}
And how they are used with encryption
private static String Encrypt(String Test1, Byte[] Key, Byte[] IV)
{
Byte[] Encrypted;
using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider())
{
AesMan.Mode = CipherMode.CBC;
AesMan.Padding = PaddingMode.ISO10126;
ICryptoTransform EncThis = AesMan.CreateEncryptor(Key, IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, EncThis, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Test1);
}
Encrypted = msEncrypt.ToArray();
}
}
};
return GetString(Encrypted);
}
private static String Decrypt(String Data, Byte[] Key, Byte[] IV)
{
String Decrypted;
using (AesCryptoServiceProvider AesMan = new AesCryptoServiceProvider())
{
AesMan.Mode = CipherMode.CBC;
AesMan.Padding = PaddingMode.ISO10126;
ICryptoTransform EncThis = AesMan.CreateDecryptor(Key, IV);
using (MemoryStream msDecrypt = new MemoryStream(GetBytes(Data)))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, EncThis, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
Decrypted = srDecrypt.ReadToEnd();
}
}
}
}
return Decrypted;
}

Categories