Encrypt and Decrypt by AES algorithm in both python and android
我想在 python 把敏感的資料先加密,傳給Android 後在Android裡解密,在stackoverflow 上面找到這一個範例,滿神奇的,程式碼貼進 python 和 android 就可以跑了,而且可以互相加/解密沒問題。
我的執行畫面:
上面 terminal 是 python 的執行結果,下面白色是Android Studio 執行結果,使用同一把的key,python 產生出來的base64 碼是:
hPbYdXXJ472jW2VsEOZLa5iBosENVulYO1xDPI23SsjvebY341uFOS5ZV
Android 產生的base64碼是:
oBOM8SX0a2YcLK2oltF1J/x+WqMP6sDj4Cbp0fPDlroepybDE1CuVsidjyIqNaeq
雖然2邊加密完成後的所產生的base64碼長的不一樣,但是把 Android 產生出來的base64碼放到python裡去解,可以解出一樣的資料出來。
* Unfortunately, Android SDK doesn`t support PBKDF2WithHmacSHA256, so we use Spongy Castle, which is the stock Bouncy Castle libraries with a couple of small changes to make it work on Android.
* For version 1.47 or higher of SpongyCastle, we can invoke PBKDF2WithHmacSHA256 directly,
* but for versions below 1.47, we could not specify SHA256 digest and it defaulted to SHA1.
* see
* 1.
* 2. http://stackoverflow.com/a/15303291/3962551
* 3. https://en.wikipedia.org/wiki/Bouncy_Castle_(cryptography)
Bouncy Castle
充氣城堡軍團(Legion of the Bouncy Castle)是一個來自澳大利亞的慈善團體,他們編寫了Bouncy Castle這個廣泛使用的類庫。該庫既提供了一個輕量級的密碼學 API,也是一個 Java 密碼擴充套件(JCE)的提供者。安卓平臺已經內建了一個精簡過的老版本 Bouncy Castle(同時為了適配安卓平臺也做了一些細小的改動)。
Spongy Castle
Spongy Castle 背後的動機是允許安卓開發者在應用程式中使用任意版本的 BouncyCastle 類庫。SpongyCastle 就是對最新版本的 BouncyCastle 進行了簡單地重新打包。
Python code :
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher:
def __init__(self, key):
self.bs = 16
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, message):
message = self._pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(message)).decode('utf-8')
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
@staticmethod
def _unpad(s):
return s[:-ord(s[len(s)-1:])]
Android Code:
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import android.annotation.SuppressLint;
import android.location.Criteria;
import android.util.Base64;
import android.util.Log;
@SuppressLint("NewApi")
public class Crypt {
private static final String tag = Crypt.class.getSimpleName();
private static final String characterEncoding = "UTF-8";
private static final String cipherTransformation = "AES/CBC/PKCS5Padding";
private static final String aesEncryptionAlgorithm = "AES";
private static final String key = "this is my key";
private static byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
private static byte[] keyBytes;
private static Crypt instance = null;
Crypt()
{
SecureRandom random = new SecureRandom();
Crypt.ivBytes = new byte[16];
random.nextBytes(Crypt.ivBytes);
}
public static Crypt getInstance() {
if(instance == null){
instance = new Crypt();
}
return instance;
}
public String encrypt_string(final String plain) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException
{
return Base64.encodeToString(encrypt(plain.getBytes()), Base64.DEFAULT);
}
public String decrypt_string(final String plain) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, ClassNotFoundException, IOException
{
byte[] encryptedBytes = decrypt(Base64.decode(plain, 0));
return Base64.encodeToString( encryptedBytes, Base64.DEFAULT);
}
public byte[] encrypt( byte[] mes)
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
IllegalBlockSizeException,
BadPaddingException, IOException {
keyBytes = key.getBytes("UTF-8");
Log.d(tag,"Long KEY: "+keyBytes.length);
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(keyBytes);
keyBytes = md.digest();
Log.d(tag,"Long KEY: "+keyBytes.length);
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec newKey = new SecretKeySpec(keyBytes, aesEncryptionAlgorithm);
Cipher cipher = null;
cipher = Cipher.getInstance(cipherTransformation);
SecureRandom random = new SecureRandom();
Crypt.ivBytes = new byte[16];
random.nextBytes(Crypt.ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, newKey, random);
// cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
byte[] destination = new byte[ivBytes.length + mes.length];
System.arraycopy(ivBytes, 0, destination, 0, ivBytes.length);
System.arraycopy(mes, 0, destination, ivBytes.length, mes.length);
return cipher.doFinal(destination);
}
public byte[] decrypt( byte[] bytes)
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
IllegalBlockSizeException,
BadPaddingException, IOException, ClassNotFoundException {
keyBytes = key.getBytes("UTF-8");
Log.d(tag,"Long KEY: "+keyBytes.length);
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(keyBytes);
keyBytes = md.digest();
Log.d(tag,"Long KEY: "+keyBytes.length);
byte[] ivB = Arrays.copyOfRange(bytes,0,16);
Log.d(tag, "IV: "+new String(ivB));
byte[] codB = Arrays.copyOfRange(bytes,16,bytes.length);
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivB);
SecretKeySpec newKey = new SecretKeySpec(keyBytes, aesEncryptionAlgorithm);
Cipher cipher = Cipher.getInstance(cipherTransformation);
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
byte[] res = cipher.doFinal(codB);
return res;
}
}