網上的demo一搜一大堆,但是,基本上都是一知半解(包括我)。爲什麼呢?我在嘗試分別在兩個平臺加密的時候,竟然發現Android DES 加密和java DES加密的程序不能互通。就是加密的結果不一樣,更不要說Android平臺的加密輸入作爲java DES的解密輸出了。這樣的話,客戶端和服務器端就不能進行通信了。我網上之前也發帖子問了不少人,但是回答都不滿意。
今天部門的另外一個同事跟我說了一下,才解決了這個不能互通的問題。
調用DES加密算法包最精要的就是下面兩句話:
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
CBC是工作模式,DES一共有電子密碼本模式(ECB)、加密分組鏈接模式(CBC)、加密反饋模式(CFB)和輸出反饋模式(OFB)四種模式,
PKCS5Padding是填充模式,還有其它的填充模式:
然後,cipher.init()一共有三個參數:Cipher.ENCRYPT_MODE, key, zeroIv,zeroIv就是初始化向量,一個8爲字符數組。
工作模式、填充模式、初始化向量這三種因素一個都不能少。否則,如果你不指定的話,那麼就要程序就要調用默認實現。問題就來了,這就與平臺有關了。難怪網上一搜"DES加密結果不一致“,出現n多網頁結果。(之前我並沒有指定IV,被折磨了2周)
源程序如下(從java平臺到android平臺,我根本沒有更改一行代碼):
另外,一般情況下,加密後的結果都會用base64編碼進行傳輸。
java平臺:
主程序
01 |
public class testDES
{ |
02 |
03 |
/** |
04 |
*
@param args |
05 |
*
@throws Exception |
06 |
*/ |
07 |
public static void main(String[]
args) throws Exception
{ |
08 |
//
TODO Auto-generated method stub |
09 |
String
key = "12345678" ; |
10 |
String
text = "12345678" ; |
11 |
String
result1 = DES.encryptDES(text,key); |
12 |
String
result2 = DES.decryptDES(result1, key); |
13 |
System.out.println(result1); |
14 |
System.out.println(result2); |
15 |
} |
16 |
} |
用到的DES加密類
01 |
import javax.crypto.Cipher; |
02 |
import javax.crypto.spec.IvParameterSpec; |
03 |
import javax.crypto.spec.SecretKeySpec; |
04 |
05 |
public class DES
{ |
06 |
private static byte []
iv = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 }; |
07 |
public static String
encryptDES(String encryptString, String encryptKey) throws Exception
{ |
08 |
//
IvParameterSpec zeroIv = new IvParameterSpec(new byte[8]); |
09 |
IvParameterSpec
zeroIv = new IvParameterSpec(iv); |
10 |
SecretKeySpec
key = new SecretKeySpec(encryptKey.getBytes(), "DES" ); |
11 |
Cipher
cipher = Cipher.getInstance( "DES/CBC/PKCS5Padding" ); |
12 |
cipher.init(Cipher.ENCRYPT_MODE,
key, zeroIv); |
13 |
byte []
encryptedData = cipher.doFinal(encryptString.getBytes()); |
14 |
|
15 |
return Base64.encode(encryptedData); |
16 |
} |
17 |
public static String
decryptDES(String decryptString, String decryptKey) throws Exception
{ |
18 |
byte []
byteMi = new Base64().decode(decryptString); |
19 |
IvParameterSpec
zeroIv = new IvParameterSpec(iv); |
20 |
//
IvParameterSpec zeroIv = new IvParameterSpec(new byte[8]); |
21 |
SecretKeySpec
key = new SecretKeySpec(decryptKey.getBytes(), "DES" ); |
22 |
Cipher
cipher = Cipher.getInstance( "DES/CBC/PKCS5Padding" ); |
23 |
cipher.init(Cipher.DECRYPT_MODE,
key, zeroIv); |
24 |
byte decryptedData[]
= cipher.doFinal(byteMi); |
25 |
|
26 |
return new String(decryptedData); |
27 |
} |
28 |
} |
用到的BASE64工具類:
001 |
import java.io.ByteArrayOutputStream; |
002 |
import java.io.IOException; |
003 |
import java.io.OutputStream; |
004 |
005 |
006 |
007 |
public class Base64
{ |
008 |
private static final char []
legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" .toCharArray(); |
009 |
/** |
010 |
*
data[]進行編碼 |
011 |
*
@param data |
012 |
*
@return |
013 |
*/ |
014 |
public static String
encode( byte []
data) { |
015 |
int start
= 0 ; |
016 |
int len
= data.length; |
017 |
StringBuffer
buf = new StringBuffer(data.length
* 3 / 2 ); |
018 |
019 |
int end
= len - 3 ; |
020 |
int i
= start; |
021 |
int n
= 0 ; |
022 |
023 |
while (i
<= end) { |
024 |
int d
= (((( int )
data[i]) & 0x0ff )
<< 16 ) |
025 |
|
(((( int )
data[i + 1 ])
& 0x0ff )
<< 8 ) |
026 |
|
((( int )
data[i + 2 ])
& 0x0ff ); |
027 |
028 |
buf.append(legalChars[(d
>> 18 )
& 63 ]); |
029 |
buf.append(legalChars[(d
>> 12 )
& 63 ]); |
030 |
buf.append(legalChars[(d
>> 6 )
& 63 ]); |
031 |
buf.append(legalChars[d
& 63 ]); |
032 |
033 |
i
+= 3 ; |
034 |
035 |
if (n++
>= 14 )
{ |
036 |
n
= 0 ; |
037 |
buf.append( "
" ); |
038 |
} |
039 |
} |
040 |
041 |
if (i
== start + len - 2 )
{ |
042 |
int d
= (((( int )
data[i]) & 0x0ff )
<< 16 ) |
043 |
|
(((( int )
data[i + 1 ])
& 255 )
<< 8 ); |
044 |
045 |
buf.append(legalChars[(d
>> 18 )
& 63 ]); |
046 |
buf.append(legalChars[(d
>> 12 )
& 63 ]); |
047 |
buf.append(legalChars[(d
>> 6 )
& 63 ]); |
048 |
buf.append( "=" ); |
049 |
} else if (i
== start + len - 1 )
{ |
050 |
int d
= ((( int )
data[i]) & 0x0ff )
<< 16 ; |
051 |
052 |
buf.append(legalChars[(d
>> 18 )
& 63 ]); |
053 |
buf.append(legalChars[(d
>> 12 )
& 63 ]); |
054 |
buf.append( "==" ); |
055 |
} |
056 |
057 |
return buf.toString(); |
058 |
} |
059 |
060 |
private static int decode( char c)
{ |
061 |
if (c
>= 'A' &&
c <= 'Z' ) |
062 |
return (( int )
c) - 65 ; |
063 |
else if (c
>= 'a' &&
c <= 'z' ) |
064 |
return (( int )
c) - 97 + 26 ; |
065 |
else if (c
>= '0' &&
c <= '9' ) |
066 |
return (( int )
c) - 48 + 26 + 26 ; |
067 |
else |
068 |
switch (c)
{ |
069 |
case '+' : |
070 |
return 62 ; |
071 |
case '/' : |
072 |
return 63 ; |
073 |
case '=' : |
074 |
return 0 ; |
075 |
default : |
076 |
throw new RuntimeException( "unexpected
code: " +
c); |
077 |
} |
078 |
} |
079 |
080 |
/** |
081 |
*
Decodes the given Base64 encoded String to a new byte array. The byte |
082 |
*
array holding the decoded data is returned. |
083 |
*/ |
084 |
085 |
public static byte []
decode(String s) { |
086 |
087 |
ByteArrayOutputStream
bos = new ByteArrayOutputStream(); |
088 |
try { |
089 |
decode(s,
bos); |
090 |
} catch (IOException
e) { |
091 |
throw new RuntimeException(); |
092 |
} |
093 |
byte []
decodedBytes = bos.toByteArray(); |
094 |
try { |
095 |
bos.close(); |
096 |
bos
= null ; |
097 |
} catch (IOException
ex) { |
098 |
System.err.println( "Error
while decoding BASE64: " +
ex.toString()); |
099 |
} |
100 |
return decodedBytes; |
101 |
} |
102 |
103 |
private static void decode(String
s, OutputStream os) throws IOException
{ |
104 |
int i
= 0 ; |
105 |
106 |
int len
= s.length(); |
107 |
108 |
while ( true )
{ |
109 |
while (i
< len && s.charAt(i) <= '
' ) |
110 |
i++; |
111 |
112 |
if (i
== len) |
113 |
break ; |
114 |
115 |
int tri
= (decode(s.charAt(i)) << 18 ) |
116 |
+
(decode(s.charAt(i + 1 ))
<< 12 ) |
117 |
+
(decode(s.charAt(i + 2 ))
<< 6 ) |
118 |
+
(decode(s.charAt(i + 3 ))); |
119 |
120 |
os.write((tri
>> 16 )
& 255 ); |
121 |
if (s.charAt(i
+ 2 )
== '=' ) |
122 |
break ; |
123 |
os.write((tri
>> 8 )
& 255 ); |
124 |
if (s.charAt(i
+ 3 )
== '=' ) |
125 |
break ; |
126 |
os.write(tri
& 255 ); |
127 |
128 |
i
+= 4 ; |
129 |
} |
130 |
} |
131 |
|
132 |
} |
adnroid平臺的主函數:
01 |
public class main extends Activity
{ |
02 |
/**
Called when the activity is first created. */ |
03 |
@Override |
04 |
public void onCreate(Bundle
savedInstanceState) { |
05 |
super .onCreate(savedInstanceState); |
06 |
setContentView(R.layout.main); |
07 |
|
08 |
String
key = "12345678" ; |
09 |
String
text = "12345678" ; |
10 |
|
11 |
|
12 |
try { |
13 |
String
result1 = DES.encryptDES(text,key); |
14 |
String
result2 = DES.decryptDES(result1, key); |
15 |
Log.i( "DES
encode text is " ,
result1); |
16 |
Log.i( "DES
encode text is " ,
result2); |
17 |
} catch (Exception
e) { |
18 |
//
TODO Auto-generated catch block |
19 |
e.printStackTrace(); |
20 |
} |
21 |
} |
22 |
} |
通過查看log日誌就可以看到結果。
兩個平臺的結果是一樣的,都是:
加密結果:X2p9Uo45Tzk6Ntu6W7Ev+Q==
解密結果:12345678