AES/CBC/PKCS5Padding nodejs encryption - java

I am trying to convert my java code to NodeJs code. It's a little more complicate because the customised format included the password and salt.
In main method there is one example.
Here is my java code:
public class App {
private static final int DYN_SALT_LENGTH = 10;
private static final int ITERATION_COUNT = 65556;
private static final int KEY_LENGTH = 256;
private static final String SECRET_KEY_ALGORITHM = "AES";
private static final String CIPHER_TRANSFORMER = "AES/CBC/PKCS5Padding";
private static Base64 base64Instance = new Base64();
public static String decrypt(String data, String password, String salt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
ITERATION_COUNT,
KEY_LENGTH);
SecretKey secretKey = factory.generateSecret(spec);
ByteBuffer buffer = ByteBuffer.wrap(base64Instance.decode(data));
buffer.position(DYN_SALT_LENGTH);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);
// Read the IV
byte[] ivBytes = new byte[cipher.getBlockSize()];
buffer.get(ivBytes, 0, ivBytes.length);
// Read encrypted text.
byte[] encryptedTextBytes = new byte[buffer.capacity() - DYN_SALT_LENGTH - ivBytes.length];
buffer.get(encryptedTextBytes);
// Initialize Cipher.
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
String result = new String(cipher.doFinal(encryptedTextBytes), StandardCharsets.UTF_8);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to decrypt data", e);
}
}
public static String encrypt(String data, String password, String salt) {
// Create new salt for every new encryption request.
byte[] saltBytes = new byte[DYN_SALT_LENGTH];
new SecureRandom().nextBytes(saltBytes);
try {
// Create secret key spec.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
ITERATION_COUNT,
KEY_LENGTH);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
byte[] ivBytes;
byte[] encryptedTextBytes;
// Initialize cipher
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
// Create initialization vector IV
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
// Encrypt the text.
encryptedTextBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
// Response will be in the form of <salt><IV><encryptedText>
ByteBuffer byteBuffer = ByteBuffer.allocate(saltBytes.length + ivBytes.length + encryptedTextBytes.length);
byteBuffer.put(saltBytes);
byteBuffer.put(ivBytes);
byteBuffer.put(encryptedTextBytes);
return base64Instance.encodeToString(byteBuffer.array());
} catch (Exception e) {
throw new RuntimeException("Failed to encrypt data", e);
}
}
public static void main(String[] args) {
String password = "password";
String salt = "salt";
String data = "hello world";
String resultEncrypted = encrypt(data, password, salt);
System.out.println(resultEncrypted);
String resultDecrypted = decrypt(resultEncrypted, password, salt);
System.out.println(resultDecrypted);
}
}
I'm trying with JS code like below but without clue about what i'm doing wrong:
function getAlgorithm(keyBase64) {
var key = Buffer.from(keyBase64, "base64");
switch (key.length) {
case 16:
return "aes-128-cbc";
case 32:
return "aes-256-cbc";
}
throw new Error("Invalid key length: " + key.length);
}
function decrypt(messagebase64, keyBase64, ivBase64) {
const key = Buffer.from(keyBase64, "base64");
const iv = Buffer.from(ivBase64, "base64");
const decipher = crypto.createDecipheriv(
getAlgorithm(keyBase64),
key,
iv.slice(0, 16)
);
let decrypted = decipher.update(messagebase64, "base64", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
const base64Encrypted =
"2vSIh0J64zhrQuayUV+UIyPTpmSaN4gAv7B3CVC/a68eBfeU0bMwRm2I";
const key = crypto.scryptSync("password", "salt", 16);
const encrypted = Buffer.from(base64Encrypted, "base64");
const encryptedWOSalt = Buffer.from(base64Encrypted, "base64").slice(10);
const iv = encrypted.slice(10, 10 + 17);
const result = decrypt(
encryptedWOSalt.toString("base64"),
key,
iv.toString("base64")
);
console.log(result);
It's throwing an error:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
Thanks

Since you only posted a NodeJS decryption code, I focus on decryption. The encryption is to be implemented analogously. If you have problems with this, please post a new question with the corresponding encryption code.
There are several bugs in the NodeJS code:
Wrong key derivation (in the NodeJS code scrypt is used, while in the Java code PBKDF2/HMAC-SHA1 is applied).
Incorrect/missing separation of salt, IV and ciphertext
Encoding bugs and unnecessary encoding/decoding cycles
The following NodeJS code works:
var crypto = require('crypto')
function getAlgorithm(key) {
switch (key.length) {
case 16:
return "aes-128-cbc";
case 32:
return "aes-256-cbc";
}
throw new Error("Invalid key length: " + key.length);
}
function decrypt(message, key, iv) {
const decipher = crypto.createDecipheriv(
getAlgorithm(key),
key,
iv
);
let decrypted = Buffer.concat([decipher.update(message), decipher.final()]);
return decrypted.toString("utf8");
}
const DYN_SALT_LENGTH = 10;
const IV_LENGTH = 16;
const ITERATION_COUNT = 65556;
const KEY_LENGTH = 256;
const base64Encrypted = "ossqoyCaaQINWUkTsHNGRe5Isd5s7c7U8KcLua78Ehm9jAxQNOd2tyjj";
// Separate salt, IV and ciphertext
const encrypted = Buffer.from(base64Encrypted, "base64");
const salt = encrypted.slice(0, DYN_SALT_LENGTH);
const iv = encrypted.slice(DYN_SALT_LENGTH, DYN_SALT_LENGTH + IV_LENGTH);
const ciphertext = encrypted.slice(DYN_SALT_LENGTH + IV_LENGTH);
// Derive key voa PBKDF2/HMAC-SHA1
const key = crypto.pbkdf2Sync("password", "salt", ITERATION_COUNT, KEY_LENGTH/8, "sha1");
// Decrypt
const result = decrypt(
ciphertext,
key,
iv
);
console.log(result); // hello world
Note that the current Java code (and therefore also the NodeJS code) does not use the random salt for key derivation, but a static salt, possibly for testing purposes. In the final solution, the random salt is to be used for security reasons.

Related

Decryption in Java for String Encrypted in C# using AES

I am trying to write a code equivalent of following c# code to decrypt a string in Java.
public string Decrypt(string cipherText)
{
if (!IsBase64String(cipherText))
throw new Exception("The cipherText input parameter is not base64 encoded");
string text;
var key = new Rfc2898DeriveBytes(EncryptionKey, SALT);
var aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); var cipher = Convert.FromBase64String(cipherText);
using (var msDecrypt = new MemoryStream(cipher))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Internal
text = srDecrypt.ReadToEnd();
}
}
}
return text;
}
Here is what I tried:
final String password = "password";
byte[] salt = []; //salt provided here
String string = "something";
System.out.println(string.length());
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");
byte[] data = Base64.getDecoder().decode(string);
System.out.println(data.length);
// skip first 4 bytes (the length of IV) and get IV byte array
byte[] iv = Arrays.copyOfRange(data, 16, 32);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
// skip IV length (4 bytes) and IV (16 bytes)
cipher.update(data, 32, data.length - 32);
String plaintext = new String(cipher.doFinal(), "UTF-8");
System.out.println(plaintext);
Getting the following Exception:
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
Not sure where I went wrong

