Java和.NET使用DES對(duì)稱加密的區(qū)別
Java和.NET的系統(tǒng)類庫(kù)里都有封裝DES對(duì)稱加密的實(shí)現(xiàn)方式,但是對(duì)外暴露的接口卻各不相同,甚至有時(shí)會(huì)讓自己難以解決其中的問題,比如Java加密后的結(jié)果在.NET中解密不出來等,由于最近項(xiàng)目有跨Java和.NET的加解密,經(jīng)過我的分析調(diào)試,終于讓它們可以互相加密解密了。
DES加密
DES是一種對(duì)稱加密(Data Encryption Standard)算法,以前我寫過一篇文章:.NET中加密解密相關(guān)知識(shí),有過簡(jiǎn)單描述。
DES算法一般有兩個(gè)關(guān)鍵點(diǎn),第一個(gè)是加密算法,第二個(gè)是數(shù)據(jù)補(bǔ)位。
加密算法常見的有ECB模式和CBC模式:
ECB模式:電子密本方式,這是JAVA封裝的DES算法的默認(rèn)模式,就是將數(shù)據(jù)按照8個(gè)字節(jié)一段進(jìn)行DES加密或解密得到一段8個(gè)字節(jié)的密文或者明文,最后一段不足8個(gè)字節(jié),則補(bǔ)足8個(gè)字節(jié)(注意:這里就涉及到數(shù)據(jù)補(bǔ)位了)進(jìn)行計(jì)算,之后按照順序?qū)⒂?jì)算所得的數(shù)據(jù)連在一起即可,各段數(shù)據(jù)之間互不影響。
CBC模式:密文分組鏈接方式,這是.NET封裝的DES算法的默認(rèn)模式,它比較麻煩,加密步驟如下:
1、首先將數(shù)據(jù)按照8個(gè)字節(jié)一組進(jìn)行分組得到D1D2......Dn(若數(shù)據(jù)不是8的整數(shù)倍,就涉及到數(shù)據(jù)補(bǔ)位了)
2、第一組數(shù)據(jù)D1與向量I異或后的結(jié)果進(jìn)行DES加密得到第一組密文C1(注意:這里有向量I的說法,ECB模式下沒有使用向量I)
3、第二組數(shù)據(jù)D2與第一組的加密結(jié)果C1異或以后的結(jié)果進(jìn)行DES加密,得到第二組密文C2
4、之后的數(shù)據(jù)以此類推,得到Cn
5、按順序連為C1C2C3......Cn即為加密結(jié)果。
數(shù)據(jù)補(bǔ)位一般有NoPadding和PKCS7Padding(JAVA中是PKCS5Padding)填充方式,PKCS7Padding和PKCS5Padding實(shí)際只是協(xié)議不一樣,根據(jù)相關(guān)資料說明:PKCS5Padding明確定義了加密塊是8字節(jié),PKCS7Padding加密快可以是1-255之間。但是封裝的DES算法默認(rèn)都是8字節(jié),所以可以認(rèn)為他們一樣。數(shù)據(jù)補(bǔ)位實(shí)際是在數(shù)據(jù)不滿8字節(jié)的倍數(shù),才補(bǔ)充到8字節(jié)的倍數(shù)的填充過程。
NoPadding填充方式:算法本身不填充,比如.NET的padding提供了有None,Zeros方式,分別為不填充和填充0的方式。
PKCS7Padding(PKCS5Padding)填充方式:為.NET和JAVA的默認(rèn)填充方式,對(duì)加密數(shù)據(jù)字節(jié)長(zhǎng)度對(duì)8取余為r,如r大于0,則補(bǔ)8-r個(gè)字節(jié),字節(jié)為8-r的值;如果r等于0,則補(bǔ)8個(gè)字節(jié)8。比如:
加密字符串為為AAA,則補(bǔ)位為AAA55555;加密字符串為BBBBBB,則補(bǔ)位為BBBBBB22;加密字符串為CCCCCCCC,則補(bǔ)位為CCCCCCCC88888888。
.NET中的DES加密
對(duì)于.NET,框架在System.Security.Cryptography命名空間下提供了DESCryptoServiceProvider作為System.Security.Cryptography.DES加密解密的包裝接口,它提供了如下的4個(gè)方法:
- public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV)
- public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV)
- public override void GenerateIV()
- public override void GenerateKey()
從.NET類庫(kù)封裝情況,加解密需要傳入一個(gè)Key和IV向量。而且Key必須為8字節(jié)的數(shù)據(jù),否則會(huì)直接拋異常出來,當(dāng)使用ECB模式下,不管傳入什么IV向量,加密結(jié)果都一樣。示例代碼如下:
- public static string EncryptWithJava(string key, string str)
- {
- if (key.Length < 8 || string.IsNullOrEmpty(str))
- {
- throw new Exception("加密key小于8或者加密字符串為空!");
- }
- byte[] bKey = Encoding.UTF8.GetBytes(key.Substring(0, 8));
- byte[] bIV = IV;
- byte[] bStr = Encoding.UTF8.GetBytes(str);
- try
- {
- DESCryptoServiceProvider desc = new DESCryptoServiceProvider();
- desc.Padding = PaddingMode.PKCS7;//補(bǔ)位
- desc.Mode = CipherMode.ECB;//CipherMode.CBC
- using (MemoryStream mStream = new MemoryStream())
- {
- using (CryptoStream cStream = new CryptoStream(mStream, desc.CreateEncryptor(bKey, bIV), CryptoStreamMode.Write))
- {
- cStream.Write(bStr, 0, bStr.Length);
- cStream.FlushFinalBlock();
- StringBuilder ret = new StringBuilder();
- byte[] res = mStream.ToArray();
- foreach (byte b in res)
- {
- ret.AppendFormat("{0:x2}", b);
- }
- return ret.ToString();
- }
- }
- }
- catch
- {
- return string.Empty;
- }
- }
由于為ECB模式,因此IV這里設(shè)置什么值都是可以的,當(dāng)為CBC模式下,則需要設(shè)置為其他值,比如:public static byte[] IV = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 },才能正常加密解密。
JAVA中的DES加密
JAVA的javax.crypto.Cipher包下,提供了加密解密的功能,它的靜態(tài)getInstance方法,可以返回一個(gè)Cipher對(duì)象,一般有public static final Cipher getInstance(String transformation)方法,transformation為:algorithm/mode/padding,分別表示算法名稱,比如DES,也可以在后面包含算法模式和填充方式,但也可以只是算法名稱,如為:"DES/CBC/PKCS5Padding","DES"等。JAVA中默認(rèn)的算法為ECB,默認(rèn)填充方式為PKCS5Padding。Cipher的Init方法用來初始化加密對(duì)象,常見的有:
- public final void init(int opmode, Key key, AlgorithmParameterSpec params)
- public final void init(int opmode,Key key, SecureRandom random)
用SecureRandom時(shí),一般用于不需要IV的算法模式,示例代碼如下:
- public static String encrypt2(String src) throws Exception {
- SecureRandom sr = new SecureRandom();
- DESKeySpec ks = new DESKeySpec(KEY.getBytes("UTF-8"));
- SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
- SecretKey sk = skf.generateSecret(ks);
- Cipher cip = Cipher.getInstance("DES/CBC/PKCS5Padding");//Cipher.getInstance("DES");
- IvParameterSpec iv2 = new IvParameterSpec(IV);
- cip.init(Cipher.ENCRYPT_MODE, sk, iv2);//IV的方式
- //cip.init(Cipher.ENCRYPT_MODE, sk, sr);//沒有傳遞IV
- String dest = byteToHex(cip.doFinal(src.getBytes("UTF-8")));
- return dest;
- }
當(dāng)默認(rèn)用DES,JAVA會(huì)用ECB模式,因此這里IV向量沒有作用,這里,但當(dāng)用CBC模式下,如果還是用SecureRandom,則每次加密的結(jié)果都會(huì)不一樣,因?yàn)镴AVA內(nèi)部會(huì)用隨機(jī)的IV來初始化Cipher對(duì)象,如示例代碼,由于Cipher.getInstance("DES/CBC/PKCS5Padding")使用了CBC,因此我這里用的javax.crypto.spec.IvParameterSpec包下的IvParameterSpec來初始化向量IV:
- Private final static byte[] IV = new byte[] {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
總 結(jié)
對(duì)于.NET和JAVA在使用DES對(duì)稱加密時(shí),需要大家指定一樣的算法和填充模式,并且JAVA在寫DES加解密算法時(shí),還需要根據(jù)創(chuàng)建Cipher對(duì)象的不同,正確使用IV向量。在不同系統(tǒng)需要互相數(shù)據(jù)時(shí),必須要明確的是加密算法,Key和算法模式,再根據(jù)不同模式是否需要IV向量,最后是填充模式。
本文是經(jīng)過自己翻閱資料和反復(fù)調(diào)試代碼而出來的,如有問題,請(qǐng)指正。
原文鏈接:http://www.cnblogs.com/Lawson/archive/2012/05/20/2510781.html