Java的DES加解密和.Net的DES加解密两者相互转换或通用的问题
近期遇到了一个DES加解密的问题,由于项目上的原因那边的DES加密是通过.Net开发,但是我这边的项目又是用Java写的,所以拿到那边传过来的密文数据后我无法对其进行解密,同时我这边加密的数据到了他们.Net那边后,他们那边也无法正常使用,所以着手做一个两边通用的DES加解密。.Net的源码:
提示:这里是.Net的DES加解密源码
我这里把秘钥去掉了,但是不影响整体逻辑,秘钥你自己加一个就行了,如果要通用的话必须保证C# 的秘钥和Java的秘钥一样。
//加解密的密钥
public static string strKey ="A&FX****";
// 向量
public static byte[] strIV = { 0x30, 0x76, 0x18, 0x50, 0x44, 0xAC, 0xBD, 0x55 };
//加密
public static string Encrypt(string value)
{
using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
{
byte[] inputByteArray = Encoding.UTF8.GetBytes(value);
des.Key = ASCIIEncoding.ASCII.GetBytes(strKey);
des.IV = strIV;
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.Zeros;
System.IO.MemoryStream ms = new System.IO.MemoryStream();
using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
cs.Close();
}
String s = Convert.ToBase64String(ms.ToArray()).Replace("+", "-");
s = s.Replace("/","_");
ms.Close();
return s;
}
}
//解密
public static string Decrypt(string value)
{
try{
value = value.Replace("-","+");
value = value.Replace("_", "/");
byte[] inputByteArray = Convert.FromBase64String(value);
using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
{
des.Key = ASCIIEncoding.ASCII.GetBytes(strKey);
des.IV = strIV;
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.Zeros;
System.IO.MemoryStream ms = new System.IO.MemoryStream();
using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
cs.Close();
}
String s = Encoding.UTF8.GetString(ms.ToArray()).Replace("\0","");
ms.Close();
return s;
}
}catch (Exception e)
{
String rs = e.ToString();
return "Excepion";
}
}
Java的源码:
提示:这里是java的DES加解密源码:
同样的我这里把秘钥去掉了,但实际开发中也一定保证秘钥和C#端的一致。
// 加解密的秘钥
public static String strKey = "A&FX****";
// 向量
public static byte[] strIV = {0x30, 0x76, 0x18, 0x50, 0x44, (byte) 0xAC, (byte) 0xBD, 0x55};
/**
* 加密
*
* @param data 待加密的数据
* @return 加密后的数据
* @throws Exception
*/
public static String encode(String data) {
try {
KeySpec ks = new DESKeySpec(strKey.getBytes("UTF-8"));
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(ks);
IvParameterSpec iv = new IvParameterSpec(strIV);
Cipher enCipher = Cipher.getInstance("DES/CBC/NoPadding");// 设置填充方式
enCipher.init(Cipher.ENCRYPT_MODE, key, iv);// 设置工作模式为加密模式,给出密钥和向量
byte[] b = data.getBytes("UTF-8");// 当这里的字节数不是8的倍数的时候
int remainder = b.length % 8;
if (0 != remainder) {
int oldLength = b.length;
// 1.扩展自身长度
b = Arrays.copyOf(b, b.length + 8 - remainder);
// 2.填充扩展内容为0
Arrays.fill(b, oldLength, b.length, (byte) 0);
}
byte[] pasByte = enCipher.doFinal(b); // 这边就必须要补字节来达到离当前字节最近的8的倍数,也就是遵循了DES加密的原则
String code = Base64.getEncoder().encodeToString(pasByte);// 所以,后面就会补上几个字节
String Encode;
Encode = code.replace("+", "-");
Encode = Encode.replace("/", "_");
return Encode;
} catch (Exception ex) {
System.out.println(ex);
return "exception";
}
}
/**
* 解密
*
* @param data 解密前的数据
* @return 解密后的数据
* @throws Exception
*/
public static String decode(String data) {
try {
data = data.replace("-", "+");
data = data.replace("_", "/");
KeySpec ks = new DESKeySpec(strKey.getBytes("UTF-8"));
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(ks);
IvParameterSpec iv = new IvParameterSpec(strIV);
Cipher deCipher = Cipher.getInstance("DES/CBC/NoPadding");
deCipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] b = data.getBytes("ASCII");
byte[] pasByte = deCipher.doFinal(Base64.getDecoder().decode(b));
return new String(pasByte, "UTF-8").replace("\0", "");
} catch (Exception ex) {
System.out.println(ex);
return "exception";
}
}
public static void main(String[] args) {
String Encode = encode("865771051726461_110*Cmd_100");
System.out.println("加密后:" + Encode);
System.out.println("直接解密C#那边:" + decode("FcztQMpmwkudoN1lVJMWYrkJG7dzhAaL4OdMz6M_NDBNJMWczkpcgkUArm3t4rJ8"));
System.out.println("经Java加密后再解密:" + decode(Encode));
}
}
注意点:
我主要做的是Java这边,关键就在于两个点:
一是,如果DES加密格式统一都是CBC格式,那么秘钥和向量两边必须一致;
二是,填充方式的问题,比如我上面的例子,注意看C#的代码有这么一段“des.Padding = PaddingMode.Zeros”,这段代码则表示C#那边不进行填充,所以Java这边也要使用这种方式,所以我Java这边的加解密方法都有这么一段代码“DES/CBC/NoPadding”,这个“NoPadding”就等同于C#那边的“Zeros”,代表不填充。其实主要就在于格式上面,一定要两边的格式都统一,这是基础。