Cannot decrypt long AES-256 GCM message with Java - java

Related to this question:
Cannot decrypt AES-256 GCM with Java
The Java decrypt issue seems to only be fixed if the encrypted message is short, i.e. two words or so. I've tried with the words, "hello" and "short string", and both of these words were decrypted fine. When I tried something like,
Alphanumeric string test1 with more numbers such as 5, 4, 3, 2, 1
AEADBadTagException came up again.
EDIT:
This issue is directly related to how long the encrypted message is. Two words is a bit of an exaggeration, but as long as the encrypted message is about as long as this or longer then Java will run into the exception.
Encrypted message sample:
d+nyOuSfH3wup+5KHJRQyVwVHE0nn7dOfLQsSxb2LsR1LuogHxmVobHoQSTbdyqupd/UvwGfbhkUQz+8CjIBSd7FoEVpgpYv9dAQ3GGUr3AtA+rJJrFHo/EM443sQlSOG4cIBQ7trF7udmrIhtiZ9wMchaBEJFmDBL5Jwl8ZMM0ath8VNWqfyyhghPW8U2NiORAy5mw6v07o7v3UT2
lBzJThBsM=
Decrypted with node:
this is a longer string to make the encrypted message longer than before
EDIT 2:
Java code:
package decryption;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DecryptAES256 {
private static String salt;
private static byte[] iv;
private static byte[] encryptedMessageAndTag;
private static byte[] key;
public static void main(String[] args) {
String key = "123456789aabbccddeefffffffffffff";
String sourceText = "zMX8Xp8lCLGP3FsF7dy1uEODFG0+lhpoWR+xZPpNAXm2D39+CJUK5Kk0z4NbDfb/WbP8lHVWcTOuXf8hRA1AmtEV2G5kP3SH3mrGbyf4QthR4aOTqEQQAvt1T8LlIkBlgx32gehP/nwwm3DYyJV+NnN21Ac17L4=";
System.out.println(decrypt(key, sourceText));
}
public static String decrypt(String masterkey, String encryptedText) {
// decode encryptedText
encryptedText = new String(Base64.getDecoder().decode(encryptedText.getBytes()));
// extract the different parts
byte[] parts = encryptedText.getBytes();
salt = new String(Arrays.copyOfRange(parts, 0, 64)); // not using for testing purposes
iv = Arrays.copyOfRange(parts, 64, 76);
encryptedMessageAndTag = Arrays.copyOfRange(parts, 76, parts.length);
try {
key = masterkey.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// not going to reach here
}
// call helper method to decrypt
byte[] decipheredText = decodeAES_256_CBC();
return new String(decipheredText);
}
private static byte[] decodeAES_256_CBC() {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec params = new GCMParameterSpec(128, iv, 0, iv.length);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, params);
return cipher.doFinal(encryptedMessageAndTag);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Failed to decrypt");
}
}
}
EDIT 3:
Cleaned up Java code for readability

Related

Spring, how to encode and decode a String

I am trying to encode a String so I can store it safely in my database. Most password encoders, like bcrypt, can only hash your String, with no way of getting it back (which for a password makes sense), and I want to decode it when I retrieve the information from my database.
I tried using org.springframework.security.crypto.encrypt.Encryptors.queryableText() but the method is deprecated.
Is there any other way to do it?
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class CryptoManager {
//Key for encryption and decryption
public static final byte[] KEY =
{118, 106, 107, 122, 76, 99, 69, 83, 101, 103, 82, 101, 116, 75, 101, 127};
public static void main(String args[]) {
String salt = "HELLO";
String encryptedString = encrypt(salt, "HelloWorld12345");
System.out.println("Encripted string is " + encryptedString);
String decryptedString = decrypt(salt, encryptedString);
System.out.println("Decrypted string is " + decryptedString);
}
public static String encrypt(String salt, String plainText) {
if (plainText == null || plainText.isEmpty()) {
System.out.println("No data to encrypt!");
return plainText;
}
Cipher cipher = null;
String encryptedString = "";
try {
// Creating a Cipher object
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// Creating a secret key from KEY byte array
final SecretKeySpec secretKey = new SecretKeySpec(KEY, "AES");
// Initializing a Cipher object
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// Encrypting the plain text string
byte[] encryptedText = cipher.doFinal(salt.concat(plainText).getBytes());
// Encoding the encrypted text to Base64
encryptedString = Base64.getEncoder().encodeToString(encryptedText);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException ex) {
System.out.println("Exception caught while encrypting : " + ex);
}
return encryptedString;
}
public static String decrypt(String salt, String cipherText) {
if (cipherText == null || cipherText.isEmpty()) {
System.out.println("No data to decrypt!");
return cipherText;
}
String decryptedString = "";
Cipher cipher = null;
try {
// Creating a Cipher object
cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
// Creating a secret key from KEY byte array
final SecretKeySpec secretKey = new SecretKeySpec(KEY, "AES");
// Initializing a Cipher object
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// Decoding from Base64
byte[] encryptedText = Base64.getDecoder().decode(cipherText.getBytes());
// Decrypting to plain text
decryptedString = new String(cipher.doFinal(encryptedText));
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException ex) {
System.out.println("Exception caught while decrypting : " + ex);
}
return decryptedString.replace(salt, "");
}
}
Check this out for more detail
Since you want to store the data securely in database, what you really need to do is encrypt the data before writing to database from application.
To encrypt/decrypt the data from application you can use Symmetric key algorithm like AES.
To automatically convert this in entity you can add a converter in corresponding column
#Convert(converter = DataEncryptDecryptConverter.class)
#Column(name = "DATA")
private String data;
And the converter
#Converter
#Component
public class DataEncryptDecryptConverter implements AttributeConverter<String, String> {
#Override
public String convertToDatabaseColumn(String attribute) {
// Code to encrypt data
}
#Override
public String convertToEntityAttribute(String dbData) {
// Code to decrypt data
}
}

Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher when decrypting

I'm getting this error when I try to decrypt a message which has already been encrypted.
I've done this by first encrypting a message in this case "Testing message"
Then running the method again but decrypting instead of encrypting.
I've looked at the other questions regarding this but could not fix the problem
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
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 PBEusing {
public static void main(String[] args) throws Exception {
PBEusing pbe = new PBEusing();
String encrypt = pbe.pbe("encrypt", "passwordTest", "Testing message");
System.out.println(encrypt);
String decrypt = pbe.pbe("decrypt", "passwordTest", encrypt);
System.out.println(decrypt);
}
public static String pbe(String cipherMethod, String clientPassword, String clientMessage) throws Exception {
String method = cipherMethod.toUpperCase();
String output = "";
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[16];
rnd.nextBytes(iv);
byte[] plaintext = clientMessage.getBytes(StandardCharsets.UTF_8); // input message from user
byte[] salt = "01234567".getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 10000, ivParamSpec);
PBEKeySpec keySpec = new PBEKeySpec(clientPassword.toCharArray());
try {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
SecretKey secretKey = kf.generateSecret(keySpec);
// On J2SE the SecretKeyfactory does not actually generate a key, it just wraps the password.
// The real encryption key is generated later on-the-fly when initializing the cipher
System.out.println(new String(secretKey.getEncoded()));
// Encrypt
if (method.equals("ENCRYPT")) {
Cipher enc = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
enc.init(Cipher.ENCRYPT_MODE, secretKey, pbeParamSpec);
byte[] encrypted = enc.doFinal(plaintext);
output = new BASE64Encoder().encode(encrypted);
System.out.println("Encrypted text: " + output);
} else {
// Decrypt
Cipher dec = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
dec.init(Cipher.DECRYPT_MODE, secretKey, pbeParamSpec);
byte[] decrypted = dec.doFinal(plaintext);
String test = new BASE64Encoder().encode(decrypted);
//String message = new String(test, StandardCharsets.UTF_8);
output = test;
}
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
}
return output;
}
}
You're Base64 encoding the output of the encryption but aren't Base64 decoding it again before you try to decrypt it.

Decryption of REST API response

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");
}
}

Encrypt complete object with triple des

