Related
Im trying to write a program to encrypt any type of file. I had my encryption classes already done, when I noticed (at first it worked) that I am getting an AEADBadTagException whenever I try to decrypt any of my files.
Here is my encryption/decryption class:
class Encryptor {
private static final String algorithm = "AES/GCM/NoPadding";
private final int tagLengthBit = 128; // must be one of {128, 120, 112, 104, 96}
private final int ivLengthByte = 12;
private final int saltLengthByte = 64;
protected final Charset UTF_8 = StandardCharsets.UTF_8;
private CryptoUtils crypto = new CryptoUtils();
// return a base64 encoded AES encrypted text
/**
*
* #param pText to encrypt
* #param password password for encryption
* #return encoded pText
* #throws Exception
*/
protected byte[] encrypt(byte[] pText, char[] password) throws Exception {
// 64 bytes salt
byte[] salt = crypto.getRandomNonce(saltLengthByte);
// GCM recommended 12 bytes iv?
byte[] iv = crypto.getRandomNonce(ivLengthByte);
// secret key from password
SecretKey aesKeyFromPassword = crypto.getAESKeyFromPassword(password, salt);
Cipher cipher = Cipher.getInstance(algorithm);
// ASE-GCM needs GCMParameterSpec
cipher.init(Cipher.ENCRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(tagLengthBit, iv));
byte[] cipherText = cipher.doFinal(pText);
// prefix IV and Salt to cipher text
byte[] cipherTextWithIvSalt = ByteBuffer.allocate(iv.length + salt.length + cipherText.length).put(iv).put(salt)
.put(cipherText).array();
Main.clearArray(password, null);
Main.clearArray(null, salt);
Main.clearArray(null, iv);
Main.clearArray(null, cipherText);
aesKeyFromPassword = null;
cipher = null;
try {
return cipherTextWithIvSalt;
} finally {
Main.clearArray(null, cipherTextWithIvSalt);
}
}
// für Files
protected byte[] decrypt(byte[] encryptedText, char[] password)
throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException {
// get back the iv and salt from the cipher text
ByteBuffer bb = ByteBuffer.wrap(encryptedText);
byte[] iv = new byte[ivLengthByte];
bb.get(iv);
byte[] salt = new byte[saltLengthByte];
bb.get(salt);
byte[] cipherText = new byte[bb.remaining()];
bb.get(cipherText);
// get back the aes key from the same password and salt
SecretKey aesKeyFromPassword;
aesKeyFromPassword = crypto.getAESKeyFromPassword(password, salt);
Cipher cipher;
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(tagLengthBit, iv));
byte[] plainText = cipher.doFinal(cipherText);
Main.clearArray(password, null);
Main.clearArray(null, iv);
Main.clearArray(null, salt);
Main.clearArray(null, cipherText);
aesKeyFromPassword = null;
cipher = null;
bb = null;
try {
return plainText;
} finally {
Main.clearArray(null, plainText);
}
}
protected void encryptFile(String file, char[] pw) throws Exception {
Path pathToFile = Paths.get(file);
byte[] fileCont = Files.readAllBytes(pathToFile);
byte[] encrypted = encrypt(fileCont, pw);
Files.write(pathToFile, encrypted);
Main.clearArray(pw, null);
Main.clearArray(null, fileCont);
Main.clearArray(null, encrypted);
}
protected void decryptFile(String file, char[] pw)
throws IOException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException {
Path pathToFile = Paths.get(file);
byte[] fileCont = Files.readAllBytes(pathToFile);
byte[] decrypted = decrypt(fileCont, pw);
Files.write(pathToFile, decrypted);
Main.clearArray(pw, null);
Main.clearArray(null, fileCont);
Main.clearArray(null, decrypted);
}
}
The corresponding CryptoUtils class:
class CryptoUtils {
protected byte[] getRandomNonce(int numBytes) {
byte[] nonce = new byte[numBytes];
new SecureRandom().nextBytes(nonce);
try {
return nonce;
} finally {
Main.clearArray(null, nonce);
}
}
// Password derived AES 256 bits secret key
protected SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
// iterationCount = 65536
// keyLength = 256
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
try {
return secret;
} finally {
secret = null;
}
}
// hex representation
protected String hex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
try {
return result.toString();
} finally {
result.delete(0, result.length() - 1);
}
}
// print hex with block size split
protected String hexWithBlockSize(byte[] bytes, int blockSize) {
String hex = hex(bytes);
// one hex = 2 chars
blockSize = blockSize * 2;
// better idea how to print this?
List<String> result = new ArrayList<>();
int index = 0;
while (index < hex.length()) {
result.add(hex.substring(index, Math.min(index + blockSize, hex.length())));
index += blockSize;
}
try {
return result.toString();
} finally {
result.clear();
}
}
}
The Exception occurs at byte[] plainText = cipher.doFinal(cipherText); in the decrypt method.
Im unsure if the tagLenthBit must be the ivLengthByte * 8, I did try it though and it didnt make any difference.
I'm providing my own example code for AES 256 GCM file encryption with PBKDF2 key derivation because I'm too lazy to check all parts of your code :-)
The encryption is done with CipherInput-/Outputstreams because that avoids "out of memory errors" when encrypting larger files (your code is reading the complete plaintext / ciphertext in a byte array).
Please note that the code has no exception handling, no clearing of sensitive data/variables and the encryption/decryption result is a simple "file exist" routine but I'm sure you can use it as a good basis for your program.
That's a sample output:
AES 256 GCM-mode PBKDF2 with SHA512 key derivation file encryption
result encryption: true
result decryption: true
code:
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
public class AesGcmEncryptionInlineIvPbkdf2BufferedCipherInputStreamSoExample {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException,
InvalidKeyException, InvalidKeySpecException, InvalidAlgorithmParameterException {
System.out.println("AES 256 GCM-mode PBKDF2 with SHA512 key derivation file encryption");
char[] password = "123456".toCharArray();
int iterations = 65536;
String uncryptedFilename = "uncrypted.txt";
String encryptedFilename = "encrypted.enc";
String decryptedFilename = "decrypted.txt";
boolean result;
result = encryptGcmFileBufferedCipherOutputStream(uncryptedFilename, encryptedFilename, password, iterations);
System.out.println("result encryption: " + result);
result = decryptGcmFileBufferedCipherInputStream(encryptedFilename, decryptedFilename, password, iterations);
System.out.println("result decryption: " + result);
}
public static boolean encryptGcmFileBufferedCipherOutputStream(String inputFilename, String outputFilename, char[] password, int iterations) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException {
SecureRandom secureRandom = new SecureRandom();
byte[] salt = new byte[32];
secureRandom.nextBytes(salt);
byte[] nonce = new byte[12];
secureRandom.nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NOPadding");
try (FileInputStream in = new FileInputStream(inputFilename);
FileOutputStream out = new FileOutputStream(outputFilename);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
out.write(nonce);
out.write(salt);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec keySpec = new PBEKeySpec(password, salt, iterations, 32 * 8); // 128 - 192 - 256
byte[] key = secretKeyFactory.generateSecret(keySpec).getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
public static boolean decryptGcmFileBufferedCipherInputStream(String inputFilename, String outputFilename, char[] password, int iterations) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException {
byte[] salt = new byte[32];
byte[] nonce = new byte[12];
Cipher cipher = Cipher.getInstance("AES/GCM/NOPadding");
try (FileInputStream in = new FileInputStream(inputFilename); // i don't care about the path as all is lokal
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(outputFilename)) // i don't care about the path as all is lokal
{
byte[] buffer = new byte[8192];
in.read(nonce);
in.read(salt);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec keySpec = new PBEKeySpec(password, salt, iterations, 32 * 8); // 128 - 192 - 256
byte[] key = secretKeyFactory.generateSecret(keySpec).getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * 8, nonce);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
}
I have followed a guide to simply encrypt and decrypt a string but I can't somehow make it work
I want to have a constant key so I don't need to save it to my database and waste space
I just want to encrypt some personal data not password
do you guys have any idea?
I'm following this guide please it
public String getAction() throws Exception {
String encodedKey = "eightkey";
byte[] key = encodedKey.getBytes();
decodedKey.length, "DES");
SecretKey myDesKey = new SecretKeySpec(key, "DES");
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] text = action.getBytes();
byte[] textEncrypted = desCipher.doFinal(text);
String getAct = ""+textEncrypted;
return getAct;
}
public void setAction(String action) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
String encodedKey = "eightkey";
byte[] key = encodedKey.getBytes();
SecretKey myDesKey = new SecretKeySpec(key, "DES");
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] text = action.getBytes();
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(text);
String setAct = ""+textEncrypted;
this.action = setAct;
}
Full error here
2018-04-12 17:06:34.587 WARN 1572 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Input length must be multiple of 8 when decrypting with padded cipher; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Input length must be multiple of 8 when decrypting with padded cipher (through reference chain: com.capstone.codegum.Codegum.Objects.Logs["action"])
I have modified your code a bit and able to run it. Here is a running example:
Pojo.java
package com.test;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class Pojo {
private byte[] action = null;
private SecretKey myDesKey = null;
private String encodedKey = "eightkey";
public String getAction() throws Exception {
Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] text = action;
byte[] textEncrypted = desCipher.doFinal(text);
String getAct = new String(textEncrypted);
return getAct;
}
public void setAction(String action) throws Exception {
Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] key = encodedKey.getBytes();
this.myDesKey = new SecretKeySpec(key, "DES");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] text = action.getBytes();
byte[] textEncrypted = desCipher.doFinal(text);
this.action = textEncrypted;
}
}
MainClass.java
package com.test;
public class MainClass {
public static void main(String[] args) throws Exception {
Pojo p = new Pojo();
p.setAction("hello");
String s = p.getAction();
System.out.println(s);
p.setAction("world");
s = p.getAction();
System.out.println(s);
}
}
Output:
hello
world
Use byte[] actionBytes instead of String action something of the sort:
private byte[] actionBytes;
public String getAction() throws Exception {
String encodedKey = "eightkey";
byte[] key = encodedKey.getBytes("UTF8");
SecretKey myDesKey = new SecretKeySpec(key, "DES");
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(actionBytes);
return new String(textEncrypted);
}
public void setAction(String action) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
String encodedKey = "eightkey";
byte[] key = encodedKey.getBytes("UTF8");
SecretKey myDesKey = new SecretKeySpec(key, "DES");
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] text = action.getBytes("UTF8");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(text);
actionBytes = textEncrypted;
}
Or if you want to keep using String action then you should do this:
public String action;
public String getAction() throws Exception {
String encodedKey = "eightkey";
byte[] key = encodedKey.getBytes("UTF8");
SecretKey myDesKey = new SecretKeySpec(key, "DES");
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(action.getBytes("UTF8"));
return new String(textEncrypted);
}
public void setAction(String action) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
String encodedKey = "eightkey";
byte[] key = encodedKey.getBytes("UTF8");
SecretKey myDesKey = new SecretKeySpec(key, "DES");
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] text = action.getBytes("UTF8");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(text);
action = new String(textEncrypted, "UTF8");
}
I have a PBE-generated secret key and I ciphered a string using the algorithm "PBEWithHmacSHA256AndAES_128" however I'm unable to decipher the said string.
Secret key generate:
private final static byte[] SALT = { (byte) 0xc9, (byte) 0x36, (byte) 0x78, (byte) 0x99, (byte) 0x52, (byte) 0x3e, (byte) 0xea,
(byte) 0xf2 };
PBEKeySpec keySpec = new PBEKeySpec(pwd.toCharArray(), SALT, 20 , 128);
try {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
PRIVATE_KEY = kf.generateSecret(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
Encypt String:
private static String cipherString(String string) {
PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(SALT, 100);
Cipher cipher;
try {
cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
cipher.init(Cipher.ENCRYPT_MODE, PRIVATE_KEY, pbeParameterSpec);
byte[] input = string.getBytes();
byte[] encryptedp = cipher.doFinal(input);
return encryptedp.toString();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Decrypt String:
private static String decipherString(String string) {
Cipher c;
try {
c = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
c.init(Cipher.DECRYPT_MODE, PRIVATE_KEY);
byte[] input = string.getBytes();
byte[] encryptedp = c.doFinal(input);
return encryptedp.toString();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Just use the following code that works on J2SE. As the algorithm PBEWithHmacSHA256AndAES_128 uses AES in CBC mode internally we also have to provide an IV, which is my example generated randomly. You have to use the same IV for encryption as well as decryption. For security reasons for each encryption you should use a new random IV and save it together with the encrypted text.
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[16];
rnd.nextBytes(iv);
String password = "password";
byte[] plaintext = "plaintext".getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(SALT, 10000, ivParamSpec);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
try {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
SecretKey secretKey = kf.generateSecret(keySpec);
// On J2SE the SecretKeyfactory does not actually generate a key, it just wraps the password.
// The real encryption key is generated later on-the-fly when initializing the cipher
System.out.println(new String(secretKey.getEncoded()));
// Encrypt
Cipher enc = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
enc.init(Cipher.ENCRYPT_MODE, secretKey, pbeParamSpec);
byte[] encrypted = enc.doFinal(plaintext);
String encryptedBase64 = new BASE64Encoder().encode(encrypted);
System.out.println("Encrypted text: " + encryptedBase64);
// Decrypt
Cipher dec = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
dec.init(Cipher.DECRYPT_MODE, secretKey, pbeParamSpec);
byte[] decrypted = dec.doFinal(new BASE64Decoder().decode(encryptedBase64));
String message = new String(decrypted, StandardCharsets.UTF_8);
System.out.println(message);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
I have a hardcoded key with which I want to encrypt a string before storing it in SharedPreferences. This is the code I have so far:
public class TokenEncryptor {
private final static String TOKEN_KEY = "91a29fa7w46d8x41";
public static String encrypt(String plain) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
return new String(cipher.doFinal(plain.getBytes()));
} catch (Exception e) {
Ln.e(e);
return null;
}
}
public static String decrypt(String encoded) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
return new String(cipher.doFinal(encoded.getBytes()));
} catch (Exception e) {
Ln.e(e);
return null;
}
}
}
It seems to be catching an exception at the end of decrypt method:
javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
Can someone point me in the right direction? I have a feeling I'm doing something wrong instantiating IvParameterSpec.
When you encrypt a string with AES, you get an array of bytes back. Trying to convert those bytes directly to a string (new String(cipher.doFinal(plaintextBytes))) will cause all sorts of problems. If you require the output from your encryption method to be a string, then use Base64 rather than attempting a direct conversion. In your decryption method, convert the Base64 string back into a byte array before decrypting the byte array.
Also, do not use getBytes() since the output depends on the system defaults. Use getBytes("utf-8") or whatever. That eliminates ambiguity.
Just in case anyone is interested (or feels too lazy to do their research), here is the the result code for AES-256 encryption and decryption that I put together, with help from the accepted answer and comments:
public class TokenEncryptor {
private final static String TOKEN_KEY = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw";
public static String encrypt(String plain) {
try {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(plain.getBytes("utf-8"));
byte[] ivAndCipherText = getCombinedArray(iv, cipherText);
return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
} catch (Exception e) {
Ln.e(e);
return null;
}
}
public static String decrypt(String encoded) {
try {
byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), "utf-8");
} catch (Exception e) {
Ln.e(e);
return null;
}
}
private static byte[] getCombinedArray(byte[] one, byte[] two) {
byte[] combined = new byte[one.length + two.length];
for (int i = 0; i < combined.length; ++i) {
combined[i] = i < one.length ? one[i] : two[i - one.length];
}
return combined;
}
}
It's an extension of Artjom B answer and working for me.
public String encryptMsg(String message, SecretKey secret)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
return Base64.encodeToString(cipherText, Base64.NO_WRAP);
}
public String decryptMsg(String cipherText, SecretKey secret)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP);
String decryptString = new String(cipher.doFinal(decode), "UTF-8");
return decryptString;
}
Kotlin version of #Oleksiy 's answer.
<script src="https://gist.github.com/kasim1011/a5a9644a60c33a4df3c29f4b34cf93a4.js"></script>
import android.util.Base64
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
private const val algorithm = "AES"
private const val tokenKey = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw"
private const val padding = "AES/CBC/PKCS5Padding"
private const val ivSize = 16
fun String.encryptAES(): String {
val tokenBytes = tokenKey.toByteArray(Charsets.UTF_8)
val secretKey = SecretKeySpec(tokenBytes, algorithm)
val ivByteArray = ByteArray(ivSize)
val iv = IvParameterSpec(ivByteArray)
val cipher = Cipher.getInstance(padding)
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv)
val cipherText = cipher.doFinal(toByteArray(Charsets.UTF_8))
val ivAndCipherText = getCombinedArray(ivByteArray, cipherText)
return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP)
}
fun String.decryptAES(): String {
val tokenBytes = tokenKey.toByteArray(Charsets.UTF_8)
val secretKey = SecretKeySpec(tokenBytes, algorithm)
val ivAndCipherText = Base64.decode(this, Base64.NO_WRAP)
val cipherText = Arrays.copyOfRange(ivAndCipherText, ivSize, ivAndCipherText.size)
val ivByteArray = Arrays.copyOfRange(ivAndCipherText, 0, ivSize)
val iv = IvParameterSpec(ivByteArray)
val cipher = Cipher.getInstance(padding)
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv)
return cipher.doFinal(cipherText).toString(Charsets.UTF_8)
}
private fun getCombinedArray(one: ByteArray, two: ByteArray): ByteArray {
val combined = ByteArray(one.size + two.size)
for (i in combined.indices) {
combined[i] = if (i < one.size) one[i] else two[i - one.size]
}
return combined
}
Before you mark this as a duplicate, please read the full question.
I've looked through countless questions here about this problem, and every answer said to install JCE. However, if I want to send the program to someone else, another computer, virtually anything off the development computer, they have to install JCE too.
Is there a way I can use a smaller keysize without having to install anything?
My encryption method;
public static String encrypt(String in) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException, IOException {
String out = " ";
// generate a key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
byte[] key = keygen.generateKey().getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
// build the initialization vector
SecureRandom random = new SecureRandom();
byte iv[] = new byte[16]; //generate random 16 byte IV. AES is always 16bytes
random.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
saveKey(key, iv); //<-- save to file
// initialize the cipher for encrypt mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
byte[] encrypted = cipher.doFinal(in.getBytes());
out = asHex(encrypted);
return out;
}
And my decrypt method:
public static String decrypt(String in) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException, IOException, KeyFileNotFoundException, UnknownKeyException {
String out = " ";
byte[] key = readKey("key").clone(); //<--from file
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
byte[] iv = readKey("iv"); //<-- from file
IvParameterSpec ivspec = new IvParameterSpec(iv);
//initialize the cipher for decryption
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
// decrypt the message
byte[] decrypted = cipher.doFinal(in.getBytes());
out = asHex(decrypted);
return out;
}
My saveKey() method:
private static void saveKey(byte[] key, byte[] iv) throws FileNotFoundException, IOException {
File keyFile = new File(Logging.getCurrentDir() + "\\cikey.key");
keys.setProperty("key", asHex(key));
keys.setProperty("iv", asHex(iv));
keys.store(new FileOutputStream(keyFile.getAbsolutePath(), false), null);
}
My readKey() method:
private static byte[] readKey(String request) throws KeyFileNotFoundException, UnknownKeyException, FileNotFoundException, IOException {
File keyFile = new File(Logging.getCurrentDir() + "\\cikey.key");
byte[] storage;
keys.load(new FileInputStream(keyFile));
if (!keyFile.exists())
throw new KeyFileNotFoundException("Key file not located.");
if (keys.containsKey(request) == false)
throw new UnknownKeyException("Key not found.");
else
storage = keys.getProperty(request).getBytes();
return storage;
}
asHex() method (transferring array to String):
public static String asHex(byte buf[]) {
StringBuilder strbuf = new StringBuilder(buf.length * 2);
for (int i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
Is there a way I can use a smaller keysize without having to install anything?
You can't use AES with keys sizes smaller than 128 bit, but there are other ciphers available: DES, Blowfish, etc. They aren't as secure as AES, but still can do the trick if your application (as most apps do) does not worth complicated hacking effort. Here's an example for 56 bit DES:
public static String encrypt(String in) throws Exception {
String out = " ";
// generate a key
KeyGenerator keygen = KeyGenerator.getInstance("DES");
keygen.init(56);
byte[] key = keygen.generateKey().getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(key, "DES");
// build the initialization vector
SecureRandom random = new SecureRandom();
byte iv[] = new byte[8]; //generate random 8 byte IV.
random.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
// initialize the cipher for encrypt mode
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
byte[] encrypted = cipher.doFinal(in.getBytes());
out = asHex(encrypted);
return out;
}
There is also a problem with storing and reading the keys in the code. You're storing them as hex, but reading as symbols from default platform encoding. Here's an example how to make both operations uniform:
private static void saveKey(byte[] key, byte[] iv) throws IOException {
File keyFile = new File("C:/cikey.key");
keys.setProperty("key", toHexString(key));
keys.setProperty("iv", toHexString(iv));
keys.store(new FileOutputStream(keyFile.getAbsolutePath(), false), null);
}
private static byte[] readKey(String request) throws IOException {
File keyFile = new File("C:/cikey.key");
keys.load(new FileInputStream(keyFile));
return toByteArray(keys.getProperty(request));
}
public static String toHexString(byte[] array) {
return DatatypeConverter.printHexBinary(array);
}
public static byte[] toByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}