最近在做項目中,遇到des加密解密的問題。
場景是安卓app端用des加密,php這邊需要解密。之前沒有接觸過des這種加密解密算法,但想着肯定會有demo。因此百度,搜了代碼來用。網上代碼也是魚龍混雜,好不容易測試在php這邊測試加密和解密成功了。為確保安卓app端提交過來的加密參數能夠解密出來,給定安卓人員一個字符串,讓他們把des加密后的字符串給我,在php這邊解密。結果一看,加密出來的字符串跟我這邊加密出來的結果不一致,自然是解密不出來。
要來java的des算法代碼,研究加密的過程,其中各種調試測試,外加各種百度,必應。發現能夠正確解密的規則,其中非常重要的三點就是,加密解密過程,雙方的key、加密模式(例如ECB、CBC等),以及iv(有些地方叫它偏移量,有些地方叫它向量,沒有深入研究)需要一致。需要着重說明的是這個iv,在ECB加密模式(java默認的加密模式)時,是不需要這個iv的,即使寫了,也不會影響加密的結果;而當加密模式為CBC時,則需要iv這個參數,否則會隨機生成該參數,這樣每次加密的結果會變。而關鍵在於,java端定義了iv,那么php這邊也需要跟java端保持一致,這樣才能夠正確解密出來。
后面就膠着在這個iv上面。查看java端代碼,iv是一個byte[],即字節數組,想都沒有想就去網上找php將字符串轉化為byte[]類型的,也試過強制轉換,解密失敗。最后才想起來去看php的數據類型,呃呃呃,壓根就沒有type類型的。真是基礎不牢,又想當然,覺得java有該數據類型,php也會有。。。慣性思維真的害人啊。這怎么辦呢?既需要byte[]去解密,又沒有該數據數據類型,已經感覺無解了。
通常這個時候,需要休息,休息一會兒。后面突然念頭閃過,php中有函數可以des解密,該函數肯定不會用php沒有的數據類型去解密,所以我試着將java加密中轉換成byte[]類型前的字符串作為php的iv,測試,終於解密成功。
附上java端加密代碼,采用CBC模式:
importjavax.crypto.Cipher;importjavax.crypto.SecretKey;importjavax.crypto.SecretKeyFactory;importjavax.crypto.spec.DESKeySpec;importjavax.crypto.spec.IvParameterSpec;public classDES {//加密數據入口
public staticString encryptString(String message, String key)throwsException {byte[] bytes =encrypt(message, key);returntoHexString(bytes).toUpperCase();
}public static byte[] encrypt(String message, String key) throwsException {
Cipher cipher= Cipher.getInstance("DES/CBC/PKCS5Padding");
DESKeySpec desKeySpec= new DESKeySpec(key.getBytes("UTF-8"));
SecretKeyFactory keyFactory= SecretKeyFactory.getInstance("DES");
SecretKey secretKey=keyFactory.generateSecret(desKeySpec);
IvParameterSpec iv= new IvParameterSpec(key.getBytes("UTF-8"));
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);return cipher.doFinal(message.getBytes("UTF-8"));
}
}
附上對應的php端解密代碼:
classDES
{public static function decrypt($str, $key)
{$midstr = hex2bin(strtolower($str));//第二個參數$key就是三個重點中的$key,而最后一個參數$key是iv,只是java加密時采用了與第二個參數相同的字符串,根據具體情況來定就好
$str = mcrypt_decrypt(MCRYPT_DES, $key, $midstr, MCRYPT_MODE_CBC, $key);
$pad = ord($str[($len = strlen($str)) - 1]);return substr($str, 0, strlen($str) - $pad);
}
}
其中hex2bin是將十六進制轉換成二進制,php自帶該函數,不需要再另行定義(網上看到很多該函數的代碼)。解密可以看作是加密的逆操作,所以java端將加密后字符串轉成十六進制,並大寫,解密時自然需要轉換回來。
可以看看下面這個鏈接,寫得不錯: