I am trying to write an encryption/decryption utility class, but not matter what I do I cannot seem to get decryption working. I keep getting a javax.crypto.BadPaddingException: Given final block not properly padded exception during decryption.
I've looked at a number of examples and other stack overflow questions but can't seem to find my mistake
public class EncryptionUtil {
private static final Log LOGGER = LogFactory.getLog(EncryptionUtil.class);
private static final String CIPHER_MODE = "AES/CBC/PKCS5PADDING";
private static final String CRYPTO_PROPERTIES_PATH = "/crypto.properties";
private static final SecretKeySpec sKey = keySpecFromProperties();
private EncryptionUtil() {}
public static byte[] encrypt(byte[] aBytes) {
try {
SecureRandom lSecureRandom = new SecureRandom();
byte[] ivBytes = new byte[16];
lSecureRandom.nextBytes(ivBytes);
IvParameterSpec lSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
cipher.init(Cipher.ENCRYPT_MODE, sKey, lSpec);
byte[] encryptedBytes = cipher.doFinal(aBytes);
byte[] outBytes = new byte[encryptedBytes.length + 16];
System.arraycopy(ivBytes, 0, outBytes, 0, 16);
System.arraycopy(encryptedBytes, 0, outBytes, 16, encryptedBytes.length);
return outBytes;
} catch (Exception aEx) {
LOGGER.error("Failed to encrypt bytes");
throw new RuntimeException(aEx);
}
}
public static byte[] decrypt(byte[] aBytes) {
try {
byte[] lIvBytes = Arrays.copyOfRange(aBytes, aBytes.length - 16, aBytes.length);
byte[] lEncryptedBytes = Arrays.copyOfRange(aBytes, 0, aBytes.length - 16);
IvParameterSpec lIvSpec = new IvParameterSpec(lIvBytes);
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
cipher.init(Cipher.DECRYPT_MODE, sKey, lIvSpec);
return cipher.doFinal(lEncryptedBytes);
}catch (Exception aEx){
LOGGER.error("Failed to decrypt bytes. Returning input bytes", aEx);
return aBytes;
}
}
private static SecretKeySpec keySpecFromProperties(){
try(InputStream lPropStream = EncryptionUtil.class.getResourceAsStream(CRYPTO_PROPERTIES_PATH)){
Properties cryptoProps = new Properties();
cryptoProps.load(lPropStream);
String lSecret = cryptoProps.getProperty("secret");
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(lSecret.getBytes("UTF-8"));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(),0, keyBytes, 0, keyBytes.length);
return new SecretKeySpec(keyBytes, "AES");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
You prepend your IV to the ciphertext on encryption, but on decryption you copy the last 16 bytes as your IV.
Whatever you do on encryption you must undo on decryption.
Related
I got an encrypting key in HEX format and its intialization vector (HEX), the length of both of them is 32, I got an encrypted phone number from the client side, and I should decrypt it using the same encryption key.
Here is the method:
private static final String key = "*";
private static final String initVector = "*";
private static final String characterEncoding = "UTF-8";
private static final String cipherTransformation = "AES/CBC/PKCS5PADDING";
private static final String aesEncryptionAlgorithem = "AES";
public static String decrypt(String encryptedText) {
String decryptedText = "";
try {
Cipher cipher = Cipher.getInstance(cipherTransformation);
byte[] keyByte = key..getBytes(characterEncoding);
SecretKeySpec secretKey = new SecretKeySpec(keyByte, aesEncryptionAlgorithem);
IvParameterSpec ivparameterspec = new IvParameterSpec(initVector.getBytes());
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivparameterspec);
Base64.Decoder decoder = Base64.getDecoder();
byte[] cipherText = decoder.decode(encryptedText.getBytes("UTF8"));
decryptedText = new String(cipher.doFinal(cipherText), "UTF-8");
} catch (Exception E) {
System.err.println("decrypt Exception : "+E.getMessage());
}
return decryptedText;
}
the length of the given key and Iv is 32, I got this exception:
Encrypt Exception : Wrong IV length: must be 16 bytes long
I think I should convert the key and the Iv to another format in order to have the wanted length.
I want to decrypt a String. Here my Decryption and Encryption methods.
public String encrypt(String message) throws Exception {
byte[] messageInBytes = message.getBytes();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = encryptionCipher.doFinal(messageInBytes);
return encode(encryptedBytes);
}
public String decrypt(String encryptedMessage) throws Exception {
byte[] messageInBytes = decode(encryptedMessage);
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(T_LEN , encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decryptedBytes = decryptionCipher.doFinal(messageInBytes);
return new String(decryptedBytes);
}
Here the main:
public static void main(String[] args) {
try {
AES aes = new AES();
aes.convertStringKeyToSecretKey();
String encryptedMessage = aes.encrypt("Peter");
String decryptedMessage = aes.decrypt(encryptedMessage);
System.err.println("Encrypted Message : " + encryptedMessage);
System.err.println("Decrypted Message : " + decryptedMessage);
} catch (Exception ignored) {
}
}
When I change encryptedMessage to a own String like:
String decryptedMessage = aes.decrypt("xDFzl9HsenqKspdEbL/m9I5X6dqn");
It does nothing
I hope you can help me.
Best Regards
Christian
public static String encryptAES(String toEncrypt, final String key1, final String key2) throws Exception {
try {
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(key1.toCharArray(), key2.getBytes(), 65536, 256);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(toEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception ex) {
throw new Exception(ex);
}
}
public static String decryptAES(String toDecrypt, final String key1, final String key2) throws Exception {
try {
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(key1.toCharArray(), key2.getBytes(), 65536, 256);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(toDecrypt)));
} catch (Exception ex) {
throw new Exception(ex);
}
}
Here you can still expand its secureness by creating your own IV Spec key, which should contain only 16 character.
Actually, this is how AES encryption worked for me, You can also check this repo in GitHub for additional encryption methods.
I want to encrypt decrypt using bouncycastle.
by code sometimes works and sometimes not.
For example for this code:
String msg="ivivivi;message";
String key="1234567891234567";
SecureRandom secureRandom = new SecureRandom();
byte[] keyB = new byte[16];
secureRandom.nextBytes(keyB);
String cipher=Encryption.encrypt(msg.getBytes(),key.getBytes(),keyB);
System.out.println("cipher: "+cipher);
String original=Encryption.decrypt(cipher.getBytes(), key.getBytes(), keyB);
System.out.println("original: "+original);
Expected output:
cipher: c÷cAn‘iµHy~‹eX03
original: ivivivi;message
This gives the output, but if run run the code again and again it sometimes gives the expected result and sometimes not.
when it is not work, it thorws error:
org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted
Here my ecrypt/decrypt functions:
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception
{
byte[] result=null;
try{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
}catch(Exception e){
System.err.println("Encryption [0010] "+e.getMessage());
}
return result;
}
public static String decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception
{
System.out.println("Encryption decrypt: "+new String(cipher));
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
try {
aes.init(false, ivAndKey);
} catch (Exception e) {
return "";
}
byte[] cip=cipherData(aes, cipher);
if(cip==null){
System.err.println("Encryption.decrypt [0011]: cip is null");
return "";
}
String result=new String(cip);
System.out.println("Encryption decrypted: "+result);
return result;
}
public static String encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception
{
System.out.println("Encryption encrypt: "+new String(plain));
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return new String(cipherData(aes, plain));
}
what am I missing?
I am getting following error while decrypting.
Once it was working fine but suddenly I am getting this error.
How can I solve this problem?
I have read many articles but couldn't get help.
java.security.InvalidKeyException: Parameters missing
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:388)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:186)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:848)
at javax.crypto.Cipher.init(Cipher.java:1212)
at javax.crypto.Cipher.init(Cipher.java:1152)
at
com.test.security.TestEncryptDecrypt.decrypt(TestEncryptDecrypt.java:92)
at com.test.security.TestEncryptDecrypt.main(TestEncryptDecrypt.java:59)
The code is give below:
public static void main(String []args){
try {
String encString = encrypt("PID=0000000003|ITC=NA|PRN=MNKB0701511135|AMT=1.00|CRN=INR|RU=https://www.testsite.com/testsk/servlet/TestResponseHandler?");
System.out.println("Enc : " + encString);
System.out.println("Dec : "+ decrypt(encString));
} catch (Exception e) {
e.printStackTrace();
}
}
Encrypt method
public static String encrypt(String data) throws Exception {
String keyFile = "E:\\testpath\\Keys\\0000000003.key";
byte[] keyb = Files.readAllBytes(Paths.get(keyFile));
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, skey);
byte[] encVal = c.doFinal(data.getBytes());
return new BASE64Encoder().encode(encVal);
}
Decrypt method
public static String decrypt(String encryptedData)
throws InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, IOException {
String keyFile = "E:\\testpath\\Keys\\0000000003.key";
byte[] keyb = Files.readAllBytes(Paths.get(keyFile));
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, skey);
byte[] decordedValue = Base64.decodeBase64(encryptedData.getBytes());
byte[] decValue = c.doFinal(decordedValue);
return new String(decValue);
}
As the error message says, you are missing a parameter in the init methods. If you're using CBC you should also specify the AlgorithmParameterSpec.
Could you perhaps try the following:
byte[] paramSpecBytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec paramSpec = new IvParameterSpec(paramSpecBytes);
And in your encrypt and decrypt methods:
c.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
c.init(Cipher.DECRYPT_MODE, skey, paramSpec);
You could fill the bytes with any value you like though, doesn't need to be zero's. Best would be some random value.
I'm trying to reverse-engineer this Java code in C#, but I keep getting wrong result:
public static String GetEncodedToken(String tokenBase64) {
String str2 = "";
try {
byte[] ivBytes = Base64Coder.decode("OSMqNE11fGUoLDg5Mmk1WQ==");
byte[] keyBytes = Base64Coder.decode("Nm4wMy5nOiM3JSpWfnwzOXFpNzRcfjB5MVNEKl8mWkw=");
byte[] tokenBytes = Base64Coder.decode(tokenBase64);
byte[] tokenEncBytes = Encrypt(ivBytes, keyBytes, tokenBytes);
str2 = URLEncoder.encode(Base64Coder.encode(tokenEncBytes), "UTF-8");
} catch (Exception e) {
}
return str2;
}
private static byte[] Encrypt(byte[] ivBytes, byte[] keyBytes, byte[] tokenBytes) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException {
AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Key secretKeySpec = new SecretKeySpec(keyBytes, "AES");
Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
instance.init(1, secretKeySpec, ivParameterSpec); // 1 = CipherMode.CBC in C#???
return instance.doFinal(tokenBytes);
}
In my understanding the code above in C# translates to the following snippet:
public static string GetEncodedToken(string tokenBase64)
{
var ivBytes = Convert.FromBase64String("OSMqNE11fGUoLDg5Mmk1WQ==");
var keyBytes = Convert.FromBase64String("Nm4wMy5nOiM3JSpWfnwzOXFpNzRcfjB5MVNEKl8mWkw=");
var tokenBytes = Convert.FromBase64String(tokenBase64);
var tokenEncBytes = Crypt.Encrypt(tokenBytes, keyBytes, ivBytes);
var tokenEnc = HttpUtility.UrlEncode(Convert.ToBase64String(tokenEncBytes));
return tokenEnc;
}
private static byte[] Encrypt(byte[] tokenBytes, byte[] keyBytes, byte[] ivBytes)
{
using (var rijndaelManaged = new RijndaelManaged
{
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
KeySize = 128,
BlockSize = 128,
Key = keyBytes,
IV = ivBytes
})
{
return rijndaelManaged.CreateEncryptor()
.TransformFinalBlock(tokenBytes, 0, tokenBytes.Length);
}
}
However the API I'm trying to log in via C# client tells me the token is wrong :(