NodeJs Decrypt AES256 Encryption From JAVA

I'm doing an integration with another system and the data given is encrypted in AES-256-CBC(Java) and need to decrypt it in NodeJs in order to proceed.
I have tried many ways from internet and stuck in error. Below is the sample code of Java(decryption) which is working and NodeJs(my code of decryption)
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 256;
private static final byte[] DEFAULT_IV = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
public static byte[] decryptToBytes(String src, String secret, String salt, byte[] iv) {
try{
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(secret.toCharArray(), salt.getBytes(), ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
return cipher.doFinal(Base64.getDecoder().decode(src));
}catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String src, String secret, String salt, byte[] iv) {
try{
return new String(decryptToBytes(src, secret, salt, iv));
}catch (Exception ex) {
return null;
}
}
public static void main(String[] args) {
String secret = "abcd123456";
String salt = "123abc";
String plainText ="This is AES256 encryption test";
String cipherText = "gbYgtu5EWxOYRSUmMsEtdn8oQLxBjejfwUBSRhhls08=";
byte[] IV = new byte[16];
String originalText = decrypt(cipherText,secret, salt, IV);
}
import crypto from "crypto";
public aesCdcDecrypt(input: string) {
let iterationCount = 65536;
let keyLength = 256;
let iv = new Buffer(16);
let keyHex = "abcd123456";
let salt = "123abc";
let decryptText: string;
try {
crypto.pbkdf2(new Buffer(keyHex), new Buffer(salt), iterationCount, keyLength, "sha256", function (err, key) {
let secretKey = key.toString("hex");
let decipher = crypto.createDecipheriv("aes-256-cbc", secretKey, iv);
decryptText = decipher.update(input, "binary", "utf8");
decryptText += decipher.final("utf8");
console.log('Result: ' + decryptText);
});
} catch (e) {
console.log(e);
}
return decryptText;
}
Result getting this error -->
Error: Invalid key length
at new Decipheriv (crypto.js:267:16)
at Object.createDecipheriv (crypto.js:627:10)
There are a few minor issues in your TS code:
key length is in bytes, not bits
new Buffer() does not decode base64 by default
Here's a working version (JS):
const crypto = require('crypto')
function aesCdcDecrypt(ciphertext) {
let iterationCount = 65536;
let keyLength = 32;
let iv = Buffer.alloc(16);
let keyHex = "abcd123456";
let salt = "123abc";
let key = crypto.pbkdf2Sync(keyHex, Buffer.from(salt), iterationCount, keyLength, "sha256");
var cipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
cipher.setAutoPadding(true);
let ciph = cipher.update(Buffer.from(ciphertext, "base64"));
let ciphf = cipher.final();
return Buffer.concat([ciph, ciphf]).toString();
}
console.log(aesCdcDecrypt("gbYgtu5EWxOYRSUmMsEtdn8oQLxBjejfwUBSRhhls08="));
Prints:
This is AES256 encryption test

Rfc2898DeriveBytes in java for this .Net code password encryption

I am trying to encrypt password in java which is connecting to the C# system below is the code. This encryption is done using Rfc2898DeriveBytes.
class StringCipher
{
private const int Keysize = 256;
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
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();
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32];
using (var rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
.net Source code here
below is what I have tried in java and it is not working for me,.net encryption is done using Rfc2898DeriveBytes, and here i have used SecureRandom sr = new SecureRandom(); as an equivalent for that, but for some reason I am not able to get the password encrypted. please help
public class PasswordEncryption {
private final static int DerivationIterations = 1000;
private final int Keysize = 256;
private static byte[] getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = new SecureRandom();
byte[] salt = new byte[32];
sr.nextBytes(salt);
return salt;
}
public static String Encrypt(String plainText, String passPhrase)
{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] saltStringBytes = getSalt();
byte[] IvStringBytes=getSalt();
byte[] pwd = plainText.getBytes("UTF-8");
char[] chars = passPhrase.toCharArray();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec pbeKeySpec = new PBEKeySpec(chars, saltStringBytes, DerivationIterations, 256);
SecretKey secretKey = factory.generateSecret(pbeKeySpec);
System.out.println(secretKey.toString());
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] result = cipher.doFinal(pwd);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(saltStringBytes);
outputStream.write(IvStringBytes);
outputStream.write(result);
byte end[] = outputStream.toByteArray();
String base64Encoded = Base64.getEncoder().encodeToString(end);
System.out.println(base64Encoded);
return base64Encoded;
}
public static void main(String[] args) {
Encrypt("passwordhere", "ABC");
}
}
Do not encrypt passwords, just use Rfc2898DeriveBytes to create a password verifier. The password should not be recoverable, that is a security vulnerability. When the attacker gains admin rights and obtains the encrypted password he will also get the encryption key and thus obtain the passwords along with the rest of the user credentials.
Create the password verifier by using Rfc2898DeriveBytes and save the salt and rounds count along with the derived password.
When verifying a password run the supplied password through Rfc2898DeriveBytes with the saved salt and rounds count and then compare the result with the saved derivation.

