Encrypt and Decrypt by AES algorithm in both python and android

我想在 python 把敏感的資料先加密,傳給Android 後在Android裡解密,在stackoverflow 上面找到這一個範例,滿神奇的,程式碼貼進 python 和 android 就可以跑了,而且可以互相加/解密沒問題。

stackoverflow:
http://stackoverflow.com/questions/29013414/encrypt-and-decrypt-by-aes-algorithm-in-both-python-and-android


我的執行畫面:

上面 terminal 是 python 的執行結果,下面白色是Android Studio 執行結果,使用同一把的key,python 產生出來的base64 碼是:

hPbYdXXJ472jW2VsEOZLa5iBosENVulYO1xDPI23SsjvebY341uFOS5ZV

Android 產生的base64碼是:

oBOM8SX0a2YcLK2oltF1J/x+WqMP6sDj4Cbp0fPDlroepybDE1CuVsidjyIqNaeq

雖然2邊加密完成後的所產生的base64碼長的不一樣,但是把 Android 產生出來的base64碼放到python裡去解,可以解出一樣的資料出來。

再更進階一點點的做法是加入Spongy Castle或Bouncy Castle:
http://stackoverflow.com/questions/6898801/how-to-include-the-spongy-castle-jar-in-android

* 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. https://rtyley.github.io/spongycastle/
* 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;
}
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *