Problem with decryption , cipher in android AES/CTR/NoPadding - java

When i use this code on android Marshmallow(Android 6.0.1) ,the decryption is Ok but when i run on device with android Oreo(Android 8) the decryption value is not the same and data is not Correct.
private void decrypt(Cipher cipher, Uri uri) throws Exception {
long a = 113845229;
InputStream inputStream = getContentResolver().openInputStream(uri);
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
cipherInputStream.skip(a);
byte[] buffer = new byte[8];
cipherInputStream.read(buffer);
}
// create cipher
private Cipher createCipher(byte[] iv, byte[] salt, String password) throws Exception {
IvParameterSpec mIvParameterSpec = new IvParameterSpec(iv);
SecretKeySpec mSecretKeySpec = generate(password, salt);
Cipher mCipher = Cipher.getInstance("AES/CTR/NoPadding");
mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, mIvParameterSpec);
return mCipher;
}
// generate key
private SecretKeySpec generate(String password, byte[] salt) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] key = md.digest(password.getBytes(StandardCharsets.UTF_8));
return new SecretKeySpec(key, "AES");
}
buffer data is ok in android 6 but in android 8 the data is not correct.

I believe you're looking for random access to ctr encrypted data;
Skip method in CipherInputStream just doesn't do that and is 'Android version independent'(still in use; not deprecated or replaced since api level 1!);
Take a look at CipherInputStream class file; It has few internal properties:
private Cipher cipher;//the cipher you pass to constructor;
// the underlying input stream
private InputStream input;
/* the buffer holding data that have been read in from the
underlying stream, but have not been processed by the cipher
engine. the size 512 bytes is somewhat randomly chosen */
private byte[] ibuffer = new byte[512];//holds encrypted data
// having reached the end of the underlying input stream
private boolean done = false;
/* the buffer holding data that have been processed by the cipher
engine, but have not been read out */
private byte[] obuffer;//a portion of data that's decrypted but not yet read;
// the offset pointing to the next "new" byte
private int ostart = 0;
// the offset pointing to the last "new" byte
private int ofinish = 0;
and this is what skip does in CipherInputStream;
public long skip(long n) throws IOException {
int available = ofinish - ostart;
if (n > available) {
n = available;
}
if (n < 0) {
return 0;
}
ostart += n;
return n;
}
It doesn't load new data to obuffer or ibuffer; it only skips on what is available in the obuffer( just increases ostart);
This should do it(has room for improvement):
private static IvParameterSpec calculateIVForOffset(final IvParameterSpec iv,
final long blockOffset) {
final BigInteger ivBI = new BigInteger(1, iv.getIV());
final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset
/ AES_BLOCK_SIZE));
final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
final IvParameterSpec ivForOffset;
if (ivForOffsetBA.length >= AES_BLOCK_SIZE) {
ivForOffset = new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - AES_BLOCK_SIZE,
AES_BLOCK_SIZE);
} else {
final byte[] ivForOffsetBASized = new byte[AES_BLOCK_SIZE];
System.arraycopy(ivForOffsetBA, 0, ivForOffsetBASized, AES_BLOCK_SIZE
- ivForOffsetBA.length, ivForOffsetBA.length);
ivForOffset = new IvParameterSpec(ivForOffsetBASized);
}
return ivForOffset;
}
long offset = 113845229;// aka a
private void decrypt(Cipher cipher, Uri uri) throws Exception {
long skip_this_much=offset-(offset%16);
InputStream inputStream = getContentResolver().openInputStream(uri);
do{
skip_this_much=skip_this_much-inputStream.skip(skip_this_much);//InputStream.skip does not necessarily skip as much as specified in parameter and returns the actually skipped value;
}while(skip_this_much!=0);//not there yet; keep skipping;
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
int read_this_much=8;
byte[] buffer=new byte[read_this_much+(offset%16)];
cipherInputStream.read(buffer,0,read_this_much+(offset%16));//improve this yourself
buffer= Arrays.copyOfRange(buffer,offset%16,read_this_much+(offset%16));
}
// create cipher for offset
private Cipher createCipher(byte[] iv, byte[] salt, String password) throws Exception {
IvParameterSpec mIvParameterSpec = new IvParameterSpec(iv);
SecretKeySpec mSecretKeySpec = generate(password, salt);
Cipher mCipher = Cipher.getInstance("AES/CTR/NoPadding");
mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, calculateIVForOffset(mIvParameterSpec,offset));
return mCipher;
}
// generate key
private SecretKeySpec generate(String password, byte[] salt) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] key = md.digest(password.getBytes(StandardCharsets.UTF_8));
return new SecretKeySpec(key, "AES");
}

