Java (Android) AES
Contents
最後編輯時間 2017/05/27
平台
Android Studio
- compileSdkVersion 25
- buildToolsVersion “25.0.1”
Base64的轉換有使用到Android的函式庫,若是純Java的使用者必須自行搜尋替代方案。
金鑰初始化
AES的金鑰為SecretKey類型,以下是產生範例。
public SecretKey genAESKey() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException {
// AES key
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, new SecureRandom());
return keyGen.generateKey();
}
初始向量初始化
相同的資料在相同的金鑰下會被加密成同樣結果,因此我們必須在每次加密使用不同初始化向量(被知道也無妨),來避免同樣的加密結果。相當於灑鹽的功效。
以下是初始化向量的產生方法。
public IvParameterSpec genIV() throws NoSuchPaddingException, NoSuchAlgorithmException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte [] iVAES = new byte[ cipher.getBlockSize() ];
new SecureRandom().nextBytes(iVAES);
return new IvParameterSpec(iVAES);
}
將字串加密
金鑰初始模式為 ENCRYPT_MODE,代表接下來要執行加密。
要記得Cipher並非thread-safe(線程安全),因此每次加密最好重新產生一個Cipher。
在AES加密完之後,用Base64把結果轉成可讀的字串以便以http傳送。雖然不做Base64亦可,只是到時候要以byte為單位做傳送。
public String encrypt(String content, SecretKey secretKey, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte [] temp = cipher.doFinal( content.getBytes() );
return Base64.encodeToString(temp, Base64.DEFAULT);
}
將字串解密
金鑰初始模式為 DECRYPT_MODE,代表接下來要執行解密。
public String decrypt(String content, SecretKey secretKey, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
byte [] temp = Base64.decode(content, Base64.DEFAULT);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte [] decodeBytes = cipher.doFinal(temp);
return new String(decodeBytes);
}
匯出金鑰與初始化向量
SecretKey使用getEncoded()可以獲得金鑰,為byte形式,若要方便閱讀與傳送可以轉換為Base64。
初始化向量本身有getIV()這個method可以使用,一樣為byte形式,可轉換為Base64。
public String secretKeyToBase64(SecretKey secretKey){
return Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT);
}
public String ivToBase64(IvParameterSpec iv){
return Base64.encodeToString(iv.getIV(), Base64.DEFAULT);
}
匯入金鑰
想把之前匯出為Base64的金鑰重新匯入的話,要使用SercretKeySpec
這個類別。
public SecretKey base64ToSecretKey(String base64Key){
byte [] key = Base64.decode(base64Key, Base64.DEFAULT);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
return secretKeySpec;
}
簡單使用範例
以下為簡單的使用範例,將字串”Hello world”加密後輸出加密結果。再將加密資料解密回原始字串並輸出。
// 預備好金鑰和初始向量
String data = "Hello world";
SecretKey secretKey = genAESKey();
IvParameterSpec iv = genIV();
// 加密
String encryptedData = encrypt(data, secretKey, iv);
System.out.println("Encrypted Data:" + encryptedData);
// 解密
String decryptedData = decrypt(encryptedData, secretKey, iv);
System.out.println("Decrypted Data:" + decryptedData);