需求:在安卓客戶端中灭返,使用AES算法對字符串內(nèi)容加密并發(fā)送到后臺秩彤,后臺使用php對加密內(nèi)容進(jìn)行解密杨凑。
1刽宪、Java
先看AESUtil_0文件的getKey方法:
private static Key getKey(@NotNull String password) throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(password.getBytes());
keyGenerator.init(128, random);
SecretKey secretKey = keyGenerator.generateKey();
byte[] enKeyBytes = secretKey.getEncoded();
return new SecretKeySpec(enKeyBytes, "AES");
}
對于AES算法,SecretKeySpec只能接受長度為16的byte數(shù)組诚卸。假如你的password的長度固定是16的葵第,可以直接
return new SecretKeySpec(password.getBytes(), "AES");
但是長度不是16時,會報InvalidKeyException錯誤合溺,因此在加密解密操作前卒密,需要先對password進(jìn)行處理。網(wǎng)上很多方法都是使用SHA1PRNG隨機算法棠赛,以password為種子哮奇,將長度設(shè)置成128位(1byte=8bit),生成一個長度為16的byte數(shù)組睛约。只要password一樣鼎俘,每次生成的數(shù)組都是一樣的,所以可以用來做加密解密的key辩涝。但是使用SHA1PRNG有一個問題贸伐,就是在php中沒有現(xiàn)成的方法實現(xiàn)。除非自己用php實現(xiàn)一個SHA1PRNG算法膀值,否則不能利用password生成同樣的key來解密內(nèi)容棍丐。因此最好換一種方法對password進(jìn)行處理。
現(xiàn)在看AESUtil_1文件:
/**
* 生成salt, 用于對password進(jìn)行處理沧踏。
* 加密和解密時用到的salt必須一致, 否則解密不了歌逢。
*/
@Nullable
public static byte[] createSalt() {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
random.nextBytes(salt);
return salt;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
private static Key getKey(@NotNull String password, @NotNull byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
int iterations = 1000;
int keySize = 128;
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keySize);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] bytes = factory.generateSecret(spec).getEncoded();
return new SecretKeySpec(bytes, "AES");
}
網(wǎng)上有人推薦PBKDF2算法,要使用這個算法翘狱,先要用SHA1PRNG算法生成一個隨機byte數(shù)組salt秘案,并利用salt對password處理得到加密解密的key。這樣做有個好處就是每次生成的key都是不一樣的潦匈,降低password被破解的風(fēng)險阱高,但是保存加密內(nèi)容的同時也要保存對應(yīng)的salt,因為解密時salt必須相同才能得到正確的內(nèi)容茬缩。
得到key之后赤惊,加密內(nèi)容的方法就比較簡單了:
/**
* 加密方法
*
* @param content 要被加密的內(nèi)容
* @param password 密碼
* @return 被加密后的內(nèi)容
*/
@Nullablepublic static String encrypt(@NotNull String content, @NotNull String password, @NotNull byte[] salt) {
try {
Key key = getKey(password, salt);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");// 創(chuàng)建密碼器
byte[] contentBytes = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(contentBytes);
return parseByte2HexStr(result); // 加密
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
得到加密的內(nèi)容后,為了方便凰锡,我把salt經(jīng)過parseByte2HexStr方法處理得到的字符串加在encrypt得到的字符串前面未舟,并一起發(fā)給后臺服務(wù)器圈暗。具體實現(xiàn)看java項目的main方法。
2裕膀、php
php解密比較簡單员串,在請求中拿到salt和加密內(nèi)容后調(diào)用Util::decrypt方法進(jìn)行解密。
<?php
class Util
{
public static function parseHexStr2Str($hexStr)
{
$str = "";
for ($i = 0, $size = strlen($hexStr) / 2; $i < $size; $i++) {
$c = hexdec(substr($hexStr, $i * 2, 2));
$str .= chr($c);
}
return $str;
}
public static function decrypt($content, $password, $salt)
{
// 這個方法得到的字符串類似于java工程中parseByte2HexStr(@NotNull byte buf[])得到的字符串;
// 數(shù)組buf的長度是16, 對應(yīng)等到的字符串長度為32, 因此這里第五個參數(shù)填32昼扛。
$hash = hash_pbkdf2("sha1", $password, $salt, 1000, 32);// 打開算法和模式對應(yīng)的模塊
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$hex_iv = '00000000000000000000000000000000';
mcrypt_generic_init($td, Util::parseHexStr2Str($hash), Util::parseHexStr2Str($hex_iv));
$resultStr = mdecrypt_generic($td, $content);// 釋放加密模塊資源
mcrypt_generic_deinit($td);
mcrypt_module_close($td);return $resultStr;
}
}
define("PASSWORD", "12345678");
if (isset($_POST["content"])) {
$content = $_POST["content"];// 前32位是salt
$salt = substr($content, 0, 32);echo "salt = " . $salt . "\n";// 剩余的是已加密的內(nèi)容
$enContent = substr($content, 32);
echo "enContent = " . $enContent . "\n";
$result = Util::decrypt(Util::parseHexStr2Str($enContent), PASSWORD, Util::parseHexStr2Str($salt));
echo "result = " . $result . "\n";
} else {
echo "沒有內(nèi)容";
}
參考:
http://blog.csdn.net/u012964281/article/details/40453873
http://stackoverflow.com/questions/31623866/java-aes-class-convert-to-php
http://stackoverflow.com/questions/31499222/unable-to-decrypt-string-in-android-app/31500093#31500093
http://stackoverflow.com/questions/19196728/aes-128-encryption-in-java-decryption-in-php