I'm using Triple DES for my encryption/decryption purpose but somehow it gives me above exception and I tried other approaches as well mentioned in the related answers, but I'm stuck. I'm new to cryptography and corresponding java libs.
private static byte[] Key = new byte[] {
0x42, 0x45, 0x49, 0x30, 0x12, 0x22, 0x35, 0x48, 0x33, 0x24, 0x28, 0x51,
0x48, 0x24, 0x30, 0x21, 0x44, 0x31, 0x14, 0x19, 0x45, 0x34, 0x47, 0x25 };
Cipher c;
public EncryptionHelper() throws Exception {
// byte[] key_hash = (Key).toString().getBytes("UTF-8");
// key_hash = Arrays.copyOf(key_hash, 32);
SecretKey key = new SecretKeySpec(Key, 0, Key.length, "DESede");
c = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, key);
}
public String Encrypt(String S) throws Exception {
byte[] base64EncryptedText = S.getBytes("UTF-8");
byte EncryptedText[] = c.doFinal(base64EncryptedText, 0, base64EncryptedText.length);
return new String(EncryptedText);
}
public String Decrypt(String S) throws Exception {
Cipher c2 = null;
// byte[] key_hash = (Key).toString().getBytes("UTF-8");
// key_hash = Arrays.copyOf(key_hash, 24);
SecretKey key = new SecretKeySpec(Key,0, Key.length, "DESede");
c2 = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c2.init(Cipher.DECRYPT_MODE, key);
byte[] base64EncryptedText = Base64.getEncoder().encode(S.getBytes());
byte[] textDecrypted = c2.doFinal(base64EncryptedText, 0, base64EncryptedText.length);
return new String(textDecrypted, "UTF-8");
}
EDIT:
Finally worked through the solution, I was just misplacing the components, specified the core logic as well.
public class EncryptionHelper {
private static byte[] Key = new byte[] {
0x42, 0x45, 0x49, 0x30, 0x12, 0x22, 0x35, 0x48, 0x33, 0x24, 0x28, 0x51,
0x48, 0x24, 0x30, 0x21, 0x44, 0x31, 0x14, 0x19, 0x45, 0x34, 0x47, 0x25 };
static Cipher c;
public EncryptionHelper() throws Exception {
// byte[] key_hash = (Key).toString().getBytes("UTF-8");
// key_hash = Arrays.copyOf(key_hash, 32);
SecretKey key = new SecretKeySpec(Key, 0, Key.length, "DESede");
c = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, key);
}
public static String Encrypt(String S) throws Exception {
byte[] base64EncryptedText = S.getBytes("UTF-8");
byte EncryptedText[] = c.doFinal(base64EncryptedText, 0, base64EncryptedText.length);
return new String(Base64.getEncoder().encode(EncryptedText));
}
// LOGIC:
// for encryption: string -> utf-8 byte array,
// encrypt and return a base 64 encoded string
// for decryption: String -> base64 -> decode base 64 array,
// decrypt and return utf-8 string
public static String Decrypt(String S) throws Exception {
Cipher c2 = null;
// byte[] key_hash = (Key).toString().getBytes("UTF-8");
// key_hash = Arrays.copyOf(key_hash, 24);
SecretKey key = new SecretKeySpec(Key, "DESede");
c2 = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c2.init(Cipher.DECRYPT_MODE, key);
byte[] base64EncryptedText = Base64.getDecoder().decode(S.getBytes());
byte[] textDecrypted = c2.doFinal(base64EncryptedText, 0, base64EncryptedText.length);
return new String(textDecrypted, "UTF-8");
}
Despite the names on your variables you have failed to base64-encode the result of encryption in your Encrypt method. Therefore, when you convert it to String you get garbage, and when you base64 decode that garbage in your Decrypt method you get garbage2.
You are base64-decoding and then decrypting, but you are not encrypting and then base64-encoding.
Related
I have the following Decryption code from a C# App written by a colleague:
private static string UrlEncryptionKey = "x0iiR!RG#753!"; // not real values here
private static byte[] salt = new byte[] { 0x41, 0x71, 0x61, 0x6e, 0x21, 0x4d, 0x65, 0x64, 0x76, 0x64, 0x63, 0x62, 0x72 }
public static string Decrypt(String cipherText)
{
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(UrlEncryptionKey, salt);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
Piecing together the equivalent cryptography, I currently have the Java equivalent as:
public static String Decrypt(String cipherText) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(UrlEncryptionKey.toCharArray(), salt, 1000, 384);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKey secretKeyAES = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeyAES, ivSpec);
byte[] decoded = Base64.getDecoder().decode(cipherText.getBytes("UTF-8"));
byte[] original = cipher.doFinal(decoded);
String originalString = new String(original, "UTF-8");
return originalString;
}
But it's throwing an exception:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 48 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:93)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:346)
at javax.crypto.Cipher.implInit(Cipher.java:809)
at javax.crypto.Cipher.chooseProvider(Cipher.java:867)
at javax.crypto.Cipher.init(Cipher.java:1399)
at javax.crypto.Cipher.init(Cipher.java:1330)
at scratch.AESUtil.Decrypt(AESUtil.java:55)
at scratch.AESUtil.main(AESUtil.java:93)
I don't understand why the key length is invalid
Today following a guide I tried to encrypt and after decrypt a string.
Unluckily, I've gotten an InvalidKeyException, I googled up that and found out that for encrypt a string that have more than 128 bits (16 bytes -> 16 characters)
I need the JCE, so I got into the oracle website, downloaded it and putted it on my java build path, under the folder /lib/security.
Still no luck, i still keep getting this error, with this code:
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class EncryptTests {
public static void main(String[] args) throws Exception {
byte[] input = "www.java2s.com".getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
System.out.println(new String(input));
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
System.out.println(new String(cipherText));
System.out.println(ctLength);
// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
System.out.println(new String(plainText));
System.out.println(ptLength);
}
}
This is the console output:
www.java2s.com Exception in thread "main" java.security.InvalidKeyException: Illegal key size or default
parameters at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1026)
at javax.crypto.Cipher.implInit(Cipher.java:801) at
javax.crypto.Cipher.chooseProvider(Cipher.java:864) at
javax.crypto.Cipher.init(Cipher.java:1249) at
javax.crypto.Cipher.init(Cipher.java:1186) at
EncryptTests.main(EncryptTests.java:23)
Any fix? :)
I am trying to encode byte[] to String, then decode this String to byte[], my code is:
byte[] aaa = new byte[1];
aaa[0] = (byte) 153;
String encoder = Base64.encodeBase64String(aaa);
System.out.println("encoder <<>> " + encoder);
// Decode the Base64 String.
byte[] bytes = Base64.decodeBase64(encoder);
String decoder = new String(bytes, "UTF08");
System.out.println("decoder <<>> " + decoder );
Result is:
encoder <<>> mQ==
decoder <<>> ?
The result are not the same one. Why does this happen?
Try this:
byte[] aaa = new byte[1];
aaa[0] = (byte) 153;
System.out.println("original bytes <<>> " + Arrays.toString(aaa));
// Encode the bytes to Base64
String encoder = Base64.encodeBase64String(aaa);
System.out.println("encoder <<>> " + encoder);
// Decode the Base64 String to bytes
byte[] bytes = Base64.decodeBase64(encoder);
System.out.println("decoded bytes <<>> " + Arrays.toString(bytes));
simple static utility methods to encode and decode the given string.
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
...
private static byte[] key = {
0x74, 0x68, 0x69, 0x73, 0x49, 0x73, 0x41, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79
}; // "ThisIsASecretKey";
public static String encrypt(String stringToEncrypt) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
final String encryptedString = Base64.encodeBase64String(cipher.doFinal(stringToEncrypt.getBytes()));
return encryptedString;
}
public static String decrypt(String stringToDecrypt) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(stringToDecrypt)));
return decryptedString;
}
I wrote the following code for text file encryption and decryption.
The encryption process works fine but I cannot decrypt the created file. For some reason, even though I use the same password, with the same length and same paddings, the decryption output is an encoded file different from the original one...
In other attempts I failed while running encryption and decryption in separated runs. But now they are both running one after another (even though they are using different key-spec, is that the reason? if so - how can I bypass it?)
public static void main(String[] args) throws Exception {
encrypt("samplePassword", new FileInputStream("file.txt"), new FileOutputStream("enc-file.txt"));
decrypt("samplePassword", new FileInputStream("enc-file.txt"), new FileOutputStream("file-from-enc.txt"));
}
public static void encrypt(String password, InputStream is, OutputStream os) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(password(password), "TripleDES");
Cipher cipher = Cipher.getInstance("TripleDES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] buf = new byte[8096];
os = new CipherOutputStream(os, cipher);
int numRead = 0;
while ((numRead = is.read(buf)) >= 0) {
os.write(buf, 0, numRead);
}
os.close();
}
public static void decrypt(String password, InputStream is, OutputStream os) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(password(password), "TripleDES");
Cipher cipher = Cipher.getInstance("TripleDES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] buf = new byte[8096];
CipherInputStream cis = new CipherInputStream(is, cipher);
int numRead = 0;
while ((numRead = cis.read(buf)) >= 0) {
os.write(buf, 0, numRead);
}
cis.close();
is.close();
os.close();
}
private static byte[] password(String password) {
byte[] baseBytes = { (byte) 0x38, (byte) 0x5C, (byte) 0x8, (byte) 0x4C, (byte) 0x75, (byte) 0x77, (byte) 0x5B, (byte) 0x43,
(byte) 0x1C, (byte) 0x1B, (byte) 0x38, (byte) 0x6A, (byte) 0x5, (byte) 0x0E, (byte) 0x47, (byte) 0x3F, (byte) 0x31,
(byte) 0xF, (byte) 0xC, (byte) 0x76, (byte) 0x53, (byte) 0x67, (byte) 0x32, (byte) 0x42 };
byte[] bytes = password.getBytes();
int i = bytes.length;
bytes = Arrays.copyOf(bytes, 24);
if(i < 24){
for(;i<24; i++){
bytes[i] = baseBytes[i];
}
}
return bytes;
}
can any one give me a hint?
On a first look,
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
should probably be
cipher.init(Cipher.DECRYPT_MODE, keySpec);
in the decrypt method.
Have a look at the documentation.
I have the following code.
byte[] input = etInput.getText().toString().getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
String strLength = new String(cipherText,"US-ASCII");
byte[] byteCiphterText = strLength.getBytes("US-ASCII");
Log.e("Decrypt", Integer.toString(byteCiphterText.length));
etOutput.setText(new String(cipherText,"US-ASCII"));
cipherText = etOutput.getText().toString().getBytes("US-ASCII");
Log.e("Decrypt", Integer.toString(cipherText.length));
ptLength += cipher.doFinal(plainText, ptLength);
Log.e("Decrypt", new String(plainText));
Log.e("Decrypt", Integer.toString(ptLength));
It works perfectly.
But once I convert it to the class. It always hit the error in this line.
ptLength += cipher.doFinal(plainText, ptLength);
Error:Pad block corrupted
I have checked and both code are exactly the same. Even the value passed in conversion string to byte all is no different from the code above. Any idea what's wrong with the code?
public String Encrypt(String strPlainText) throws Exception, NoSuchProviderException,
NoSuchPaddingException {
byte[] input = strPlainText.getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return new String(cipherText, "US-ASCII");
}
public String Decrypt(String strCipherText) throws Exception,
NoSuchProviderException, NoSuchPaddingException {
byte[] cipherText = strCipherText.getBytes("US-ASCII");
int ctLength = cipherText.length;
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
return new String(plainText);
}
As Yann Ramin said, using String is a failure for cipher in/output. This is binary data that
can contain 0x00
can contain values that are not defined or mapped to strange places in the encoding used
Use plain byte[] as in your first example or go for hex encoding or base64 encoding the byte[].
// this is a quick example - dont use sun.misc inproduction
// - go for some open source implementation
String encryptedString = new sun.misc.BASE64Encoder.encodeBuffer(encryptedBytes);
This string can be safely transported and mapped back to bytes.
EDIT
Perhaps safest way to deal with the length issue is to always use streaming implementation (IMHO):
Example
static public byte[] decrypt(Cipher cipher, SecretKey key, byte[]... bytes)
throws GeneralSecurityException, IOException {
cipher.init(Cipher.DECRYPT_MODE, key);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (int i = 0; i < bytes.length; i++) {
bos.write(cipher.update(bytes[i]));
}
bos.write(cipher.doFinal());
return bos.toByteArray();
}
You have specified PKCS7 Padding. Is your padding preserved when stored in your String object? Is your string object a 1:1 match with the bytes output by the cipher? In general, String is inappropriate for passing binary data, such as a cipher output.
In your case cipher uses padding, that means in other words input data will be padded/rounded into blocks with some predefined size (which depends on padding algorithm). Let's say you have supplied 500 bytes to encrypt, padding block size is 16 bytes, so encrypted data will have size of 512 bytes (32 blocks) - 12 bytes will be padded.
In your code you're expecting encrypted array of the same size as input array, which causes exception. You need to recalculate output array size keeping in mind padding.