AES Encryption in C# and Decryption in JS using crypto-js - c#

UseCase:
I am developing a service that is in C# and I have to send an encrypted string to a different system that is already coded in JS using CryptoJS which will be decrypting the string I am going to be sending.
The receiving system is not going to make any changes to its implementation. They have shared the JS code they are using to DECRYPT along with the required credentials that I would need for my encryption.
JS code of both Encrypt and Decrypt from the receiver
var message = "hello world!!!";
function generateKey(salt, passPhrase, keySize, iterationCount) {
const key = CryptoJS.PBKDF2(passPhrase, CryptoJS.enc.Hex.parse(salt), {
keySize: keySize / 32,
iterations: iterationCount
});
return key;
}
function Encrypt(cipherText) {
const salt = "DUMMYSALT-VALUE-of-Size-32CHARACTERS";
const iv ="DUMMYIV-VALUE-of-Size-32CHARACTERS";
const passPhrase = "SomeSecretPassPhrase";
const keySize = 128
const iterationCount = 1
const key = generateKey(salt, passPhrase, keySize, iterationCount);
const encrypted = CryptoJS.AES.encrypt(cipherText, key, {
iv: CryptoJS.enc.Hex.parse(iv)
});
return encrypted.ciphertext.toString();
}
function Decrypt(cipherText, pass = "SomeSecretPassPhrase") {
const salt = "DUMMYSALT-VALUE-of-Size-32CHARACTERS";
const iv ="DUMMYIV-VALUE-of-Size-32CHARACTERS";
const passPhrase = "SomeSecretPassPhrase";
const keySize = 128
const iterationCount = 1
const key = generateKey(salt, passPhrase, keySize, iterationCount);
const cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Hex.parse(cipherText)
});
const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
iv: CryptoJS.enc.Hex.parse(iv)
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
var encrypted = Encrypt(message);
//equivalent to CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(message), key);
var decrypted = Decrypt(encrypted);
$('#1').text("Encrypted: "+encrypted.toString(CryptoJS.enc.Utf8));
$('#2').text("Decrypted: "+decrypted.toString(CryptoJS.enc.Utf8));
Output
Encrypted: 24a935f11a3e0b35dd6604cd7dbee292
Decrypted: hello world!!!
Question: While I try to port this over the C# and try to encrypt a string using the below, my encrypted value is different and also the JS code is unable to decrypt it.
my code to encrypt:
var MyKey = GenerateKey("DUMMYSALT-VALUE-of-Size-32CHARACTERS", "SomeSecretPassPhrase");
var EncryptedString = EncryptString(MyKey, "hello world!!!");
public byte[] GenerateKey(string saltVal, string passphrase)
{
byte[] salt = ConvertHexStringToByteArray("DUMMYSALT-VALUE-of-Size-32CHARACTERS");
int iterations = 1;
var rfc2898 = new Rfc2898DeriveBytes(passphrase, salt, iterations);
byte[] key = rfc2898.GetBytes(16);
return key;
}
public string EncryptString(byte[] key, string stringToEncrypt )
{
AesManaged aesCipher = new AesManaged();
aesCipher.KeySize = 128;
aesCipher.BlockSize = 128;
aesCipher.Mode = CipherMode.CBC;
aesCipher.Padding = PaddingMode.PKCS7;
aesCipher.Key = key;
aesCipher.IV = ConvertHexStringToByteArray("DUMMYPRESETIV-VALUE-of-Size-32CHARACTERS");
byte[] b = System.Text.Encoding.UTF8.GetBytes(stringToEncrypt);
ICryptoTransform encryptTransform = aesCipher.CreateEncryptor();
byte[] ctext = encryptTransform.TransformFinalBlock(b, 0, b.Length);
System.Console.WriteLine("IV:" + Convert.ToBase64String(aesCipher.IV));
System.Console.WriteLine("Cipher text: " + Convert.ToBase64String(ctext));
return Convert.ToBase64String(ctext);
}
public byte[] ConvertHexStringToByteArray(string hexString)
{
int NumberChars = hexString.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
return bytes;
}
I am not sure if am doing the correct equivalent of the JS method CryptoJS.enc.Hex.parse()
,

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

Encrypt a file in c# and decrypt in flutter

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

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

Only Able to Decrypt Encrypted Data First Time

