I'm trying to encrypt/decrypt a String in Java. No problem concerning the encryption then stored a in sqlite table. But I always get the same error trying to decrypt it :
"java.security.InvalidKeyException : no IV set when one expected"
Here is my code snippet :
public String encrypt(String password){
try
{
String key = "mysecretpassword";
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
return new String(cipher.doFinal(password.getBytes()));
}
catch (Exception e)
{
return null;
}
}
public String decrypt(String password){
try
{
String key = "mysecretpassword";
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE,keySpec);
return new String(cipher.doFinal(password.getBytes()));
}
catch (Exception e)
{
System.out.println(e);
return null;
}
}
What am I doing wrong?
You will need to specify an initialization vector in the cipher.init() method:
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE,keySpec, ivSpec);
See: http://docs.oracle.com/javase/1.5.0/docs/api/javax/crypto/spec/IvParameterSpec.html
The initialization vector should be a random byte array, for a discussion see:
http://en.wikipedia.org/wiki/Initialization_vector
You need an appropriate AES key, try with:
String key = "mysecretpassword";
KeySpec spec = new PBEKeySpec(key.toCharArray(), Salt, 12345678,256);
SecretKey encriptionKey = factory.generateSecret(spec);
Key encriptionKey = new SecretKeySpec(encriptionKey.getEncoded(), "AES");
Related
I trying to implement this same code to encrypt on c# but always i get some different encrypted code :
This is my Java class :
public class AES256 {
private static final String SECRET_KEY = "my_super_secret_key_ho_ho_ho";
private static final String SALT = "ssshhhhhhhhhhh!!!!";
public static String encrypt(String strToEncrypt) {
try {
byte[] iv = "1234567891234567".getBytes("UTF-8");
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
return Base64.getEncoder()
.encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
}
How can i implement the same on c#? i usig this as a model -> c# AES Encrypt but the result is diffetent even using the same secret , salt ,interaction hash count
This what i have on c# :
var passPhrase = "my_super_secret_key_ho_ho_ho";
// Initialization Vector (16 bit length)
var iv = "1234567891234567";
// Encrypt & Decrypt (with advanced settings)
var opts = new AESCryptOptions()
{
PasswordHash = AESPasswordHash.SHA1,
PasswordHashIterations = 65536,
PasswordHashSalt = "ssshhhhhhhhhhh!!!!",
PaddingMode = PaddingMode.PKCS7,
MinSaltLength = 4,
MaxSaltLength = 16,
FixedKeySize=256
};
var encryptedText = new AESCrypt(passPhrase, iv, opts).Encrypt(text);
And the encrypt Method, i changed the hash to sha256, the only change made from the implement from gitHub:
PasswordDeriveBytes password = new PasswordDeriveBytes(
passPhrase,
saltValueBytes,
("SHA256"),
Options.PasswordHashIterations);
// Convert key to a byte array adjusting the size from bits to bytes.
keyBytes = password.GetBytes(keySize / 8);
I am developing an application, where I am encrypting and decrypting a text entered by the user.
But, I am getting the following error:
javax.crypto.IllegalBlockSizeException: last block incomplete in
decryption
below is my code for encryption and decryption. Encryption works perfectly, while I am getting this error while decrypting. Please refer the code below:
public static String fncEncrypt(String strClearText, String strKey) throws Exception{
String strData = "";
try {
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(strClearText.getBytes());
strData = new String(encrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
public static String fncDecrypt(String strEecrypted, String strKey) throws Exception {
String strData = "";
try {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(strEecrypted.getBytes());
strData = new String(decrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
Please respond if you have a solution for this.
You should decode the string instead of encoding the platform specific representation of the string, right at the start of your method.
byte[] base64TextToDecrypt = Base64.decodeBase64(textToDecrypt);
or more precisely:
byte[] bytesToDecrypt = Base64(base64TextToDecrypt);
if you name your variables correctly.
In general, each time you (feel like you have to) use the String.getBytes(): byte[] method or the String(byte[]) constructor you are likely doing something wrong. You should first think about what you are trying to do, and specify a character-encoding if you do need to use it.
In your case, the output in the converted variable is probably character-encoded. So you you could use the following fragment:
String plainText = new String(converted, Charset.forName("UTF8"));
System.out.println(plainText);
instead of what you have now.
Reference : https://stackoverflow.com/a/13274072/8416317
String class method getBytes() or new String(byte bytes[]) encode / decode with Charset.defaultCharset().name(), and some encrypted data would be ignored by encoding with special charset.
you could directly return byte[] by fncEncrypt and input byte[] to fncDecrypt. or encode result with BASE64.
public static byte[] fncEncrypt(String strClearText, String strKey) throws Exception{
byte[] encrypted = null;
try {
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
encrypted = cipher.doFinal(strClearText.getBytes());
} catch (Exception e){
e.printStackTrace();
}
return encrypted;
}
public static String fncDecrypt(byte[] ecrypted, String strKey) throws Exception {
String strData = "";
try {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(ecrypted);
strData = new String(decrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
The reason is when you use new String(encrypted) it will not fully encode the bytes to string. Try the code below
public static byte[] fncEncrypt(String strClearText, String strKey) throws Exception{
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(strClearText.getBytes());
return encrypted;
}
public static String fncDecrypt(byte[] encrypted, String strKey) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted);
}
You can encrypt and decrypt using the code below
String message = "Hello!";
byte[] encrypted = fncEncrypt(message, "key");
String decrypted = fncDecrypt(encrypted, "key");
System.out.println(decrypted);
I need some help validating the below code snippet for Java AES encryption with CBC, PKCS5Padding and IV.
I tested the code and was able to encrypt and decrypt. I have a few queries as described below.
Where should the password be stored as a good convention?
Is the way of appending/retrieving salt and IV bytes to the ciphetext fine?
Any other comments highly appreciated, thanks!
public class Encryption {
private static int iterations = 65536;
private static int keySize = 128;
private static char[] password = "password".toCharArray();
private static String algorithm= "PBKDF2WithHmacSHA1";
private static final String SEPARATOR = "~";
public static void main(String []args) throws Exception {
String filePath = "test.xml";
String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
String encrMesg = encrypt(fileContent);
System.out.println("Encrypted: " + encrypt(encrMesg));
System.out.println("Decrypted: " + decrypt(encrMesg));
}
public static String encrypt(String plaintext) throws Exception {
byte[] saltBytes = getSalt().getBytes();
SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
PBEKeySpec spec = new PBEKeySpec(password, saltBytes, iterations, keySize);
SecretKey secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] cipherText = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(ivBytes)+SEPARATOR+DatatypeConverter.printBase64Binary(saltBytes)
+SEPARATOR+DatatypeConverter.printBase64Binary(cipherText);
}
public static String decrypt(String encryptedText) throws Exception {
System.out.println(encryptedText);
String[] encryptedArr = encryptedText.split(SEPARATOR);
byte[] ivBytes = DatatypeConverter.parseBase64Binary(new String(encryptedArr[0]));
byte[] salt = DatatypeConverter.parseBase64Binary(new String(encryptedArr[1]));
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedArr[2]));
SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keySize);
SecretKey secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
public static String getSalt() throws Exception {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return new String(salt);
}
}
Queries
Where should the password be stored as a good convention?
Symmetric keys should go preferably to a vault. Otherwise they should go on a keystore, but then you have the issue of securing the keystore password.
Is the way of appending/retrieving Salt and IV bytes to the Cipher
text is fine?
Salt should be generated with:
SecureRandom random = SecureRandom.getInstanceStrong();
Otherwise you are using weaker entropy pools (i.e. /dev/urandom in linux) to generate your secure numbers, and that leads to weak keys that can be more easily broken.
Any other comments highly appreciated, thanks!
You should consistently use the same encoding when dealing with String conversion, i.e., .getBytes("UTF-8") to avoid issues. You don't use it when converting the salt for example.
I have an application that needs to store some secret passwords in a configuration file such as database and ftp passwords/detail. I've looked around and found a lot of encryption/decryption solutions using AES, but I can't seem to figure out how to make it work without changing the key. That means I can encrypt and decrypt (using the same SecretKey), but to maintain persistence across restarts etc. I can't seem to make the SecretKey stay the same. The example below shows my methods working:
String secret = Encryptor.encrpytString("This is secret");
String test = Encryptor.decrpytString(secret);
System.out.println(test); //This is secret is printed
So far so good. However if I run it once I might get the value of '2Vhht/L80UlQ184S3rlAWw==' as my secret, the next time it is 'MeC4zCf9S5wUUKAu8rvpCQ==', so presumably the key is changing. I'm assuming I am applying some counter-intuative logic to the problem and would appreciate if someone can shed some light on either a) what I'm doing wrong, or b) a solution that would allow me to store the password information encrypted and retrievable with the information provided.
My methods are as follows:
private static final String salt = "SaltySalt";
private static byte [] ivBytes = null;
private static byte[] getSaltBytes() throws Exception {
return salt.getBytes("UTF-8");
}
private static char[] getMasterPassword() {
return "SuperSecretPassword".toCharArray();
}
private static byte[] getIvBytes() throws Exception {
if (ivBytes == null) {
//I don't have the parameters, so I'll generate a dummy encryption to create them
encrpytString("test");
}
return ivBytes;
}
public static String encrpytString (String input) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrpytString (String input) throws Exception {
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(input);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(getIvBytes()));
byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
return new String(decryptedTextBytes);
}
Thanks for the help!
OK, looks like I've found the answer to my question. I sourced my information from this Stackoverflow post.
From what I understand, the IV (initialisation vector) is used to add entropy into the encryption process. Each time you create a new cipher, Java creates a slightly different IV. There are therefore two solutions:
User a fixed IV, or
Store the IV along with the encrypted data.
From what I've read, option 1 is not very good practice; so option 2 it is. I understand that it should be possible to simply append the IV to the encrypted string (as the secret is still required) and therefore the IV can be reconstructed when it comes time to decrypt.
Here is the almost complete solution. I'm still getting some padding errors on decryption (see my comment). I don't have time to spend on it now, so as a temporary measure I immediately try decrypting an encrypted string and keep on trying (iterating) until it works. It seems to have about a 50% hit rate + I'm not encrypting often enough for it to be a performance concern. Would be nice if someone could suggest a fix though (just for completeness sake).
private static final String salt = "SaltySalt";
private static final int IV_LENGTH = 16;
private static byte[] getSaltBytes() throws Exception {
return salt.getBytes("UTF-8");
}
private static char[] getMasterPassword() {
return "SuperSecretPassword".toCharArray();
}
public static String encrpytString (String input) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536,256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(input.getBytes("UTF-8"));
byte[] finalByteArray = new byte[ivBytes.length + encryptedTextBytes.length];
System.arraycopy(ivBytes, 0, finalByteArray, 0, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, finalByteArray, ivBytes.length, encryptedTextBytes.length);
return DatatypeConverter.printBase64Binary(finalByteArray);
}
public static String decrpytString (String input) throws Exception {
if (input.length() <= IV_LENGTH) {
throw new Exception("The input string is not long enough to contain the initialisation bytes and data.");
}
byte[] byteArray = DatatypeConverter.parseBase64Binary(input);
byte[] ivBytes = new byte[IV_LENGTH];
System.arraycopy(byteArray, 0, ivBytes, 0, 16);
byte[] encryptedTextBytes = new byte[byteArray.length - ivBytes.length];
System.arraycopy(byteArray, IV_LENGTH, encryptedTextBytes, 0, encryptedTextBytes.length);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(getMasterPassword(), getSaltBytes(), 65536, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
return new String(decryptedTextBytes);
}
Use a static Initialization Vector, e.g. a zero IV:
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
Since you're storing passwords you probably want to use a random IV and/or random salt and store them with the cipher text so the same passwords don't encrypt to the same ciphertext.
You need to setSeed() before
class Encryptor {
static final String salt = "SaltSalt";
public static byte[] encryptString(String input) throws Exception {
byte[] bytes = input.getBytes("UTF-8");
Cipher cipher = Cipher.getInstance("AES");
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(salt.getBytes("UTF-8"));
keyGenerator.init(256, secureRandom);
Key key = keyGenerator.generateKey();
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] a = cipher.doFinal(bytes);
return a;
}
public static String decryptString(byte[] input) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(salt.getBytes("UTF-8"));
keyGenerator.init(256, secureRandom);
Key key = keyGenerator.generateKey();
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(input);
String result = new String(decrypted, "UTF-8");
return result;
}
}
Below is the code im using for Decryption.
public String decrypt(String strToBeDecrypted) {
try {
if((strToBeDecrypted.trim().length()!=0) && !(strToBeDecrypted.trim().equals("")) && !(strToBeDecrypted.trim().equalsIgnoreCase("NA"))){
strToBeDecrypted = URLDecoder.decode(strToBeDecrypted, "UTF-8");
DESKeySpec desKeySpec = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey skey = keyFactory.generateSecret(desKeySpec);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, skey, ivSpec);
byte[] keyByteArray = new BASE64Decoder().decodeBuffer(strToBeDecrypted);
byte[] original = cipher.doFinal(keyByteArray);
return new String(original, "UTF-8");
}
}
catch (Exception e) {
logger.error(ExceptionUtil.getDetailedMessage(e));
}
return "";
}
Im getting "Unknown exception details: name=javax.crypto.BadPaddingException;message=Given final block not properly padded;" at the below line
byte[] original = cipher.doFinal(keyByteArray);
Can anyone pls tell me what is the problem here? Im using the following keys
encryptkey=QvgC9vBXDZyM7RoAxevpHaawEbL5CW8Sp1zjEQ
iterations=19
segments=5