i try to decrypt a encrypted text and use this code but get this error to me:
Exception in thread "main" java.security.InvalidKeyException: Illegal key size
and decryption code is:
String key = "ffce885876a617e7";
String vector = "9ee153a3df56965e7baf13a7fa1075cc";
IvParameterSpec ivSpec = new IvParameterSpec(key.getBytes());
SecretKeySpec keySpec = new SecretKeySpec(vector.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); //error occured in this line
.getBytes() will not automagically convert a "hex string" to the matching bytes.
Instead, try this utility method:
private static byte[] hexStringToBytes(final String input)
{
final int len = input.length();
if (len % 2 != 0)
throw new IllegalArgumentException();
final byte[] ret = new byte[len / 2];
int offset = 0;
for (int i = 0; i < ret.length; i++) {
ret[i] = (byte) Integer.parseInt(input.substring(offset, offset+2), 16);
offset += 2;
}
return ret;
}
Then in your code use hexStringToBytes(key) etc.
I uses this one to decrypt AES. But in Android env instead of Java vanila.
public String decrypt(String enc) throws Exception {
try {
String key = "ffce885876a617e7";
String vector = "9ee153a3df56965e7baf13a7fa1075cc";
IvParameterSpec ivSpec = new IvParameterSpec(key.getBytes());
SecretKeySpec keySpec = new SecretKeySpec(vector.getBytes(),
"AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return new String(cipher.doFinal(hexToByte(enc)), "UTF-8");
} catch (Exception localException) {
throw new Exception("[decrypt] " + localException.getMessage());
}
}
private static byte[] hexToByte(String enc) {
int j = enc.length() / 2;
byte[] arrayOfByte = new byte[j];
if (j >= 2) {
for (int i = 0; i < j; i++) {
arrayOfByte[i] = ((byte) Integer.parseInt(
enc.substring(i * 2, 2 + i * 2), 16));
}
}
return arrayOfByte;
}
Related
In Java code, i have source work well, this is use for encrypt:
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class HelloWorld{
private static final String hexKey = "B8EE12E123C0E300A202074A153CC0D27D739357480FFFFFFFFFFFFFFFFFFFEF";
public static void main(String []args){
System.out.println("Encryt ==== ");
String textToEncrypt = "From=ABC&Key=FootID1234&Value=ResultValue2324";
String encryptedText = encrypt(textToEncrypt);
System.out.println(encryptedText);
System.out.println("Decrypt ==== ");
String decryptedText = decrypt(encryptedText);
System.out.println(decryptedText);
}
public static String encrypt (String plainText) {
String encryptedText = null;
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKey = new SecretKeySpec(hexToBytes(hexKey), "AES");
IvParameterSpec ivparameterspec = new IvParameterSpec(hexKey.getBytes(), 0, 16);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivparameterspec);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF8"));
encryptedText = bytesToHex(cipherText);
} catch (Exception E) {
System.out.println("Encrypt Exception : " + E.getMessage());
}
return encryptedText;
}
public static String decrypt(String encryptedText) {
String decryptedText = null;
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKey = new SecretKeySpec(hexToBytes(hexKey), "AES");
IvParameterSpec ivparameterspec = new IvParameterSpec(hexKey.getBytes("UTF8"), 0, 16);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivparameterspec);
byte[] cipherText = hexToBytes(encryptedText);
byte[] dcrbyte = cipher.doFinal(cipherText);
decryptedText = new String(dcrbyte, "UTF-8");
} catch (Exception E) {
System.out.println("Encrypt Exception : " + E.getMessage());
}
return decryptedText;
}
private static byte[] hexToBytes(String hexStr) {
byte[] val = new byte[hexStr.length() / 2];
for (int i = 0; i < val.length; i++) {
int idx = i * 2;
int j = Integer.parseInt(hexStr.substring(idx, idx + 2), 16);
val[i] = (byte) j;
}
return val;
}
private static String bytesToHex(byte[] hashInBytes) {
char[] hexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[hashInBytes.length * 2];
for (int i = 0; i < hashInBytes.length; i++) {
int v = hashInBytes[i] & 0xFF;
hexChars[i * 2] = hexArray[v >>> 4];
hexChars[i * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}
And in c#, i try to write decryptAes() function like this:
public static class Encryption
{
// use these parameters to test decryptAes()
//string key = "B8EE12E123C0E300A202074A153CC0D27D739357480FFFFFFFFFFFFFFFFFFFEF";
//string textToDecrypt = "756AD4D80E2CF1E289D55A23E092F012E8D5F372A343A419BC87F77B6335F04EFB41C3B56F5CDA167F90F67CD672A186";
public static string decryptAes(string key, string textToDecrypt)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
// Assumed Mode and padding values.
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.PKCS7;
// AssumedKeySize and BlockSize values.
rijndaelCipher.KeySize = 0x80; //128
rijndaelCipher.BlockSize = 0x80;
// Convert Hex keys to byte Array.
byte[] encryptedData = HexToBytes(textToDecrypt);
//byte[] pwdBytes = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(key);
byte[] pwdBytes = HexToBytes(key);
byte[] keyBytes = new byte[0x10]; //16
int len = pwdBytes.Length;
if (len > keyBytes.Length)
{
len = keyBytes.Length;
}
Array.Copy(pwdBytes, keyBytes, len);
rijndaelCipher.Key = keyBytes;
rijndaelCipher.IV = keyBytes;
// Decrypt data
byte[] plainText = rijndaelCipher.CreateDecryptor()
.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
return Encoding.UTF8.GetString(plainText);
}
public static byte[] HexToBytes(string str)
{
if (str.Length == 0 || str.Length % 2 != 0)
return new byte[0];
byte[] buffer = new byte[str.Length / 2];
char c;
for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
{
// Convert first half of byte
c = str[sx];
buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) << 4);
// Convert second half of byte
c = str[++sx];
buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0'));
}
return buffer;
}
public static string ByteToHex(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString().ToUpper();
}
}
But the c# decryptAes() function does not work as i expect. An error
System.Security.Cryptography.CryptographicException: 'Padding is invalid and cannot be removed.'
has occured at line rijndaelCipher.Padding = PaddingMode.PKCS7;
When i change to rijndaelCipher.Padding = PaddingMode.None, it does not work as i expect, the c# result is not the same as the result of java.
Please help, any advice would be appreciated!
Thanks!
You need to explicitly set the padding for both encryption and decryption. Unless you have a reason to do otherwise, use PKCS#7 padding.
rijndaelCipher.Padding=PaddingMode.none;
I first generated IV using this method:
public static String generateRandomIV() {
SecureRandom ranGen = new SecureRandom();
byte[] aesKey = new byte[16];
ranGen.nextBytes(aesKey);
StringBuffer result = new StringBuffer();
for (byte b : aesKey) {
result.append(String.format("%02x", b));
}
if (16 > result.toString().length()) {
return result.toString();
} else {
return result.toString().substring(0, 16);
}
}
And then tried to decrypt a String which was encrypted using IV generated by above method:
private String decrypt(String _inputText, String _encryptionKey,
String _initVector) throws Exception {
String _out = "";
int len = _encryptionKey.getBytes("UTF-8").length;
if (_encryptionKey.getBytes("UTF-8").length >= _key.length) {
len = _key.length;
int ivlen = _initVector.getBytes("UTF-8").length;
if (_initVector.getBytes("UTF-8").length > _iv.length)
ivlen = _iv.length;
System.arraycopy(_encryptionKey.getBytes("UTF-8"), 0, _key, 0, len);
System.arraycopy(_initVector.getBytes("UTF-8"), 0, _iv, 0, ivlen);
SecretKeySpec keySpec = new SecretKeySpec(_key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(_iv);
//Decryption starts
_cx.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decodeValue = Base64.decode(_inputText.getBytes(), Base64.DEFAULT);
Following Exception is coming at last line of above code snippet:
java.lang.RuntimeException: java.security.InvalidAlgorithmParameterException: IV must be specified in CBC mode
I don't see any use of your IV. Try the following cipher initialization:
_cx.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
I'm attempting to convert this C# encryption algorithm to Java; however, I keep retrieving slightly different encrypted results (haven't tried decryption yet). It may also be important to point out that I'm not able to change the C# code.
However when I call the encrypt function in C# on the string "test" it will return nmj8MjjO52y928Syqf0J+g== However in Java it'll return C6xyQjJCqVo=
The C#
private static String key = "012345678901234567890123";
public static string encrypt(String stringToEncrypt)
{
TripleDES des = CreateDES(key);
ICryptoTransform ct = des.CreateEncryptor();
byte[] input = Encoding.Unicode.GetBytes(stringToEncrypt);
byte[] output = ct.TransformFinalBlock(input, 0, input.Length);
//return output;
return Convert.ToBase64String(output);
}
public static String decrypt(string encryptedString)
{
byte[] input = Convert.FromBase64String(encryptedString);
TripleDES des = CreateDES(key);
ICryptoTransform ct = des.CreateDecryptor();
byte[] output = ct.TransformFinalBlock(input, 0, input.Length);
return Encoding.Unicode.GetString(output);
}
public static TripleDES CreateDES(string key)
{
MD5 md5 = new MD5CryptoServiceProvider();
TripleDES des = new TripleDESCryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
des.IV = new byte[des.BlockSize / 8];
return des;
}
My Attempt with converting to Java
private static String key = "012345678901234567890123";
public static void main(String[] args) throws Exception {
String text = "test";
String codedtext = encrypt(text);
//String decodedtext = decrypt(codedtext);
System.out.println(new String(codedtext));
//System.out.println(decodedtext);
}
public static String encrypt(String message) throws Exception {
MessageDigest md = MessageDigest.getInstance("md5");
byte[] digestOfPassword = md.digest(key.getBytes("unicode"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
//for (int j = 0, k = 16; j < 8;) {
// keyBytes[k++] = keyBytes[j++];
//}
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] plainTextBytes = message.getBytes();
byte[] cipherText = cipher.doFinal(plainTextBytes);
String output = Base64.encode(cipherText);
return output;
}
public static String decrypt(String message) throws Exception {
byte[] messageBytes = Base64.decode(message);
MessageDigest md = MessageDigest.getInstance("md5");
byte[] digestOfPassword = md.digest(key.getBytes());
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] plainText = decipher.doFinal(messageBytes);
return new String(plainText);
}
Does anyone see what I'm overseeing?
You are missing two things. You are using a 16 length key on the c# side since it is not padded like the Java version. By default if the key is 16 bytes in length it will be padded with the first 8 bytes of the key.
To make this match on the Java side you will have to uncomment that line that adds the padding to the key:
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
Also, on the java side there was a suggestion to make sure to use UTF-LE for the text. Make sure to use it for everything. So the lines:
byte[] digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
byte[] plainTextBytes = clearText.getBytes("UTF-16LE");
In general I would make sure to set the c# params of all the tripledes object, and not depend on defaults.
Here are two versions that match in c# and java
Java
String key = "012345678901234567890123";
String clearText = "test";
MessageDigest md = MessageDigest.getInstance("md5");
byte[] digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
String byteText = Arrays.toString(keyBytes);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] plainTextBytes = clearText.getBytes("UTF-16LE");
byte[] cipherText = cipher.doFinal(plainTextBytes);
String output = Base64.encode(cipherText);
c#
string clearText = "test";
string key = "012345678901234567890123";
string encryptedText = "";
MD5 md5 = new MD5CryptoServiceProvider();
TripleDES des = new TripleDESCryptoServiceProvider();
des.KeySize = 128;
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.PKCS7;
byte[] md5Bytes = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
byte[] ivBytes = new byte[8];
des.Key = md5Bytes;
des.IV = ivBytes;
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
ICryptoTransform ct = des.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
encryptedText = Convert.ToBase64String(ms.ToArray());
}
Edited: Both versions now return the test case result "nmj8MjjO52y928Syqf0J+g=="
I'm getting a Triple DES decrypted string from the clients server, which has been coded in c# (see below):
using System.IO;
using System;
using System.Security.Cryptography;
using System.Collections;
using System.Text;
class Program
{
static void Main()
{
Console.WriteLine("Hello, World!");
var encryption = TripleDESEncrypt("12345678901234", "C9AF269DF8A78A06D1216BFFF8F0536A");
Console.WriteLine(encryption);
}
public static string TripleDESEncrypt(string strClearText, string strKey)
{
byte[] bytClearText;
byte[] bytClearTextChunk = new byte[8];
byte[] bytEncryptedChunk = new byte[8];
int BytesCount = 0;
int nArrayPosition = 0;
string strEncryptedChar;
string strEncryptedText = "";
ArrayList Input = new ArrayList();
ArrayList Output = new ArrayList();
TripleDESCryptoServiceProvider tdes = (TripleDESCryptoServiceProvider)TripleDESCryptoServiceProvider.Create();
tdes.Key = HexToByteArray(strKey);
tdes.Mode = CipherMode.ECB;
ICryptoTransform tdesEncrypt = tdes.CreateEncryptor();
bytClearText = ASCIIEncoding.ASCII.GetBytes(strClearText);
BytesCount = bytClearText.Length;
for (int i = 0; i < BytesCount; i++)
{
if (nArrayPosition == 8)
{
Input.Add(bytClearTextChunk);
bytClearTextChunk = new byte[8];
nArrayPosition = 0;
}
bytClearTextChunk[nArrayPosition] = bytClearText[i];
nArrayPosition++;
}
if (nArrayPosition != 0)
Input.Add(bytClearTextChunk);
foreach (byte[] Cbyte in Input)
{
tdesEncrypt.TransformBlock(Cbyte, 0, 8, bytEncryptedChunk, 0);
Output.Add(bytEncryptedChunk);
bytEncryptedChunk = null;
bytEncryptedChunk = new byte[8];
}
foreach (byte[] Cbyte in Output)
{
foreach (byte BByte in Cbyte)
{
strEncryptedChar = BByte.ToString("X");
strEncryptedChar = strEncryptedChar.PadLeft(2, Convert.ToChar("0"));
strEncryptedText += strEncryptedChar;
}
}
return strEncryptedText;
}
private static byte[] HexToByteArray(string strHex)
{
byte[] bytArray = new byte[strHex.Length / 2];
int positionCount = 0;
for (int i = 0; i < strHex.Length; i += 2)
{
bytArray[positionCount] = byte.Parse(strHex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
positionCount++;
}
return bytArray;
}
}
I am then trying to Triple DES decrypt it in Java using this key: C9AF269DF8A78A06D1216BFFF8F0536A
Here is my code to decrypt:
public String DesDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
String UNICODE_FORMAT = "UTF8";
String decryptedPinText = null;
byte[] hexConvert = hexStringtoByteArray(encryptKey);
SecretKey desKey = null;
byte[] tdesKey = new byte[24];
System.arraycopy(hexConvert, 0, tdesKey, 0,16);
System.arraycopy(hexConvert, 0, tdesKey, 0,8);
byte[] encryptKeyBytes = encryptKey.getBytes(UNICODE_FORMAT);
KeySpec desKeySpec = new DESedeKeySpec(tdesKey);
Cipher desCipher;
SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
desCipher = Cipher.getInstance("DESede/ECB/NoPadding");
try {
desKey = skf.generateSecret(desKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
desCipher.init(Cipher.DECRYPT_MODE, desKey);
byte[] decryptPin = desCipher.doFinal(pin.getBytes());
decryptedPinText = new String(decryptPin, "UTF-8");
return decryptedPinText;
}
The sample out put would be input/output would be "12345678901234" however, I'm getting jumbled nonsense returned e.g ��0�8��/0��
So something is getting lost between c# and java...
This is a follow on from a previous question I asked here
I'd appreciate help on this
changes to code
public String DesDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
String UNICODE_FORMAT = "UTF8";
String decryptedPinText = null;
SecretKey desKey = null;
byte[] encryptKeyBytes = EncodingUtils.getAsciiBytes(encryptKey);
byte[] tdesKey = new byte[24];
System.arraycopy(encryptKeyBytes, 8, tdesKey, 0, 8);
System.arraycopy(encryptKeyBytes, 0, tdesKey, 8, 16);
KeySpec desKeySpec = new DESedeKeySpec(tdesKey);
Cipher desCipher;
SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
desCipher = Cipher.getInstance("DESede/ECB/NoPadding");
try {
desKey = skf.generateSecret(desKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
desCipher.init(Cipher.DECRYPT_MODE, desKey);
byte[] decryptPin = desCipher.doFinal(EncodingUtils.getAsciiBytes(pin));
decryptedPinText = new String(decryptPin, "ASCII");
return decryptedPinText;
}
c# decrypt code
using System.IO;
using System;
using System.Security.Cryptography;
using System.Collections;
using System.Text;
class Program
{
static void Main()
{
Console.WriteLine("Hello, World!");
var encryption = TripleDESDecrypt("1D30CC3DE1641D7F5E821D13FC1200C3", "C9AF269DF8A78A06D1216BFFF8F0536A");
Console.WriteLine(encryption);
}
public static string TripleDESDecrypt(string strEncryptedText, string strKey)
{
string errorMessage = "";
int errorCode = 0;
string strDecryptedText = "";
try
{
byte[] bytEncryptedChunk = new byte[8];
byte[] bytClearTextChunk = new byte[8];
byte[] _bytesEmpty = new byte[8];
int BytesCount = 0;
int positionCount = 0;
ArrayList Input = new ArrayList();
ArrayList Output = new ArrayList();
TripleDESCryptoServiceProvider tdes = (TripleDESCryptoServiceProvider)TripleDESCryptoServiceProvider.Create();
tdes.Key = HexToByteArray(strKey);
tdes.Mode = CipherMode.ECB;
ICryptoTransform tdesDecrypt = tdes.CreateDecryptor();
BytesCount = strEncryptedText.Length;
for (int i = 0; i < BytesCount; i += 2)
{
if (positionCount == 8)
{
positionCount = 0;
Input.Add(bytEncryptedChunk);
bytEncryptedChunk = new byte[8];
}
bytEncryptedChunk[positionCount] = byte.Parse(strEncryptedText.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
positionCount++;
}
if (positionCount != 0)
{
Input.Add(bytEncryptedChunk);
}
foreach (byte[] Cbyte in Input)
{
tdesDecrypt.TransformBlock(Cbyte, 0, 8, _bytesEmpty, 0);
tdesDecrypt.TransformBlock(Cbyte, 0, 8, bytClearTextChunk, 0);
Output.Add(bytClearTextChunk);
bytClearTextChunk = null;
bytClearTextChunk = new byte[8];
}
foreach (byte[] Cbyte in Output)
{
strDecryptedText += ASCIIEncoding.ASCII.GetString(Cbyte);
}
}
catch (Exception ex)
{
errorCode = 1;
errorMessage = ex.Message;
}
Console.WriteLine(strDecryptedText);
return strDecryptedText;
}
private static byte[] HexToByteArray(string strHex)
{
byte[] bytArray = new byte[strHex.Length / 2];
int positionCount = 0;
for (int i = 0; i < strHex.Length; i += 2)
{
bytArray[positionCount] = byte.Parse(strHex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
positionCount++;
}
return bytArray;
}
}
This returns what is inputting into the encrypt 12345678901234
In your C# code, you use ASCII:
bytClearText = ASCIIEncoding.ASCII.GetBytes(strClearText);
While in Java you use UNICODE:
byte[] encryptKeyBytes = encryptKey.getBytes(UNICODE_FORMAT);
Try to change your C# to use UNICODE or your java code to use ASCII.
Also, since the C# is padding the output :
strEncryptedChar = strEncryptedChar.PadLeft(2, Convert.ToChar("0"));
You probably must check to remove all the '00' in the crypted string, so 1D30CC3DE1641D7F5E821D13FC1200C3 will become 1D30CC3DE1641D7F5E821D13FC12C3
(you must check if it's in the boundaries of an hex expression: 1C01A1 should probably be modified since it got a padding on the second Hexa 1C 01 A1: 1C1A1
acording https://stackoverflow.com/a/33768305/1140304 you can use
unicode instead of UTF-8 in java code
encrypt in c# :
public static string Encrypt2(string clearText,string key)
{
try
{
string encryptedText = "";
MD5 md5 = new MD5CryptoServiceProvider();
TripleDES des = new TripleDESCryptoServiceProvider();
des.KeySize = 128;
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.PKCS7;
byte[] md5Bytes = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
byte[] ivBytes = new byte[8];
des.Key = md5Bytes;
des.IV = ivBytes;
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
ICryptoTransform ct = des.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
encryptedText = Convert.ToBase64String(ms.ToArray());
}
return encryptedText;
}
catch (Exception exception)
{
return "";
}
}
for decode in c# you can use:
public static string Decrypt2(string cipher,string key)
{
try
{
byte[] clearBytes = Convert.FromBase64String(cipher);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] md5Bytes = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
string encryptedText = "";
TripleDES des = new TripleDESCryptoServiceProvider();
des.KeySize = 128;
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.PKCS7;
byte[] ivBytes = new byte[8];
des.Key = md5Bytes;
des.IV = ivBytes;
ICryptoTransform ct = des.CreateDecryptor();
byte[] resultArray = ct.TransformFinalBlock(clearBytes, 0, clearBytes.Length);
encryptedText = Encoding.Unicode.GetString(resultArray);
return encryptedText;
}
catch (Exception exception)
{
return "";
}
}
now, for encrypt in java you can use :
private String _encrypt2(String clearText,String key )
{
try
{
/**
* create md5
*/
MessageDigest md = MessageDigest.getInstance("md5");
byte[] digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8; )
{
keyBytes[k++] = keyBytes[j++];
}
SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] plainTextBytes = clearText.getBytes("UTF-16LE");
byte[] cipherText = cipher.doFinal(plainTextBytes);
String output = Base64.encodeToString(cipherText,Base64.DEFAULT);
return output;
}
catch (Exception ex) {}
return "";
}
and for decrypt in java :
private String _decrypt2(String encryptText,String key)
{
MessageDigest md = null;
byte[] digestOfPassword = null;
try
{
byte[] message = Base64.decode(encryptText.getBytes("UTF-16LE"), Base64.DEFAULT);
/**
* make md5
*/
md = MessageDigest.getInstance("md5");
digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8; )
{
keyBytes[k++] = keyBytes[j++];
}
SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] cipherText = cipher.doFinal(message);
return new String(cipherText, "UTF-16LE");
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
catch (InvalidAlgorithmParameterException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
return "";
}
If someone find himself/herself in the same problem like I did, here is a java implementation (android) of the same .NET decrypt function:
public static byte[] byteArrayConcat(byte[] array1, byte[] array2) {
byte[] result = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, result, 0, array1.length);
System.arraycopy(array2, 0, result, array1.length, array2.length);
return result;
}
private byte[] fGPKeyTo3DESKey(byte[] GPKey) {
byte[] _3DESKey = new byte[24];
byte[] tmp = new byte[8];
arraycopy(GPKey, 0, tmp, 0, 8);
_3DESKey = DaPlugUtils.byteArrayConcat(GPKey, tmp);
return _3DESKey;
}
private static byte[] hexStringtoByteArray(String hex) {
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
public String desDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
int bytesCount = 0;
int positionCount = 0;
byte[] bytEncryptedChunk = new byte[8];
ArrayList<byte[]> Input = new ArrayList();
bytesCount = pin.length();
for (int i = 0; i < bytesCount; i += 2) {
if (positionCount == 8) {
positionCount = 0;
Input.add(bytEncryptedChunk);
bytEncryptedChunk = new byte[8];
}
bytEncryptedChunk[positionCount] = (byte) (Integer.parseInt(pin.substring(i, i + 2), 16));
positionCount++;
}
if (positionCount != 0) {
Input.add(bytEncryptedChunk);
}
byte[] _3DESKey = fGPKeyTo3DESKey(hexStringtoByteArray(encryptKey));
DESedeKeySpec keySpec = new DESedeKeySpec(_3DESKey);
SecretKey k = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, k);
String res = "";
for (byte[] bs : Input) {
byte[] decryptPin = cipher.doFinal(bs);
String a = new String(decryptPin, StandardCharsets.US_ASCII);
res += a;
}
return res.trim();
}
I am able to encrypt an SMS and send it from one simulator (Android 2.2) to another.
On the receiving end I am able to do the decryption successfully. But the problem is if do the encryption in one OS version (i.e Android 2.2) and trying to decrypt in another OS version ( Android 2.3 ) i am getting 'Bad padding exception'. I checked that i used the same key on both ends.
The code is shown below
public class ED {
private String Key;
public ED() {
Key = "abc12"; // Assigning default key.
}
public ED(String key) {
// TODO Auto-generated constructor stub
Key = key;
}
public String encrypt(String toEncrypt) throws Exception {
byte[] rawKey = getRawKey(Key.getBytes("UTF-8"));
byte[] result = encrypt(rawKey, toEncrypt.getBytes("UTF-8"));
return toHex(result);
}
public byte[] encrypt(byte[] key, byte[] toEncodeString) throws Exception {
SecretKeySpec sKeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(toEncodeString);
return encrypted;
}
private byte[] getRawKey(byte[] key) throws Exception {
KeyGenerator kGen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(key);
kGen.init(128, sr);
SecretKey sKey = kGen.generateKey();
byte[] raw = sKey.getEncoded();
return raw;
}
/************************************* Decription *********************************************/
public String decrypt(String encryptedString) throws Exception {
byte[] rawKey = getRawKey(Key.getBytes("UTF-8"));
System.out.println("Decrypted Key in bytes : "+rawKey);
System.out.println("Key in decryption :"+rawKey);
SecretKeySpec sKeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
byte[] decrypted = cipher.doFinal(toByte(encryptedString));
System.out.println("Decrypted mess in bytes---------->" +decrypted);
return new String(decrypted);
}
public String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2*buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final String HEX = "0123456789ABCDEF";
private void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
}
public byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
return result;
}
}
And I am using sendTextMessage() function to send an sms. I read that encryption/decryption doesn't depend on OS but in this case that is not true. Am I missing any important things while configuring the Cipher (in AES) ? Please let me know.
It's setSeed(). It does not do what you think it does: it just adds the entropy of the given seed to the underlying algorithm. You'll probably find out that it returns somehthing different on both platforms. SHA1PRNG is a pseudo random function, but if it is already seeded, it's likely to return different results.
If the problem is in the key length, you could derivate a key from your password, instead of using it directly. You could use a Hash (like SHA-1, MD5, etc) and crop it to the correct size (128, 192 or 256 bits), or use PBEKeySpec instead of SecretKeySpec.
That to remove problems with the key length. If the padding problems were in the plaintext, I suggest you to use CipherInputStream and CipherOutputStream, which are more programmer-friendly to use than Cipher.doFinal.
Don't rely on KeyGenerator to generate the same key just because you seeded the RNG the same way. If you are pre-sharing a key, share the key, not the seed.
You should also specify the encryption transform completely: "AES/ECB/PKCS5Padding"
Finally, ECB mode is not secure for general use.
See another answer of mine for an example to perform encryption correctly with the JCE.
The problem is with SecureRandom generation. It is giving different results on different platforms. It's because of a bug fix on line 320 (in Gingerbread source) of SHA1PRNG_SecureRandomImpl.java in the engineNextBytes() method where
bits = seedLength << 3 + 64;
was changed to
bits = (seedLength << 3) + 64;
Use SecretKeyFactory() to generate a Secure key instead of secure random.
public class Crypto {
Cipher ecipher;
Cipher dcipher;
byte[] salt = { 1, 2, 4, 5, 7, 8, 3, 6 };
int iterationCount = 1979;
Crypto(String passPhase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) {
// TODO: handle exception
//Toast.makeText(this, "I cought ", Toast.LENGTH_LONG).show();
}
}
public String encrypt(String str) {
String rVal;
try {
byte[] utf8 = str.getBytes("UTF8");
byte[] enc = ecipher.doFinal(utf8);
rVal = toHex(enc);
} catch (Exception e) {
// TODO: handle exception
rVal = "Exception Caught "+e.getMessage();
}
return rVal;
}
public String decrypt(String str) {
String rVal;
try {
byte[] dec = toByte(str);
byte[] utf8 = dcipher.doFinal(dec);
rVal = new String(utf8, "UTF8");
} catch(Exception e) {
rVal = "Error in decrypting :"+e.getMessage();
}
return rVal;
}
private static byte[] toByte(String hexString ) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for ( int i=0; i<len; i++ ) {
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16 ).byteValue();
}
return result;
}
private static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer( 2*buf.length);
for ( int i=0; i<buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}