2011年6月30日 星期四

JAVA和C#3DES加密解密

一個項目.net 要調用JAVA的WEB
SERVICE,數據採用3DES加密,涉及到兩種語言3DES一致性的問題,
下面分享一下,
這裡的KEY採用Base64編碼,便用分發,因為Java的Byte範圍為-128至127,c#的Byte範圍是0-255
核心是確定Mode和Padding,關於這兩個的意思可以搜索3DES算法相關文章
一個是C#採用CBC
Mode,PKCS7 Padding,Java採用CBC Mode,PKCS5Padding Padding,
另一個是C#採用ECB
Mode,PKCS7 Padding,Java採用ECB Mode,PKCS5Padding
Padding,
Java的ECB模式不需要IV
對字符加密時,雙方採用的都是UTF-8編碼.


主要差異如下:

1、 對於待加密解密的數據,各自的填充模式不一樣
C#的模式有:ANSIX923、ISO10126、None、PKCS7、Zero,而Java有:NoPadding、PKCS5Padding、SSL3Padding

2、 各自默認的3DES實現,模式和填充方式不一樣
C#的默認模式為CBC,默認填充方式為PKCS7; java的默認模式為ECB,默認填充方式為PKCS5Padding

3、 各自的key的size不一樣
C#中key的size為16和24均可;java中要求key的size必須為24;對於CBC模式下的向量iv的size兩者均要求必須為8

翻看了3DES的原理:

DES主要採用替換和移位的方法,用56位密鑰對64位二進制數據塊進行加密,每次加密可對64位的輸入數據進行16輪編碼,
經一系列替換和移位後,輸入的64位轉換成安全不同的64的輸出數據.

3DES:是在DES的基礎上採用三重DES,即用兩個56位的密鑰K1,K2,發送方用K1加密,K2解密,再使用K1加密.接收方使用K1解密,K2加密,再使用K1解密,

其效果相當於密鑰長度加倍.

於是嘗試在java中,對key進行補位,即用前8個字節作為byte[24] 中的byte[16]~byte[23];發現與c#中加密的結果相同!於是大膽假設C#中可能是檢查key的size為16的時候

自動將前8個字節作為k3進行了補位,而java沒有實現這一點(因為java的3DES算法中強制要求key的size必須為24)。這樣的情況下,可能就是發送方用k1加密、k2解密、k3再加密;接受方k3解密、k2加密、再k1解密來實現。

最終經過編碼驗證,確認key大小為24時,java和c#的加密解密結果相一致。

Java中實現時,只要注意對大小不足24的key進行補位,和採用CBC模式,填充模式為PKCS5Padding即可。

還有一種讓Java和.Net兼容的方式,在.Net中指定模式為ECB,填充為PKCS7,然後在Java中採用其默認的模式(DESede/ECB/PKCS5Padding)即可,注意雙方約定key的size為24個字節。建議雙方對key以base64編碼字符串進行告知,因為java和.net中byte字節的範圍不相同(前者-128~127,後者0~255),避免不必要的處理。


下面是C#代码


///
/// DES3加密解密
///

public class Des3
{
#region CBC模式**

///
/// DES3 CBC模式加密
///

/// 密钥
/// IV
/// 明文的byte数组
/// 密文的byte数组
public static byte[] Des3EncodeCBC( byte[] key, byte[] iv, byte[] data )
{
//复制于MSDN

try
{
// Create a MemoryStream.
MemoryStream mStream = new MemoryStream();

TripleDESCryptoServiceProvider tdsp
= new TripleDESCryptoServiceProvider();
tdsp.Mode
= CipherMode.CBC; //默认值
tdsp.Padding = PaddingMode.PKCS7; //默认值

// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream cStream = new CryptoStream( mStream,
tdsp.CreateEncryptor( key, iv ),
CryptoStreamMode.Write );

// Write the byte array to the crypto stream and flush it.
cStream.Write( data, 0, data.Length );
cStream.FlushFinalBlock();

// Get an array of bytes from the
// MemoryStream that holds the
// encrypted data.
byte[] ret = mStream.ToArray();

// Close the streams.
cStream.Close();
mStream.Close();

// Return the encrypted buffer.
return ret;
}
catch ( CryptographicException e )
{
Console.WriteLine(
"A Cryptographic error occurred: {0}", e.Message );
return null;
}
}

///
/// DES3 CBC模式解密
///

