1.概述
在基於互連網的應用中,發送端將字元採用某種方式加密後傳輸;而接受端根據事先約定的密鑰進行解密,這樣即使傳輸的字元被截獲,也不會輕易被識別。而且,現在很多應用環境都很複雜,服務端是JAVA應用,用戶端有JAVA應用、智能手機應用。我們以服務端為JAVA應用,用戶端為智能手機IOS應用為例,實現在服務端加密一段字元,傳輸到用戶端解密;在用戶端又加密一段字元,傳輸到服務端解密,這樣一個較為複雜的過程。
對於這種需求,有很多實現方式,如採用https加安全數位憑證來實現,它在金融行業用得比較多。
這裡採用DES演算法完成這種字元安全傳輸的需求。首先聲明一下,DES演算法我也瞭解不多,下面的論述肯定有遺漏、錯誤等等不足之處,請參考性閱讀,發現錯誤等請告訴我。在此先行謝過。
2.Java字元編碼
所有電腦的字元都是按照某種字元集進行編碼的,在網路中真正傳輸的是位元組。
在用戶端發送某串字元如”miki西遊:mikixiyou@126.com”,它會按照用戶端的字元集進行編碼,形成位元組流,在傳輸到某個服務端前,還需要使用BASE64進行編碼,然後傳到服務端。
服務端接收到之後,使用BASE64進行解碼,然後按照它的預設字元集進行解碼,形成字串。如果用戶端和服務端使用的預設字元集是一致的,如都是GBK,那麼就會正確顯示這段字元文字。如果不正確,如用戶端用GBK編碼,而伺服器端用UTF8編碼,那麼就會出現亂碼。我們經常在瀏覽器上見到亂碼啊問號號等字元,就是這樣字元集不一致所導致的。
public static void main(String args[]) throws Exception {
String source = "miki西遊| mikixiyou@126.com";
StringcharsetName=System.getProperty("file.encoding");
System.out.println("file.encodingis "+charsetName);
System.out.println("source="+source);
System.out.println(parseByte2HexStr(source.getBytes("GBK")));
System.out.println(parseByte2HexStr(source.getBytes()));
System.out.println(parseByte2HexStr(source.getBytes("UTF-8")));
String source_utf8=newString (source.getBytes("UTF-8"),"UTF-8");
System.out.println("source_utf8="+source_utf8);
String source_gbk=newString (source.getBytes("GBK"),"GBK");
System.out.println("source_gbk="+source_gbk);
}
一般我們使用這個方法source.getBytes()的source字串預設字元集的編碼。
String source = "西遊abc@126.com";
System.out.println(parseByte2HexStr(source.getBytes()));
輸出結果為
CEF7D3CE616263403132362E636F6D
前兩個位元組CEF7表示“西”,後兩個位元組D3CE表示“遊”。GBK字元集對於漢字採用兩位元組編碼的。
字串source的預設字元集可以通過系統屬性得到,它是每一個JAVA的檔案編碼。擷取的方法如下:
StringcharsetName=System.getProperty("file.encoding");
System.out.println("file.encodingis "+charsetName);
輸出結果為
file.encoding is GBK
如果按照UTF-8字元集擷取編碼,那麼輸出的位元組流將按照UTF-8編碼方式進行輸出。
System.out.println(parseByte2HexStr(source.getBytes("UTF-8")));
String source_utf8=newString (source.getBytes("UTF-8"),"UTF-8");
輸出結果為
E8A5BFE6B8B8616263403132362E636F6D
前三個位元組E8A5BF表示“西”,後三個位元組E6B8B8表示“遊”。UTF-8字元集對於漢字採用三位元組編碼的。
在互連網中,傳輸的位元組流還需要進行BASE64編碼。我不知道是不是因為位元組流太長了什麼的,需要BASE64編碼壓縮一下,還是其他什麼目的。
BASE64的使用很簡單,網上原始碼很多。基本是使用這兩個方法,“String encode(byte[] data)“將位元組數組編碼成字串,“byte[]decode(String s)”將字串還原成位元組數組。
3.Java位元組加密
在JAVA類中匯入 javax.crypto.Cipher;包,使用Cipher.getInstance("DES/CBC/PKCS5Padding");方法實現加密。
注意,這裡使用PKCS5Padding演算法,密鑰只能是8個位元組。
因為在ios中,支援的DES密碼編譯演算法是kCCOptionPKCS7Padding |kCCOptionECBMode。在使用PKCS7Padding,它的密鑰可以是8個位元組,也可以不是。如果密鑰不是8個位元組的話,那麼JAVA端的PKCS5Padding演算法就不能解密了。
我對DES演算法也瞭解甚少,這裡只說一下自己的理解。在密鑰都是8個位元組的前提下,PKCS7Padding和PKCS5Padding的加密和解密是通用的。因此,不必糾結於兩個演算法不一樣怎麼辦,如何讓IOS也支援JAVA的密碼編譯演算法,甚至不用DES了等等。
我覺得都沒必要,我們做的是工程,一種需求的實現方法。只要遵守密鑰為8個位元組的約定,就能實現需求,又何必去找其他的演算法。好吧,我理解你覺得這樣不安全,其實也沒絕對的安全。
回到正題,JAVA中DES加密實現方法如下:
private static byte[] iv = {1, 2, 3, 4, 5, 6, 7, 8};
public static byte[]encryptDES(String encryptString, String encryptKey)
throws Exception {
System.out.println("willencryptedData with UTF-8 encoding =" + parseByte2HexStr(encryptString.getBytes("UTF-8")));
IvParameterSpec zeroIv =new IvParameterSpec(iv);
SecretKeySpec key = newSecretKeySpec(encryptKey.getBytes(), "DES");
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,key, zeroIv);
byte[] encryptedData =cipher.doFinal(encryptString.getBytes("UTF-8"));
System.out.println("didencryptedData =" + parseByte2HexStr(encryptedData));
return encryptedData;
}
public static StringencryptDESwithBase64(String encryptString,String encryptKey) throws Exception
{
return XYBase64.encode(encryptDES(encryptString,encryptKey));
}
JAVA中DES解密實現方法如下:
public static String decryptDES(byte[] encryptedData, StringdecryptKey)
throws Exception {
System.out.println("willdecryptedData =" + parseByte2HexStr(encryptedData));
IvParameterSpec zeroIv =new IvParameterSpec(iv);
SecretKeySpec key = newSecretKeySpec(decryptKey.getBytes("UTF-8"), "DES");
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,key, zeroIv);
byte decryptedData[] =cipher.doFinal(encryptedData);
System.out.println("diddecryptedData with UTF-8 encoding =" + parseByte2HexStr(decryptedData));
String decryptedString =new String(decryptedData, "UTF-8");
System.out.println("diddecryptedString with UTF-8 encoding =" + decryptedString);
return decryptedString;
}
public static StringdecryptDESwithBase64(String encryptedString, String decryptKey) throws Exception
{
byte[]encryptedData=XYBase64.decode(encryptedString);
return decryptDES(encryptedData,decryptKey);
}
在main()中調試一下,結果符合預期。
public static void main(String[] args) throws Exception {
String plainText = "abcdefghihjjjkelaemn";
String keyText = "20120401";
plainText = "miki西遊| mikixiyou@126.com";
keyText = "abcd1234";
byte[] encryptedData = encryptDES(plainText,keyText);
String decryptedString=decryptDES(encryptedData,keyText);
String cipherText = parseByte2HexStr(encryptedData);
System.out.println("明文:" + plainText);
System.out.println("密鑰:" + keyText);
System.out.println("密文 Base 64 編碼:" + cipherText);
System.out.println("解密後:" + decryptedString);
String encryptedString =encryptDESwithBase64(plainText, keyText);
decryptedString=decryptDESwithBase64(encryptedString,keyText);
System.out.println("明文:" + plainText);
System.out.println("密鑰:" + keyText);
System.out.println("密文:" + encryptedString);
System.out.println("解密後:" + decryptedString);
}
輸出結果如下:
will encryptedData with UTF-8 encoding=E8A5BFE6B8B8616263403132362E636F6D
did encryptedData =A69C602B3F74BD6273DE730D6214026B8FE538E4AB9F8547
will decryptedData =A69C602B3F74BD6273DE730D6214026B8FE538E4AB9F8547
did decryptedData with UTF-8 encoding =E8A5BFE6B8B8616263403132362E636F6D
did decryptedString with UTF-8 encoding =miki西遊| mikixiyou@126.com
明文:miki西遊| mikixiyou@126.com
密鑰:abcd1234
密文 Base 64 編碼:A69C602B3F74BD6273DE730D6214026B8FE538E4AB9F8547
解密後:miki西遊| mikixiyou@126.com
will encryptedData with UTF-8 encoding =E8A5BFE6B8B8616263403132362E636F6D
did encryptedData =A69C602B3F74BD6273DE730D6214026B8FE538E4AB9F8547
will decryptedData =A69C602B3F74BD6273DE730D6214026B8FE538E4AB9F8547
did decryptedData with UTF-8 encoding=E8A5BFE6B8B8616263403132362E636F6D
did decryptedString with UTF-8 encoding =miki西遊| mikixiyou@126.com
明文:miki西遊| mikixiyou@126.com
密鑰:abcd1234
密文:ppxgKz90vWJz3nMNYhQCa4/lOOSrn4VH
解密後:miki西遊| mikixiyou@126.com
這樣,我們就實現了JAVA中的加密和解密。但要在用戶端也實現這樣的加密和解密演算法才算最終完成任務。
4.Objective-C字元編碼
待續
5.Objective-C位元組加密
待續
6.參考資料
http://www.cnblogs.com/midea0978/articles/1437257.html
http://www.cnblogs.com/silentjesse/archive/2011/11/04/2235674.html
關於Objective-c和Java下DES加密保持一致的方式。這個文檔我找不到原始作者,只看到很多轉載,所以不寫超連結了。