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;
}
Related
I have logic that works perfectly in C# for encrypting and decrypting text using AES CBC 128 Bit
Now I have a problem where the other party cannot decrypt the text and nor can this site:
https://www.devglan.com/online-tools/aes-encryption-decryption
How can I get the IV into a version that can be used to decrypt outside of C#?
I tried
stream = encryptionInfo.InversionVectorText.ToMemoryStream(Encoding.ASCII);
also
stream = encryptionInfo.InversionVectorText.ToMemoryStream(Encoding.UTF8);
Neither of these give me a value that I can paste into the site above and have work
The other side want me to send the IV in plain text
My full code is below for reference:
public static class SecurityExtensions
{
private static Aes GetAes(string keyText, byte[] iv)
{
var key = keyText.ToByteArray();
var result = Aes.Create();
result.Mode = CipherMode.CBC;
result.KeySize = 128;
if (iv.Length > 0)
{
result.IV = iv;
}
result.Key = key;
return result;
}
private static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
{
var result = crypto.GenerateRandomCryptoString(length, charset);
return result;
}
}
private static string GenerateRandomCryptoString(this RNGCryptoServiceProvider random,
int length,
string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
RandomString(random.GetBytes, length, charset.ToCharArray());
private static string RandomString(Action<byte[]> fillRandomBuffer, int length, char[] charset)
{
var maxIdx = charset.Length;
var chars = new char[length];
var randomBuffer = new byte[length * 4];
fillRandomBuffer(randomBuffer);
for (var i = 0; i < length; i++)
chars[i] = charset[BitConverter.ToUInt32(randomBuffer, i * 4) % maxIdx];
var result = new string(chars);
return result;
}
public static AesEncryptionInfo EncryptWithAes(this string plainText, string keyText)
{
//Generate an IV made up of only alphanumeric characters to avoid encoding/decoding issues
var ivText = GenerateRandomCryptoString(16);
var iv = Encoding.ASCII.GetBytes(ivText);
var aesAlg = GetAes(keyText, iv);
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
byte[] encrypted;
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
aesAlg.Dispose();
var result = new AesEncryptionInfo(encrypted, aesAlg.IV);
return result;
}
public static string DecryptFromAes(this byte[] cipherText, string keyText, byte[] iv)
{
string result;
var aesAlg = GetAes(keyText, iv);
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
return result;
}
}
public class AesEncryptionInfo
{
public AesEncryptionInfo(byte[] encrypted, byte[] inversionVector)
{
InversionVector = inversionVector;
Encrypted = encrypted;
}
public byte[] InversionVector { get; set; }
public byte[] Encrypted { get; set; }
public string InversionVectorText => Encoding.Default.GetString(InversionVector);
}
The other side are adamant they do not want to do any kind of decoding, which is obviously not a good approach!
So I have some code that generates a random 16 character string of numbers or letters. This definitely generates a string of 16 characters.
When I decrypt this using the logic above the text is decrypted correctly, but I also see random characters at the start. I dont know why this happens?
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);
}
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;
}
}
I want to encrypt a text in PHP and to decrypt in in C#, but i can't.
This is my PHP code:
define('AES_256_ECB', 'aes-256-ecb');
$encryption_key = "SomeSimpleTest";
$data = "Test123";
$encryptedData = openssl_encrypt($data, AES_256_ECB, $encryption_key, 0);
..and this is my C# code:
(AESEncryption.cs class)
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
namespace AESCrypto
{
class AESEncryption
{
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here to meet your flavor:
byte[] saltBytes = passwordBytes;
// Example:
//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 = 256;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.ECB;
//AES.Padding = PaddingMode.PKCS7;
using (CryptoStream cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
public static string Decrypt(string decryptedText, byte[] passwordBytes)
{
byte[] bytesToBeDecrypted = Convert.FromBase64String(decryptedText);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] decryptedBytes = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
// Getting the size of salt
int saltSize = GetSaltSize(passwordBytes);
// Removing salt bytes, retrieving original bytes
byte[] originalBytes = new byte[decryptedBytes.Length - saltSize];
for (int i = saltSize; i < decryptedBytes.Length; i++)
{
originalBytes[i - saltSize] = decryptedBytes[i];
}
return Encoding.UTF8.GetString(originalBytes);
}
public static int GetSaltSize(byte[] passwordBytes)
{
var key = new Rfc2898DeriveBytes(passwordBytes, passwordBytes, 1000);
byte[] ba = key.GetBytes(2);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ba.Length; i++)
{
sb.Append(Convert.ToInt32(ba[i]).ToString());
}
int saltSize = 0;
string s = sb.ToString();
foreach (char c in s)
{
int intc = Convert.ToInt32(c.ToString());
saltSize = saltSize + intc;
}
return saltSize;
}
public static byte[] GetRandomBytes(int length)
{
byte[] ba = new byte[length];
RNGCryptoServiceProvider.Create().GetBytes(ba);
return ba;
}
}
}
Usage of it:
using AESCrypto;
...
public string DecryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeDecrypted = Convert.FromBase64String(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesDecrypted = AESEncryption.AES_Decrypt(bytesToBeDecrypted, passwordBytes);
string result = Encoding.UTF8.GetString(bytesDecrypted);
return result;
}
private void btn1_Click(object sender, EventArgs e)
{
textBox1.Text = DecryptText("KEY_ENCRYPTED_WITH_PHP", "SomeSimpleTest");
}
I even tried with CBC but does not work...The mode of encryption is not important. I only want to make it work as it should.
Thanks.
php code:
define('AES_128_ECB', 'aes-128-ecb');
$encryption_key = "MY_16_CHAR_KEY:)";
$data = "MyOwnEncryptedSecretText";
$encryptedData = openssl_encrypt($data, AES_128_ECB, $encryption_key, 0);
C# code:
public String Decrypt(String text, String key)
{
//decode cipher text from base64
byte[] cipher = Convert.FromBase64String(text);
//get key bytes
byte[] btkey = Encoding.ASCII.GetBytes(key);
//init AES 128
RijndaelManaged aes128 = new RijndaelManaged();
aes128.Mode = CipherMode.ECB;
aes128.Padding = PaddingMode.PKCS7;
//decrypt
ICryptoTransform decryptor = aes128.CreateDecryptor(btkey, null);
MemoryStream ms = new MemoryStream(cipher);
CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
byte[] plain = new byte[cipher.Length];
int decryptcount = cs.Read(plain, 0, plain.Length);
ms.Close();
cs.Close();
//return plaintext in String
return Encoding.UTF8.GetString(plain, 0, decryptcount);
}
and usage of it:
string DecryptedText = Decrypt("GENERATED_KEY", "MY_16_CHAR_KEY:)");
Now it works great :)
Thanks.
Why it does get wrong results?
It not pkcs7 supported by the crypto ++?
I would like to know the value of the result to be like what to do.
Iv value is equal to the supposed well-delivered.
// c# code
private byte[] _iv;
private readonly string key = "7794b12op901252bfcea66d6f0521212";
public string decrypt(string Input)
{
string str = "";
RijndaelManaged managed = new RijndaelManaged();
managed.KeySize = 128;
managed.BlockSize = 128;
managed.Mode = CipherMode.CBC;
managed.Padding = PaddingMode.Zeros;
managed.Key = Encoding.UTF8.GetBytes(this.key);
managed.IV = this._iv;
try
{
ICryptoTransform transform = managed.CreateDecryptor();
byte[] bytes = null;
using (MemoryStream stream = new MemoryStream())
{
using (CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
byte[] buffer = Convert.FromBase64String(Input);
stream2.Write(buffer, 0, buffer.Length);
}
bytes = stream.ToArray();
}
str = Encoding.ASCII.GetString(bytes);
}
catch (Exception)
{
}
return str;
}
public string encrypt(string Input)
{
RijndaelManaged managed = new RijndaelManaged();
managed.KeySize = 128;
managed.BlockSize = 128;
managed.Mode = CipherMode.CBC;
managed.Padding = PaddingMode.Zeros;
managed.Key = Encoding.ASCII.GetBytes(this.key);
managed.GenerateIV();
this._iv = managed.IV;
ICryptoTransform transform = managed.CreateEncryptor(managed.Key, managed.IV);
byte[] inArray = null;
using (MemoryStream stream = new MemoryStream())
{
using (CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
byte[] bytes = Encoding.UTF8.GetBytes(Input);
stream2.Write(bytes, 0, bytes.Length);
}
inArray = stream.ToArray();
}
return Convert.ToBase64String(inArray);
}
Below is qt5 code.
Omit details.
QT code
QString aeskey = "7794b12op901252bfcea66d6f0521212";
QString _iv;
void Cipher::GenerateIV()
{
AutoSeededRandomPool rnd;
byte iv3[AES::BLOCKSIZE];
rnd.GenerateBlock(iv3, AES::BLOCKSIZE);
QByteArray out((char*)iv3, AES::BLOCKSIZE);
_iv = out.toBase64();
}
QString Cipher::AESencrypt(QString Qstr_in)
{
string str_in = Qstr_in.toStdString();
string key = aeskey.toStdString();
GenerateIV();
string iv = _iv.toStdString();
string str_out;
CBC_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV((byte*)key.c_str(), key.length(), (byte*)iv.c_str());
StringSource encryptor(str_in, true,
new StreamTransformationFilter(encryption,
new Base64Encoder(
new StringSink(str_out)
// ,StreamTransformationFilter::PKCS_PADDING
,StreamTransformationFilter::ZEROS_PADDING
)
)
);
return QString::fromStdString(str_out);
}
QString Cipher::AESdecrypt(QString Qstr_in)
{
string str_in = Qstr_in.toStdString();
string key = aeskey.toStdString();
string iv = _iv.toStdString();
string str_out;
CBC_Mode<AES>::Decryption decryption;
decryption.SetKeyWithIV((byte*)key.c_str(), key.length(), (byte*)iv.c_str());
StringSource decryptor(str_in, true,
new Base64Decoder(
new StreamTransformationFilter(decryption,
new StringSink(str_out)
// ,StreamTransformationFilter::PKCS_PADDING
,StreamTransformationFilter::DEFAULT_PADDING
)
)
);
return QString::fromStdString(str_out);
}
I don't understand really what your question is and I can't really comment so here what I think:
ICryptoTransform transform = managed.CreateEncryptor(managed.Key, managed.IV);
ICryptoTransform transform = managed.CreateDecryptor();
Both need key and IV, or at least need to be the same....
Then you used once Rijndael then AES. You could use AES in you C# too.
A couple things jump out... In C# code, you do this:
private readonly string key = "7794b12op901252bfcea66d6f0521212";
...
managed.Key = Encoding.UTF8.GetBytes(this.key);
In Crypto++ code, you do this:
QString aeskey = "7794b12op901252bfcea66d6f0521212";
...
string key = aeskey.toStdString();
You need to HexDecode the string in Crypto++.
Also, GenerateIV Base64 encodes on the Qt side of things:
AutoSeededRandomPool rnd;
byte iv3[AES::BLOCKSIZE];
rnd.GenerateBlock(iv3, AES::BLOCKSIZE);
QByteArray out((char*)iv3, AES::BLOCKSIZE);
_iv = out.toBase64();
But C# uses a byte[] (presumably not Base64 encoded):
private byte[] _iv;