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
}
}
Related
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;
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.
I tried GCM encryption using OpenSSL library in VC++. I am getting the encrypted data, but the data is not matching with the output data as in JAVA and C#. I used same parameter credentials.
Here is the code I am using:
int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *aad,int aad_len, unsigned char *key, unsigned char *iv,unsigned char *ciphertext, unsigned char *tag)
{
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_len;
/* Create and initialise the context */
if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
/* Initialise the encryption operation. */
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
handleErrors();
/* Set IV length if default 12 bytes (96 bits) is not appropriate */
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
handleErrors();
/* Initialise key and IV */
if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
/* Provide any AAD data. This can be called zero or more times as
* required
*/
if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can be called multiple times if necessary
*/
if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;
/* Finalise the encryption. Normally ciphertext bytes may be written at
* this stage, but this does not occur in GCM mode
*/
if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;
/* Get the tag */
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
handleErrors();
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
The C# Code we are using
public byte[] GCM_Encryption(byte[] plainText)
{
byte[] authenticationTag;
byte[] Nonce;
byte[] AAD;
byte[] ciphertext;
string _nonce = Cur_DnT.Substring(Cur_DnT.Length - 12);
string _AAD = Cur_DnT.Substring(Cur_DnT.Length - 16);
Nonce = new byte[] { 0x2d, 0x32, 0x35, 0x54, 0x31, 0x35, 0x3a, 0x31, 0x35, 0x3a, 0x32, 0x39 };//Encoding.UTF8.GetBytes(_nonce);
AAD = new byte[] { 0x37, 0x2d, 0x30, 0x35, 0x2d, 0x32, 0x35, 0x54, 0x31, 0x35, 0x3a, 0x31, 0x35, 0x3a, 0x32, 0x39 };//Encoding.UTF8.GetBytes(_AAD);
using (AuthenticatedAesCng aes = new AuthenticatedAesCng())
{
aes.Key = new byte[] { 0xF9, 0x28, 0x01, 0x98, 0xBB, 0xC9, 0x43, 0xF8, 0x85, 0x6D, 0xA8, 0x36, 0x0F, 0x07, 0x37, 0x92, 0x7C, 0x49, 0x45, 0x29, 0x6C, 0xAF, 0xD5, 0x30, 0x1B, 0x92, 0x0E, 0x32, 0xAA, 0x20, 0xA2, 0x33 };//SKey;
aes.IV = Nonce;
aes.CngMode = CngChainingMode.Gcm;
// This data is required to verify the authentication tag, but will not go into the
// ciphertext
aes.AuthenticatedData = AAD;
// Do the encryption
using (MemoryStream ms = new MemoryStream())
using (IAuthenticatedCryptoTransform encryptor = aes.CreateAuthenticatedEncryptor())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
// Encrypt the plaintext
byte[] plaintext = plainText;
cs.Write(plaintext, 0, plaintext.Length);
// Complete the encryption operation, and generate the authentication tag
cs.FlushFinalBlock();
// Get the generated ciphertext and authentication tag
ciphertext = ms.ToArray();
authenticationTag = encryptor.GetTag();
}
}
string ddf = BitConverter.ToString(ciphertext);
return ciphertext.Concat(authenticationTag).ToArray();
}
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.
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.