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);
}
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 encrypted file in C# with this method:
public byte[] Crypt(byte[] filearray, string sKey)
{
AesManaged DES = new AesManaged();
DES.Key = Encoding.UTF8.GetBytes(sKey);
DES.Mode = CipherMode.ECB;
DES.Padding = PaddingMode.PKCS7;
ICryptoTransform crypt = DES.CreateEncryptor();
byte[] cipher = crypt.TransformFinalBlock(niz, 0, filearray.Length);
String encryptedText = Convert.ToBase64String(cipher);
return cipher.ToArray();
}
Now I try to decrypt file with Java:
private static byte[] transform(string base64Key, final byte[] fileBytes) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchProviderException
{
final byte[] keyBytes = base64Key.getBytes(StandardCharsets.UTF_8);
final byte[] ivBytes = base64Iv.getBytes(StandardCharsets.UTF_8);
final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
byte[] transformedBytes = null;
try
{
Security.addProvider(new BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");;
cipher.init(Cipher.DECRYPT_MODE, keySpec);
transformedBytes = cipher.doFinal(fileBytes);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException |
BadPaddingException e)
{
e.printStackTrace();
}
return transformedBytes;
}
When I try to execute this function I get exception
javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
Try using a different padding, for example:
Cipher.getInstance("AES/ECB/NoPadding");
or
Cipher.getInstance("AES/ECB/PKCS5Padding");
instead of
Cipher.getInstance("AES/ECB/PKCS7Padding");
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 need to encrypt and decrypt a text using AES. I can call both method locally and it works, however when I run it outside the class I get javax.crypto.BadPaddingException error. I think I am losing data somewhere but U cannot find where.
Here is the code:
public class AES {
public String encryptAES(String seed, String cleartext) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public String decryptAES(String seed, String encrypted) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
private byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
private byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
return result;
}
private String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
String HEX = "0123456789ABCDEF";
for (int i = 0; i < buf.length; i++) {
result.append(HEX.charAt((buf[i] >> 4) & 0x0f)).append(HEX.charAt(buf[i] & 0x0f));
}
return result.toString();
}
the error points to
byte[] decrypted = cipher.doFinal(encrypted);
I see several things that need fixing ..
Firstly the seed to SecureRandom won't make it produce the same output. So if you try to create the same key by specifying the same seed, it wont work.
Secondly .. you should make sure to instantiate your encrypt and decrypt cipher with the same properties .. currently you don't.
Also, when you specify CBC mode you need to handle the initialization vector. If you don't specify one on the encrypt, the cipher makes one for you .. this you need to grab and provide when you decrypt.
Fixing these things don't necessarily fix everything, but should lead you in the right direction. Take a look at some of the related question at the right side .. StackOverflow have numerous working AES examples.
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
}