I have the following code which works on Windows and Linux/Unix but will not decrypt an encrypted text that has been encrypted on Linux/Unix or vice versa.
I am aware that this will also generate the same encrypted text everytime, but I've removed some of the code to get to the very basic.
I am sure it's not related to Charset encoding, as the encoded String is the same on both OS, even the SecretKey is the same on both.
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class Encryptor {
private static final String password = "passwordKey";
private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final byte [] salt = "test salt as string".getBytes(UTF_8);
public static String encrypt(String text) throws UnsupportedEncodingException {
IvParameterSpec ivSpec = new IvParameterSpec(password.getBytes(UTF_8));
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 10000, ivSpec);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 10000);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithSHA256AndAES_256");
SecretKey key = secretKeyFactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("PBEWithSHA256AndAES_256");
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
byte [] encrypted = cipher.doFinal(text.getBytes(UTF_8));
return Base64.encodeBase64String(encrypted);
}
public static String decrypt(String encryptedText) throws UnsupportedEncodingException {
IvParameterSpec ivSpec = new IvParameterSpec(password.getBytes(UTF_8));
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 10000, ivSpec);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 10000);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithSHA256AndAES_256");
SecretKey key = secretKeyFactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("PBEWithSHA256AndAES_256");
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
byte [] decoded = Base64.decodeBase64String(encryptedText);
byte [] decrypted = cipher.doFinal(decoded);
return new String(decrypted, UTF_8);
}
public static void main(String args []) throws UnsupportedEncodingException {
String encryptedFromWindows = "eFRvTevgk/oslll+234r5tdsss==";
System.out.println(decrypt(encryptedFromWindows));
}
}
Exception:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
The problem was a bug in the JDK version of _64. Upgraded to _121 and it worked. IT wasn't a known oracle bug but they had mentioned in another similar bug of issues with encryption in that release.
Related
I was implementing encryption decryption of a file using the AES/CBC/PKCS5PADDING algorithm. And I noticed some peculiarity. With the correct order of initialization of the IV, everything is decrypted correctly. But if the order is wrong (see commented out lines), the beginning of the line is decoded incorrectly.
But if the decryption happens with wrong IV in CBC mode then nothing should be decrypted. After all, that's how AES/CBC works.
My question is - why is the string still decrypted with the wrong IV ?
Output
org.junit.ComparisonFailure:
Expected :Test string Test string Test string Test string Test string Test string
Actual :�Eݠ�/ՙ�, 9B�� string Test string Test string Test string Test string
Code
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
public class CryptographyService {
private static final String SECRET_KEY_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final String CIPHER_ALGORITHM_MODE_PADDING = "AES/CBC/PKCS5PADDING";
private static final String CIPHER_ALGORITHM = "AES";
private static final int SALT_LEN = 32;
private static byte[] createSalt() {
byte[] salt = new byte[SALT_LEN];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
return salt;
}
private static SecretKey secretKeyCreate(String userPassword, byte[] salt) throws NoSuchAlgorithmException,
InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_DERIVATION_ALGORITHM);
KeySpec spec = new PBEKeySpec(userPassword.toCharArray(), salt, 25000, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), CIPHER_ALGORITHM);
return secret;
}
public static void encrypt(String userPassword, String fileEncryptName, String jsonPasswordsData)
throws NoSuchAlgorithmException, InvalidKeySpecException,
NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IOException {
byte[] salt = createSalt();
SecretKey secretKey = secretKeyCreate(userPassword, salt);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_MODE_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
//cipher.init(Cipher.ENCRYPT_MODE, secretKey); // incorrect decrypt
try (FileOutputStream fileOutputStream = new FileOutputStream(fileEncryptName);
CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher)) {
fileOutputStream.write(iv);
fileOutputStream.write(salt);
fileOutputStream.flush();
cipherOutputStream.write(jsonPasswordsData.getBytes(StandardCharsets.UTF_8));
}
}
public static String decrypt(String userPassword, String fileDecryptName) throws NoSuchPaddingException,
NoSuchAlgorithmException, IOException, InvalidParameterSpecException, InvalidKeySpecException,
InvalidAlgorithmParameterException, InvalidKeyException {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_MODE_PADDING);
AlgorithmParameters params = cipher.getParameters();
int ivLength = params.getParameterSpec(IvParameterSpec.class).getIV().length;
byte[] iv = new byte[ivLength];
byte[] salt = new byte[SALT_LEN];
byte[] plainText;
try (FileInputStream fileInputStream = new FileInputStream(fileDecryptName);
CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher)) {
fileInputStream.read(iv);
fileInputStream.read(salt);
SecretKey secretKey = secretKeyCreate(userPassword, salt);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
plainText = cipherInputStream.readAllBytes();
}
return new String(plainText, StandardCharsets.UTF_8);
}
}
The following groovy/java code does ecnrypt given string with password and iv.
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;
byte[] iv = "1234567812345678";
byte[] keyb = "ABCDEFGHIJKLMNOPQRSTUVWX";
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
String plainText = "Encrypt this text with AES - MODE CBC";
byte[] input = plainText.getBytes("UTF-8");
byte[] encoded = ci.doFinal(input);
System.out.println(encoded.encodeBase64().toString());
I am looking for some guidance on how can I add both salt and iterations to the process.
I believe you are thinking of Password Based Encryption (PBE). To do this in Java, you are going to want something like this (source):
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public static byte[] encrypt(final byte[] data, final char[] password,
final byte[] salt, final int noIterations) {
try {
final String method = "PBEWITHHMACSHA512ANDAES_256";
final SecretKeyFactory kf = SecretKeyFactory.getInstance(method);
final PBEKeySpec keySpec = new PBEKeySpec(password);
final SecretKey key = kf.generateSecret(keySpec);
final Cipher ciph = Cipher.getInstance(method);
final PBEParameterSpec params = new PBEParameterSpec(salt, noIterations);
return ciph.doFinal(data);
} catch (final Exception e) {
// best not to let the encryption error bubble out
throw new RuntimeException("Spurious encryption error");
}
}
To decrypt an AES encrypted field through REST API, i've have one issue in understanding the IV(Initialization Vector) and how to use that to decrypt the first block of 16 characters.
Encrypted field from the REST response JSON (description form field):
"description":"84d1d37bdb7a3200750573ffbf96191f:0aZdRxsIqSpFtuszNr73na/J9JuMLNB>0J6T2f2FrV0sUlMmbW4prbZMmXGnLU4W6CDlb5F1lb8js\r\nRHw6tfyZd5ZL//ZUlozE916wvP+zd+>uUfjpk2Bl9o2uAu+1bsNoAVdtP5m5fbnkjxf9yLRzREVVO\r\nIwYQOxNI/CeX2dzF/Uc="
I was able to identify this
"0aZdRxsIqSpFtuszNr73na/J9JuMLNB>0J6T2f2FrV0sUlMmbW4prbZMmXGnLU4W6CDlb5F1lb8js>r\nRHw6tfyZd5ZL//ZUlozE916wvP+zd+>uUfjpk2Bl9o2uAu+1bsNoAVdtP5m5fbnkjxf9yLRzREVV>O\r\nIwYQOxNI/CeX2dzF/Uc="
as the actual description and,
"84d1d37bdb7a3200750573ffbf96191f"
to be somehow related to my question in this post.
Encryption method: AES 128 Bit.
Password: 1234567890123456
Original Text: “new description for new incident.
www.google.com
lets see if the initial part is same or it changes for this new incident”
Decrypted output: “bGOn>22H~KH:38/_for new incident.
www.google.com
lets see if the initial part is same or it changes for this new incident”
Decryption Used: AES/CBC/PKCS5Padding
How to decrypt the first block which is jumbled in the Decrypted output. In other words, how to interpret 84d1d37bdb7a3200750573ffbf96191f in terms of IV to decrypt the first 16 characters ?
Any help would be appreciated.
Below is the Java code:
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AESDecryption {
private static String key = "1234567890123456";
private static String encryptedStr = "0aZdRxsIqSpFtuszNr73na/J9JuMLNB0J6T2f2FrV0sUlMmbW4prbZMmXGnLU4W6CDlb5F1lb8js\r\nRHw6tfyZd5ZL//ZUlozE916wvP+zd+uUfjpk2Bl9o2uAu+1bsNoAVdtP5m5fbnkjxf9yLRzREVVO\r\nIwYQOxNI/CeX2dzF/Uc=";
private static String padding = "AES/CBC/PKCS5Padding";
private static int iterationCount = 65536;
private static int keyLength = 128;
private static String secretKeyAlg = "PBEWithHmacSHA1AndAES_128";
public static void main(String[] args) throws Exception {
String finalStrDec = null;
SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKeyAlg);
PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), generateSalt(), iterationCount, keyLength);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
Cipher cipherDec = Cipher.getInstance(padding);
cipherDec.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
byte[] original = cipherDec.doFinal(Base64.decodeBase64(encryptedStr));
finalStrDec = new String(original);
System.out.println(finalStrDec);
}
public static byte[] generateSalt() throws UnsupportedEncodingException {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String salt = new String(bytes);
return salt.getBytes("UTF-8");
}
}
Encryption and Decryption in Java is still very difficult for me to understand. I have been using the following class and methods. I wonder how to improve the safety and how long does the keystring (schlüssel) need to be?
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class AES
{
public static SecretKeySpec makeKey(String schlüssel) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
byte[] key = (schlüssel).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
return new SecretKeySpec(key, "AES");
}
public static String encryptString(String text, SecretKeySpec schlüssel) throws Exception
{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, schlüssel);
byte[] encrypted = cipher.doFinal(text.getBytes());
BASE64Encoder myEncoder = new BASE64Encoder();
return myEncoder.encode(encrypted);
}
public static String decryptString(String text, SecretKeySpec schlüssel) throws Exception
{
BASE64Decoder myDecoder2 = new BASE64Decoder();
byte[] crypted2 = myDecoder2.decodeBuffer(text);
Cipher cipher2 = Cipher.getInstance("AES");
cipher2.init(Cipher.DECRYPT_MODE, schlüssel);
byte[] cipherData2 = cipher2.doFinal(crypted2);
return new String(cipherData2);
}
}
I have been reading about the topic. But I did not understand how to transfer the ideas into my code. Any help is appreciated, please be kind with an encryption beginner. Thank you.
There are a lot of things wrong in this class.
the class uses a cryptographic hash instead of a password hash - such as PBKDF2 - to derive a key from the password;
you are using ECB mode encryption (the default), you need to use at least CBC, together with an initialization vector (IV);
your class doesn't add any integrity protection, in other words the ciphertext is malleable;
It depends on the use case if you require the integrity protection. So I'll point you to this question for more information about password based encryption (PBE). Note that the answers may still deliver malleable ciphertext.
Furthermore the class contains the following Java mistakes:
it doesn't distinguish between runtime related exceptions (missing algorithms) and input related exceptions;
it uses the default platform encoding for your plaintext;
it is using a Sun internal class to perform the Base 64 encoding/decoding.
Note that people will probably point out to you that you are using 128 bit AES encryption. That's however quite strong and - certainly at this point in time - the least of your worries. Upgrading to 192 or 256 bit AES won't increase security significantly.
Refering to Maarten Bodeswes code I try to bring his code into the form I am using.
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class AESplus
{
public static SecretKeySpec makeKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
password = String.format("%040x", new BigInteger(1,password.getBytes(Charset.forName("UTF-8"))));
password = password.substring(password.length()-32, password.length());
final byte[] symKeyData = DatatypeConverter.parseHexBinary(password);
return new SecretKeySpec(symKeyData, "AES");
}
public static String encryptString(String text, SecretKeySpec key) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
final byte[] encodedMessage = text.getBytes(Charset.forName("UTF-8"));
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// generate random IV using block size
final byte[] ivData = new byte[blockSize];
final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
rnd.nextBytes(ivData);
final IvParameterSpec iv = new IvParameterSpec(ivData);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] encryptedMessage = cipher.doFinal(encodedMessage);
// concatenate IV and encrypted message
final byte[] ivAndEncryptedMessage = new byte[ivData.length + encryptedMessage.length];
System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage, blockSize, encryptedMessage.length);
return DatatypeConverter.printBase64Binary(ivAndEncryptedMessage);
}
public static String decrytString(String crypttext, SecretKeySpec key) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
final byte[] ivAndEncryptedMessage = DatatypeConverter.parseBase64Binary(crypttext);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// retrieve random IV from start of the received message
final byte[] ivData = new byte[blockSize];
System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
final IvParameterSpec iv = new IvParameterSpec(ivData);
// retrieve the encrypted message itself
final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length - blockSize];
System.arraycopy(ivAndEncryptedMessage, blockSize, encryptedMessage, 0, encryptedMessage.length);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
final byte[] encodedMessage = cipher.doFinal(encryptedMessage);
// concatenate IV and encrypted message
final String message = new String(encodedMessage,Charset.forName("UTF-8"));
return message;
}
}
I'm writing an application for Android that uses symmetric key encryption to protect sensitive data. As far as I can tell, Android only directly supports "PBEWithMD5AndDES". How secure is this algorithm? Also, I've included my code below (non-andriod). Is my code correctly encrypting the data?
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class CipherTest
{
private static class EncryptInfo
{
private final byte[] encryptedData;
private final byte[] initVector;
private final byte[] salt;
public EncryptInfo(byte[] encryptedData, byte[] initVector, byte[] salt)
{
this.encryptedData = encryptedData.clone();
this.initVector = initVector.clone();
this.salt = salt.clone();
}
public byte[] getEncryptedData()
{
return encryptedData;
}
public byte[] getInitVector()
{
return initVector;
}
public byte[] getSalt()
{
return salt;
}
}
private static final String keyGenAlgorithm = "PBEWithMD5AndDES";
private static final String keyAlgorithm = "DES";
private static final String cipherTransform = "PBEWithMD5AndDES/CBC/PKCS5Padding";
private static EncryptInfo encrypt(char[] password, byte[] data)
throws NoSuchAlgorithmException, InvalidKeySpecException,
NoSuchPaddingException, InvalidKeyException,
InvalidParameterSpecException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException
{
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
PBEKeySpec keySpec = new PBEKeySpec(password, salt, 1024);
SecretKeyFactory secretKeyFactory = SecretKeyFactory
.getInstance(keyGenAlgorithm);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
keySpec.clearPassword();
byte[] key = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, keyAlgorithm);
Cipher cipher = Cipher.getInstance(cipherTransform);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] initVector = cipher.getParameters().getParameterSpec(
IvParameterSpec.class).getIV();
return new EncryptInfo(cipher.doFinal(data), initVector, salt);
}
public static byte[] decrypt(byte[] data, char[] password, byte[] salt,
byte[] initVector) throws NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException
{
PBEKeySpec keySpec = new PBEKeySpec(password, salt, 1024);
SecretKeyFactory secretKeyFactory = SecretKeyFactory
.getInstance(keyGenAlgorithm);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
keySpec.clearPassword();
byte[] key = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, keyAlgorithm);
Cipher cipher = Cipher.getInstance(cipherTransform);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(
initVector));
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception
{
char[] password = "password".toCharArray();
EncryptInfo info = encrypt(password, "Message".getBytes());
byte[] decyptedText = decrypt(info.getEncryptedData(), password, info
.getSalt(), info.getInitVector());
System.out.println(new String(decyptedText));
}
}
Both MD5 and DES are weak. If your data being encrypted is really valuable, you should look for some external crypto library for Android that offers AES and SHA256/SHA512 algorithms.
If you want to encrypt data using a symmetric key encryption, I recommend:
1) Use AES, because it is certified by the NSA for data classified as secret.
2) Use a well reviewed implementation so you don't have to research the proper way to configure the code. For example, AESCrypt.
You can find AESCrypt here: http://www.aescrypt.com/java_aes_crypt.html
I've seen AESCrypt used in several financial institutions. AESCrypt for java is a single class that calls JCE methods. Android, JCE is implemented by bouncycastle. I have seen bouncycastle used in several major financial institutions.