I came to the conclusion after research .you should implement InputStream with specific Cipher.
private static final int AES_BLOCK_SIZE = 16;
private InputStream mUpstream;
private Cipher mCipher;
private SecretKeySpec mSecretKeySpec;
private IvParameterSpec mIvParameterSpec;
public StreamingCipherInputStream(InputStream inputStream, Cipher cipher,
SecretKeySpec secretKeySpec, IvParameterSpec ivParameterSpec) {
super(inputStream, cipher);
mUpstream = inputStream;
mCipher = cipher;
mSecretKeySpec = secretKeySpec;
mIvParameterSpec = ivParameterSpec; }
#Override
public int read(byte[] b, int off, int len) throws IOException {
return super.read(b, off, len); }
public long forceSkip(long bytesToSkip) throws IOException {
long skipped = mUpstream.skip(bytesToSkip);
try {
int skip = (int) (bytesToSkip % AES_BLOCK_SIZE);
long blockOffset = bytesToSkip - skip;
long numberOfBlocks = blockOffset / AES_BLOCK_SIZE;
BigInteger ivForOffsetAsBigInteger = new BigInteger(1,
mIvParameterSpec.getIV()).add(BigInteger.valueOf(numberOfBlocks));
byte[] ivForOffsetByteArray = ivForOffsetAsBigInteger.toByteArray();
IvParameterSpec computedIvParameterSpecForOffset;
if (ivForOffsetByteArray.length < AES_BLOCK_SIZE) {
byte[] resizedIvForOffsetByteArray = new byte[AES_BLOCK_SIZE];
System.arraycopy(ivForOffsetByteArray, 0, resizedIvForOffsetByteArray,
AES_BLOCK_SIZE - ivForOffsetByteArray.length, ivForOffsetByteArray.length);
computedIvParameterSpecForOffset = new IvParameterSpec(resizedIvForOffsetByteArray);
} else {
computedIvParameterSpecForOffset = new IvParameterSpec(ivForOffsetByteArray, ivForOffsetByteArray.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
}
mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec, computedIvParameterSpecForOffset);
byte[] skipBuffer = new byte[skip];
mCipher.update(skipBuffer, 0, skip, skipBuffer);
Arrays.fill(skipBuffer, (byte) 0);
} catch (Exception e) {
return 0;
}
return skipped;
}
#Override
public int available() throws IOException {
return mUpstream.available();}

Related

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

InvalidAlgorithmParameterException: IV must be specified in CBC mode

