Special characters at the beginning of message after decryption - java

I'm encrypting a JSON message and sending it to the user. When they decrypt it, some of the messages show special characters at the beginning of the messages.
When i tried to decrypt from my side it's working fine. If they are decrypting it it shows special characters, but only for some messages as some are decrypting just fine.
Below is the Java code I am using to encrypt the messages and also adding code they are using to decrypt in .NET. Please help me understand this situation and why it's happening.
JAVA CODE(Encryption):
package com.kcs.mule.encryption;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
import org.mule.api.MuleEventContext;
import org.mule.api.lifecycle.Callable;
import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AESEncryption implements Callable {
private static final String password = "fvtQhQcKZVWMCXRLbqmRgfEBXYWshTEP";
private static int pswdIterations = 65536;
private static int keySize = 256;
private static byte[] ivBytes;
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
String plainText = eventContext.getMessageAsString();
byte[] saltBytes = password.getBytes("UTF-8");
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
// encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
byte[] encryptedOutput = new byte[ivBytes.length+encryptedTextBytes.length];
System.arraycopy(ivBytes, 0, encryptedOutput, 0, ivBytes.length);
System.arraycopy(encryptedTextBytes,0,encryptedOutput,ivBytes.length,encryptedTextBytes.length);
return encryptedOutput;
}
}
DOT Net Code(Decryption code):
byte[] saltBytes = key;
PBEKeySpec keySpec = new PBEKeySpec(key, saltBytes, 65536);
var keyHash = keySpec.GetBytes(32);
using (Aes aesCrypto = Aes.Create())
{
//set the BlockSize and the KeySize before you set the Key and the IV
//to avoid padding exceptions.
aesCrypto.BlockSize = 128;
aesCrypto.KeySize = 256; // AES256
aesCrypto.Key = keyHash;
byte[] cipherTextCombined = request.MessageBytes;
byte[] IV = new byte[aesCrypto.BlockSize / 8];
byte[] cipherText = new byte[cipherTextCombined.Length - IV.Length];
Array.Copy(cipherTextCombined, IV, IV.Length);
Array.Copy(cipherTextCombined, IV.Length, cipherText, 0, cipherText.Length);
aesCrypto.IV = IV; //Initialization vector
aesCrypto.Mode = CipherMode.CBC; //Cipher Block Chaining mode
aesCrypto.Padding = PaddingMode.PKCS7;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesCrypto.CreateDecryptor(aesCrypto.Key, aesCrypto.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt, new UTF8Encoding(false)))
{
// Read the decrypted bytes from stream to string.
response.MessageText = srDecrypt.ReadToEnd();
}
}
}
}
}
Actual result:
����wߞ*�/�r5le": {
"System": "KCS",
"Train": {
"TrainNumber": "36181542",
"TrainID": "G-CDMY -26",
Expected Result:
TrainSchedule: {
"System": "KCS",
"Train": {
"TrainNumber": "36181542",
"TrainID": "G-CDMY -26",

It's possible You are not encrypting the string "TrainSchedule" but they are decrypting TrainSchedule along with the payload.
Either encrypt the header or tell them to separate the payload from the header before decrypting it and concatenate it after.
Or, they are only decrypting the payload and ignoring the header. Without looking at their code it's impossible to know.

Related

C#.net to Java- Encryption and decryption using AES with Password

I am trying to reproduce the following encryption/decryption algorithm in Java, but I can't find the alternatives for multiple methods such as Rfc2898DeriveBytes() and RijndaelManaged(). How do I do this?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace CryptingTest
{
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 = 128;
// 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 = Generate128BitsOfRandomEntropy();
var ivStringBytes = Generate128BitsOfRandomEntropy();
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 = 128;
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] + [16 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 16 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 16 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 = 128;
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[] Generate128BitsOfRandomEntropy()
{
var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
}
Any help would be really appreciated.
Here's a code snippet from what I tried so far but it's still off:
private static byte[] Generate128BitsOfRandomEntropy()
{
var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits.
SecureRandom rngCsp = new SecureRandom();
// Fill the array with cryptographically secure random bytes.
rngCsp.nextBytes(randomBytes);
return randomBytes;
}
public static String encrypt(String plainText, String passPhrase)
{
try
{
var saltStringBytes = Generate128BitsOfRandomEntropy();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(passPhrase.toCharArray(), saltStringBytes, 1000, 384);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[16];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 16);
System.arraycopy(secretKey.getEncoded(), 16, iv, 0, 16);
SecretKeySpec secret = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
Rfc2898DeriveBytes implements PBKDF2, and RijndaelManaged with a block size of 128 bits implements AES. Both seem to be applied correctly in the Java code.
However, there are differences in determining the IV and regarding concatenation: In the C# code, the salt and IV are determined randomly and concatenated with the ciphertext at the end.
In the Java code only the salt is determined randomly, the IV is derived together with the key and the concatenation is missing.
I.e. the encrypt() method in the Java code could be changed for instance as follows, so that a decryption with the C# code is possible:
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
...
byte[] salt = Generate128BitsOfRandomEntropy();
byte[] iv = Generate128BitsOfRandomEntropy();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, 1000, 128);
SecretKey secretKey = factory.generateSecret(pbeKeySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] ciphertext = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
byte[] saltIvCiphertext = ByteBuffer.allocate(salt.length + iv.length + ciphertext.length).put(salt).put(iv).put(ciphertext).array();
return Base64.getEncoder().encodeToString(saltIvCiphertext);
Note that for PBKDF2, an iteration count of 1000 is generally too low.

NoSuchAlgorithmException with SecretKeyFactory

I keep getting a NoSuchAlgorithmExeception when I'm passing PBKDF2WithHmacSHA1 to getInstance().
Why is this happening. Am I missing some imports?
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.SecureRandom;
import java.util.Scanner;
import java.security.spec.*;
import java.security.AlgorithmParameters;
import javax.crypto.SecretKeyFactory.*;
class AES
{
static public String encrypt(String input, String password)
{
SecureRandom random = new SecureRandom();
byte salt[] = new byte[8];
random.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(input.getBytes("UTF-8"));
String text = new String(ciphertext, "UTF-8");
return text;
}
}
Also is there a way to use SHA2 instead of SHA1 ?
If you are using OpenJDK, then this might be your case. The accepted answer states that:
The OpenJDK implementation does only provide a
PBKDF2HmacSHA1Factory.java which has the "HmacSHA1" digest harcoded.
As far as I tested, the Oracle JDK is not different in that sense.
What you have to do is derive the PBKDF2HmacSHA1Factory (come on, it
is open!) and add a parameter to its constructor. You may avoid the
mess of creating your own Provider, and just initialize and use your
factory as follows:
PBKDF_SecretKeyFactory kf = new PBKDF_SecretKeyFactory("HmacSHA512");
KeySpec ks = new PBEKeySpec(password,salt,iterations,bitlen);
byte key[] = kf.engineGenerateSecret(ks).getEncoded();
About using SHA2, this post might have what you're looking for. Use this code snippet:
public byte[] hash(String password) throws NoSuchAlgorithmException
{
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] passBytes = password.getBytes();
byte[] passHash = sha256.digest(passBytes);
return passHash;
}

AES Encryption key generation that is consistent each execution?

I found this well written example of how to use AES encryption, and I will admit some of the more advanced aspects are beyond me. The class works fine, provided I'm using the same instance object. If I create another object, using the same exact passPhrase - that object can no longer properly decode any kind of string or data that the previous object created. I can only conclude that since this code takes the rather weak passPhrase string, mixes SALT, and builds a stronger 128-bit key - that this process of key construction is somehow randomized each time around. The significance being:
new AESEncrypter("MyPassword") <> new AESEncrypter("MyPassword")
Could someone help me modify the class below to get the desired behavior:
AESEncrypter a = new AESEncrypter("MyPassword")
String encoded = a.encrypt("my message")
AESEncrypter b = new AESEncrypter("MyPassword")
b.decrypt(encoded) == "my message"
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class AESEncrypter {
private static final byte[] SALT = {
(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 128;
public Cipher ecipher;
public Cipher dcipher;
AESEncrypter(String passPhrase) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
// I Think the problem is here???
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, secret);
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
}
public String encrypt(String encrypt) throws Exception {
byte[] bytes = encrypt.getBytes("UTF8");
byte[] encrypted = encrypt(bytes);
return new BASE64Encoder().encode(encrypted);
}
public byte[] encrypt(byte[] plain) throws Exception {
return ecipher.doFinal(plain);
}
public String decrypt(String encrypt) throws Exception {
byte[] bytes = new BASE64Decoder().decodeBuffer(encrypt);
byte[] decrypted = decrypt(bytes);
return new String(decrypted, "UTF8");
}
public byte[] decrypt(byte[] encrypt) throws Exception {
return dcipher.doFinal(encrypt);
}
public static void main(String[] args) throws Exception {
String message = "MESSAGE";
String password = "PASSWORD";
AESEncrypter encrypter1 = new AESEncrypter(password);
AESEncrypter encrypter2 = new AESEncrypter(password);
String encrypted1 = encrypter1.encrypt(message);
String encrypted2 = encrypter2.encrypt(message);
System.out.println("Display Encrypted from object 1 and 2..why do they differ?" );
System.out.println(encrypted1) ;
System.out.println(encrypted2) ;
System.out.println("Display Each object decrypting its own encrypted msg. Works as expected" );
System.out.println(encrypter1.decrypt(encrypted1)) ;
System.out.println(encrypter2.decrypt(encrypted2)) ;
System.out.println("Attempt to decrypt the each others msg.. will fail" );
System.out.println(encrypter1.decrypt(encrypted2)) ;
System.out.println(encrypter2.decrypt(encrypted1)) ;
}
}
Display Encrypted from object 1 and 2..why do they differ?
drGy+BNSHPy34NWkkcNqLQ==
9p06VfBgTuh7TizZSbvKjw==
Display Each object decrypting its own encrypted msg. Works as expected
MESSAGE
MESSAGE
Attempt to decrypt the each others msg.. will fail
Error:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
The problem is that when you initialize a new Cipher in CBC mode, it generates a fresh and random IV for you. This Initialization Vector doesn't have to be secret, but it has to be unpredictable to provide semantic security. You can simply put the IV in front of the ciphertext and use it for decryption.
public byte[] encrypt(byte[] plain) throws Exception {
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] ct = ecipher.doFinal(plain);
byte[] result = new byte[ct.length + iv.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ct, 0, result, iv.length, ct.length);
return result;
}
public byte[] decrypt(byte[] encrypt) throws Exception {
byte[] iv = new byte[dcipher.getBlockSize()];
byte[] ct = new byte[encrypt.length - dcipher.getBlockSize()];
System.arraycopy(encrypt, 0, iv, 0, dcipher.getBlockSize());
System.arraycopy(encrypt, dcipher.getBlockSize(), ct, 0, ct.length);
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
return dcipher.doFinal(ct);
}
You will need to store secret during the initialization step in a private variable for the decryption to work.
Keep in mind that the salt for PBDKF2 should also be random and 16 bytes long. You can store it alongside of the IV.

AES 128 encryption in PHP using PKCS5p adding

I have been trying to encrypt string using AES-128 CBC which was originally crypted using JAVA AES encryption. In java PKCS5 padding is used. And i have tried to encrypt using similar PHP code. But i am getting different result. I think I am getting wrong somewhere in IV parameter value. Can anyone help me on this.
JAVA Code
package com.idbi.ib.util;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class AESEncryptionDecryptionAlg
{
public AESEncryptionDecryptionAlg()
{
}
public static String Encrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes= new byte[16];
byte[] b= key.getBytes("UTF-8");
int len= b.length;
if (len > keyBytes.length) len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE,keySpec,ivSpec);
byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(results);
}
}
And the equivalent PHP code.
function mc_encrypt($plaintext,$key) {
$iv = $key;
//For PKCS5 padding
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padding = $block - (strlen($plaintext) % $block);
$plaintext .= str_repeat(chr($padding), $padding);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC);
$crypttext64 = base64_encode($crypttext);
$plaintext = $crypttext64;
return $plaintext;
}

