I am trying to write a class that will encrypt/decrypt text using AES.
I want to generate a key, store the key in a database column and use that key to encrypt/decrypt corresponding text in the database row containing the key.
The following is the class that I wrote to generate the key and do encryption and decryption tasks.
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class StringDecryptor {
public static String encrypt(String text, String key) {
Key aesKey = null;
Cipher cipher = null;
byte[] encrypted = null;
try {
aesKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
encrypted = cipher.doFinal(text.getBytes());
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeyException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
return new String(encrypted);
}
public static String decrypt(String text, String key) {
Key aesKey = null;
Cipher cipher;
String decrypted = null;
try {
aesKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, aesKey);
decrypted = new String(cipher.doFinal(text.getBytes()));
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeyException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
return decrypted;
}
public static String generateKey() {
SecretKey secretKey = null;
try {
secretKey = KeyGenerator.getInstance("AES").generateKey();
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
String keyString = bytesToString(secretKey.getEncoded());
return keyString;
}
public static String bytesToString(byte[] b) {
String decoded = null;
try {
decoded = new String(b, "UTF-8");
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
return decoded;
}
public static void main(String args[]) {
String key = generateKey();
System.out.println("key: " + key);
String str = "This is the original string...";
String enc = encrypt(str, key);
System.out.println("enc: " + enc);
String dec = decrypt(enc, key);
System.out.println("dec: " + dec);
}
}
This code is throwing the following exception.
SEVERE: null
java.security.InvalidKeyException: Invalid AES key length: 26 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.encrypt(StringDecryptor.java:27)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.main(StringDecryptor.java:95)
Exception in thread "main" java.lang.NullPointerException
at java.lang.String.<init>(String.java:556)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.encrypt(StringDecryptor.java:42)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.main(StringDecryptor.java:95)
Java Result: 1
Is there any way to generate a key string that does not cause the AES key conversion to throw exceptions?
To be able to encrypt with the AES-algorithm you will need to base your key on an atleast 128bit String. (Other values are also legal, but I don't have them in my head)
To convert that into number of characters needed in your provided key-string you can take 128 divided by 8
128 / 8 = 16 alpha-numeric characters
This should solve your problem.
EDIT:
Answer on comment about BASE64:
BASE64 is a different way of encoding a string. The result of a BASE64-encoding may be a 128bit string, but not by default. There is not actually a default length of the outcome of a BASE64-encoding. The result may be 8 characters or 512, or some other output that conforms with the encoding rules of BASE64, it all depends on the string you are encoding.
AES needs a key that is of length either 128, 192, or 256 bits. Try using a longer key.
Here's a working example to generate AES key:
private static SecretKeySpec key;
private static final int ENCRYPTION_KEY_SIZE = 128;
private static SecretKey generateKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = null;
keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(ENCRYPTION_KEY_SIZE);
SecretKey key = keyGenerator.generateKey();
return key;
}
public void createNewEncrytionKey() throws NoSuchAlgorithmException, Exception {
SecretKey newKey = generateKey();
saveKey(newKey);
LogUtil.logMessage("createNewEncrytionKey", "New random encryption key was created.", logger);
key = (SecretKeySpec) newKey;
}
you can then use that key to encrypt\decrypt your code (in the example I convert the encrypted value to hexdecimal format:
/**
* Encrypt a given text
*
* #param value String to encrypt
* #return encrypted String value
*/
public static String encrypt(String value) {
if (null == key) {
throw new RuntimeException("Secret encryption key not found!");
}
Cipher c;
String result = null;
try {
c = Cipher.getInstance(ENCRYPTION_ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
// 1. convert value to byte array
byte[] bvalue = value.getBytes();
// 2. convert to encrypted byte array
byte[] encrypted = c.doFinal(bvalue);
// 3. convert to hexadecimal representation
result = toHexString(encrypted);
} catch (Exception e) {
LogUtil.logError("encrypt", e, logger);
}
return result;
}
/**
* Decrypt a given text
*
* #param value encrypted String value
* #return Decrypted value
*/
public static String decrypt(String value) {
if (null == key) {
throw new RuntimeException("Secret encryption key not found!");
}
Cipher c;
String result = null;
try {
// 1. convert hex to encrypted byte array
byte[] encrypted = hexToByteArray(value);
// 2. convert to decrypted byte array
c = Cipher.getInstance(ENCRYPTION_ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = c.doFinal(encrypted);
// 3. convert to plain string
result = bytesToString(decrypted);
} catch (Exception e) {
LogUtil.logError("decrypt", e, logger);
}
return result;
}
Your problem has nothing to do with AES or cryptography. You are however trying to interpret a random sequence of bytes as UTF-8 encoded characters (b being the AES key):
String decoded = new String(b, "UTF-8");
That is not going to work, since your byte array will most likely contain several byte sequences, which are invalid for UTF-8. If you need a string representation of the byte array content, you have to encode the data using e.g. base64 or hex encoding.
Related
Actually I am trying to add Aes Encryption and decryption functionality inside my android app. During encryption it will give me crash in my motoG5 S plus device but its working properly inside my One Plus device.
Here is my Code: AesUtil.Java
public class AesUtil {
private int keySize = 256;
private int iterationCount = 1000 ;
private Cipher cipher;
private final String salt = "36e8fc9a6adf090665f459a7ad1b864d";
private final String iv = "ab00b7ea4e88500f2f0a17a7b5c7bcb1";
private final String passphrase = "ArknRQxD1YgaSFRHrjYazX7JMrlRxTERdkQx0dhENVlz";
public AesUtil() {
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw fail(e);
}
}
public String encrypt(String plaintext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext.getBytes("UTF-8"));
return Base64.encodeBase64String(encrypted);
}
catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
return new String(decrypted, "UTF-8");
} catch (Exception e) {
throw fail(e);
}
}
private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
try {
cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
return cipher.doFinal(bytes);
} catch (InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| BadPaddingException e) {
throw fail(e);
}
}
public SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
}
catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
return null;
}
}
public static byte[] base64(String str) {
return Base64.decodeBase64(str);
}
public static byte[] hex(String str) {
try {
return Hex.decodeHex(str.toCharArray());
}
catch (DecoderException e) {
throw new IllegalStateException(e);
}
}
private IllegalStateException fail(Exception e) {
e.printStackTrace();
return null;
} }
Call Function in my main activity:
String encryptedText=AesUtil().encrypt("codeinsidecoffee")
Error Log Inside Moto G5s Plus:
java.lang.NoSuchMethodError: No static method encodeBase64String([B)Ljava/lang/String; in class Lorg/apache/commons/codec/binary/Base64; or its super classes (declaration of 'org.apache.commons.codec.binary.Base64' appears in /system/framework/org.apache.http.legacy.boot.jar)
at com.justcodenow.bynfor.utils.AesUtil.encrypt(AesUtil.java:40)
Method1: This method is available since version 1.4 of the Apache Commons Codec. If the OS of the phone has only an older version of the Codec package, the method won't be available. Alternatively, we can use a method that exists in older versions.
Instead of:
String encodedString = Base64.encodeBase64String(bytes);
Use:
String encodedString = new String(Base64.encodeBase64(bytes));
And for decoding, instead of:
byte[] bytes = Base64.decodeBase64(encodedString);
Use:
byte[] bytes = Base64.decodeBase64(encodedString.getBytes());
Method-2: You can Also Use Below Complete code for Encryption and decryption.
import android.util.Base64;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
public class AesUtil {
private int keySize = 256;
private int iterationCount = 1000 ;
private Cipher cipher;
private final String salt = "36e8fc9a6adf090665f459a7ad1b864d";
private final String iv = "ab00b7ea4e88500f2f0a17a7b5c7bcb1";
private final String passphrase = "ArknRQxD1YgaSFRHrjYazX7JMrlRxTERdkQx0dhENVlz";
public AesUtil() {
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw fail(e);
}
}
public String encrypt(String plaintext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext.getBytes("UTF-8"));
return Base64.encodeToString(encrypted, Base64.DEFAULT);
}
catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
return new String(decrypted, "UTF-8");
} catch (Exception e) {
throw fail(e);
}
}
private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
try {
cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
return cipher.doFinal(bytes);
} catch (InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| BadPaddingException e) {
throw fail(e);
}
}
public SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
}
catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
return null;
}
}
public static byte[] base64(String str) {
return Base64.decode(str, Base64.DEFAULT);
}
public static byte[] hex(String str) {
try {
return Hex.decodeHex(str.toCharArray());
}
catch (DecoderException e) {
throw fail(e);
}
}
private IllegalStateException fail(Exception e) {
e.printStackTrace();
return null;
}
private static IllegalStateException fail(DecoderException e) {
e.printStackTrace();
return null;
} }
After so many retries. I thought of copy pasting all the logic from sdk to my project. but seems like only one static function is not available in some of the android apache/commons framework. which is Base64.encodeBase64String()
So what I did is, I copied just one function in my project from library and recursive function call from the library exists in older framework..
So instead of using Base64.encodeBase64String() copy this function in your file and use this. This will work same.
fun encodeBase64String(binaryData: ByteArray?): String? {
return StringUtils.newStringUsAscii(
org.apache.commons.codec.binary.Base64.encodeBase64(
binaryData,
false
)
)
}
My problem is with symmetric decryption. Not asymmetric decryption. So the correct answer is here Decrypt PGP encrypted file with passphrase only in Java
I use gpg to encrypt "hello":
[root#shc-sma-cd13 opt]# echo "hello" | gpg --symmetric --armor --cipher-algo AES256 --passphrase "2R79P7z5f8350VEp" --batch
-----BEGIN PGP MESSAGE-----
Version: GnuPG v2.0.22 (GNU/Linux)
jA0ECQMC1XpaSrXhBAfU0jsBXw817k4k4iT++AGV8MUev4/gKkuIwAW2VaJsEANa
+0ZuqZgFp/8N7AndRhyNj5WGcloQQkLkwvIV3Q==
=GwQi
-----END PGP MESSAGE-----
I use Java to decrypt the string:
public class AESUtils1 {
private static final String KEY_VAL = "2R79P7z5f8350VEp";
public static String AESDecode(String content) {
try {
SecretKey key = new SecretKeySpec(KEY_VAL.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] byte_content = new BASE64Decoder().decodeBuffer(content);
byte[] byte_decode = cipher.doFinal(byte_content);
String AES_decode = new String(byte_decode, "utf-8");
return AES_decode;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
//如果有错就返加nulll
return null;
}
public static void main(String[] args) {
String encryptString = "jA0ECQMC1XpaSrXhBAfU0jsBXw817k4k4iT++AGV8MUev4/gKkuIwAW2VaJsEANa\n" +
" +0ZuqZgFp/8N7AndRhyNj5WGcloQQkLkwvIV3Q==\n" +
" =GwQi";
String decryptString = AESDecode(encryptString);
System.out.println("decryptString: " + decryptString);
}
}
But it fails with error message:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:936)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at com.hpe.itsma.itsmaInstaller.AESUtils1.AESDecode(AESUtils1.java:33)
at com.hpe.itsma.itsmaInstaller.AESUtils1.main(AESUtils1.java:57)
decryptString: null
I am curious that what is the real encrypted string from gpg that I can put it into Java. The output of gpg is different from using Java to encrypt "hello".
And another interesting thing is that every time I run command echo "hello" | gpg --symmetric --armor --cipher-algo AES256 --passphrase "2R79P7z5f8350VEp" --batch, the result is always different. Is that possible to decrypt the string which is encrypted by gpg. Or the wrong way I used of gpg?
Thanks all. Finally, I figure out the solution. Cause my data was symmetric encrypted. The decryption will be different from the asymmetric decryption. I put my code below, also you can find the same answer here Decrypt PGP encrypted file with passphrase only in Java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
import org.bouncycastle.util.io.Streams;
import java.io.*;
import java.security.NoSuchProviderException;
import java.security.Security;
public class SymmetricDecyption {
public static byte[] decrypt(byte[] var0, char[] var1) throws IOException, PGPException, NoSuchProviderException {
ByteArrayInputStream var2 = new ByteArrayInputStream(var0);
InputStream var11 = PGPUtil.getDecoderStream(var2);
PGPObjectFactory var3 = new PGPObjectFactory(var11);
Object var5 = var3.nextObject();
PGPEncryptedDataList var4;
if (var5 instanceof PGPEncryptedDataList) {
var4 = (PGPEncryptedDataList) var5;
} else {
var4 = (PGPEncryptedDataList) var3.nextObject();
}
PGPPBEEncryptedData var6 = (PGPPBEEncryptedData) var4.get(0);
InputStream var7 = var6.getDataStream((new JcePBEDataDecryptorFactoryBuilder((new JcaPGPDigestCalculatorProviderBuilder()).setProvider("BC").build())).setProvider("BC").build(var1));
PGPObjectFactory var8 = new PGPObjectFactory(var7);
PGPCompressedData var9 = (PGPCompressedData) var8.nextObject();
var8 = new PGPObjectFactory(var9.getDataStream());
PGPLiteralData var10 = (PGPLiteralData) var8.nextObject();
return Streams.readAll(var10.getInputStream());
}
public static void main(String[] var0) throws Exception {
String password = "2R79P7z5f8350VEp";
File file = new File("C:\\Users\\zhongtao.CORPDOM\\Desktop\\file.txt.asc");
InputStream input = new FileInputStream(file);
byte[] byt = new byte[input.available()];
input.read(byt);
Security.addProvider(new BouncyCastleProvider());
byte[] var5 = decrypt(byt, password.toCharArray());
System.out.println("Decrypted data is: " + new String(var5));
}
}
guys
Here's a snippet of code of Java about AES encrypt:
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
...
public static String encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] byteContent = content.getBytes("utf-8");
byte[] result = cipher.doFinal(byteContent);
return Base64.encode(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
Now I try to implement it in Ruby(not work):
def self.aes128_encrypt(password, content)
cipher = OpenSSL::Cipher.new('AES-128-ECB')
cipher.encrypt
cipher.key = password
result = cipher.update(content) + cipher.final
Base64.encode64(result).chomp
end
After some research, I think the problem is the cipher#key: In Java, it's generate by securerandom with password as seed, But in Ruby, I use the password directly.
So I want to generate key like Java. But I find the random number generator used by Java is NativePRNG, which I do not find the same implementation so far.
Now, I want to ask help from guys familiar with Ruby and Java:
How can I implement the same AES encrypt in Ruby?
Or how to generate random string in Ruby with NativePRNG.
Any advice is welcome, and Thank you very much for each reply.
I'm trying to save few text fields securely. For that I'm trying to encrypt and decrypt the content. This is the code:
public class SecureStorage {
public String getPassword() {
if(!isRooted()) {
String password = pref.getPassword("");
System.out.println("pass getPass: " + password);
return password.isEmpty() ? password : new String(decrypt(Base64.decode(password, Base64.DEFAULT)));
} else
return "";
}
public void setPassword(String passwordStr) {
if(!isRooted()) {
byte[] password = encrypt(passwordStr.getBytes());
pref.setPassword(password == null ? "" : Base64.encodeToString(password, Base64.DEFAULT));
}
}
private SecretKey generateKey() {
// Generate a 256-bit key
final int outputKeyLength = 256;
try {
SecureRandom secureRandom = new SecureRandom();
// Do *not* seed secureRandom! Automatically seeded from system entropy.
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(outputKeyLength, secureRandom);
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
private byte[] getRawKey(byte[] key) throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto");
secureRandom.setSeed(key);
keyGenerator.init(128, secureRandom); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
byte[] rawKey = secretKey.getEncoded();
return rawKey;
}
/** The method that encrypts the string.
#param toEncrypt The string to be encrypted.
#return The encrypted string in bytes. */
//****************************************
private byte[] encrypt(byte[] toEncrypt) {
byte[] encryptedByte = new String().getBytes();
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
encryptedByte = cipher.doFinal(toEncrypt);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return encryptedByte;
}
//**************************************
/** The method that decrypts the string.
#param encryptedByte The string to be encrypted.
#return The decrypted string in bytes. */
//****************************************
private byte[] decrypt(byte[] encryptedByte) {
byte[] decryptedByte = new String().getBytes();
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
decryptedByte = cipher.doFinal(encryptedByte);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return decryptedByte;
}
}
I'm able to encrypt the Text.
I'm using SharedPreferences to store the encrypted text and getting the sharedprefs to decrypt the text and give it to a TextView. But in the getPassword() I'm getting the SharedPreference value and trying to decrypt if there is any value in the SharedPrefs. I'm getting the SharedPrefs into a string (password) and trying to decrypt it, but I'm unable to! I'm getting an empty String!
CBC mode needs an initialization vector (IV) in order to operate. The IV is there to randomize the ciphertext and prevent an attacker from determining whether previous plaintexts had the same prefix as the current one.
Since you're not generating any IV, it will be generated for you. A wrong IV only affects the first block (first 16 bytes for AES). If your plaintext is shorter than a block, then this will lead to completely different decryption and then the padding cannot be removed with a probability of roughly 255/256.
The IV is not supposed to be secret. It is common to prepend it to the ciphertext and slice it off before decryption.
public byte[] encrypt(byte[] toEncrypt) throws Exception {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] iv = cipher.getIV();
byte[] ct = cipher.doFinal(toEncrypt);
byte[] result = new byte[ct.length + iv.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ct, 0, result, iv.length, ct.length);
return result;
} catch(...) {...}
return new byte[0];
}
public byte[] decrypt(byte[] encryptedByte) throws Exception {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
byte[] ct = new byte[encryptedByte.length - cipher.getBlockSize()];
System.arraycopy(encryptedByte, 0, iv, 0, cipher.getBlockSize());
System.arraycopy(encryptedByte, cipher.getBlockSize(), ct, 0, ct.length);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
return cipher.doFinal(ct);
} catch (...) {...}
return new byte[0];
}
The problem with this might be that the ciphertext is bigger than anticipated (16 bytes additionally for the IV). If you can make sure that an attacker doesn't get any useful information from determining that previous plaintexts had the same prefix, then you could use a static IV. But be aware that this is usually not that great of an idea and should only be done if you really need that space.
private static final byte[] IV = new byte[16];
...
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV));
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV));
I have written my program in java by following this tutorial http://www.java2s.com/Code/Android/Security/AESEncryption.htm
But i am getting an exception i.e. "javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher"
Can anybody help me?
public class utils {
public static String encrypt(String message,String secretPhrase){
try{
MessageDigest mdig=MessageDigest.getInstance("MD5");
byte[] digestedBytes=mdig.digest(secretPhrase.getBytes("UTF-8"));
SecretKeySpec keySpec=new SecretKeySpec(digestedBytes,"AES");
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes=cipher.doFinal(message.getBytes("UTF-8"));
return new String(encryptedBytes,"UTF-8");
}catch(Exception exc){
return null;
}
}
public static String decrypt(String message,String secretPhrase){
try{
MessageDigest mdig=MessageDigest.getInstance("MD5");
byte[] digestedBytes=mdig.digest(secretPhrase.getBytes("UTF-8"));
SecretKeySpec keySpec=new SecretKeySpec(digestedBytes,"AES");
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] encryptedBytes=cipher.doFinal(message.getBytes("UTF-8"));
return new String(encryptedBytes,"UTF-8");
}catch(Exception exc){
return null;
}
}
}
Well try this ,
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
} catch (Exception e) {
}
return null;
}
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (Exception e) {
}
return null;
}
Example using it
try {
// Generate a temporary key. In practice, you would save this key.
// See also Encrypting with DES Using a Pass Phrase.
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
// Create encrypter/decrypter class
DesEncrypter encrypter = new DesEncrypter(key);
// Encrypt
String encrypted = encrypter.encrypt("Don't tell anybody!");
// Decrypt
String decrypted = encrypter.decrypt(encrypted);
} catch (Exception e) {
}