網上的demo一搜一大堆,可是,基本上都是隻知其一;不知其二(包括我)。爲何呢?我在嘗試分別在兩個平臺加密的時候,居然發現Android DES 加密和Java DES加密的程序不能互通。就是加密的結果不同,更不要說Android平臺的加密輸入做爲java DES的解密輸出了。這樣的話,客戶端和服務器端就不能進行通訊了。我網上以前也發帖子問了很多人,可是回答都不滿意。java
今天部門的另一個同事跟我說了一下,才解決了這個不能互通的問題。android
調用DES加密算法包最精要的就是下面兩句話:算法
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
數組
CBC是工做模式,DES一共有電子密碼本模式(ECB)、加密分組連接模式(CBC)、加密反饋模式(CFB)和輸出反饋模式(OFB)四種模式,服務器
PKCS5Padding是填充模式,還有其它的填充模式:
app
而後,cipher.init()一共有三個參數:Cipher.ENCRYPT_MODE, key, zeroIv,zeroIv就是初始化向量,一個8爲字符數組。ide
工做模式、填充模式、初始化向量這三種因素一個都不能少。不然,若是你不指定的話,那麼就要程序就要調用默認實現。問題就來了,這就與平臺有關了。難怪網上一搜"DES加密結果不一致「,出現n多網頁結果。(以前我並無指定IV,被折磨了2周)函數
源程序以下(從java平臺到android平臺,我根本沒有更改一行代碼):工具
另外,通常狀況下,加密後的結果都會用base64編碼進行傳輸。post
java平臺:
主程序
public class testDES { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { // TODO Auto-generated method stub String key = "12345678"; String text = "12345678"; String result1 = DES.encryptDES(text,key); String result2 = DES.decryptDES(result1, key); System.out.println(result1); System.out.println(result2); } }
用到的DES加密類
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec;
public class DES { private static byte[] iv = {1,2,3,4,5,6,7,8}; public static String encryptDES(String encryptString, String encryptKey) throws Exception { // IvParameterSpec zeroIv = new IvParameterSpec(new byte[8]); IvParameterSpec zeroIv = new IvParameterSpec(iv); SecretKeySpec key = new SecretKeySpec(encryptKey.getBytes(), "DES"); Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv); byte[] encryptedData = cipher.doFinal(encryptString.getBytes()); return Base64.encode(encryptedData); } public static String decryptDES(String decryptString, String decryptKey) throws Exception { byte[] byteMi = new Base64().decode(decryptString); IvParameterSpec zeroIv = new IvParameterSpec(iv); // IvParameterSpec zeroIv = new IvParameterSpec(new byte[8]); SecretKeySpec key = new SecretKeySpec(decryptKey.getBytes(), "DES"); Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key, zeroIv); byte decryptedData[] = cipher.doFinal(byteMi); return new String(decryptedData); } }
用到的BASE64工具類:
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; public class Base64 { private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); /** * data[]進行編碼 * @param data * @return */ public static String encode(byte[] data) { int start = 0; int len = data.length; StringBuffer buf = new StringBuffer(data.length * 3 / 2); int end = len - 3; int i = start; int n = 0; while (i <= end) { int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 0x0ff) << 8) | (((int) data[i + 2]) & 0x0ff); buf.append(legalChars[(d >> 18) & 63]); buf.append(legalChars[(d >> 12) & 63]); buf.append(legalChars[(d >> 6) & 63]); buf.append(legalChars[d & 63]); i += 3; if (n++ >= 14) { n = 0; buf.append(" "); } } if (i == start + len - 2) { int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 255) << 8); buf.append(legalChars[(d >> 18) & 63]); buf.append(legalChars[(d >> 12) & 63]); buf.append(legalChars[(d >> 6) & 63]); buf.append("="); } else if (i == start + len - 1) { int d = (((int) data[i]) & 0x0ff) << 16; buf.append(legalChars[(d >> 18) & 63]); buf.append(legalChars[(d >> 12) & 63]); buf.append("=="); } return buf.toString(); } private static int decode(char c) { if (c >= 'A' && c <= 'Z') return ((int) c) - 65; else if (c >= 'a' && c <= 'z') return ((int) c) - 97 + 26; else if (c >= '0' && c <= '9') return ((int) c) - 48 + 26 + 26; else switch (c) { case '+': return 62; case '/': return 63; case '=': return 0; default: throw new RuntimeException("unexpected code: " + c); } } /** * Decodes the given Base64 encoded String to a new byte array. The byte * array holding the decoded data is returned. */ public static byte[] decode(String s) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { decode(s, bos); } catch (IOException e) { throw new RuntimeException(); } byte[] decodedBytes = bos.toByteArray(); try { bos.close(); bos = null; } catch (IOException ex) { System.err.println("Error while decoding BASE64: " + ex.toString()); } return decodedBytes; } private static void decode(String s, OutputStream os) throws IOException { int i = 0; int len = s.length(); while (true) { while (i < len && s.charAt(i) <= ' ') i++; if (i == len) break; int tri = (decode(s.charAt(i)) << 18) + (decode(s.charAt(i + 1)) << 12) + (decode(s.charAt(i + 2)) << 6) + (decode(s.charAt(i + 3))); os.write((tri >> 16) & 255); if (s.charAt(i + 2) == '=') break; os.write((tri >> 8) & 255); if (s.charAt(i + 3) == '=') break; os.write(tri & 255); i += 4; } } }
adnroid平臺的主函數:
public class main extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); String key = "12345678"; String text = "12345678"; try { String result1 = DES.encryptDES(text,key); String result2 = DES.decryptDES(result1, key); Log.i("DES encode text is ", result1); Log.i("DES encode text is ", result2); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
經過查看log日誌就能夠看到結果。
兩個平臺的結果是同樣的,都是:
加密結果:X2p9Uo45Tzk6Ntu6W7Ev+Q==解密結果:12345678