AES Encryption C# (WIndows) & Java (Android) not giving same output

I have implemented the following AES Encryption in C# code. Which I can't change even if I want to.
public string Encrypt(string InputForEncryption, string Password, string IV)
{
IBuffer buffInputForEncryption = CryptographicBuffer.ConvertStringToBinary(InputForEncryption, BinaryStringEncoding.Utf8);
IBuffer buffPassword = CryptographicBuffer.ConvertStringToBinary(Password, BinaryStringEncoding.Utf8);
IBuffer buffIV = CryptographicBuffer.ConvertStringToBinary(IV, BinaryStringEncoding.Utf8);
KeyDerivationAlgorithmProvider DerAlgo = KeyDerivationAlgorithmProvider.OpenAlgorithm("PBKDF2_SHA1");
KeyDerivationParameters KeyPara = KeyDerivationParameters.BuildForPbkdf2(buffIV, 1024);
CryptographicKey KeyPassword = DerAlgo.CreateKey(buffPassword);
IBuffer buffMatPassword = CryptographicEngine.DeriveKeyMaterial(KeyPassword, KeyPara, 32);
CryptographicKey KeyIV = DerAlgo.CreateKey(buffPassword);
IBuffer buffMatIV = CryptographicEngine.DeriveKeyMaterial(KeyIV, KeyPara, 16);
SymmetricKeyAlgorithmProvider SymAlgo = SymmetricKeyAlgorithmProvider.OpenAlgorithm("AES_CBC_PKCS7");
CryptographicKey KeySym = SymAlgo.CreateSymmetricKey(buffMatPassword);
IBuffer buffRESULT = CryptographicEngine.Encrypt(KeySym, buffInputForEncryption, buffMatIV);
string Result = CryptographicBuffer.EncodeToBase64String(buffRESULT);
return Result;
}
Following code is for Java in Android
private Cipher cipher;
private SecretKey secretKey;
private IvParameterSpec ivParameterSpec;
int iterationCount = 1024;
int keyStrength = 128;
private String sampleInputForPassSaltIV = "Abcd1234Abcd1234";
private String encryptInput = "helloAES";
private String encryptedOutput = "";
private String decryptedOutput = "";
public Boolean initializeEncryption() throws Exception {
String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
SecretKeyFactory secretKeyFactory;
KeySpec keySpec;
SecretKey secretKeyTemp;
String passPhrase = sampleInputForPassSaltIV;
String keySalt = sampleInputForPassSaltIV;
secretKeyFactory = SecretKeyFactory.getInstance(secretKeyAlgorithm);
keySpec = new PBEKeySpec(passPhrase.toCharArray(), keySalt.getBytes(), iterationCount, keyStrength);
secretKeyTemp = secretKeyFactory.generateSecret(keySpec);
secretKey = new SecretKeySpec(secretKeyTemp.getEncoded(), "AES");
byte[] IV = sampleInputForPassSaltIV.getBytes();
ivParameterSpec = new IvParameterSpec(IV);
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
return true;
}
private void encrypt(String dataToEncrypt) throws Exception {
if (dataToEncrypt.length() > 0) {
byte[] UTF8Data;
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
UTF8Data = cipher.doFinal(dataToEncrypt.getBytes());
encryptedOutput = Base64.encodeToString(UTF8Data, 0);
Toast toast = Toast.makeText(context, "Encrypted Text : " + encryptedOutput, Toast.LENGTH_LONG);
toast.show();
}
}
I have tried everything I could to get same output from both code but I couldn't find the solution.
I have tried changing KeyLength, BlockSize, check for encoding and decoding of strings but NO...
I can't change C# code since its already been used a lot so please somebody help me for this java code what should I do to get both outputs same.
I have already tried many solutions from threads on StackOverflow.
I think it must be padding issue or key size.
For testing only I am using same pass and same salt.
Please Help...