I need to encrypt a complete java object. I am having a code which i have seen on internet which shows how to encrypt and decrypt text not the java object. So i was confused whether this is possible to encrypt complete java object. The code which i am using is below.
package security;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* This class defines methods for encrypting and decrypting using the Triple DES
* algorithm and for generating, reading and writing Triple DES keys. It also
* defines a main() method that allows these methods to be used from the command
* line.
*/
public class TripleDesEncryptionDecryption {
/**
* The program. The first argument must be -e, -d, or -g to encrypt,
* decrypt, or generate a key. The second argument is the name of a file
* from which the key is read or to which it is written for -g. The -e and
* -d arguments cause the program to read from standard input and encrypt or
* decrypt to standard output.
*/
private static final String UNICODE_FORMAT = "UTF8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DES/ECB/NoPadding";
private KeySpec myKeySpec;
private SecretKeyFactory mySecretKeyFactory;
private Cipher cipher;
byte[] keyAsBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
static String stringToEncrypt="";
public void setKey(String myKey) throws Exception
{
myEncryptionKey = myKey ;
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
myKeySpec = new DESedeKeySpec(keyAsBytes);
mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = mySecretKeyFactory.generateSecret(myKeySpec);
}
/**
* Method To Encrypt The String
*/
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
BASE64Encoder base64encoder = new BASE64Encoder();
encryptedString = base64encoder.encode(encryptedText);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
/**
* Returns String From An Array Of Bytes
*/
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
/**
* Testing The DESede Encryption And Decryption Technique
*/
public static void main(String args []) throws Exception
{
TripleDesEncryptionDecryption myEncryptor= new TripleDesEncryptionDecryption();
String encrypted=myEncryptor.encrypt(stringToEncrypt);
String decrypted=myEncryptor.decrypt(encrypted);
System.out.println("String To Encrypt: "+stringToEncrypt);
System.out.println("Encrypted Value :" + encrypted);
System.out.println("Decrypted Value :"+decrypted);
}
}
There is a Java-class called SealedObject (doc) which exactly does what you want to achieve.
This class enables a programmer to create an object and protect its confidentiality with a cryptographic algorithm.
There is only one restriction for the Object to encrypt, it must be Serializable.
MyObject myObj = new MyObject(); // must be serializable
Cipher cipher;
/* initialize fully with IV, key and Cipher.ENCRYPT_MODE */
/* encrypt `myObj` */
SealedObject sealedObj = new SealedObject(myObj, cipher);
/* decrypt `sealedObj` */
MyObjct decryptedObj = (MyObject) sealedObj.get(key); // `key` = encryption-key
Basically this class does the serialization with ObjectOutputStream and ByteArrayOutputStream for you and automatically tracks the algorithm used for encryption.
You can encrypt bytes. Text is bytes, you can serialize a Java object to bytes, so technically it's possible (for example with an ObjectOutputStream connected to a ByteArrayOutputStream).
However it sounds strange, why do you think you need to encrypt an object, instead of the essential data inside an object?

How to use 3DES algorithm on Android?

On the server side, the encyption/decryption of the password field is done in C#.
Now, i need to implement same functionality in my android application. So, i followed this tutorial: http://ttux.net/post/3des-java-encrypter-des-java-encryption/ as below:
import java.security.MessageDigest;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.apache.commons.codec.binary.Base64;
public class Encrypter {
private KeySpec keySpec;
private SecretKey key;
private IvParameterSpec iv;
public Encrypter(String keyString, String ivString) {
try {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest(Base64.decodeBase64(keyString.getBytes("utf-8")));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
keySpec = new DESedeKeySpec(keyBytes);
key = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
iv = new IvParameterSpec(ivString.getBytes());
} catch(Exception e) {
e.printStackTrace();
}
}
public String encrypt(String value) {
try {
Cipher ecipher = Cipher.getInstance("DESede/CBC/PKCS5Padding","SunJCE");
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
if(value==null)
return null;
// Encode the string into bytes using utf-8
byte[] utf8 = value.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new String(Base64.encodeBase64(enc),"UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String decrypt(String value) {
try {
Cipher dcipher = Cipher.getInstance("DESede/CBC/PKCS5Padding","SunJCE");
dcipher.init(Cipher.DECRYPT_MODE, key, iv);
if(value==null)
return null;
// Decode base64 to get bytes
byte[] dec = Base64.decodeBase64(value.getBytes());
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
but i dont know what values i need to provide for KeyValue and ivValue for the above code. Please help me...
Use this code to encrypt your string
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import android.util.Base64;
//string encryption
public class EncryptionHelper {
// Encrypts string and encode in Base64
public static String encryptText(String plainText) throws Exception {
// ---- Use specified 3DES key and IV from other source --------------
byte[] plaintext = plainText.getBytes();//input
byte[] tdesKeyData = Constants.getKey().getBytes();// your encryption key
byte[] myIV = Constants.getInitializationVector().getBytes();// initialization vector
Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(myIV);
c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
byte[] cipherText = c3des.doFinal(plaintext);
String encryptedString = Base64.encodeToString(cipherText,
Base64.DEFAULT);
// return Base64Coder.encodeString(new String(cipherText));
return encryptedString;
}
}
This is how you can encrypt the string
String encryptedPassword = EncryptionHelper.encryptText(edtText.getText().toString());
EDIT
Code for Constants.java
Class Constants {
private final String initializationVector = "INITALIZATION_VECTOR";
private final String ecnryptionKey = "ENCRYPTION_KEY";
public static String getInitializationVector() {
return initializationVector;
}
public static String getKey() {
return ecnryptionKey;
}
}
Triple DES is called "DESede" (DES using single DES Encrypt, Decrypt, Encrypt for encryption) in both Java and Android runtimes. So it is build in functionality which can be access through the Cipher class. It also lists the available algorithms. For triple DES you could use "DESede/CBC/PKCS5Padding"`. Don't forget to supply it a random IV of 8 bytes.
Triple DES should only be used for backwards compatibility. If you decide to use it at least supply it 24 bytes of key material, otherwise there is a chance that your ciphertext can be cracked. For a more modern approach use AES, preferably in an authenticated mode such as GCM ("AES/GCM/NoPadding"). Note that GCM requires a unique nonce of 12 bytes.

Categories

Resources