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");
}
}
}
Related
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
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...
I have a below PHP function that I want to implement in Java with no luck, I'm not able to obtain the same output:
function encryptText( $plainText, $key )
{
$mcopen = mcrypt_module_open (MCRYPT_TripleDES, "", MCRYPT_MODE_ECB,"");
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size ($mcopen), MCRYPT_RAND);
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$cryptedHash = '';
if (mcrypt_generic_init($td, $key, $iv) != -1)
{
$cryptedHash = mcrypt_generic($td, $plainText);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
return base64_encode($cryptedHash);
}
This is my java code found here :
public static String encrypt(String message, String key) throws Exception {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest(key.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey keyz = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyz, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
final String encodedCipherText = new sun.misc.BASE64Encoder().encode(cipherText);
return encodedCipherText;
}
You have a couple of problems.
Since you want to use ECB mode, then you need to specify ECB mode. CBC is a different mode.
You're not running your key through MD5 in the PHP code, so you shouldn't do that in Java.
A block cipher in ECB or CBC mode needs a padding in order to encrypt arbitrary messages. Mcrypt uses zero padding by default, but you're specifying PKCS#5 padding in Java. Since the default Sun providers doesn't implement ZeroPadding, you will either need to implement it yourself by specifying "NoPadding" and implementing the zero padding yourself, or use the BouncyCastle provider: Cipher.getInstance("DESede/ECB/ZeroPadding", "BC").
Other considerations:
Never use ECB mode. It's not semantically secure. If you encrypt the same message under the same key twice, the attacker can easily see that you encrypted the same message (or same part of the message) again.
Solved, the solution :
public static String cryptBC(String data, String key) throws Exception{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] input = data.getBytes();
byte[] keyBytes = key.getBytes() ;
SecretKeySpec skey = new SecretKeySpec(keyBytes, "DESede");
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding", "BC");
if(input.length % 8 != 0){
byte[] padded = new byte[input.length + 8 - (input.length % 8)];
System.arraycopy(input, 0, padded, 0, input.length);
input = padded;
}
System.out.println("input : " + new String(input));
cipher.init(Cipher.ENCRYPT_MODE, skey);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return new String(Base64.encodeBase64(cipherText));
}
I have a PSKC XML file, that contains my Salt/Key, IV(Initialization
Vector), Encrypted Data, and password.
Now, I am trying to Do Password Based Encyption and Decryption using the PBEWITHSHA1AND128BITAES-CBC-BC Algorithm.
It works if I generate salt and IV randomly.
However, it fails if i don't generate it randomly. Instead of generating Salt and IV randomly, I am using my own salt, IV, password and everything.
Here is my code:
/**
* parts of this code were copied from the StandardPBEByteEncryptor class from
* the Jasypt (www.jasypt.org) project
*/
public class AESCrypt
{
private final String KEY_ALGORITHM = "PBEWITHSHA1AND128BITAES-CBC-BC";
// private final String KEY_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
private final String MODE_PADDING = "/CBC/PKCS5Padding";
private final int DEFAULT_SALT_SIZE_BYTES = 128;
private byte[] ivParamSpec1 =
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
private final SecureRandom rand;
private final String passwd = "kn4\"bE,W11kewsUd";
public AESCrypt() 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
{
// SecretKeySpec Spec = new SecretKeySpec(keyBytes, algorithm);
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];
SecretKeySpec Spec = new SecretKeySpec(keyBytes, algorithm);
System.arraycopy(tmpKey.getEncoded(), 0, keyBytes, 0, keyBytes.length);
System.out.println("SecretKeySpec(keyBytes, algorithm)---->"+Spec);
return Spec;
}
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);
final byte[] salt = "EW0h0yUcDX72WU9UiKiCwDpXsJg=".getBytes();
SecretKey key = generateKey(algorithm, keySize, salt);
// create a new IV for each encryption
// final IvParameterSpec ivParamSpec = new IvParameterSpec();
ivParamSpec1 = "ixYgnjjY58RNacxZHwxgBQ==".getBytes();
final IvParameterSpec ivParamSpec = new IvParameterSpec(ivParamSpec1);
// 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;
}
System.out.println("saltSizeBytes:" + saltSizeBytes);
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 saltSize = 32;
// System.out.println("saltSize:"+saltSize);
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];
System.out.println("saltSize:" + saltSize);
System.out.println("ivSize:" + ivSize);
encryptedMessageKernel = new byte[encMesKernelSize];
System.out.println("encryptedMessageKernel");
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);
System.out.println("ekey");
ivParamSpec1 = "ixYgnjjY58RNacxZHwxgBQ==".getBytes();
IvParameterSpec ivParamSpec = new IvParameterSpec(ivParamSpec1);
// Perform decryption using the Cipher
cipher.init(Cipher.DECRYPT_MODE, key, ivParamSpec);
decryptedMessage = cipher.doFinal(encryptedMessageKernel);
// Return the results
return decryptedMessage;
}
private byte[] decryptWithLWCrypto(byte[] cipher, String password, byte[] salt, final int iterationCount)
throws Exception
{
PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(new SHA256Digest());
char[] passwordChars = password.toCharArray();
final byte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
pGen.init(pkcs12PasswordBytes, salt, iterationCount);
CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(256, 128);
aesCBC.init(false, aesCBCParams);
PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC,new PKCS7Padding());
byte[] plainTemp = new byte[aesCipher.getOutputSize(cipher.length)];
int offset = aesCipher.processBytes(cipher, 0, cipher.length, plainTemp, 0);
int last = aesCipher.doFinal(plainTemp, offset);
final byte[] plain = new byte[offset + last];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
System.out.println("Plain--->"+plain);
return plain;
}
public static void main(String[] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
AESCrypt aesCrypt = new AESCrypt();
String originalText = "6skYngWZxkTgsRIoFPLS1mpI32Q=";
String toDecrypt = new String(Base64.encode(aesCrypt.encrypt("AES", 128, originalText.getBytes())));
System.out.println("Original Data----->"+originalText);
System.out.println("After Encrpytion--->"+toDecrypt);
byte[] criptata = Base64.decode(toDecrypt);
byte[] decriptata = aesCrypt.decrypt("AES", 128, criptata);
String msgdecriptato = new String(decriptata);
System.out.println("After Decrption--->"+msgdecriptato);
if (!originalText.equals(msgdecriptato))
{
throw new IllegalStateException("Strings do not match!");
}
else
{
System.out.println("Matched.....");
}
// byte[] cipher = "RVcwaDB5VWNEWDcyV1U5VWlLaUN3RHBYc0pnPWl4WWduampZNThSTmFjeFoJfgJbigcnDs0TJdNu7yEkprlJFdilLrLJT8uoQ3dm/A==".getBytes();
// String password = "kn4\"bE,W11kewsUd";
// byte[] salt = "EW0h0yUcDX72WU9UiKiCwDpXsJg=".getBytes();
// System.out.println(aesCrypt.decryptWithLWCrypto(cipher, password, salt , 128));
}
}
When I try to run this... Am getting following Errors,
Exception in thread "main" java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
at com.sun.crypto.provider.SunJCE_f.a(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineInit(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at AESCrypt.encrypt(AESCrypt.java:113)
at AESCrypt.main(AESCrypt.java:213)
I can't truncate the salt size or IV size, then there will not be any data integrity.
Even if i truncate my IV size to 16 bytes from 28 bytes, it works for encryption, but it doesn't work for decryption. It produces the following errors:
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at AESCrypt.decrypt(AESCrypt.java:163)
at AESCrypt.main(AESCrypt.java:178)
Can anyone help me?
I am trying to encrypt some text using the AES algorithm on both the Android and IPhone platforms. My problem is, even using the same encryption/decryption algorithm (AES-128) and same fixed variables (key, IV, mode), I get different result on both platforms. I am including code samples from both platforms, that I am using to test the encryption/decryption. I would appreciate some help in determining what I am doing wrong.
Key: “123456789abcdefg”
IV: “1111111111111111”
Plain Text: “HelloThere”
Mode: “AES/CBC/NoPadding”
Android Code:
public class Crypto {
private final static String HEX = "0123456789ABCDEF";
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("CBC");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static String toHex(String txt) {
return toHex(txt.getBytes());
}
public static String fromHex(String hex) {
return new String(toByte(hex));
}
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
IPhone (Objective-C) Code:
- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData {
NSData* secretKey = [Cipher md5:cipherKey];
CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;
uint8_t iv[kCCBlockSizeAES128];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
status = CCCryptorCreate(encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[secretKey bytes], kCCKeySizeAES128, iv, &cryptor);
if (status != kCCSuccess) {
return nil;
}
size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length], true);
void * buf = malloc(bufsize * sizeof(uint8_t));
memset(buf, 0x0, bufsize);
size_t bufused = 0;
size_t bytesTotal = 0;
status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
buf, bufsize, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
CCCryptorRelease(cryptor);
return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
}
+ (NSData *) md5:(NSString *) stringToHash {
const char *src = [stringToHash UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(src, strlen(src), result);
return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
}
Some of my references :
http://code.google.com/p/aes-encryption-samples/wiki/HowToEncryptWithJava
http://automagical.rationalmind.net/2009/02/12/aes-interoperability-between-net-and-iphone/
AES interoperability between .Net and iPhone?
For iPhone I used AESCrypt-ObjC, and for Android use this code:
public class AESCrypt {
private final Cipher cipher;
private final SecretKeySpec key;
private AlgorithmParameterSpec spec;
public AESCrypt(String password) throws Exception
{
// hash password with SHA-256 and crop the output to 128-bit for key
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(password.getBytes("UTF-8"));
byte[] keyBytes = new byte[32];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
key = new SecretKeySpec(keyBytes, "AES");
spec = getIV();
}
public AlgorithmParameterSpec getIV()
{
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
IvParameterSpec ivParameterSpec;
ivParameterSpec = new IvParameterSpec(iv);
return ivParameterSpec;
}
public String encrypt(String plainText) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedText = new String(Base64.encode(encrypted, Base64.DEFAULT), "UTF-8");
return encryptedText;
}
public String decrypt(String cryptedText) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] bytes = Base64.decode(cryptedText, Base64.DEFAULT);
byte[] decrypted = cipher.doFinal(bytes);
String decryptedText = new String(decrypted, "UTF-8");
return decryptedText;
}
}
It makes me no wonder that you get different results.
Your problem is that you use misuse a SHA1PRNG for key derivation. AFAIK there is no common standard how a SHA1PRNG work internally. AFAIR even the J2SE and Bouncycaste implementation output different results using the same seed.
Hence your implementation of your getRawKey(byte[] seed) will generate you a random key. If you use the key for encryption you are getting an result that depends on that key. As the key is random you will not get the same key on iOS and therefore you are getting a different result.
If you want a key derivation function use a function like PBKDF2 with is nearly fully standardized regarding the key derivation.
On Android, you are using getBytes(). This is an error as it means you are using the default charset rather than a known charset. Use getBytes("UTF-8") instead so you know exactly what bytes you are going to get.
I don't know the equivalent for Objective-C, but don't rely on the default. Explicitly specify UTF-8 when converting strings to bytes. That way you will get the same bytes on both sides.
I also note that you are using MD5 in the Objective-C code but not in the Android code. Is this deliberate?
See my answer for password-based AES encryption, since, you are effectively using your "seed" as a password. (Just change the key length of 256 to 128, if that's what you want.)
Trying to generate the same key by seeding a DRBG with the same value is not reliable.
Next, you are not using CBC or the IV in your Android encryption. My example shows how to do that properly too. By the way, you need to generate a new IV for every message you encrypt, as my example shows, and send it along with the cipher text. Otherwise, there's no point in using CBC.
Note: For android in java
I have written this manager file and its functions are working perfectly fine for me. This is for AES 128 and without any salt.
public class CryptoManager {
private static CryptoManager shared;
private String privateKey = "your_private_key_here";
private String ivString = "your_iv_here";
private CryptoManager(){
}
public static CryptoManager getShared() {
if (shared != null ){
return shared;
}else{
shared = new CryptoManager();
return shared;
}
}
public String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(ivString.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(privateKey.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return android.util.Base64.encodeToString(encrypted, android.util.Base64.DEFAULT);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(ivString.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(privateKey.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = new byte[0];
original = cipher.doFinal(android.util.Base64.decode(encrypted, android.util.Base64.DEFAULT));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
You need to call the functions like this.
String dataToEncrypt = "I need to encrypt myself";
String encryptedData = CryptoManager.getShared().encrypt(data);
And you will get your encrypted string with the following line
String decryptedString = CryptoManager.getShared().decrypt(encryptedData);
If you want an example of compatible code for Android and iPhone, look at the RNCryptor library for iOS and the JNCryptor library for Java/Android.
Both projects are open source and share a common data format. In these libraries, AES 256-bit is used, however it would be trivial to adapt the code if necessary to support 128-bit AES.
As per the accepted answer, both libraries use PBKDF2.