How do you generate a PBEKey for Serpent in Java?

I'm trying to support PBE for AES, Serpent, and TwoFish. Currently I am able to generate an AES PBEKey in Java using BC like this:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC", provider);
PBEKeySpec pbeKeySpec = new PBEKeySpec("Password12".toCharArray());
SecretKey key = factory.generateSecret(pbeKeySpec);
but I can't figure out how to generate a PBEKey for Serpent, so I'm assuming its not possible out of the box. How would I go about implementing this? Is there a hook somewhere that I can just register my own SecretKeyFactory to handle Serpent keys?
Coincidentally, I have noticed that using an AES PBEKey (as generated above) for encrypting/decrypting with Serpent/TwoFish "works", but I have no idea what the repercussions are. Could I just get away with using the AES PBEKey?
After discussions with PaĆ­loEbermann (above), I put together the following solution. It generates a PBE key for AES256 and then simply copies the required number of bytes from the generated key into a new SecretKeySpec(), which allows me to specify the desired algorithm and key length. Currently I am salting the password AND creating a random IV on each call to encrypt. My assumption is that the IV is unnecessary since a random salt is applied to each encrypted message, but I wasn't 100% sure so I added the IV anyway. I'm hoping someone can confirm or deny this assumption, since if the IV isnt needed, then its bloating the size of the output from encrypt() for no valid reason. Ideally I would be able to generate a PBEKey of variable length with no algorithm ties (as per PKCS5), but it appears I am bound to the key sizes defined in the available PBE schemes provided by the selected Provider. This implementation is therefore bound to using BouncyCastle, since I was unable to find a PBE scheme that provided at least 256bit keys from the standard JCE provider.
/**
* parts of this code were copied from the StandardPBEByteEncryptor class from the Jasypt (www.jasypt.org) project
*/
public class PBESample {
private final String KEY_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
private final String MODE_PADDING = "/CBC/PKCS5Padding";
private final int DEFAULT_SALT_SIZE_BYTES = 16;
private final SecureRandom rand;
private final String passwd = "(Password){12}<.....>!";
public PBESample() throws Exception {
rand = SecureRandom.getInstance("SHA1PRNG");
}
private byte[] generateSalt(int size) {
byte[] salt = new byte[size];
rand.nextBytes(salt);
return salt;
}
private SecretKey generateKey(String algorithm, int keySize, byte[] salt) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException{
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray(), salt, 100000);
SecretKey tmpKey = factory.generateSecret(pbeKeySpec);
byte[] keyBytes = new byte[keySize / 8];
System.arraycopy(tmpKey.getEncoded(), 0, keyBytes, 0, keyBytes.length);
return new SecretKeySpec(keyBytes, algorithm);
}
private byte[] generateIV(Cipher cipher) {
byte[] iv = new byte[cipher.getBlockSize()];
rand.nextBytes(iv);
return iv;
}
private byte[] appendArrays(byte[] firstArray, byte[] secondArray) {
final byte[] result = new byte[firstArray.length + secondArray.length];
System.arraycopy(firstArray, 0, result, 0, firstArray.length);
System.arraycopy(secondArray, 0, result, firstArray.length, secondArray.length);
return result;
}
public byte[] encrypt(String algorithm, int keySize, final byte[] message) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm + MODE_PADDING);
// The salt size for the chosen algorithm is set to be equal
// to the algorithm's block size (if it is a block algorithm).
int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES;
int algorithmBlockSize = cipher.getBlockSize();
if (algorithmBlockSize > 0) {
saltSizeBytes = algorithmBlockSize;
}
// Create salt
final byte[] salt = generateSalt(saltSizeBytes);
SecretKey key = generateKey(algorithm, keySize, salt);
// create a new IV for each encryption
final IvParameterSpec ivParamSpec = new IvParameterSpec(generateIV(cipher));
// Perform encryption using the Cipher
cipher.init(Cipher.ENCRYPT_MODE, key, ivParamSpec);
byte[] encryptedMessage = cipher.doFinal(message);
// append the IV and salt
encryptedMessage = appendArrays(ivParamSpec.getIV(), encryptedMessage);
encryptedMessage = appendArrays(salt, encryptedMessage);
return encryptedMessage;
}
public byte[] decrypt(String algorithm, int keySize, final byte[] encryptedMessage) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm + MODE_PADDING);
// determine the salt size for the first layer of encryption
int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES;
int algorithmBlockSize = cipher.getBlockSize();
if (algorithmBlockSize > 0) {
saltSizeBytes = algorithmBlockSize;
}
byte[] decryptedMessage = new byte[encryptedMessage.length];
System.arraycopy(encryptedMessage, 0, decryptedMessage, 0, encryptedMessage.length);
// extract the salt and IV from the incoming message
byte[] salt = null;
byte[] iv = null;
byte[] encryptedMessageKernel = null;
final int saltStart = 0;
final int saltSize = (saltSizeBytes < decryptedMessage.length ? saltSizeBytes : decryptedMessage.length);
final int ivStart = (saltSizeBytes < decryptedMessage.length ? saltSizeBytes : decryptedMessage.length);
final int ivSize = cipher.getBlockSize();
final int encMesKernelStart = (saltSizeBytes + ivSize < decryptedMessage.length ? saltSizeBytes + ivSize : decryptedMessage.length);
final int encMesKernelSize = (saltSizeBytes + ivSize < decryptedMessage.length ? (decryptedMessage.length - saltSizeBytes - ivSize) : 0);
salt = new byte[saltSize];
iv = new byte[ivSize];
encryptedMessageKernel = new byte[encMesKernelSize];
System.arraycopy(decryptedMessage, saltStart, salt, 0, saltSize);
System.arraycopy(decryptedMessage, ivStart, iv, 0, ivSize);
System.arraycopy(decryptedMessage, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize);
SecretKey key = generateKey(algorithm, keySize, salt);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
// Perform decryption using the Cipher
cipher.init(Cipher.DECRYPT_MODE, key, ivParamSpec);
decryptedMessage = cipher.doFinal(encryptedMessageKernel);
// Return the results
return decryptedMessage;
}
public static void main(String[] args) throws Exception {
// allow the use of the BC JCE
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final String message = "Secret Message";
PBESample engine = new PBESample();
byte[] encryptedMessage = engine.encrypt("AES", 128, message.getBytes());
byte[] decryptedMessage = engine.decrypt("AES", 128, encryptedMessage);
if (message.equals(new String(decryptedMessage))) {
System.out.println("AES OK");
}
encryptedMessage = engine.encrypt("Serpent", 256, message.getBytes());
decryptedMessage = engine.decrypt("Serpent", 256, encryptedMessage);
if (message.equals(new String(decryptedMessage))) {
System.out.println("Serpent OK");
}
encryptedMessage = engine.encrypt("TwoFish", 256, message.getBytes());
decryptedMessage = engine.decrypt("TwoFish", 256, encryptedMessage);
if (message.equals(new String(decryptedMessage))) {
System.out.println("TwoFish OK");
}
}
}

Categories