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
Related
I am trying to Encrypt and Decrypt a string using AES256. But The decrypted string is not matching the original one. I am not sure, but maybe I am getting the Encoding part wrong.
I am using CSPRNG to generate the IV and PBDKF2 for generating a Key to be used for AES Encryption
Program.cs:
using System;
using System.Text;
namespace AESEncryptionUtility
{
class Program
{
private static string _pass = "MasterPass";
private static string _msg = "Mohit";
private static byte[] key = EncryptionUtility.GenerateKey(_pass, 32);
private static byte[] IV = EncryptionUtility.GenerateSalt(16);
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
byte[] encrypted = Encrypt(_msg);
byte[] decrypted = Decrypt(Encoding.ASCII.GetString(encrypted));
}
public static byte[] Encrypt(string msg)
{
byte[] asciiBytesOriginal = Encoding.ASCII.GetBytes(_msg);
byte[] encrypted = EncryptionUtility.Encrypt(asciiBytesOriginal, key, IV);
Console.WriteLine("encrypted started");
foreach(var b in encrypted)
{
Console.Write(b + " ");
}
Console.WriteLine("\nencrypted ended");
return encrypted;
}
public static byte[] Decrypt(string cipher)
{
byte[] asciiBytes = Encoding.ASCII.GetBytes(cipher);
byte[] originalBytes = EncryptionUtility.Decrypt(asciiBytes, key, IV);
Console.WriteLine("decrypted started");
foreach(var b in originalBytes)
{
Console.Write(b + " ");
}
Console.WriteLine("\ndecrypted ended");
string original = Encoding.ASCII.GetString(originalBytes);
Console.WriteLine("original string: " + original);
return originalBytes;
}
}
}
EncryptionUtility.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace AESEncryptionUtility
{
public static class EncryptionUtility
{
public static byte[] Encrypt(byte[] plainBytes, byte[] key, byte[] IV)
{
byte[] encrypted = null;
using (AesCryptoServiceProvider aesAlgo = new AesCryptoServiceProvider())
{
aesAlgo.Key = key;
aesAlgo.BlockSize = 128;
aesAlgo.Mode = CipherMode.CBC;
//aesAlgo.Padding = PaddingMode.PKCS7;
aesAlgo.Padding = PaddingMode.Zeros;
aesAlgo.IV = IV;
ICryptoTransform encryptor = aesAlgo.CreateEncryptor();
encrypted = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
}
return encrypted;
}
public static byte[] Decrypt(byte[] cipherBytes, byte[] key, byte[] IV)
{
byte[] decrypted = null;
using (AesCryptoServiceProvider aesAlgo = new AesCryptoServiceProvider())
{
aesAlgo.Key = key;
aesAlgo.BlockSize = 128;
aesAlgo.Mode = CipherMode.CBC;
//aesAlgo.Padding = PaddingMode.PKCS7;
aesAlgo.Padding = PaddingMode.Zeros;
aesAlgo.IV = IV;
ICryptoTransform decryptor = aesAlgo.CreateDecryptor();
decrypted = decryptor.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
}
return decrypted;
}
public static byte[] GenerateKey(string masterPassword, int size) //size in bytes
{
byte[] salt = GenerateSalt(size);
Rfc2898DeriveBytes pbfdk = new Rfc2898DeriveBytes(masterPassword, salt, 20000);
return pbfdk.GetBytes(size);
}
public static byte[] GenerateSalt(int size) //size in bytes
{
RNGCryptoServiceProvider generator = new RNGCryptoServiceProvider();
byte[] salt = new byte[size];
generator.GetNonZeroBytes(salt);
return salt;
}
}
}
You can't convert arbitrary binary data to a string:
byte[] decrypted = Decrypt(Encoding.ASCII.GetString(encrypted));
And expect it to, just by chance, make sense as the particular character encoding you have chosen. It doesn't work that way. Encryption algorithms operate on bytes, not strings. If you change the following, your code will work:
...
public static byte[] Decrypt(byte[] cipher)
{
byte[] asciiBytes = cipher;
...
Have you tried changing the second last line to:
byte [] decrypted = Decrypt ( encrypted);
I had andriod code and I tried to convert it to c#. It's a simple Encryption class. But when I try to decrypt data with it I catch: Wrong algorithm: AES or Rijndael required.
Here is my converted code:
public static string decrypt(string data)
{
byte[] dataBytes = Convert.FromBase64String(data);
SecretKey secretKey = getSecretKey(hashTheKey("ABCD"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, secretKey, new IvParameterSpec(new byte[16]),
SecureRandom.getInstance("SHA1PRNG"));
var x = cipher.doFinal(dataBytes);
return System.Text.Encoding.UTF8.GetString(x);
}
public static SecretKey getSecretKey(char[] key)
{
var secretKeyType = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
var secretkey = secretKeyType.generateSecret(new PBEKeySpec(key,
System.Text.Encoding.UTF8
.GetBytes("ABCD"),
100, 128)).getEncoded();
return new SecretKeySpec(secretkey, "AES/CBC/PKCS5Padding");
}
public static char[] hashTheKey(string key)
{
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(System.Text.Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(messageDigest.digest()).ToCharArray();
}
Here is my original android code:
private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(key.getBytes());
return Base64.encodeToString(messageDigest.digest(),
Base64.NO_PADDING).toCharArray();
}
private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
return new SecretKeySpec(
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
.generateSecret(new PBEKeySpec(key,
"ABCD".getBytes("UTF8"),
100, 128)).getEncoded(), "AES");
}
public String decrypt(String data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeySpecException {
byte[] dataBytes = Base64.decode(data, Base64.DEFAULT);
SecretKey secretKey = getSecretKey(hashTheKey("ABCD"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, secretKey, new IvParameterSpec(new byte[16]),
SecureRandom.getInstance("SHA1PRNG"));
return new String(cipher.doFinal(dataBytes));
}
c# and java are using the same well-estabilished cryptography algorithms, but differs in approach how to invoke them. It is still possible to convert the code though.
One key point is difference in base64 encoding - C# always use padding.
Converted code goes like:
const int KeySize = 128;
static string HashTheKey(string key) {
String hashKey;
using (var sha = new SHA1Managed()) {
hashKey = Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(key)));
}
// beware - you're on C# now so remove the padding and add the newline to match java
return hashKey.Replace("=", "") + "\n";
}
static byte[] GetSecretKey(string password) {
var salt = Encoding.UTF8.GetBytes("JVAaVhAiddKAaghraikhmaini");
using (var pass = new Rfc2898DeriveBytes(password, salt, 65536)) {
return pass.GetBytes(KeySize / 8);
}
}
static void Main(string[] args) {
string encrypted = "vtlkQHTz7/oz2weuAAkLz2Q5c2yj2LGukF7SHJjT+TA8oRLixTQSXQ7dG1O736hyT1HJxcz0P4DzzVaO5chWKKSJQ2uPEpDQJu/fZGguqDw=";
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
using (var aes = new AesManaged()) {
aes.KeySize = KeySize;
aes.Padding = PaddingMode.PKCS7;
aes.Key = GetSecretKey(HashTheKey("Android"));
// you're using the same init vector in your android code
aes.IV = new byte[16];
using (var decryptor = aes.CreateDecryptor()) {
// dumps {"barcode":"12345678","token":"cad603fc-1e53-4a95-9150-f1694baa07f9"}
Console.Out.WriteLine(Encoding.UTF8.GetString(decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length)));
}
}
}
C# does not handle the encryption algorithms as Android or java do you have to use either AES or Rijndael algorithm as you can see the error to covert to the simple text into Encrypted Base64 and vice versa you can use the following class in C#
public static class Stringcipher
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 256;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
I am working on re-writing our encryption class to be FIPS compliant, and in doing so have to re-work how we're handling non-secret payload data. At the moment, I'm writing out the size of my non-secret payload, then writing the size of my IV. I follow that up by writing my non-secret payload and IV, with all of these writes sharing a BinaryWriter. Lastly, I then share the same MemoryStream and write my the data needing to be encrypted into the the CryptoStream.
This is what the class currently looks like:
public class Encryption
{
private const int SaltBlockSize = 8;
private const int SaltBitSize = 64;
private const int KeyBitSize = 256;
private const int SaltIterations = 10000;
private const int nonSecretPayloadOffsetInPayload = 0;
private const int ivOffsetInPayload = 1;
public byte[] GetNonSecretPayload(byte[] completePayload)
{
byte[] nonSecretPayload;
using (var memoryStream = new MemoryStream(completePayload))
{
using (var binaryReader = new BinaryReader(memoryStream))
{
int nonSecretPayloadLength = binaryReader.ReadInt32();
binaryReader.BaseStream.Position = 3;
nonSecretPayload = binaryReader.ReadBytes(nonSecretPayloadLength);
}
}
return nonSecretPayload;
}
public byte[] EncryptMessageWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(password))
{
throw new InvalidOperationException("You can not provide an empty password, you must give a string that is at least 12 characters in size. If you just want to obfuscate the message without any protection, an alternative way is to use a Base64 String");
}
else if (password.Length < 12)
{
throw new InvalidOperationException("The minimum size your password can be is 12 characters.");
}
byte[] saltHash;
byte[] saltKey = this.CreateSaltKeysFromPassword(password, 0, out saltHash);
byte[] encryptedValue = null;
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = saltKey;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.GenerateIV();
using (MemoryStream memoryStream = new MemoryStream())
{
// Write our IV out first so we can pull the IV off later during decryption.
// The IV does not need to be encrypted, it is safe to store as as unencrypted buffer in the encrypted byte array.
using (BinaryWriter ivWriter = new BinaryWriter(memoryStream, Encoding.UTF8, true))
{
// The first two writes to the stream should be the size of the non-secret payload
// and the size of the IV. If no payload exists, then we write 0.
if (nonSecretPayload == null || nonSecretPayload.Length == 0)
{
ivWriter.Write(0);
}
else
{
ivWriter.Write(nonSecretPayload.Length);
}
ivWriter.Write(aesProvider.IV.Length);
// If we have a payload, write it out.
if (nonSecretPayload != null && nonSecretPayload.Length > 0)
{
ivWriter.Write(nonSecretPayload);
}
// Write the Initialization Vector.
ivWriter.Write(aesProvider.IV);
}
// Create our encryptor and write the secret message to the encryptor stream.
var encryptor = aesProvider.CreateEncryptor(saltKey, aesProvider.IV);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(secretMessage, 0, secretMessage.Length);
cryptoStream.FlushFinalBlock();
}
// Get the non-secret payload, IV, payload and IV lengths and encrypted data back as an array of bytes.
encryptedValue = memoryStream.ToArray();
}
}
return encryptedValue;
}
public string EncryptMessageWithPassword(string secretMessage, string password, byte[] nonSecretPayLoad = null)
{
byte[] secreteMessageBytes = Encoding.UTF8.GetBytes(secretMessage);
byte[] encryptedMessage = this.EncryptMessageWithPassword(secreteMessageBytes, password, nonSecretPayLoad);
return Convert.ToBase64String(encryptedMessage);
}
private byte[] CreateSaltKeysFromPassword(string password, int nonSecretPayloadSize, out byte[] saltHash)
{
byte[] saltKey;
//Use Random Salt to prevent pre-generated weak password attacks.
using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / SaltBlockSize, SaltIterations))
{
// Get a generated salt derived from the user password, hashed n-times where n = SaltIterations
saltHash = generator.Salt;
//Generate Keys
saltKey = generator.GetBytes(KeyBitSize / SaltBlockSize);
}
return saltKey;
}
}
I would expect in my GetNonSecretPayload(byte[] payload); that by setting the position, or using binaryReader.BaseStream.Seek(2); to skip the IV length item, I would skip the IV size entry in the byte[] array and be able to read the bytes associated with the actual non-secret data. This doesn't work though, presumably because this isn't an array underneath the covers that I can just move to the next element in the array, skipping the IV length wrote out originally.
I have the following unit test.
[TestClass]
public class EncryptionTests
{
private const string _ContentToEncrypt = "This is a test to make sure the encryption Type actually encrypts the data right.";
private const string _Password = "EncryptedPassword1";
[TestMethod]
public void Extract_non_secret_payload_content_from_encrypted_string()
{
// Arrange
var encryption = new Encryption();
string nonSecretData = "My payload is not considered secret and can be pulled out of the payload without decrypting";
// Convert the secret and non-secret data into a byte array
byte[] payload = Encoding.UTF8.GetBytes(nonSecretData);
byte[] encodedBytes = Encoding.UTF8.GetBytes(_ContentToEncrypt);
// Encrypt the secret data while injecting the nonsecret payload into the encrypted stream.
byte[] encryptedValue = encryption.EncryptMessageWithPassword(encodedBytes, _Password, payload);
// Act
// Pull the non-secret payload out of the encrypted message - without having to decrypt it.
byte[] UnencryptedPayloadWithinEncryptedArray = encryption.GetNonSecretPayload(encryptedValue);
string payloadContent = Encoding.UTF8.GetString(UnencryptedPayloadWithinEncryptedArray);
// Assert
Assert.AreEqual(nonSecretData, payloadContent);
}
}
What I get with my current binaryReader.BaseStream.Position = 3 is
"\0\u0010\0\0\0My payload is not considered secret and can be pulled out of the payload without decry"
I've read and wrote data like this in the past using a BinaryWriter, but I've never had to seek through it in order to skip data. What am I doing wrong here?
Getting Exception " length of the data to ENCRYPTION is invalid".
private static readonly byte[] salt = Encoding.ASCII.GetBytes("S#sh#kt# VMS");
public static string Encrypt(string textToEncrypt, string encryptionPassword)
{
byte[] encryptedBytes = null;
try
{
var algorithm = GetAlgorithm(encryptionPassword);
algorithm.Padding = PaddingMode.None;
using (ICryptoTransform encryptor = algorithm.CreateEncryptor(algorithm.Key, algorithm.IV))
{
byte[] bytesToEncrypt = Encoding.UTF8.GetBytes(textToEncrypt);
encryptedBytes = InMemoryCrypt(bytesToEncrypt, encryptor);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return Convert.ToBase64String(encryptedBytes);
}
// Performs an in-memory encrypt/decrypt transformation on a byte array.
private static byte[] InMemoryCrypt(byte[] data, ICryptoTransform transform)
{
MemoryStream memory = new MemoryStream();
using (Stream stream = new CryptoStream(memory, transform, CryptoStreamMode.Write))
{
stream.Flush();
stream.Write(data, 0, data.Length);
//stream.FlushFinalBlock();
}
return memory.ToArray();
}
private static RijndaelManaged GetAlgorithm(string encryptionPassword)
{
// Create an encryption key from the encryptionPassword and salt.
var key = new Rfc2898DeriveBytes(encryptionPassword, salt);
// Declare that we are going to use the Rijndael algorithm with the key that we've just got.
var algorithm = new RijndaelManaged();
int bytesForKey = algorithm.KeySize/8;
int bytesForIV = algorithm.BlockSize/8;
algorithm.Key = key.GetBytes(bytesForKey);
algorithm.IV = key.GetBytes(bytesForIV);
return algorithm;
}
And the decryption routine is:
public static string Decrypt(string encryptedText, string encryptionPassword)
{
var algorithm = GetAlgorithm(encryptionPassword);
algorithm.Padding = PaddingMode.PKCS7;
byte[] descryptedBytes;
using (ICryptoTransform decryptor = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV))
{
byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
descryptedBytes = InMemoryCrypt(encryptedBytes, decryptor);
}
return Encoding.UTF8.GetString(descryptedBytes);
}
PaddingMode.None requires that the input is a multiple of the block size. Use somethink like PaddingMode.PKCS7 instread.
A few other issues with your code:
A constant doesn't make a good salt
The constant salt together with deterministic derivation of the IV from the password means that you're reusing (Key, IV) pairs, which should not be done
You don't add authentication/some kind of MAC. That often leads to padding oracles or similar attacks
You read more the native size from the PBKDF2 output. That halves your key derivation speed without slowing down an attacker.
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.