I have data encrypted with AES/CBC/PKCS5Padding
I have the secret key but no iv.
I am unable to decrypt the data. What exactly are the steps. I copied a page's steps but I am sure I am doing something wrong since the data is hex.
Below is my code. I use the decrypt function with two params.
public class DCrypt2 {
private static String key = "3jiUqR/0J4/HX98XimcDvg==";//
private static String msg ="636F98E19CCAEB9C6ED1095F70C4739AEBA6200E83926EA3DA42DA4A391AC08B";//"2925D99C3A7520D84D64A80AAFB20BF63B22B6A8017B7438598BE36419B71174";
public static void main(String[] args) {
byte[] myIV = getIV(msg);
byte[] myMSG = getMSG(msg);
String my_msg = myMSG.toString();
decrypt(msg, myIV);
System.out.println("Second Try ");
System.out.println(key);
System.out.println(msg);
System.out.println("____________________________________");
//System.out.println(Base64.getDecoder().decode(msg));
//System.out.println(myDeHex(msg));
//msg = myDeHex(msg);
int extra = msg.length()%16;
System.out.println(extra+" ok "+msg.length());
for(int i = 0; i<extra;i++) {
System.out.println("ANOTHER");
msg +=" ";
}
System.out.println(extra+" ok "+msg.length());
}
public static String decrypt(String msg, byte[] iv) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Cipher cipher = Cipher.getInstance("AES/CBC/nopadding");
SecretKeySpec the_key = new SecretKeySpec(key.getBytes(), "AES");// get / create symmetric encryption key
//SecretKeySpec the_key = new SecretKeySpec(Base64.getDecoder().decode(key), "AES");
// byte[] decoded = Base64.getDecoder().decode(msg);
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, the_key, ivspec);
byte[] input = Base64.getEncoder().encode(msg.getBytes());
if(input.length%16 > 0 ) {
System.out.println(input.length%16);
input = Arrays.copyOf(input, (input.length)+(16-(input.length%16)));
System.out.println(input.length+ " my size");
}
String res = new String(cipher.doFinal( input));
//String res = Base64.getEncoder().encodeToString(cipher.doFinal(msg.getBytes("UTF-8")));
//String res = new String(Base64.getDecoder().decode(cipher.doFinal(msg.getBytes("UTF-8"))));
System.out.println("Results: "+res);
return res;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
}
There is a good technical write-up here: https://www.rfc-editor.org/rfc/rfc3602#section-2
You need an Initialization Vector:
https://crypto.stackexchange.com/questions/29134/precisely-how-does-cbc-mode-use-the-initialization-vector
The IV's purpose is to ensure same plaintexts encrypt to different
ciphertexts. When an adversary learns the IV after the plaintext has
been encrypted, no harm is done, since it has already served its
purpose.
The IV can be made public after encryption, without impacting the
security of the system. Usually, it is prefixed to the ciphertext.
Source / More info
Related
I take a data string = "AkhilRanjanBiharabcdefghijklmnopMovedtoChennai18", encrypt it first and then decrypt it. The string which I get back on decryption is "AkhilRanjanBiharÙ†+™¸„À–ýæó#Movedtoñhennai18" which is almost fine for the first 16 and final 16 characters, but the 16 characters in the middle are absolute junk. What can possibly be going wrong?
My encryption code:-
public String encrypt(String value) {
log.info("This method is not going to be used");
String key = "theabcd#heymaths";
initVector = "{{{{{{{{{{{{{{{{";
String encryptedStr="";
byte[] encrBytes =null;
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
encrBytes = cipher.doFinal(value.getBytes());
encryptedStr = new String(encrBytes);
} catch (Exception ex) {
ex.printStackTrace();
}
String strToBeEncoded = encryptedStr +"::"+initVector;
encrBytes = strToBeEncoded.getBytes();
//String encoded = Base64.encodeBase64String(encrBytes);
String encoded = Base64.getEncoder().encodeToString(encrBytes);
String urlEncoded = null;
try {
urlEncoded = java.net.URLEncoder.encode(encoded, CHARSET);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return urlEncoded;
}
Decryption code:-
public String decrypt(String encrypted) {
String decryptedStr = null;
byte[] base64Bytes = null;
String urlDecoded = null;
String key = HmCommonProperty.getProperty("abcd_crypt_key");
if(key == null || key.isEmpty()) {
key = securityKey;
}
String encryptionMech = HmCommonProperty.getProperty("abcd_crypt_algo");
if(encryptionMech == null || encryptionMech.isEmpty()) {
encryptionMech = CRYPT_MECHANISM;
}
try {
//Url and Base64 decoding
urlDecoded = java.net.URLDecoder.decode(encrypted, CHARSET);
//base64Bytes = Base64.decodeBase64(urlDecoded);
base64Bytes = Base64.getDecoder().decode(urlDecoded);
//Generating IV
String str = new String(base64Bytes);
String[] bodyIVArr = str.split("::");
initVector = bodyIVArr[1];
String bodyStr = bodyIVArr[0];
//AES Decryption
Cipher cipher = Cipher.getInstance(encryptionMech);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());
System.out.println("initVector Length -> "
+iv.getIV().length);
System.out.println("input length -> "
+bodyStr.getBytes().length);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decryptedBytes = cipher.doFinal(bodyStr.getBytes());
decryptedStr = new String(decryptedBytes);
} catch (Exception ex) {
ex.printStackTrace();
log.error("Error occurred while decryption abcd data",ex);
}
return decryptedStr;
}
Your encrypted data is a sequence of bytes. If you need to encode it as a string, you should use base64 or a similar encoding that is intended for encoding arbitrary byte arrays. Pretending that your arbitrary byte array is a valid string-encoding is going to cause you trouble, even if you use ISO_8859_1.
Replace
encryptedStr = new String(encrBytes)
with
encryptedStr = Base64.getEncoder().encodeToString(encrBytes)
and replace
bodyStr.getBytes()
with
Base64.getDecoder().decode(bodyStr)
See also: How to correctly and consistely get bytes from a string for AES encryption?
Your error lies here:
encryptedStr = new String(encrBytes);
strToBeEncoded.getBytes();
These methods use the platform default character set, and when you convert from byte[] to String and back to byte[] the process is lossy in the general case. The only way it's not lossy is if the platform default character set is "ISO_8859_1".
I changed all of 11 such calls to:
encryptedStr = new String(encrBytes, StandardCharsets.ISO_8859_1);
strToBeEncoded.getBytes(StandardCharsets.ISO_8859_1);
(I didn't change CHARSET). The output I get now is:
initVector Length -> 16
input length -> 48
AkhilRanjanBiharabcdefghijklmnopMovedtoChennai18
Bonus warning 1: The encryption uses hardcoded "AES/CBC/NoPadding" but the decryption is dynamic (it should of course also use "AES/CBC/NoPadding").
Bonus warning 2: The chance is low but it's entirely possible that "::" appears inside the encrBytes, screwing up your str.split("::");. One solution is to search for the last occurrence of "::" and only split on that.
I have tried adding getbytes("UTF") or getbytes("UTF-8"), since it was suggested in a similar question.
It said we need to try UTF while converting bytes to string and vice a versa.
But still it is not working for my code...please help
public class Password1 {
private static final String ALGO = "AES";
private static byte[] keyValue = new byte[]{'t','h','y','u','e','f','z','s','y','k','f','l','d','a','b','m'};
public static void main(String[] args) {
//Password1 p = new Password1();
Scanner sc = new Scanner(System.in);
String i = sc.nextLine();
System.out.println("Password = "+i);
try {
String en = encrypt(i);
System.out.println(en);
String dec = decrypt(en);
System.out.println("Encrypted = " + en);
System.out.println("Decrypted = " + dec);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static String encrypt(String Data) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes("UTF-8"));
String encrypted = new BASE64Encoder().encode(encVal);
return encrypted;
}
public static String decrypt(String encrypted) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
//Byte bencrypted = Byte.valueOf(encrypted);
byte[] decoded = new BASE64Decoder().decodeBuffer(encrypted);
byte[] decValue = c.doFinal(decoded);
String decrypted = new String(decValue);
return decrypted;
}
private static Key generateKey() throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-1");
keyValue = sha.digest(keyValue);
keyValue = Arrays.copyOf(keyValue, 16);
SecretKeySpec key = new SecretKeySpec(keyValue, ALGO);
return key;
}
}
When you call encrypt() you replace the password by its hash, and then use the hash as a key.
Then you call decrypt(), and rehash the hash, and use the hashed hash as the key. So you're not using the same key for encryption and decryption.
Generate the key once in main(), and pass it as parameter to encrypt() and decrypt(). Make keyValue final. Or even better, make it a local variable of main.
On the line where you have Cipher.getInstance(ALGO) have your ALGO variable be "AES/CBC/PKCS5Padding" (or whatever other mode and padding you want to use). This should resolve any padding issues you might run into like the one you are now.
If you don't do it in this way I believe you'll have to handle all of the padding logic yourself.
JB Nizet's answer about how you're generating keys is the other half of your problem.
Here's a sample dummy code called test(), I run this 100K times and I get different encrypted messages for the same plain text (obviously, the decryption I get is the original plain text).
I guess the reason behind this is to avoid frequency; but how come there can be MANY encryption to ONE decryption? shouldn't it be a one to one?
public static void test()
{
String plainMessage = "I'm gonna bid 100 USD on this project";
String password = "A99922000001000004581F0F0CCD0000";
Set<String> set = new HashSet<String>();
for (int x=0; x<100000; x++)
{
String enc = AESEncryption.encryptMessage(plainMessage, password);
System.out.println(enc);
set.add(enc);
String dec = AESEncryption.decryptMessage(enc, password);
if (!dec.equals(plainMessage))
{
System.out.println("wrong decryption"); //never gets here
break;
}
}
System.out.println(">"+set.size()); //just to make sure they are all unique
}
the encryption
public static String encryptMessage(final String plainMessage, final String symKeyHex)
{
final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);
final byte[] encodedMessage = plainMessage.getBytes(Charset.forName("UTF-8"));
try {
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// create the key
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
// generate random IV using block size (possibly create a method for
// this)
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, symKey, 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);
final String ivAndEncryptedMessageBase64 = DatatypeConverter.printBase64Binary(ivAndEncryptedMessage);
return ivAndEncryptedMessageBase64;
}catch (InvalidKeyException e)
{
throw new IllegalArgumentException("key argument does not contain a valid AES key");
}catch (GeneralSecurityException e)
{
throw new IllegalStateException("Unexpected exception during encryption", e);
}
}
To add some clarity to the comments:
The iv or initialization vector is like a second key which is used to encrypt the data.
Since you use a random iv each time (this is good), you get different encrypted text each time. This second key is actually included with the encrypted text, so you don't need to pass it along separately.
Having just the iv doesn't enable you to crack the encryption (which is why you can pass it along with the encrypted text), but by using it, you can send the same plain text with the same password multiple times (using different ivs) and get completely different encrypted values.
I'm a newbie here in need of some help. I have a code here that is use to decrypt data. It is working meaning it can decrypt the data, but the problem is; it can only show 1 value of the decrypted data(line 29) instead of 3 or more depend on how I want it (how do i get it to also decrypt the data on line 30 and line 31?). Below i posted the related code to my problem, hope anyone here can help me with this problem.
public class CipherUtils
{
static Log log = LogFactory.getLog(CipherUtils.class);
private static byte[] key = "xxxxxx".getBytes();
private static byte[] iv = "xxxxxx".getBytes();
public static String decrypt(String strToDecrypt)
{
try
{
//Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
Cipher cipher = Cipher.getInstance("aes/cbc/nopadding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ips = new IvParameterSpec(iv);
//cipher.init(Cipher.DECRYPT_MODE, secretKey);
cipher.init(Cipher.DECRYPT_MODE, secretKey,ips);
final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(strToDecrypt)));
return decryptedString.trim();
}
catch (Exception e)
{
log.error("Error while decrypting : " + strToDecrypt , e);
}
return null;
}
public static void main(String args[]) throws Exception
{
String a = CipherUtils.decrypt("yXTVA6oG4kWOlvfKN/qXwa3VgEyiBu4kkgKh9WHt0s8="
,"yX7JI7IaExK3eBC6BU5RdCvkCrAAcyV3YTmHqYH5nG0="
,"yj56tfZEh3405yEwladp+ml/nk/h8Cx56XnP5Ycdeio=");
System.out.println(">>>"+a.trim());
}
}
Thanks in advance.
Your method decrypt only takes a single parameter (named strToDecrypt), yet you are passing three.
You could do several things here, the easiest would be to simly call decrypt 3 times, each with a different string.
You could also rewrite list to iterate over a list instead.
http://www.tutorialspoint.com/java/java_using_iterator.htm
public class CipherUtils
{
static Log log = LogFactory.getLog(CipherUtils.class);
private static byte[] key = "xxxxxx".getBytes();
private static byte[] iv = "xxxxxx".getBytes();
public static String[] decrypt(String[] strToDecrypt){
try{
String[] strDecrypted;
//Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
Cipher cipher = Cipher.getInstance("aes/cbc/nopadding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ips = new IvParameterSpec(iv);
//cipher.init(Cipher.DECRYPT_MODE, secretKey);
cipher.init(Cipher.DECRYPT_MODE, secretKey,ips);
for(int i =0;i<strToDecrypt.length ; i++){
final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(strToDecrypt [i])));
strDecrypted[i] = decryptedString ;
}
return strDecrypted;
}catch (Exception e){
log.error("Error while decrypting : " + strToDecrypt , e);
}
return null;
}
public static void main(String args[]) throws Exception
{
String array[] = {"yXTVA6oG4kWOlvfKN/qXwa3VgEyiBu4kkgKh9WHt0s8=","yX7JI7IaExK3eBC6BU5RdCvkCrAAcyV3YTmHqYH5nG0=","yj56tfZEh3405yEwladp+ml/nk/h8Cx56XnP5Ycdeio="};
String a = CipherUtils.decrypt(array);
System.out.println(">>>"+a.trim());
}
}
What I have done is I have taken all your strings to decrypt inside a String array and passed it to the method. Then inside the method, i decrypt the string and add it to another array. And then when done, I return the array containing all the decrypted lines. You can then fetch it from the array.
public static String[] decrypt(String[] strToDecrypt)
{
String decryptedString[] = new String[strToDecrypt.length];
for(int i = 0;i<strToDecrypt.length;i++) {
try
{
//Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
Cipher cipher = Cipher.getInstance("aes/cbc/nopadding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ips = new IvParameterSpec(iv);
//cipher.init(Cipher.DECRYPT_MODE, secretKey);
cipher.init(Cipher.DECRYPT_MODE, secretKey,ips);
decryptedString[i] = new String(cipher.doFinal(Base64.decodeBase64(strToDecrypt[i])));
}
catch (Exception e)
{
log.error("Error while decrypting : " + strToDecrypt[i] , e);
}
}return decryptedString;
}
Why does not this AES encryption work? I've written it in Java to test, but I am not able to decrypt. I get garbage upon decryption. Why? Its so simple - In the main method, print plain text, encrypt, print cipher text, decrypt, print plain text again. Am I doing something wrong? Please help me figure out the problem.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESTest {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
String cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String cipherText = new String(cipher.doFinal(plainText.getBytes()));
return cipherText;
}
public static String decrypt(String cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String plainText = new String(cipher.doFinal(cipherText.getBytes()));
return plainText;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
The output of the cipher is a sequence of random-looking bytes. You have no guarantee that these bytes will be a valid encoding for a character string in whatever is your system's default encoding. So this line:
String cipherText = new String(cipher.doFinal(.....));
is likely to lose information that you'll need for decryption.
Therefore you will not get the right bytes reconstructed in your decrypt operation. For example, if your default encoding is UTF-8, it is overwhelmingly unlikely that the correct ciphertext is something that String.getBytes() is even able to produce.
Two things:
No padding can only work if you use input that is an exact mulitple of your key size, which is 128 bit or 16 bytes. So in your particular case "Hello World!!!!!".getBytes() is actually a multiple of 16, but this is of course not true for arbitrary Strings.
Use "AES/CBC/PKCS5Padding" instead to solve this issue.
Do not turn your encrypted data into a String - this will and change the encrypted output. There's no guarantee that new String(byte[]).getBytes() returns the exact same byte array!
So you should leave the encrypted data as what it is - a stream of bytes. Thus encrypt should return byte[] instead and decrypt should take byte[] as input - this is a working example:
public class NewClass {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
byte[] cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
// -> Hello World!!!!
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(plainText.getBytes());
}
public static String decrypt(byte[] cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return new String(cipher.doFinal(cipherText));
}
You need to create the SecretKeySpec object once and use it for both encrypt and decrypt. Currently the code is creating two different keys for each operation and this will definitely lead to incorrect results.