问题
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
回答1:
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
回答2:
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 "";
}
回答3:
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();
}
来源:https://stackoverflow.com/questions/29679283/triple-des-encrypt-c-sharp-decrypt-in-java