/// 密钥
/// IV
/// 密文的byte数组
/// 明文的byte数组
public static byte[] Des3DecodeCBC( byte[] key, byte[] iv, byte[] data )
{
try
{
// Create a new MemoryStream using the passed
// array of encrypted data.
MemoryStream msDecrypt = new MemoryStream( data );

TripleDESCryptoServiceProvider tdsp
= new TripleDESCryptoServiceProvider();
tdsp.Mode
= CipherMode.CBC;
tdsp.Padding
= PaddingMode.PKCS7;

// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream csDecrypt = new CryptoStream( msDecrypt,
tdsp.CreateDecryptor( key, iv ),
CryptoStreamMode.Read );

// Create buffer to hold the decrypted data.
byte[] fromEncrypt = new byte[data.Length];

// Read the decrypted data out of the crypto stream
// and place it into the temporary buffer.
csDecrypt.Read( fromEncrypt, 0, fromEncrypt.Length );

//Convert the buffer into a string and return it.
return fromEncrypt;
}
catch ( CryptographicException e )
{
Console.WriteLine(
"A Cryptographic error occurred: {0}", e.Message );
return null;
}
}

#endregion

#region ECB模式

///
/// DES3 ECB模式加密
///

/// 密钥
/// IV(当模式为ECB时,IV无用)
/// 明文的byte数组
/// 密文的byte数组
public static byte[] Des3EncodeECB( byte[] key, byte[] iv, byte[] data )
{
try
{
// Create a MemoryStream.
MemoryStream mStream = new MemoryStream();

TripleDESCryptoServiceProvider tdsp
= new TripleDESCryptoServiceProvider();
tdsp.Mode
= CipherMode.ECB;
tdsp.Padding
= PaddingMode.PKCS7;
// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream cStream = new CryptoStream( mStream,
tdsp.CreateEncryptor( key, iv ),
CryptoStreamMode.Write );

// Write the byte array to the crypto stream and flush it.
cStream.Write( data, 0, data.Length );
cStream.FlushFinalBlock();

// Get an array of bytes from the
// MemoryStream that holds the
// encrypted data.
byte[] ret = mStream.ToArray();

// Close the streams.
cStream.Close();
mStream.Close();

// Return the encrypted buffer.
return ret;
}
catch ( CryptographicException e )
{
Console.WriteLine(
"A Cryptographic error occurred: {0}", e.Message );
return null;
}

}

///
/// DES3 ECB模式解密
///

/// 密钥
/// IV(当模式为ECB时,IV无用)
/// 密文的byte数组
/// 明文的byte数组
public static byte[] Des3DecodeECB( byte[] key, byte[] iv, byte[] data )
{
try
{
// Create a new MemoryStream using the passed
// array of encrypted data.
MemoryStream msDecrypt = new MemoryStream( data );

TripleDESCryptoServiceProvider tdsp
= new TripleDESCryptoServiceProvider();
tdsp.Mode
= CipherMode.ECB;
tdsp.Padding
= PaddingMode.PKCS7;

// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream csDecrypt = new CryptoStream( msDecrypt,
tdsp.CreateDecryptor( key, iv ),
CryptoStreamMode.Read );

// Create buffer to hold the decrypted data.
byte[] fromEncrypt = new byte[data.Length];

// Read the decrypted data out of the crypto stream
// and place it into the temporary buffer.
csDecrypt.Read( fromEncrypt, 0, fromEncrypt.Length );

//Convert the buffer into a string and return it.
return fromEncrypt;
}
catch ( CryptographicException e )
{
Console.WriteLine(
"A Cryptographic error occurred: {0}", e.Message );
return null;
}
}

#endregion

///
/// 类测试
///

public static void Test()
{
System.Text.Encoding utf8
= System.Text.Encoding.UTF8;

//key为abcdefghijklmnopqrstuvwx的Base64编码
byte[] key = Convert.FromBase64String( "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4" );
byte[] iv = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; //当模式为ECB时,IV无用
byte[] data = utf8.GetBytes( "中国ABCabc123" );

System.Console.WriteLine(
"ECB模式:" );
byte[] str1 = Des3.Des3EncodeECB( key, iv, data );
byte[] str2 = Des3.Des3DecodeECB( key, iv, str1 );
System.Console.WriteLine( Convert.ToBase64String( str1 ) );
System.Console.WriteLine( System.Text.Encoding.UTF8.GetString( str2 ) );

System.Console.WriteLine();

System.Console.WriteLine(
"CBC模式:" );
byte[] str3 = Des3.Des3EncodeCBC( key, iv, data );
byte[] str4 = Des3.Des3DecodeCBC( key, iv, str3 );
System.Console.WriteLine( Convert.ToBase64String( str3 ) );
System.Console.WriteLine( utf8.GetString( str4 ) );

System.Console.WriteLine();

}

}