I have the following code to Encrypt/Decrypt PII data.
public static class Encryption
{
public static readonly int KeyLengthBits = 256; //AES Key Length in bits
public static readonly int SaltLength = 8; //Salt length in bytes
private static readonly RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
private const int PBKDF2SubkeyLength = 256 / 8; // 256 bits
private const int SaltSize = 128 / 8; // 128 bits
public static string DecryptString(string ciphertext, string passphrase)
{
var inputs = ciphertext.Split(":".ToCharArray(), 3);
var iv = Convert.FromBase64String(inputs[0]); // Extract the IV
var salt = Convert.FromBase64String(inputs[1]); // Extract the salt
var ciphertextBytes = Convert.FromBase64String(inputs[2]); // Extract the ciphertext
// Derive the key from the supplied passphrase and extracted salt
byte[] key = DeriveKeyFromPassphrase(passphrase, salt);
// Decrypt
byte[] plaintext = DoCryptoOperation(ciphertextBytes, key, iv, false);
// Return the decrypted string
return Encoding.UTF8.GetString(plaintext);
}
public static string EncryptString(string plaintext, string passphrase)
{
var salt = GenerateRandomBytes(SaltLength); // Random salt
var iv = GenerateRandomBytes(16); // AES is always a 128-bit block size
var key = DeriveKeyFromPassphrase(passphrase, salt); // Derive the key from the passphrase
// Encrypt
var ciphertext = DoCryptoOperation(Encoding.UTF8.GetBytes(plaintext), key, iv, true);
// Return the formatted string
return String.Format("{0}:{1}:{2}", Convert.ToBase64String(iv), Convert.ToBase64String(salt), Convert.ToBase64String(ciphertext));
}
private static byte[] DeriveKeyFromPassphrase(string passphrase, byte[] salt, int iterationCount = 2000)
{
var keyDerivationFunction = new Rfc2898DeriveBytes(passphrase, salt, iterationCount); //PBKDF2
return keyDerivationFunction.GetBytes(KeyLengthBits / 8);
}
private static byte[] GenerateRandomBytes(int lengthBytes)
{
var bytes = new byte[lengthBytes];
rng.GetBytes(bytes);
return bytes;
}
// This function does both encryption and decryption, depending on the value of the "encrypt" parameter
private static byte[] DoCryptoOperation(byte[] inputData, byte[] key, byte[] iv, bool encrypt)
{
byte[] output;
using (var aes = new AesCryptoServiceProvider())
using (var ms = new MemoryStream())
{
var cryptoTransform = encrypt ? aes.CreateEncryptor(key, iv) : aes.CreateDecryptor(key, iv);
using (var cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
cs.Write(inputData, 0, inputData.Length);
output = ms.ToArray();
}
return output;
}
}
If I publish this code and encrypt data and then decrypt it, there's no problem. I correctly receive the unencrypted data. If I rebuild my application without making any changes, then publish it again without re-encrypting the data, (it's already encrypted at this point), I'm unable to decrypt it. It just returns the encrypted strings.
Why would re-publishing to the same server to this? Should I be using a different approach? If so, please recommend that different approach.
Thanks

C# / Java | AES256 encrypt/decrypt

I want to encrypt all the data I send through the Java/C# sockets (Java server, C# client).
I would like to use AES256, but I can't get the Java and C# to generate the same encrypted code. Can anyone give me two examples, 1 in Java and 1 in C# that generate the same results and decrypts the results properly?
What I tried so far:
public Encrypt(AOBCore instance){
try {
String message="This is just an example";
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(256); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey(); //Cantget 'test' in here...
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted =
cipher.doFinal(message.getBytes());
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original =
cipher.doFinal(encrypted);
String originalString = new String(original);
System.out.println("Original string: " +
originalString + " " + asHex(original));
} catch (Exception e) {
instance.logMessage(e.getMessage());
}
}
public static String asHex (byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
static void Main(string[] args)
{
while (true)
{
var plain = Console.ReadLine();
var key = GenerateKey(256);
var encoded = Encrypt(plain, key, 256);
Console.WriteLine("Encoded: " + encoded);
Console.WriteLine(Decrypt(encoded, key, 256));
}
}
private static string GenerateKey(int keySize)
{
return "test";
}
private static string Encrypt(string plainStr, string completeEncodedKey, int keySize)
{
RijndaelManaged aesEncryption = new RijndaelManaged();
aesEncryption.KeySize = keySize;
aesEncryption.BlockSize = 256;
aesEncryption.Mode = CipherMode.CBC;
aesEncryption.Padding = PaddingMode.PKCS7;
aesEncryption.IV = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[0]);
aesEncryption.Key = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[1]);
byte[] plainText = ASCIIEncoding.UTF8.GetBytes(plainStr);
ICryptoTransform crypto = aesEncryption.CreateEncryptor();
// The result of the encryption and decryption
byte[] cipherText = crypto.TransformFinalBlock(plainText, 0, plainText.Length);
return Convert.ToBase64String(cipherText);
}
private static string Decrypt(string encryptedText, string completeEncodedKey, int keySize)
{
RijndaelManaged aesEncryption = new RijndaelManaged();
aesEncryption.KeySize = keySize;
aesEncryption.BlockSize = 128;
aesEncryption.Mode = CipherMode.CBC;
aesEncryption.Padding = PaddingMode.PKCS7;
aesEncryption.IV = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[0]);
aesEncryption.Key = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[1]);
ICryptoTransform decrypto = aesEncryption.CreateDecryptor();
byte[] encryptedBytes = Convert.FromBase64CharArray(encryptedText.ToCharArray(), 0, encryptedText.Length);
return ASCIIEncoding.UTF8.GetString(decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length));
}
The problem is that you aren't specifying the ciphermode or the padding in the Java code. This will use the algorithm defaults, which is never something you want to do when interoperability with other libraries is required. Initialize your Cipher like this:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
PKCS5 in Java should be compatible with PKCS7 in .Net according to this answer. Since you are wisely using CBC you are going to need to modify the code to use the same initialization vector for both encryption and decryption. You should NOT use the secret key for that. The IV should be randomly generated. You can use the IV that the Java Cipher generated for encryption by calling cipher.getIV().
Also, take care to be consistent with character encodings as has been mentioned in the comments.

Categories