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();
}
}
Related
im facing a problem in my code.
I want to encrypt the Password from the user and save it to an SQL Database.
The Code im Using to Encrypt the Password with AES:
String key = "1234567890123456"; // 128 bit key
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(Passwort.getBytes());
String verschlüsselt = new String(encrypted);
Now im saving the String "verschlüsselt" to my Database.
In my LoginGUI im getting the String with the Password from the database:
String psswd = res.getString("Passwort"); //Getting the Password from Database and saving it into String
System.out.println(psswd);
The PrintOut is: "!?¿[ŸÊm,r~¤u" which is the correct encrypted password.
Now i'm trying to decrypt the password with following code:
String key = "1234567890123456"; // 128 bit key
cipher.init(Cipher.DECRYPT_MODE, aesKey);
EPasswort = new String(cipher.doFinal(bpsswd)); //Im facing the problem here
I Know that i can't decrypt the String and that I have to convert the String into an byte[].
I've done that in several ways:
byte[] password = psswd.getBytes();
System.out.println(password);
password = psswd.getBytes("UTF-8");
System.out.println(password);
EPasswort = new String(cipher.doFinal(password));
But the Output is always something the decrypter can't work with:
[B#ab33b4
[B#189090b
How do i convert my String with the Password (!?¿[ŸÊm,r~¤u) into an byte[] so my decrypter can work with it?
Thanks for any help.
You should encrypt the String and than save it as a String, in this example I'm using a Base64 encoder and a different cypher but the logic behind is the same. You should save bytes or String instead of using the reference to your bytes, here an example:
#Test
public void testCrypt () {
final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
final int TAG_LENGTH_BIT = 128;
final int IV_LENGTH_BYTE = 12;
final int SALT_LENGTH_BYTE = 16;
final String password = "password";
final byte[] salt = getRandomNonce(SALT_LENGTH_BYTE);
final byte[] iv = getRandomNonce(IV_LENGTH_BYTE);
final SecretKey aesKeyFromPassword = getAESKeyFromPassword(password.toCharArray(), salt);
String msg = "Test";
String out = "";
try {
out = encrypt(ENCRYPT_ALGO, TAG_LENGTH_BIT, iv, aesKeyFromPassword, msg);
} catch (Exception e) {
e.printStackTrace();
}
byte[] plainText = new byte[0];
try {
plainText = decrypt(ENCRYPT_ALGO, TAG_LENGTH_BIT, iv, aesKeyFromPassword, out);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Decrypted " + out + " in: " + new String(plainText, StandardCharsets.ISO_8859_1));
}
following the encrypt method:
private String encrypt(final String ENCRYPT_ALGO, final int TAG_LENGTH_BIT, final byte[] iv,
final SecretKey aesKeyFromPassword, String msg) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
final byte[] pText = msg.getBytes(StandardCharsets.ISO_8859_1);
Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
final byte[] cipherText = cipher.doFinal(pText);
String out = Base64.getEncoder().encodeToString(cipherText);
System.out.println("Encrypted " + msg + " as: " + out);
return out;
}
private static SecretKey getAESKeyFromPassword(final char[] password, final byte[] salt) {
final int KEY_LENGTH = 128;
final int ITERATION_COUNT = 65536;
try {
final SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
final KeySpec spec = new PBEKeySpec(password, salt, ITERATION_COUNT, KEY_LENGTH);
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
} catch (final Exception e) {
LOGGER.error(e);
throw new RedException(e);
}
}
private static byte[] getRandomNonce(final int numBytes) {
final byte[] nonce = new byte[numBytes];
new SecureRandom().nextBytes(nonce);
return nonce;
}
Once you save the msgCrypted in db you should retrieve it and process it to decrypt like this:
private byte[] decrypt(final String ENCRYPT_ALGO, final int TAG_LENGTH_BIT, final byte[] iv,
final SecretKey aesKeyFromPassword, String out) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
Cipher dCipher = Cipher.getInstance(ENCRYPT_ALGO);
dCipher.init(Cipher.DECRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
final byte[] plainText = dCipher.doFinal(Base64.getDecoder().decode(out));
return plainText;
}
In the console you should have something like this:
You are printing the reference to the byte array, not the elements.
Run this for demonstration:
public static void main (String [] args) {
String s = "12345678";
byte [] b = s.getBytes ();
System.out.println (b);
for (int i = 0; i < 8; i++) {
System.out.println (b [i]);
}
}
Output:
[B#7a81197d
49
50
51
52
53
54
55
56
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();}
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'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()
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?