I am getting above mentioned error during a hybrid cryptography implementation.
as per https://en.wikipedia.org/wiki/Hybrid_cryptosystem
I am just stucked at the last step
My code is
private void button1_Click(object sender, EventArgs e)
{
try
{
CspParameters cspParams = new CspParameters { ProviderType = 1 };
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(2048, cspParams);
string publicKey =lblPublicKey.Text = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = lblPrivateKey.Text= Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
string symmericKey = txtBoxSymmetricKey.Text = "Kamran12";
txtEncryptedData.Text = EncryptData(txtInputData.Text, symmericKey);
txtBoxEncryptedSymmetricKey.Text = RSA_Encrypt(symmericKey, publicKey);
txtBoxDescryptedSymmetricKey.Text = RSA_Decrypt(txtBoxEncryptedSymmetricKey.Text, privateKey);
txtDecryptedData.Text = DecryptData(txtEncryptedData.Text, txtBoxDescryptedSymmetricKey.Text); //getting error length of the data to decrypt is invalid
}
catch (Exception exc)
{
}
}
public static string RSA_Decrypt(string encryptedText, string privateKey)
{
CspParameters cspParams = new CspParameters { ProviderType = 1 };
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);
rsaProvider.ImportCspBlob(Convert.FromBase64String(privateKey));
var buffer = Convert.FromBase64String(encryptedText);
byte[] plainBytes = rsaProvider.Decrypt(buffer, false);
string plainText = Encoding.UTF8.GetString(plainBytes, 0, plainBytes.Length);
return plainText;
}
public static string RSA_Encrypt(string data, string publicKey)
{
CspParameters cspParams = new CspParameters { ProviderType = 1 };
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);
rsaProvider.ImportCspBlob(Convert.FromBase64String(publicKey));
byte[] plainBytes = Encoding.UTF8.GetBytes(data);
byte[] encryptedBytes = rsaProvider.Encrypt(plainBytes, false);
return Convert.ToBase64String(encryptedBytes);
}
public string EncryptData(string data, string key)
{
string encryptedData = null;
byte[] buffer = Encoding.UTF8.GetBytes(data);
DESCryptoServiceProvider desCryptSrvckey = new DESCryptoServiceProvider
{
Key = new UTF8Encoding().GetBytes(key)
};
desCryptSrvckey.IV = desCryptSrvckey.Key;
using (MemoryStream stmCipherText = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(stmCipherText, desCryptSrvckey.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
cs.FlushFinalBlock();
encryptedData = Encoding.UTF8.GetString(stmCipherText.ToArray());
}
}
return encryptedData;
}
public string DecryptData(string data, string key)
{
byte[] encryptedMessageBytes = Encoding.UTF8.GetBytes(data);
string decryptedData = null;
DESCryptoServiceProvider desCryptSrvckey = new DESCryptoServiceProvider
{
Key = new UTF8Encoding().GetBytes(key)
};
desCryptSrvckey.IV = desCryptSrvckey.Key;
using (MemoryStream encryptedStream = new MemoryStream(encryptedMessageBytes))
{
using (
CryptoStream cs = new CryptoStream(encryptedStream, desCryptSrvckey.CreateDecryptor(),
CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decryptedData = sr.ReadToEnd();
}
}
}
return decryptedData;
}
You declare encryptedData as a string. This is incorrect. Your encrypted data is bytes, not a character string. Attempting to convert raw bytes to UTF-8 text, as in encryptedData = Encoding.UTF8.GetString(stmCipherText.ToArray()); will not result in UTF-8 text but give you garbage and possibly lose data.
If you want the output from your encryption to be as text, then take the cyphertext bytes and use Convert.ToBase64String() to turn them into a text string.
When decrypting, convert the Base64 string back into bytes and decrypt the bytes.
Related
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);
}
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.
I have two strings as bellow:
String A = 29CEDC4A435DD625 (key)
String B = 5272B624753D79AF (encodedText)
I try to do decrypt string B by String A to get out the result: 8BC141DCC122B74F but don't successful with the decrypt function as follow:
public static string Decrypt (string encodedText, string key)
{
TripleDESCryptoServiceProvider desCrytoProvider = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashMD5Provider = new MD5CryptoServiceProvider();
byte[] byteHash;
byte[] byteBuff;
byteHash = hashMD5Provider.ComputeHash(Encoding.UTF8.GetBytes(key));
desCrytoProvider.Key = byteHash;
desCrytoProvider.Mode = CipherMode.ECB;
byteBuff = Convert.FromBase64String(encodedText);
string plaintext = Encoding.UTF8.GetString(desCrytoProvider.CreateDecryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
return plaintext;
}
I think you have a trouble with converting from byte array to String and vice versa. Try to use Convert.ToBase64String(...), Convert.FromBase64String(...). It works:
static TripleDESCryptoServiceProvider desCrytoProvider = new TripleDESCryptoServiceProvider();
static MD5CryptoServiceProvider hashMD5Provider = new MD5CryptoServiceProvider();
static void Main(string[] args)
{
String key = "29CEDC4A435DD625";//(key)
String encodedText = Encrypt("just4fun", key);
Console.WriteLine(Decrypt(encodedText, key));
}
public static String Encrypt(String text, String key)
{
byte[] byteHash;
byte[] byteBuff;
var bytes = Encoding.UTF8.GetBytes(key);
byteHash = hashMD5Provider.ComputeHash(bytes);
desCrytoProvider.Key = byteHash;
desCrytoProvider.Mode = CipherMode.ECB;
byteBuff = Encoding.UTF8.GetBytes(text);
return Convert.ToBase64String(desCrytoProvider.CreateEncryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
}
public static String Decrypt(String encodedText, String key)
{
byte[] byteHash;
byte[] byteBuff;
var bytes = Encoding.UTF8.GetBytes(key);
byteHash = hashMD5Provider.ComputeHash(bytes);
desCrytoProvider.Key = byteHash;
desCrytoProvider.Mode = CipherMode.ECB;
byteBuff = Convert.FromBase64String(encodedText);
return Encoding.UTF8.GetString(desCrytoProvider.CreateDecryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
}
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;
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();