I am trying to add my 16-bit IV used to encrypt my data as the final block in the byte array used to hold the encrypted data. I want to do this obviously for the decryption part so that I can use a completely random IV for each encryption/decryption call. I have the following for testing purposes:
public static String encrypt(String plainText) throws Exception {
encryptionKey = new SecretKeySpec(eKey.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, initialisationVector);
byte[] eDataAndIv = appendIvToEncryptedData(cipher.doFinal(plainText.getBytes("UTF-8")), initialisationVector.getIV());
return bytesToHexString(eDataAndIv);
}
public static String decrypt(String hexEncoded) throws Exception {
byte[] decodedBytes = hexStringToBytes(hexEncoded);
ArrayList<byte[]> al = retreiveIvFromByteArray(decodedBytes);
byte[] eData = al.get(0);
byte[] iv = al.get(1);
cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv));
return reconstructedPlainText(cipher.doFinal(eData));
}
private static byte[] appendIvToEncryptedData(byte[] eData, byte[] iv) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(eData);
os.write(iv);
return os.toByteArray();
}
private static ArrayList<byte[]> retreiveIvFromByteArray(byte[] dataPlusIv) {
ByteArrayOutputStream iv = new ByteArrayOutputStream(16);
ByteArrayOutputStream eData = new ByteArrayOutputStream();
iv.write(dataPlusIv, dataPlusIv.length - 16, 16);
eData.write(dataPlusIv, 0, dataPlusIv.length - 16);
ArrayList<byte[]> al = new ArrayList<byte[]>();
al.add(eData.toByteArray());
al.add(iv.toByteArray());
return al;
}
The list of steps for encryption are:
Create IV
Encrypt data
Append IV to end of encrypted data byte array
Encode byte array using hex
The list of steps for decryption are:
Decode hex
Break encrypted data and IV from byte array
Decrypt data using IV
What I have works, but I guess what I want to know is, is there a "better" way of doing this? By that I mean, is there a set, or simpler, way to do this using the Cipher* types? I can't find them.
Thanks.
Well you can certainly avoid the long-winded retreiveIvFromByteArray code:
public static String decrypt(String hexEncoded) throws Exception {
byte[] decodedBytes = hexStringToBytes(hexEncoded);
int ivIndex = decodedBytes.length - 16;
cipher.init(Cipher.DECRYPT_MODE, encryptionKey,
new IvParameterSpec(decodedBytes, ivIndex, 16));
return reconstructedPlainText(cipher.doFinal(decodedBytes, 0, ivIndex));
}
Is that the sort of thing you were thinking of?
Likewise for the appendIvToEncryptedData you could just create a new byte array of the appropriate length, and use System.arraycopy twice.
Related
I have the following code to encrypt-decrypt a string using a key and random IV. However during decrypt I get a lot of zeros at the end in my IDE.
public class Example {
private static final String AES_MODE = "AES/CBC/PKCS5Padding";
private static final String CHARSET = "UTF-8";
private static final String HASH_ALGORITHM = "SHA-256";
private static final String KEY = "SUPER_SECURE_KEY";
private static SecretKeySpec getSecretKey() throws NoSuchAlgorithmException, UnsupportedEncodingException {
final MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] bytes = KEY.getBytes(CHARSET);
digest.update(bytes, 0, bytes.length);
byte[] key = digest.digest();
return new SecretKeySpec(key, "AES");
}
public static String encrypt(String message) {
if(message == null || message.isEmpty()) {
return "";
}
try {
final SecretKeySpec key = getSecretKey();
byte[] cipherText = encrypt(key, message.getBytes(CHARSET));
return Base64.getEncoder().encodeToString(cipherText);
} catch (Exception e) {
System.out.print(e.toString());
return "";
}
}
private static byte[] encrypt(final SecretKeySpec key, final byte[] message) throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance(AES_MODE);
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = new byte[iv.length + cipher.getOutputSize(message.length)];
System.arraycopy(iv, 0, ciphertext, 0, iv.length);
cipher.doFinal(message, 0, message.length, ciphertext, iv.length);
return ciphertext;
}
// ========================================================================================
public static String decrypt(String base64EncodedCipherText) {
if(base64EncodedCipherText == null || base64EncodedCipherText.isEmpty()) {
return "";
}
try {
final SecretKeySpec key = getSecretKey();
byte[] decodedCipherText = Base64.getDecoder().decode(base64EncodedCipherText);
byte[] decryptedBytes = decrypt(key, decodedCipherText);
return new String(decryptedBytes, CHARSET);
} catch (Exception e) {
System.out.print(e.toString());
return "";
}
}
private static byte[] decrypt(final SecretKeySpec key, final byte[] decodedCipherText) throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance(AES_MODE);
IvParameterSpec ivSpec = new IvParameterSpec(decodedCipherText, 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
int plainTextLength = decodedCipherText.length - cipher.getBlockSize();
byte[] plaintext = new byte[plainTextLength];
cipher.doFinal(decodedCipherText, cipher.getBlockSize(), plainTextLength, plaintext, 0);
return plaintext;
// return cipher.doFinal(decodedCipherText);
}
// ========================================================================================
public static void main(String[] args) {
String message = "Message to encrypt.";
String encryptedText = encrypt(message);
System.out.println(encryptedText);
String decryptedText= decrypt(encryptedText);
System.out.println(decryptedText);
}
}
The output I get in IntelliJ IDEA is:
here
I think I am correctly separating the IV from the ciphertext, and decrypt the ciphertext with the key and the random IV. But still end up getting zeros in the end. Any pointers to what is wrong?
Reading is fundamental. The docs for getOutputSize indicate you can't use it for this purpose:
The actual output length of the next update or doFinal call may be smaller than the length returned by this method.
Encrypt it then check the resulting byte array, or do something with the return value of the doFinal method (which really tells you how many bytes it made), or make a ByteArrayOutputStream and send both the iv and the bytes from doFinal (taking into account what it returns) there, then ask it for the byte[], or use a ByteBuffer.
Note that CBC is dubious, as is pass hashing with SHA-256. It works, but it's 'too fast', it's very easy for a hacker to try a few billion passwords a second. In general you shouldn't be handrolling this stuff.
CBC mode as normally used requires padding, which your code correctly specifies, so the ciphertext (before adding and after removing the IV) is longer than the plaintext. You allocate a buffer for this longer size and Cipher.doFinal only stores the actual plaintext to it, leaving the remaining bytes with the value initialized by new byte[n] which is (always) zero.
You could determine the size the output will be using ciper.getOutputSize(int) much as you did for encrypt This doesn't work; Maarten is right.
You could continue to overallocate the output buffer, but save the return value from cipher.doFinal (input,off,len, output,off) which is an int that tells you the number of bytes output (decrypted), and then use only that many bytes from the buffer e.g. new String (output, 0, outlen, charset) or Arrays.copyOf(output, outlen)
But the easiest way is to use the doFinal overload that allocates the buffer itself (with the correct size) and returns it:
return cipher.doFinal(decodedCipherText, cipher.getBlockSize(), decodedCipherText.length - cipher.getBlockSize());
Concur with not using a simple hash on a password, but your example doesn't show or say if your 'key' is really a password (handled by humans, and needing 'stretching') or just a text form of something with adequate entropy, for which a simple hash is okay.
I want to use a barcode (code 39) to represent a string, and I want this string to be encrypted using AES.
However, I can only display 43 characters with the barcode. How can I encrypt it so that the result uses only the available set of characters?
Here's what I have so far:
public static byte[] encryptAES(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes("ASCII"));
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(cleartext.getBytes("ASCII"));
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(BLOCKS, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
public static void main(String [] args)
{
String str = "312432432";
String key = "4AFJ3243J";
String result = new String(encryptAES(key,str), "ASCII");
}
Thanks!
What you have is an encoding issue, the problem being you want to convert to a non-standard encoding. What I would do is convert to a base43 encoding. However, you will likely need to implement your own conversion. You should look into how to convert between arbitrary bases, and do the conversion on the byte output of the encryption. Essentially you will take the base10 value of the byte (between 0 and 255 if unsigned), and convert it to two different base43 characters.
A quick Google search for base43 gave me this. Which I haven't used myself, but looks like it could work.
For all haters, I READ MANY topics like this one, and non of them was helpful.
eg. here javax.crypto.BadPaddingException: Given final block not properly padded error while decryption or here Given final block not properly padded
I want to encrypt and then decrypt Strings. Read many topics about
"Given final block not properly padded" exception, but non of these solutions worked.
My Class:
package aes;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.JOptionPane;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class EncryptionExample {
private static SecretKeySpec key;
private static IvParameterSpec ivSpec;
private static Cipher cipher;
private static byte[] keyBytes;
private static byte[] ivBytes;
private static int enc_len;
public static void generateKey() throws Exception
{
String complex = new String ("9#82jdkeo!2DcASg");
keyBytes = complex.getBytes();
key = new SecretKeySpec(keyBytes, "AES");
complex = new String("#o9kjbhylK8(kJh7"); //just some randoms, for now
ivBytes = complex.getBytes();
ivSpec = new IvParameterSpec(ivBytes);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
public static String encrypt(String packet) throws Exception
{
byte[] packet2 = packet.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted = new byte[cipher.getOutputSize(packet2.length)];
enc_len = cipher.update(packet2, 0, packet2.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);
return packet = new String(encrypted);
}
public static String decrypt(String packet) throws Exception
{
byte[] packet2 = packet.getBytes();
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(packet2, 0, enc_len, decrypted, 0);
HERE EXCEPTION>>>>> dec_len += cipher.doFinal(decrypted, dec_len); <<<<<<<<<
return packet = new String(decrypted);
}
// and display the results
public static void main (String[] args) throws Exception
{
// get the text to encrypt
generateKey();
String inputText = JOptionPane.showInputDialog("Input your message: ");
String encrypted = encrypt(inputText);
String decrypted = decrypt(encrypted);
JOptionPane.showMessageDialog(JOptionPane.getRootFrame(),
"Encrypted: " + new String(encrypted) + "\n"
+ "Decrypted: : " + new String(decrypted));
.exit(0);
}
}
The thing is, when I decrypt strings (about 4/10 of shots), I get that exception:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:479)
at javax.crypto.Cipher.doFinal(Cipher.java:2068)
at aes.EncryptionExample.deszyfrujBez(EncryptionExample.java:HERE tag)
at aes.EncryptionExample.main(EncryptionExample.java:Main starting)
Does anybody know what to change here (key? *.doFinal() method?) to make it work?
# for those curious - methods have to be static, as this is a part of something bigger ;)
When you use byte[] packet2 = packet.getBytes() you are converting the string based on the default encoding, which could be UTF-8, for example. That's fine. But then you convert the ciphertext back to a string like this: return packet = new String(encrypted) and this can get you into trouble if this does not round-trip to the same byte array later in decrypt() with another byte[] packet2 = packet.getBytes().
Try this instead: return packet = new String(encrypted, "ISO-8859-1"), and byte[] packet2 = packet.getBytes("ISO-8859-1") -- it's not what I would prefer, but it should round-trip the byte arrays.
The result of encryption is binary data. In most cases it cannot be interpreted as a valid string encoding. So the call to new String(encrypted) will most likely distort the encrypted bytes and after doing packet.getBytes() you end up with a byte array with different content.
The decryption now fails because the cypher text has been changed. The padding bytes are not correctly recovered and cannot be removed.
To fix that, don't convert the cypher text to a string, keep the byte array.
I want a secure solution for caching a user's password on their PC during their session.
I have trawled numerous AES examples and know that this has been answered elsewhere but I must say it is a little confusing. My aesSecretKey or aesInitialisationVector are not working in the decryption correctly but not sure where the issue lies.
Decrypting results in a javax.crypto.BadPaddingException: Given final block not properly padded exception.
My class looks like this
public class LockManagerTest {
// Need to share the IV and key between encode and decode
private static byte[] aesInitialisationVector;
private static SecretKey aesSecretKey;
private static Cipher aesCipher;
public LockManagerTest(String sessionKey) {
try {
byte[] key = getSecretKey(sessionKey.toCharArray(), getSalt(32),
65536, 128);
aesSecretKey = new SecretKeySpec(key, "AES");
aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
AlgorithmParameters params = aesCipher.getParameters();
aesInitialisationVector =
params.getParameterSpec(IvParameterSpec.class).getIV();
} catch (Exception e) {
Util.handleException(e);
}
}
private static byte[] getSecretKey(char[] plaintext,
byte[] salt,
int iterations,
int keySize)
throws Exception {
PBEKeySpec spec = new PBEKeySpec(plaintext, salt, iterations, keySize);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return skf.generateSecret(spec).getEncoded();
}
private static byte[] getSalt(int keyLength) throws Exception {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
byte[] salt = new byte[keyLength];
random.nextBytes(salt);
return salt;
}
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
public byte[] decryptAes(byte[] ciphertext) throws Exception {
aesCipher.init(Cipher.DECRYPT_MODE,
aesSecretKey, new IvParameterSpec(aesInitialisationVector));
byte[] plaintext = aesCipher.doFinal(ciphertext);
return plaintext;
}
}
Comments regarding the level of security appreciated also.
You need to pass the IV when calling init() in encryptedAes().
AES is a CBC algorithm and divides input into blocks. These blocks must be of a specific size. In the case of AES, I believe it is 16 bytes. If the input is not a multiple of 16 bytes, it must be padded with nulls before encryption.
Instead of generating new IV while decrypting, you need to pass same IV which you use for encrypting. Remember AES is Symmetric Cipher.
Edit:
What you are doing is:
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
Instead store the IvParameterSpec as a static, as per below (u can do proper variable declaration in your program)
public byte[] encryptedAes(char[] input) throws Exception {
//declare as static so initVector can be reused when decrypting
IvParamterSpec initVector = new IvParameterSpec(aesSecretKey);
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey, initVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
make the changes and then run your program. make sure you use the same initVector while decrypting. in your program you are creating new IvParameterSpec(...)
I have a file encrypted with java application using AES. I also have a key file was encrypted with. But i can't understand how to use the key to decrypt file. Most tutorials and examples create temporary random key, encrypt file and decrypt it in one place.
So, question is how to specify a key which have to be used for decryption?
EDIT:
Samples i found use following code to generate key. I have no idea where i can use my key here.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey key = kgen.generateKey();
Just to summarise my comments to Lucifer's answer.
If you don't know what padding was used to encrypt, then decrypt with 'no padding' set. That will decrypt everything, including the padding, and won't throw an error because of mismatched padding.
When you have decrypted the cyphertext, have a look at the last block of the output and see what padding was used. Different paddings leave different byte patterns, so it is usually easy enough to tell.
Set your decryption method to expect the correct type of padding, and it will be automatically removed for you.
The answer could be simply to put the key data as bytes into a SecretKeySpec like this:
SecretKeySpec aesKey = new SecretKeySpec(myKeyData, "AES");
Note that SecretKeySpec implements the Key interface, so you can use it directly in a Cipher.init() method. So there is no SecretKeyFactory needed, which you would use otherwise.
Please try following methods, if might helpful for you.
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception
{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
private static byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(false, ivAndKey);
return cipherData(aes, cipher);
}
private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return cipherData(aes, plain);
}
Complete example of encrypting/Decrypting a huge video without throwing Java OutOfMemoryException and using Java SecureRandom for Initialization Vector generation. Also depicted storing key bytes to database and then reconstructing same key from those bytes.
https://stackoverflow.com/a/18892960/185022