在维护旧版本的代码需要将java版的des加密换成c#版的,java 的DES的填充模式默认是ecb模式,c#对应的模式也要改成ecb模式,关于两种语言的DES算法加解密网上的文章多的是,自行搜索即可。我要讲的是在java端用了SecureRandom来指定明文密钥作为种子生成密钥后加密后,c#要使用同样的明文密钥来解密一致性问题。
JAVA DES 生成密钥的方式
/**
* 将二进制转换成16进制字符串
* @param buf
* @return
*/
public static String parseByte2HexStr(byte buf []){
StringBuffer sb = new StringBuffer();
for(int i = 0; i < buf.length; i++){
String hex = Integer.toHexString(buf[i] & 0xFF);
if(hex.length() == 1){
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
String secret = "977050781250000";
byte[] seed = secret.getBytes("UTF-8");
//KeyGenerator keyGen = KeyGenerator.getInstance("AES");
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
//keyGen.init(128, sr); //AES使用
keyGen.init( sr);
SecretKey key = keyGen.generateKey();
byte[] keyArray =key.getEncoded();
System.out.println("key:"+parseByte2HexStr(keyArray)); //结果为 892C9D649B382C9B
java DES的密钥如果要换成c#版的密钥,只需对明文密钥做两次SHA1后转成十六进制取前16位字符即为C#版的密钥。但c#DES的密钥用两次SHA1后,是没法解密出java的加密串的,这个问题困扰了我很多天,网上基本找不到解决方案。后来我查了java jdk的源代码,得知java的DES密钥除了两次SHA1外,加多了一个奇偶数校验调整,于是看到了光明。
C# DES 生成java对应的密钥方式
/// <summary>
/// 将DES明文密钥转成java对应的通过SecureRandom后转换的des密钥
/// </summary>
/// <param name="key">输入字符串。</param>
/// <param name="charset">字符编码。</param>
/// <returns>转换后的DES密钥。(16个十六进制字符)</returns>
public static string ToJavaDesKey(string key, String charset = "")
{
Encoding encoding;
if (charset == "")
{
encoding = Encoding.UTF8;
}
else
{
encoding = Encoding.GetEncoding(charset);
}
byte[] keyArray;
byte[] seed = encoding.GetBytes(key);
//两次sha1,即可转换成和java aes一样的密钥
using (var st = new SHA1CryptoServiceProvider())
{
using (var nd = new SHA1CryptoServiceProvider())
{
keyArray = nd.ComputeHash(st.ComputeHash(seed));
}
}
setParityBit(keyArray, 0); //奇偶校验调整
return byteToHexStr(keyArray).Substring(0, 16); //取前16位作为DES的密钥
}
//奇偶校验调整
static void setParityBit(byte[] key, int offset)
{
if (key == null)
return;
for (int i = 0; i < 8; i++)
{
int b = key[offset] & 0xfe;
b |= (BitCount(b) & 1) ^ 1;
key[offset++] = (byte)b;
}
}
//统计一个数的二进制位有多少个 ,对应java的Integer.bitCount
static int BitCount(int value)
{
value = (value & 0x55555555) + ((value >> 1) & 0x55555555);
value = (value & 0x33333333) + ((value >> 2) & 0x33333333);
value = (value & 0x0f0f0f0f) + ((value >> 4) & 0x0f0f0f0f);
value = (value * (0x01010101)) >> 24;
return value;
}
/// 字节数组转16进制字符串
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
StringBuilder strbuf = new StringBuilder(bytes.Length * 2);
int i;
for (i = 0; i < bytes.Length; i++)
{
strbuf.Append(((int)bytes[i] & 0xff).ToString("X2"));
}
return strbuf.ToString();
}
/**
* @Description 16进制转化为2进制
* @param hexStr
* @return
*/
public static byte[] parseHexStr2Byte(String hexStr)
{
try
{
if (hexStr.Length < 1)
{
return null;
}
byte[] result = new byte[hexStr.Length / 2];
string tt = "";
string kk = "";
for (int i = 0; i < hexStr.Length / 2; i++)
{
tt = hexStr.Substring(i * 2, 2);
result[i] = (byte)Convert.ToByte(tt, 16);
}
return result;
}
catch (Exception e)
{
throw;
}
}
String key = "977050781250000";
string new_key = ToJavaDesKey(key); //得到的结果为 892C9D649B382C9B,和java的一致
//des.Key = parseHexStr2Byte(new_key ); //将得到的new_key用于DES,即可解密出java加密的结果,这里不再叙述。