Error "Input length must be multiple of 8 when decrypting with padded cipher"
- by Ross Peoples
I am trying to move a project from C# to Java for a learning exercise. I am still very new to Java, but I have a TripleDES class in C# that encrypts strings and returns a string value of the encrypted byte array. Here is my C# code:
using System;
using System.IO;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace tDocc.Classes
{
/// <summary>
/// Triple DES encryption class
/// </summary>
public static class TripleDES
{
private static byte[] key = { 110, 32, 73, 24, 125, 66, 75, 18, 79, 150, 211, 122, 213, 14, 156, 136, 171, 218, 119, 240, 81, 142, 23, 4 };
private static byte[] iv = { 25, 117, 68, 23, 99, 78, 231, 219 };
/// <summary>
/// Encrypt a string to an encrypted byte array
/// </summary>
/// <param name="plainText">Text to encrypt</param>
/// <returns>Encrypted byte array</returns>
public static byte[] Encrypt(string plainText)
{
UTF8Encoding utf8encoder = new UTF8Encoding();
byte[] inputInBytes = utf8encoder.GetBytes(plainText);
TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();
ICryptoTransform cryptoTransform = tdesProvider.CreateEncryptor(key, iv);
MemoryStream encryptedStream = new MemoryStream();
CryptoStream cryptStream = new CryptoStream(encryptedStream, cryptoTransform, CryptoStreamMode.Write);
cryptStream.Write(inputInBytes, 0, inputInBytes.Length);
cryptStream.FlushFinalBlock();
encryptedStream.Position = 0;
byte[] result = new byte[encryptedStream.Length];
encryptedStream.Read(result, 0, (int)encryptedStream.Length);
cryptStream.Close();
return result;
}
/// <summary>
/// Decrypt a byte array to a string
/// </summary>
/// <param name="inputInBytes">Encrypted byte array</param>
/// <returns>Decrypted string</returns>
public static string Decrypt(byte[] inputInBytes)
{
UTF8Encoding utf8encoder = new UTF8Encoding();
TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();
ICryptoTransform cryptoTransform = tdesProvider.CreateDecryptor(key, iv);
MemoryStream decryptedStream = new MemoryStream();
CryptoStream cryptStream = new CryptoStream(decryptedStream, cryptoTransform, CryptoStreamMode.Write);
cryptStream.Write(inputInBytes, 0, inputInBytes.Length);
cryptStream.FlushFinalBlock();
decryptedStream.Position = 0;
byte[] result = new byte[decryptedStream.Length];
decryptedStream.Read(result, 0, (int)decryptedStream.Length);
cryptStream.Close();
UTF8Encoding myutf = new UTF8Encoding();
return myutf.GetString(result);
}
/// <summary>
/// Decrypt an encrypted string
/// </summary>
/// <param name="text">Encrypted text</param>
/// <returns>Decrypted string</returns>
public static string DecryptText(string text)
{
if (text == "")
{
return text;
}
return Decrypt(Convert.FromBase64String(text));
}
/// <summary>
/// Encrypt a string
/// </summary>
/// <param name="text">Unencrypted text</param>
/// <returns>Encrypted string</returns>
public static string EncryptText(string text)
{
if (text == "")
{
return text;
}
return Convert.ToBase64String(Encrypt(text));
}
}
/// <summary>
/// Random number generator
/// </summary>
public static class RandomGenerator
{
/// <summary>
/// Generate random number
/// </summary>
/// <param name="length">Number of randomizations</param>
/// <returns>Random number</returns>
public static int GenerateNumber(int length)
{
byte[] randomSeq = new byte[length];
new RNGCryptoServiceProvider().GetBytes(randomSeq);
int code = Environment.TickCount;
foreach (byte b in randomSeq)
{
code += (int)b;
}
return code;
}
}
/// <summary>
/// Hash generator class
/// </summary>
public static class Hasher
{
/// <summary>
/// Hash type
/// </summary>
public enum eHashType
{
/// <summary>
/// MD5 hash. Quick but collisions are more likely. This should not be used for anything important
/// </summary>
MD5 = 0,
/// <summary>
/// SHA1 hash. Quick and secure. This is a popular method for hashing passwords
/// </summary>
SHA1 = 1,
/// <summary>
/// SHA256 hash. Slower than SHA1, but more secure. Used for encryption keys
/// </summary>
SHA256 = 2,
/// <summary>
/// SHA348 hash. Even slower than SHA256, but offers more security
/// </summary>
SHA348 = 3,
/// <summary>
/// SHA512 hash. Slowest but most secure. Probably overkill for most applications
/// </summary>
SHA512 = 4,
/// <summary>
/// Derrived from MD5, but only returns 12 digits
/// </summary>
Digit12 = 5
}
/// <summary>
/// Hashes text using a specific hashing method
/// </summary>
/// <param name="text">Input text</param>
/// <param name="hash">Hash method</param>
/// <returns>Hashed text</returns>
public static string GetHash(string text, eHashType hash)
{
if (text == "")
{
return text;
}
if (hash == eHashType.MD5)
{
MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
}
else if (hash == eHashType.SHA1)
{
SHA1Managed hasher = new SHA1Managed();
return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
}
else if (hash == eHashType.SHA256)
{
SHA256Managed hasher = new SHA256Managed();
return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
}
else if (hash == eHashType.SHA348)
{
SHA384Managed hasher = new SHA384Managed();
return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
}
else if (hash == eHashType.SHA512)
{
SHA512Managed hasher = new SHA512Managed();
return ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
}
else if (hash == eHashType.Digit12)
{
MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
string newHash = ByteToHex(hasher.ComputeHash(Encoding.ASCII.GetBytes(text)));
return newHash.Substring(0, 12);
}
return "";
}
/// <summary>
/// Generates a hash based on a file's contents. Used for detecting changes to a file and testing for duplicate files
/// </summary>
/// <param name="info">FileInfo object for the file to be hashed</param>
/// <param name="hash">Hash method</param>
/// <returns>Hash string representing the contents of the file</returns>
public static string GetHash(FileInfo info, eHashType hash)
{
FileStream hashStream = new FileStream(info.FullName, FileMode.Open, FileAccess.Read);
string hashString = "";
if (hash == eHashType.MD5)
{
MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();
hashString = ByteToHex(hasher.ComputeHash(hashStream));
}
else if (hash == eHashType.SHA1)
{
SHA1Managed hasher = new SHA1Managed();
hashString = ByteToHex(hasher.ComputeHash(hashStream));
}
else if (hash == eHashType.SHA256)
{
SHA256Managed hasher = new SHA256Managed();
hashString = ByteToHex(hasher.ComputeHash(hashStream));
}
else if (hash == eHashType.SHA348)
{
SHA384Managed hasher = new SHA384Managed();
hashString = ByteToHex(hasher.ComputeHash(hashStream));
}
else if (hash == eHashType.SHA512)
{
SHA512Managed hasher = new SHA512Managed();
hashString = ByteToHex(hasher.ComputeHash(hashStream));
}
hashStream.Close();
hashStream.Dispose();
hashStream = null;
return hashString;
}
/// <summary>
/// Converts a byte array to a hex string
/// </summary>
/// <param name="data">Byte array</param>
/// <returns>Hex string</returns>
public static string ByteToHex(byte[] data)
{
StringBuilder builder = new StringBuilder();
foreach (byte hashByte in data)
{
builder.Append(string.Format("{0:X1}", hashByte));
}
return builder.ToString();
}
/// <summary>
/// Converts a hex string to a byte array
/// </summary>
/// <param name="hexString">Hex string</param>
/// <returns>Byte array</returns>
public static byte[] HexToByte(string hexString)
{
byte[] returnBytes = new byte[hexString.Length / 2];
for (int i = 0; i <= returnBytes.Length - 1; i++)
{
returnBytes[i] = byte.Parse(hexString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
}
return returnBytes;
}
}
}
And her is what I've got for Java code so far, but I'm getting the error "Input length must be multiple of 8 when decrypting with padded cipher" when I run the test on this:
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.tdocc.utils.Base64;
public class TripleDES {
private static byte[] keyBytes = { 110, 32, 73, 24, 125, 66, 75, 18, 79, (byte)150, (byte)211, 122, (byte)213, 14, (byte)156, (byte)136, (byte)171, (byte)218, 119, (byte)240, 81, (byte)142, 23, 4 };
private static byte[] ivBytes = { 25, 117, 68, 23, 99, 78, (byte)231, (byte)219 };
public static String encryptText(String plainText) {
try {
if (plainText.isEmpty()) return plainText;
return Base64.decode(TripleDES.encrypt(plainText)).toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] encrypt(String plainText) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException {
try {
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(ivBytes);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = plainText.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return cipherText;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decryptText(String message) {
try {
if (message.isEmpty()) return message;
else return TripleDES.decrypt(message.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt(byte[] message) {
try {
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(ivBytes);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
final byte[] plainText = cipher.doFinal(message);
return plainText.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}