Why does decryption function return garbage codes?

I have translate a C# based decrypt function into Java. It works fine and could be used to decrypted the passwords which have been encrypted by C# program.
Here is the source code:
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
public class TestDecrpt {
public static void main(String[] args) throws Exception {
String data = "encrypted data";
String sEncryptionKey = "encryption key";
byte[] rawData = new Base64().decode(data);
byte[] salt = new byte[8];
System.arraycopy(rawData, 0, salt, 0, salt.length);
Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(sEncryptionKey, salt);
byte[] IV = keyGen.getBytes(128 / 8);
byte[] keyByte = keyGen.getBytes(256 / 8);
Key key = new SecretKeySpec(keyByte, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(IV));
int pureDataLength = rawData.length - 8;
byte[] pureData = new byte[pureDataLength];
System.arraycopy(rawData, 8, pureData, 0, pureDataLength);
String plaintext = new String(cipher.doFinal(pureData), "UTF-8").replaceAll("\u0000", "");
System.out.println(plaintext);
}
}
I follow its algorithm to write the encrypt function. And codes is:
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
public class testEncrypt {
public static void main(String[] args) throws Exception {
String data = "Welcome2012~1#Welcome2012~1#Welcome2012~1#Welcome2012~1#Welcome2012~1#";
String sEncryptionKey = "encryption key"; # the same key
byte[] rawData = new Base64().decode(data);
SecureRandom random = new SecureRandom();
byte[] salt = new byte[8];
random.nextBytes(salt);
Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(sEncryptionKey, salt);
byte[] IV = keyGen.getBytes(128 / 8);
byte[] keyByte = keyGen.getBytes(256 / 8);
Key key = new SecretKeySpec(keyByte, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV));
byte[] out2 = cipher.doFinal(rawData);
byte[] out = new byte[8 + out2.length];
System.arraycopy(salt, 0, out, 0, 8);
System.arraycopy(out2, 0, out, 8, out2.length);
//String outStr=new String(out,"UTF-8");
String outStr = new Base64().encodeToString(out);
System.out.println(outStr);
System.out.print(outStr.length());
}
}
However, the encrypted data could not be decrypted correctly, it always return garbage codes, such as
ꉜ뙧巓妵峩枢펶땝ꉜ뙧巓妵峩枢펶땝ꉜ뙧巓�
Is there something wrong with the encrypt function?
================================================================================
[Update]
After changing the code to
byte[] rawData = data.getBytes("UTF-8");
The data could be encrypted and decrypted successfully.
However, the data which is encrypted in Java could not be correctly descrypted in C#.
Here is the C# version decrypt function:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Test
{
class Program
{
public static void Main(string[] args)
{
string data="EncryptedData";
string sEncryptionKey="EncryptionKey";
byte[] rawData = Convert.FromBase64String(data);
byte[] salt = new byte[8];
for (int i = 0; i < salt.Length; i++)
salt[i] = rawData[i];
Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(sEncryptionKey, salt);
Rijndael aes = Rijndael.Create();
aes.IV = keyGenerator.GetBytes(aes.BlockSize / 8);
aes.Key = keyGenerator.GetBytes(aes.KeySize / 8);
using (MemoryStream memoryStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(rawData, 8, rawData.Length - 8);
cryptoStream.Close();
byte[] decrypted = memoryStream.ToArray();
Console.Out.WriteLine(Encoding.Unicode.GetString(decrypted));
Console.In.ReadLine();
}
}
}
}
I find that the original code are using "Unicode" as output format,
Encoding.Unicode.GetString(decrypted)
so I change my Java code to "Unicode".
For Decrypt in Java:
String plaintext = new String(cipher.doFinal(pureData), "Unicode");
System.out.println(plaintext);
For Encrypt in Java:
byte[] rawData = data.getBytes("Unicode");
But using the C# code to decrypt the data which has been encrypted by the Java program still meet garbage codes.
How could I fix this issue? Is there any magical trick?
[Last Update]
After using "UTF-16LE" instead of "UTF-8", the issue has gone. It seems that "UTF-16LE" is the Java equivalent to the "Unicode" of C#.
This is the problem:
String data = "Welcome2012~1#Welcome2012~1#Welcome2012~1#Welcome2012~1#Welcome2012~1#";
byte[] rawData = new Base64().decode(data);
That text is not meant to be base64-encoded binary data. It's just text. Why are you trying to decode it as base64 data?
You want:
byte[] rawData = data.getBytes("UTF-8");
That way, when you later write:
String plaintext = new String(cipher.doFinal(pureData), "UTF-8")
.replaceAll("\u0000", "");
you're doing the reverse action. (Admittedly you probably shouldn't need the replaceAll call, but that's a different matter.)
For anything like this, you need to make sure that the steps you take on the way "out" are the reverse of the steps on the way "in". So in the correct code, you have:
Unencrypted text data => unencrypted binary data (encode via UTF-8)
Unencrypted binary data => encrypted binary data (encrypt with AES)
Encrypted binary data => encrypted text data (encode with base64)
So to reverse, we do:
Encrypted text data => encrypted binary data (decode with base64)
Encrypted binary data => unencrypted binary data (decrypt with AES)
Unencrypted binary data => unencrypted text data (decode via UTF-8)

Categories

Resources