TripleDES encryption working fine in java but not in c# - java

i am facing weak key error while doing tripleDES encryption.Code working fine in java but giving error in C# .net.
i have java code in which TripleDES ecryption is working fine i need to convert my java code in c#.i am facing Weak key error during conversion.Below both java and c# code given.
1) Java Code
public class TripleDES {
private DESedeKeySpec desKeySpec;
public TripleDES(String key) {
try {
byte[] keyBytes = { (byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02};
this.desKeySpec = new DESedeKeySpec(keyBytes);
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] encrypt(byte[] origData) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(this.desKeySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(origData);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public byte[] decrypt(byte[] crypted) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(this.desKeySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding"); //DESede/CBC/PKCS5Padding
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(crypted);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception {
TripleDES des = new TripleDES("");
byte[] data = { (byte)0x04, (byte)0x12, (byte)0x05, (byte)0xFF, (byte)0xFB, (byte)0xA6, (byte)0x66, (byte)0xCF};
//byte[] data = { (byte)0x04, (byte)0x12, (byte)0x15, (byte)0xAF, (byte)0xFD, (byte)0xD8, (byte)0x88, (byte)0xBB};
//-----------------Edited-----------------
String text = new BigInteger(1, data).toString(16);
System.out.println("Before encryption = " +text);
byte[] crypted = des.encrypt(data);
String text1 = new BigInteger(1, crypted).toString(16);
System.out.println("Encrypted = " +text1);
byte[] decrypted = des.decrypt(crypted);
String text2 = new BigInteger(1, decrypted).toString(16);
System.out.println("Decrypted = " +text2);
}
}
2) C# Code
static void Main(string[] args)
{
String Data = EncryptDES("041205FFFBA666CF");
}
public static string EncryptDES(string InputText)
{
byte[] key = new byte[] { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
byte[] clearData = System.Text.Encoding.UTF8.GetBytes(InputText);
MemoryStream ms = new MemoryStream();
TripleDES alg = TripleDES.Create();
alg.Key = key;
alg.Mode = CipherMode.ECB;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(clearData, 0, clearData.Length);
cs.FlushFinalBlock();
byte[] CipherBytes = ms.ToArray();
ms.Close();
cs.Close();
string EncryptedData = Convert.ToBase64String(CipherBytes);
return EncryptedData;
}
Key: 02020202020202020202020202020202
Data : 041205FFFBA666CF
Result : A334C92CEC163D9F
Can anyone please write code in c# which produce result same as java.

Before I start, I want to say that I do not recommend or endorse you to follow this way for forcing TripleDES to use a key it thinks is weak, however, given that you are using it for Decryption only, here is a way to do it using Reflection.
Firstly, you must 'force' the TripeDES class to take the weak key you want to use. To do this, we use Reflection to bypass the check for a weak key that is performed when you attempt to set the key (alg.Key = key) and set the member variable directly:
//alg.Key = key; - THIS IS REPLACED BY THE BELOW
FieldInfo keyField = alg.GetType().GetField("KeyValue", BindingFlags.NonPublic | BindingFlags.Instance);
keyField.SetValue(alg, key);
The key value of TripleDES will now be set to your weak key (alg.Key);
Next you have a minor mistake, in that you have forgotten to turn off padding:
alg.Mode = CipherMode.ECB;
alg.Padding = PaddingMode.None; // Add this, as the default padding is PKCS7
Finally, there will be a further check for a weak key when creating the Decryptor, so we must use Reflection again to bypass the check and create the ICryptoTransform:
// Comment out the below line and use the code below
// CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
ICryptoTransform Decryptor;
MethodInfo createMethod = alg.GetType().GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
Decryptor = createMethod.Invoke(alg, new object[] { alg.Key, alg.Mode, alg.IV, alg.FeedbackSize, 1 }) as ICryptoTransform;
CryptoStream cs = new CryptoStream(ms, Decryptor, CryptoStreamMode.Write);
The code will now run and accept weak keys, and perform the decryption you are looking for.
However, it does not look to me that the output is what you are expecting:
?Data
"4aU3DcHkiCTEywpiewWIow=="
At least now though you are able to use whichever key you want with TripleDES.

A 3DES key is a 24-byte value which is broken down two three 8-byte values: key0, key1, key2.
Because 3DES is DES_Encrypt(key2, DES_Decrypt(key1, DES_Encrypt(key0, data))) any time key1 is equal to key2 or key0 the algorithm is reduced to DES. This is what .NET's TripleDES is warning you about.
The proper test here should account for clearing (or setting, or fixing) the parity bits in each byte, but a hand-waved version is:
SymmetricAlgorithm alg;
IEnumerable<byte> key0 = key.Take(8);
IEnumerable<byte> key1 = key.Skip(8).Take(8);
IEnumerable<byte> key2 = key.Skip(16);
if (key0.SequenceEquals(key1))
{
alg = DES.Create();
alg.Key = key2.ToArray();
}
else if (key1.SequenceEquals(key2))
{
alg = DES.Create();
alg.Key = key0.ToArray();
}
else
{
alg = TripleDES.Create();
alg.Key = key;
}
To replace your two liner:
TripleDES alg = TripleDES.Create();
alg.Key = key;

Related

Flutter encrypt - AES result is not the same in java

I'm trying to decode a Uint8list in kotlin previously encrypted in flutter, but the decryption is not correct, i'm probably doing something wrong but I can't find what, could you help me ?
Context :
the key and the IV are constants for the moment and are 128bits, so pointyCatle should use AES128 and I should be able to decrypt it with the same key and IV
Example
with mainkey = 11112222 33344444
Uint8List mainKey = Uint8List.fromList([0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04]);
var SessionCipher=new AESEncryptor();
...
var MainCipher=new AESEncryptor();
MainCipher.setIV(SessionCipher.getIv());
MainCipher.setKey(mainKey);
var encryptedSessionKey = MainCipher.encrypt(SessionCipher.getKey());
and iv = 00000000 00000000
data is a secureRandom generated Key
input :{ad,11,b4,6b,61,69,5f,e9,12,e7,05,35,b8,95,3a,09}
encrypted input : {0x2C, 0xC3, 0x81, 0xE9, 0x68, 0xD2, 0x83, 0x30, 0xF7, 0xA9, 0x5C, 0x4F, 0x67, 0x06, 0x3F, 0x5B}
encrypt decryption : {ad,11,b4,6b,61,69,5f,e9,12,e7,05,35,b8,95,3a,09} GOOD
javax.crypto decryption : {0x30, 0xAB, 0xEF, 0x34, 0x95, 0x75, 0x9D, 0x96, 0xE9,0x23, 0x7E, 0xE6, 0x64, 0x77, 0xF4, 0xD0} BAD
Both use the same key and IV, both are in CBC/no-padding
Could it come from the libraries that use a different algorithm ?
Am I doing something wrong ?
Temporary Dart encryption class
import 'package:ble_connect/utils/ble/FrameBuilder.dart';
import 'package:encrypt/encrypt.dart';
IV newIV() {
// return IV.fromLength(16);
return(IV.fromBase16("00000000000000000000000000000000"));
}
class AESEncryptor {
var iv;
var encrypter;
var key;
AESEncryptor() {
this.key = Key.fromSecureRandom(16);
this.iv = newIV();
this.encrypter = Encrypter(AES(key, mode: AESMode.cbc, padding: null));
}
void setIV(iv) {
this.iv = iv;
}
IV getIv() {
return (this.iv);
}
void setKey(key) {
this.key = key;
}
Key getKey() {
return (this.key);
}
//encrypt method meant to encrypt a 128bits List or 128bits Key
//input : data (Key or Uint8List of length 16)
dynamic encrypt(data) {
print("iv is : ${this.iv.base16}");
if (data.runtimeType == Key) {
data = toInt(combineByTwo(data.base16.split(''))); //convert a Key to a list of bytes (UGLY)
//additional info : data (Key) is converted to hex String by .base16
//then converted to list of char by .split('')
//then the elements of the list are grouped by two (in order to get bytes at the end ex : [f,f]-->[ff]
//finaly parsed to become a list of in values between 0-255
}
return encrypter.encryptBytes(data, iv: this.iv);
}
}
Thank you for your help !
EDIT :
Encrypt library : https://github.com/leocavalcante/encrypt
Kotlin code
private object AES128{
private fun cipher(opmode: Int, secretKey: ByteArray, iv: IvParameterSpec):Cipher{
val c = Cipher.getInstance("AES/CBC/NoPadding")
val sk = SecretKeySpec(secretKey, "AES")
c.init(opmode, sk, iv)
return c
}
fun encrypt(byteArray: ByteArray, secretKey: ByteArray, iv: IvParameterSpec): ByteArray? {
return cipher(Cipher.ENCRYPT_MODE, secretKey, iv).doFinal(padFrame(byteArray))
}
fun decrypt(byteArray: ByteArray, frameDataLength: Int, secretKey: ByteArray, iv: IvParameterSpec):ByteArray?{
return unpadFrame(cipher(Cipher.DECRYPT_MODE, secretKey, iv).doFinal(byteArray), frameDataLength)
}
private fun padFrame(frame: ByteArray): ByteArray {
// MANUAL PADDING
println(frame.size)
val remain = (frame.size % 16).also {
println(it)
}
if (remain != 0) {
println("padding")
val paddedValue = ByteArray(frame.size + 16 - remain)
System.arraycopy(frame, 0, paddedValue, 0, frame.size)
for (i in 0 until 16 - remain) {
paddedValue[frame.size + i] = 0x0F
}
return paddedValue
}
println("not padding")
return frame
}
private fun unpadFrame(frame: ByteArray, frameDataLength: Int): ByteArray {
if(frameDataLength > 5) {
val unpaddedValue = ByteArray(frameDataLength)
System.arraycopy(frame, 0, unpaddedValue, 0, frameDataLength)
return unpaddedValue
}
return frame
}
}

AES encryption for hex key, IV and data in Java

I have to encrypt the hex string with key and IV also as Hex data. Please find the code i have used below. The problem is the string which i need to encrypt is in plaintext. I need to convert the plaintext into Hex and i do not know how to do that. Can any one help me with it ??
I have been confirming using the webtool,
http://aes.online-domain-tools.com/
static byte[] IV = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
static String plaintext = "00010101010102020202020303030303";
static byte[] encryptionKey = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 };
public static void main(String[] args) {
try {
System.out.println("==Java==");
System.out.println("plain: " + plaintext);
byte[] cipher = encrypt(plaintext, encryptionKey);
System.out.print("cipher: ");
for (int i = 0; i < cipher.length; i++) {
// System.out.print(new Integer(cipher[i]) + " ");
byte b = cipher[i];
System.out.print(String.format("%02x", b & 0xFF) + " ");
}
String decrypted = decrypt(cipher, encryptionKey);
System.out.println("decrypt: " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, byte[] encryptionKey)
throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(encryptionKey, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV));
return cipher.doFinal(plainText.getBytes("UTF-8"));
}
public static String decrypt(byte[] cipherText, byte[] encryptionKey)
throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(encryptionKey, "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(IV));
return new String(cipher.doFinal(cipherText), "UTF-8");
}
for the above data i should get,
b6 06 da b9 cd dc 2d 89 4b 49 0a ab 4e e7 dc 58
but instead am getting,
e3 62 34 3f ad 8b 89 37 57 81 91 31 ee 79 49 52 26 bf 40 cb d0 ce 36 bd 8a 04 6b af 34 d9 f3 d7
Thanks in Advance.
Probably the best thing to learn from this is that modern modes of operation such as CBC (as well as the underlying block cipher) operate on binary data, usually provided as a set of bytes.
In that sense it is probably best to stick to binary data as much as possible. Don't use more conversions than strictly necessary and only use hexadecimal encoding if it is meant for human consumption. Base 64 encoding can be used if you need to transfer binary using a text based representation.
Encoding/decoding should preferably be performed using a library such as Apache Commons Codec, Guava, Bouncy Castle etc., Java 8 still doesn't hex encode/decode byte arrays (sigh).
I've created a small code sample that outputs hex formatted as if it was a Java byte array literal. I'll leave it to you to create different versions.
static byte[] IV = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
static byte[] plaintext = { 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03 };
static byte[] encryptionKey = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 };
public static void main(String[] args) {
try {
System.out.println("==Java==");
System.out.println("plain: " + toJavaHex(plaintext));
byte[] cipher = encrypt(plaintext, encryptionKey);
System.out.println("cipher: " + toJavaHex(cipher));
String decrypted = toJavaHex(decrypt(cipher, encryptionKey));
System.out.println("decrypt: " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String toJavaHex(byte[] data) {
StringBuilder sb = new StringBuilder(13 * data.length);
for (int i = 0; i < data.length; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(String.format("(byte) 0x%02x", data[i]));
}
return sb.toString();
}
public static byte[] encrypt(byte[] plainText, byte[] encryptionKey)
throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(encryptionKey, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV));
return cipher.doFinal(plainText);
}
public static byte[] decrypt(byte[] cipherText, byte[] encryptionKey)
throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(encryptionKey, "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(IV));
return cipher.doFinal(cipherText);
}

AES encryption/decryption java bouncy castle explanation?

Can someone please explain what this program is doing pointing out some of the major points? I'm looking at the code and I'm completely lost. I just need explanation on the encryption/decryption phases. I think it generates an AES 192 key at one point but I'm not 100% sure. I'm not sure what the byte/ivBytes are used for either.
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
public class RandomKey
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
byte[] ivBytes = new byte[] {
0x00, 0x00, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
//initializing a new initialization vector
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
//what does this actually do?
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
//what does this do?
KeyGenerator generator = KeyGenerator.getInstance("AES","BC");
//I assume this generates a key size of 192 bits
generator.init(192);
//does this generate a random key?
Key encryptKey = generator.generateKey();
System.out.println("input: " +toHex(input));
//encryption phase
cipher.init(Cipher.ENCRYPT_MODE, encryptKey, ivSpec);
//what is this doing?
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
//what is this doing?
int ctLength = cipher.update(input, 0, input.length, cipherText,0);
//getting the cipher text length i assume?
ctLength += cipher.doFinal (cipherText, ctLength );
System.out.println ("Cipher: " +toHex(cipherText) + " bytes: " + ctLength);
//decryption phase
cipher.init(Cipher.DECRYPT_MODE, encryptKey, ivSpec);
//storing the ciphertext in plaintext i'm assuming?
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
//getting plaintextLength i think?
ptLength= cipher.doFinal (plainText, ptLength);
System.out.println("plain: " + toHex(plainText, ptLength));
}
private static String digits = "0123456789abcdef";
public static String toHex(byte[] data, int length)
{
StringBuffer buf = new StringBuffer();
for (int i=0; i!= length; i++)
{
int v = data[i] & 0xff;
buf.append(digits.charAt(v >>4));
buf.append(digits.charAt(v & 0xf));
}
return buf.toString();
}
public static String toHex(byte[] data)
{
return toHex(data, data.length);
}
}
//what does this actually do?
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
This uses the Bouncy Castle ("BC") provider to get an instance of the AES cypher, set up in Counter mode (CTR) with no padding (NoPadding). See Wikipedia for Block Cypher modes and Padding. The Javadoc will also help you.
//what does this do?
KeyGenerator generator = KeyGenerator.getInstance("AES","BC");
Again this uses the Bouncy Castle provider to set up a key generator for AES. You can read the Javadoc to learn more.
//what is this doing?
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
It sets up an array of bytes large enough to hold the encrypted output.
//what is this doing?
int ctLength = cipher.update(input, 0, input.length, cipherText,0);
It is actually doing the encyphering. Check the Javadoc for the update() method for a good explanation.
//getting the cipher text length i assume?
ctLength += cipher.doFinal (cipherText, ctLength );
No. Look at that += It is updating the cyphertext length. Again read the Javadoc for the differences between the update() and doFinal() methods.
Did you try looking here?
http://docs.oracle.com/javase/6/docs/api/javax/crypto/Cipher.html
http://docs.oracle.com/javase/6/docs/api/javax/crypto/KeyGenerator.html
The code you have is a pretty straight forward code example of how to encrypt and decrypt byte data in Java.
Once you have read through the class documentation you can better articulate your questions about the behavior of this code.

password based en/decrypting in java: fails to decrypt the file even though the password is identical

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.

Decryption Error: Pad block corrupted

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.

Categories