I first generated IV using this method:
public static String generateRandomIV() {
SecureRandom ranGen = new SecureRandom();
byte[] aesKey = new byte[16];
ranGen.nextBytes(aesKey);
StringBuffer result = new StringBuffer();
for (byte b : aesKey) {
result.append(String.format("%02x", b));
}
if (16 > result.toString().length()) {
return result.toString();
} else {
return result.toString().substring(0, 16);
}
}
And then tried to decrypt a String which was encrypted using IV generated by above method:
private String decrypt(String _inputText, String _encryptionKey,
String _initVector) throws Exception {
String _out = "";
int len = _encryptionKey.getBytes("UTF-8").length;
if (_encryptionKey.getBytes("UTF-8").length >= _key.length) {
len = _key.length;
int ivlen = _initVector.getBytes("UTF-8").length;
if (_initVector.getBytes("UTF-8").length > _iv.length)
ivlen = _iv.length;
System.arraycopy(_encryptionKey.getBytes("UTF-8"), 0, _key, 0, len);
System.arraycopy(_initVector.getBytes("UTF-8"), 0, _iv, 0, ivlen);
SecretKeySpec keySpec = new SecretKeySpec(_key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(_iv);
//Decryption starts
_cx.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decodeValue = Base64.decode(_inputText.getBytes(), Base64.DEFAULT);
Following Exception is coming at last line of above code snippet:
java.lang.RuntimeException: java.security.InvalidAlgorithmParameterException: IV must be specified in CBC mode
I don't see any use of your IV. Try the following cipher initialization:
_cx.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

Concatenating MAC and salt with ciphertext

I've been having trouble adding a MAC to my password-based AES encryption/decryption program. I am trying to add the MAC'd plaintext and salt (to be used with password) (both byte arrays) to a final array along with the ciphertext, and then decrypt by reading in the ciphertext file and splitting it back up into salt, MAC, and cipher text byte arrays.
The encryption class seems to be running smoothly but the decryption class does not. I debugged the the class and found that it fails because it never enters the if statement that checks whether the computed and recovered MACs are the same:
if(Arrays.equals(macBytes, hmac))
I couldn't figure out why until I printed out the byte arrays for the salt, message, and MAC, and found that they don't match when printed from the encryption and decryption classes. All the array sizes match up across the two classes, but the byte values change somewhere.
Both classes worked perfectly without the MAC before, but I didn't add the salt directly to the encrypted data then and instead wrote it to a separate file. Including it with the encrypted data makes this slightly more portable for me, but was it a bad choice to do so? Is it better to write it to a separate file? Or am I just missing something blatantly obvious in my code?
Here is the full code.
Encryption class
public class AESEncryption
{
private final String ALGORITHM = "AES";
private final String MAC_ALGORITHM = "HmacSHA256";
private final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private final String KEY_DERIVATION_FUNCTION = "PBKDF2WithHmacSHA1";
private final String PLAINTEXT = "/Volumes/CONNOR P/Unencrypted.txt";
private final String ENCRYPTED = "/Volumes/CONNOR P/Encrypted.txt";
private final String PASSWORD = "javapapers";
private final String LOC = Paths.get(".").toAbsolutePath().normalize().toString();
private static final int SALT_SIZE = 64;
private final int KEY_LENGTH = 128;
private final int ITERATIONS = 100000;
public AESEncryption()
{
try
{
encrypt();
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, "Error: " + ex.getClass().getName(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
private void encrypt() throws Exception
{
File encrypted = new File(ENCRYPTED);
File plaintext = new File(PLAINTEXT);
int encryptedSize = (int)encrypted.length();
int plaintextSize = (int)plaintext.length();
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(PLAINTEXT));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(ENCRYPTED));
//Create salt
byte[] salt = new byte[SALT_SIZE];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(salt);
//Create cipher key
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_DERIVATION_FUNCTION);
KeySpec keySpec = new PBEKeySpec(PASSWORD.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKey secret = new SecretKeySpec(factory.generateSecret(keySpec).getEncoded(), ALGORITHM);
//Create cipher
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
//Read plaintext file into byte array
byte[] input = new byte[encryptedSize];
Path path = Paths.get(PLAINTEXT);
input = Files.readAllBytes(path);
byte[] crypt = cipher.doFinal(input);
//Create MAC object and apply to the byte array crypt[] containing the plaintext
KeyGenerator keyGenerator = KeyGenerator.getInstance(MAC_ALGORITHM);
SecretKey macKey = keyGenerator.generateKey();
Mac mac = Mac.getInstance(MAC_ALGORITHM);
mac.init(macKey);
byte[] macBytes = mac.doFinal(crypt);
//Add salt, MAC'd plaintext, and encrypted plaintext to final array
byte[] output = new byte[SALT_SIZE + crypt.length + macBytes.length];
System.arraycopy(salt, 0, output, 0, SALT_SIZE);
System.arraycopy(macBytes, 0, output, SALT_SIZE, macBytes.length);
System.arraycopy(crypt, 0, output, SALT_SIZE + macBytes.length, crypt.length);
//Write array with encrypted data to a new file
bufferedOutputStream.write(output);
bufferedInputStream.close();
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
Decryption class
public class AESDecryption
{
private final String ALGORITHM = "AES";
private final String MAC_ALGORITHM = "HmacSHA256";
private final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private final String KEY_DERIVATION_FUNCTION = "PBKDF2WithHmacSHA1";
private final String PLAINTEXT = "/Volumes/CONNOR P/De-Encrypted.txt";
private final String ENCRYPTED = "/Volumes/CONNOR P/Encrypted.txt";
private final String PASSWORD = "javapapers";
private final String LOC = Paths.get(".").toAbsolutePath().normalize().toString();
private final int SALT_SIZE = 64;
private final int IV_SIZE = 16;
private final int KEY_LENGTH = 128;
private final int ITERATIONS = 100000;
public AESDecryption()
{
try
{
decrypt();
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, "Error: " + ex.getClass().getName(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
private void decrypt() throws Exception
{
File encrypted = new File(ENCRYPTED);
File plaintext = new File(PLAINTEXT);
int encryptedSize = (int)encrypted.length();
int plaintextSize = (int)plaintext.length();
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(encrypted));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(plaintext));
//Read in the encrypted data
byte[] input = new byte[encryptedSize];
Path path = Paths.get(ENCRYPTED);
input = Files.readAllBytes(path);
int increment = (input.length-SALT_SIZE)/2;
if(input.length >= (SALT_SIZE + increment))
{
//Recover salt, MAC, and encrypted data and store in arrays
byte[] salt = Arrays.copyOfRange(input, 0, SALT_SIZE);
byte[] macBytes = Arrays.copyOfRange(input, SALT_SIZE, SALT_SIZE + increment);
byte[] crypt = Arrays.copyOfRange(input, SALT_SIZE + increment, input.length);
//Regenerate original MAC
KeyGenerator keyGenerator = KeyGenerator.getInstance(MAC_ALGORITHM);
SecretKey macKey = keyGenerator.generateKey();
Mac mac = Mac.getInstance(MAC_ALGORITHM);
mac.init(macKey);
byte[] hmac = mac.doFinal(crypt);
if(Arrays.equals(macBytes, hmac)) //This is where it fails, never enters
{
//Regenerate cipher and decrypt data
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_DERIVATION_FUNCTION);
KeySpec keySpec = new PBEKeySpec(PASSWORD.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKey secret = new SecretKeySpec(factory.generateSecret(keySpec).getEncoded(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
//Write decrypted data to new text file
byte[] output = cipher.doFinal(crypt);
bufferedOutputStream.write(output);
bufferedInputStream.close();
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
}
Thanks for any help. It is much appreciated.
public class AESEncryption
{
private final String ALGORITHM = "AES";
private final String MAC_ALGORITHM = "HmacSHA256";
private final String PRNG_ALGORITHM = "SHA1PRNG";
private final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private final String PLAINTEXT = "/Volumes/CONNOR P/Unencrypted.txt";
private final String ENCRYPTED = "/Volumes/CONNOR P/Encrypted.txt";
private final String PASSWORD = "javapapers";
private final String IV_FILE_NAME = "iv.enc";
private final String LOC = Paths.get(".").toAbsolutePath().normalize().toString();
private final int SALT_SIZE = 16;
private final int IV_SIZE = 16;
private final int KEY_LENGTH = 128;
private final int ITERATIONS = 100000;
private final int START = 0;
public AESEncryption()
{
try
{
encrypt();
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, "Error: " + ex.getClass().getName(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
private void encrypt() throws Exception
{
File encrypted = new File(ENCRYPTED);
File plaintext = new File(PLAINTEXT);
int plaintextSize = (int)plaintext.length();
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(plaintext));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(encrypted));
//Create salt for cipher key
byte[] salt = new byte[SALT_SIZE];
SecureRandom saltSecureRandom = SecureRandom.getInstance(PRNG_ALGORITHM);
saltSecureRandom.nextBytes(salt);
//Create cipher key & use to initialize cipher
byte[] keyBytes = PBEKeyFactory.getKey(PASSWORD, salt, ITERATIONS, KEY_LENGTH);
SecretKeySpec secret = new SecretKeySpec(keyBytes, ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
//Create byte array of encrypted data
byte[] input = new byte[plaintextSize];
Path path = Paths.get(PLAINTEXT);
input = Files.readAllBytes(path);
byte[] crypt = cipher.doFinal(input);
//Create salt for the MAC key for added security
byte[] macsalt = new byte[SALT_SIZE];
SecureRandom macsaltSecureRandom = SecureRandom.getInstance(PRNG_ALGORITHM);
macsaltSecureRandom.nextBytes(macsalt);
//PBEKeyFactory.getKey(password, salt, iterations, keylength)
//returns a byte array representation of a SecretKey.
//Used a SecretKeyFactory instead of a KeyGenerator to make key.
//SecretKeyFactory gives back the same key given the same specifications
//whereas KeyGenerator gives back a new random key each time.
byte[] macPBE = PBEKeyFactory.getKey(PASSWORD, macsalt, ITERATIONS, KEY_LENGTH);
SecretKeySpec macKey = new SecretKeySpec(macPBE, MAC_ALGORITHM);
Mac mac = Mac.getInstance(MAC_ALGORITHM);
mac.init(macKey);
byte[] macBytes = mac.doFinal(crypt);
byte[] output = new byte[SALT_SIZE + SALT_SIZE + crypt.length + macBytes.length];
System.arraycopy(salt, START, output, START, SALT_SIZE);
System.arraycopy(macsalt, START, output, SALT_SIZE, SALT_SIZE);
System.arraycopy(macBytes, START, output, SALT_SIZE + SALT_SIZE, macBytes.length);
System.arraycopy(crypt, START, output, SALT_SIZE + SALT_SIZE + macBytes.length, crypt.length);
bufferedInputStream.close();
bufferedOutputStream.write(output);
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
public class AESDecryption
{
private final String ALGORITHM = "AES";
private final String MAC_ALGORITHM = "HmacSHA256";
private final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private final String PLAINTEXT = "/Volumes/CONNOR P/De-Encrypted.txt";
private final String ENCRYPTED = "/Volumes/CONNOR P/Encrypted.txt";
private final String PASSWORD = "javapapers";
private final String LOC = Paths.get(".").toAbsolutePath().normalize().toString();
private final int SALT_SIZE = 16;
//MAC key size is 256 bits (32 bytes) since it is created with
//the HmacSHA256 algorithm
private final int MAC_SIZE = 32;
private final int IV_SIZE = 16;
private final int START = 0;
private final int KEY_LENGTH = 128;
private final int ITERATIONS = 100000;
public AESDecryption()
{
try
{
decrypt();
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, "Error: " + ex.getClass().getName(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
private void decrypt() throws Exception
{
File encrypted = new File(ENCRYPTED);
File plaintext = new File(PLAINTEXT);
int encryptedSize = (int)encrypted.length();
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(encrypted));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(plaintext));
//Read in encrypted data
byte[] input = new byte[encryptedSize];
Path path = Paths.get(ENCRYPTED);
input = Files.readAllBytes(path);
if(input.length >= (SALT_SIZE*2 + MAC_SIZE))
{
byte[] cryptSalt = Arrays.copyOfRange(input, START, SALT_SIZE);
byte[] macSalt = Arrays.copyOfRange(input, SALT_SIZE, SALT_SIZE*2);
byte[] macBytes = Arrays.copyOfRange(input, SALT_SIZE*2, (SALT_SIZE*2 + MAC_SIZE));
byte[] cryptBytes = Arrays.copyOfRange(input, (SALT_SIZE*2 + MAC_SIZE), input.length);
//This generates the same MAC key from encryption.
//Before, the KeyGenerator created a new random key
//meaning the derived and computed MAC keys were never the same
byte[] macKeyBytes = PBEKeyFactory.getKey(PASSWORD, macSalt, ITERATIONS, KEY_LENGTH);
SecretKeySpec macKey = new SecretKeySpec(macKeyBytes, MAC_ALGORITHM);
Mac mac = Mac.getInstance(MAC_ALGORITHM);
mac.init(macKey);
byte[] compMacBytes = mac.doFinal(cryptBytes);
//Check if computed and derived MAC's are the same
if(Arrays.equals(macBytes, compMacBytes))
{
//Creates same key from encryption
byte[] cryptKeyBytes = PBEKeyFactory.getKey(PASSWORD, cryptSalt, ITERATIONS, KEY_LENGTH);
SecretKeySpec cryptKey = new SecretKeySpec(cryptKeyBytes, ALGORITHM);
//Creates cipher and reads decrypted data to array
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, cryptKey, new IvParameterSpec(new byte[16]));
byte[] output = cipher.doFinal(cryptBytes);
bufferedInputStream.close();
bufferedOutputStream.write(output);
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
}
}
//This class has only one method, getKey(), which returns a byte array
//of a SecretKey of the corresponding parameters
public class PBEKeyFactory
{
private static final String KEY_DERIVATION_FUNCTION = "PBKDF2WithHmacSHA1";
public static byte[] getKey(String password, byte[] salt, int iterations, int length) throws Exception
{
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterations, length);
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_DERIVATION_FUNCTION);
return factory.generateSecret(keySpec).getEncoded();
}
}

using java to encrypt/decrypt files return wrong number of bytes when using padding

I'm trying to make an application that encrypts / decrypt and it is working fine when I use a cipher mode without padding. With padding, the number of blocks is unpredictable and I cant find the source of the problem.
I am using the following code to test:
private static final int KEY_LEN = 16;
private static final String ENCRYPTION_ALGO = "AES";
private static final String HASHING_ALGO = "SHA-256";
private static final String CIPHER_MODE = "AES/CFB/PKCS5Padding";
private static final String ENCODING = "UTF-8";
static private byte[] initVector;
static private SecretKey key;
static Cipher cipher;
private static SecretKey generateKey(byte[] password) {
MessageDigest md = MessageDigest.getInstance(HASHING_ALGO);
byte[] hashedKey = md.digest(password);
return new SecretKeySpec(hashedKey, 0, KEY_LEN, ENCRYPTION_ALGO);
}
public static byte[] getIV() {
return initVector;
}
public static void setup(String password)
{
key = generateKey(password.getBytes(ENCODING));
cipher = Cipher.getInstance (CIPHER_MODE);
cipher.init (Cipher.ENCRYPT_MODE, key);
AlgorithmParameters params = cipher.getParameters ();
initVector = params.getParameterSpec (IvParameterSpec.class).getIV();
}
public static void setup(String password, byte[] iv)
{
key = generateKey(password.getBytes(ENCODING));
cipher = Cipher.getInstance (CIPHER_MODE);
// define init vector that was used for encryption
cipher.init (Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
}
private static byte[] crypt(byte[] plaintext, boolean hasNext)
if (hasNext) {
return cipher.update(plaintext);
}else {
return cipher.doFinal(plaintext);
}
}
public static void main(String [] args)
{
try {
File input = new File ("input.txt");
File eoutput = new File ("output.enc");
File doutput = new File ("decrypted.txt");
FileInputStream fin = new FileInputStream(input);
FileOutputStream fout = new FileOutputStream (eoutput);
byte [] buffer = new byte [32];
int nBytes;
//encrypt
setup("password");
while ((nBytes = fin.read(buffer))!=-1) {
byte[] cyphertext;
if (nBytes < buffer.length) {
byte[] trimbuffer = new byte [nBytes];
System.arraycopy(buffer, 0, trimbuffer, 0, nBytes);
cyphertext = crypt(trimbuffer, false);
}else {
cyphertext = crypt(buffer, true);
}
fout.write(cyphertext);
}
fin.close();
fout.close();
FileInputStream fin2 = new FileInputStream(eoutput);
FileOutputStream fout2 = new FileOutputStream (doutput);
byte[] iv = getIV();
// decrypt
setup("password",iv);
while ((nBytes = fin2.read(buffer))!=-1) {
byte[] plaintext;
if (nBytes < buffer.length) {
byte[] trimbuffer = new byte [nBytes];
System.arraycopy(buffer, 0, trimbuffer, 0, nBytes);
plaintext = crypt(trimbuffer, false);
}else {
plaintext = crypt(buffer, true);
}
fout2.write(plaintext);
}
fin2.close();
fout2.close();
}
I forgot to mention that the problem seems to be in decryption side.
I just found the problem after lots of testing. The problem is that when I read files and the last block had the exact same size of the buffer, I was always calling crypt(buffer, true) that does a cipher.update() insted of cipher.doFinal()

PBEWITHSHA1AND128BITAES-CBC-BC Algorithm - must be 16 bytes Long error

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?

Categories