接着是Java代码

import java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Des3 {
public static void main(String[] args) throws Exception {

byte[] key=new BASE64Decoder().decodeBuffer("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4");
byte[] keyiv = { 1, 2, 3, 4, 5, 6, 7, 8 };

byte[] data="中国ABCabc123".getBytes("UTF-8");

System.out.println(
"ECB加密解密");
byte[] str3 = des3EncodeECB(key,data );
byte[] str4 = ees3DecodeECB(key, str3);
System.out.println(
new BASE64Encoder().encode(str3));
System.out.println(
new String(str4, "UTF-8"));

System.out.println();

System.out.println(
"CBC加密解密");
byte[] str5 = des3EncodeCBC(key, keyiv, data);
byte[] str6 = des3DecodeCBC(key, keyiv, str5);
System.out.println(
new BASE64Encoder().encode(str5));
System.out.println(
new String(str6, "UTF-8"));

}

/**
* ECB加密,不要IV
*
@param key 密钥
*
@param data 明文
*
@return Base64编码的密文
*
@throws Exception
*/
public static byte[] des3EncodeECB(byte[] key, byte[] data)
throws Exception {

Key deskey
= null;
DESedeKeySpec spec
= new DESedeKeySpec(key);
SecretKeyFactory keyfactory
= SecretKeyFactory.getInstance("desede");
deskey
= keyfactory.generateSecret(spec);

Cipher cipher
= Cipher.getInstance("desede" + "/ECB/PKCS5Padding");

cipher.init(Cipher.ENCRYPT_MODE, deskey);
byte[] bOut = cipher.doFinal(data);

return bOut;
}

/**
* ECB解密,不要IV
*
@param key 密钥
*
@param data Base64编码的密文
*
@return 明文
*
@throws Exception
*/
public static byte[] ees3DecodeECB(byte[] key, byte[] data)
throws Exception {

Key deskey
= null;
DESedeKeySpec spec
= new DESedeKeySpec(key);
SecretKeyFactory keyfactory
= SecretKeyFactory.getInstance("desede");
deskey
= keyfactory.generateSecret(spec);

Cipher cipher
= Cipher.getInstance("desede" + "/ECB/PKCS5Padding");

cipher.init(Cipher.DECRYPT_MODE, deskey);

byte[] bOut = cipher.doFinal(data);

return bOut;

}

/**
* CBC加密
*
@param key 密钥
*
@param keyiv IV
*
@param data 明文
*
@return Base64编码的密文
*
@throws Exception
*/
public static byte[] des3EncodeCBC(byte[] key, byte[] keyiv, byte[] data)
throws Exception {

Key deskey
= null;
DESedeKeySpec spec
= new DESedeKeySpec(key);
SecretKeyFactory keyfactory
= SecretKeyFactory.getInstance("desede");
deskey
= keyfactory.generateSecret(spec);

Cipher cipher
= Cipher.getInstance("desede" + "/CBC/PKCS5Padding");
IvParameterSpec ips
= new IvParameterSpec(keyiv);
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
byte[] bOut = cipher.doFinal(data);

return bOut;
}

/**
* CBC解密
*
@param key 密钥
*
@param keyiv IV
*
@param data Base64编码的密文
*
@return 明文
*
@throws Exception
*/
public static byte[] des3DecodeCBC(byte[] key, byte[] keyiv, byte[] data)
throws Exception {

Key deskey
= null;
DESedeKeySpec spec
= new DESedeKeySpec(key);
SecretKeyFactory keyfactory
= SecretKeyFactory.getInstance("desede");
deskey
= keyfactory.generateSecret(spec);

Cipher cipher
= Cipher.getInstance("desede" + "/CBC/PKCS5Padding");
IvParameterSpec ips
= new IvParameterSpec(keyiv);

cipher.init(Cipher.DECRYPT_MODE, deskey, ips);

byte[] bOut = cipher.doFinal(data);

return bOut;

}

}

下面是运行结果


ECB模式:
rmWB4+r9Ug93WI0KAEuMig==
中国ABCabc123

CBC模式:
4aabWF8UFour/vNfnzJrjw==
中国ABCabc123


source:

http://www.cnblogs.com/neil-zhao/archive/2011/06/01/2066058.html

http://gaoge2000.blog.hexun.com/18731819_d.html

沒有留言: