Android 中数据加密 ---- AES加密

本文详细介绍如何在Android平台使用AES加密算法进行数据加密和解密,包括CBC模式和PKCS5Padding填充方式的具体实现,以及通过Cipher接口进行加密解密操作的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

对于AES加密,在博文  AES 加密 已经有了详细说明,这边博文将其用Android 实现。

 

更多的加密可以看:

数据加密 ---- 总篇

Android 中数据加密 ---- 异或加密

Android 中数据加密 ---- DES加密

Android 中数据加密 ---- 3DES加密

Android 中数据加密 ---- MD5加密

Android 中数据加密 ---- SHA加密

Android 中数据加密 ---- RSA加密

 

实例:

所需公共类 BlockEncryptionHelper 如下:

public class BlockEncryptionHelper {
    /**
     * get a random secret key.
     * must use the same key for encrypting and decrypting.
     */
    private static SecretKeySpec getRandomSecretKeySpec(String alg, String key) {
        try {
            SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
            sr.setSeed(key.getBytes("UTF-8"));

            KeyGenerator gen = KeyGenerator.getInstance(alg);
            gen.init(128, sr);

            SecretKey secKey = gen.generateKey();
            return new SecretKeySpec(secKey.getEncoded(), alg);
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    /**
     * get a stationary secret key.
     * must use the same key for encryption and decryption.
     */
    private static SecretKey getSecretKey(String alg, String key) {
        try {
            byte[] rawKey = key.getBytes("UTF-8");
            return new SecretKeySpec(rawKey, alg);
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    public static Cipher getCipher(int mode, String alg, String key) {
        if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
            return null;

        SecretKey secretKey = getSecretKey(alg, key);
        if (secretKey == null)
            return null;

        if (TextUtils.equals(alg, "DES") || TextUtils.equals(alg, "DESede")) {
            key = key.substring(0, 8);
        }
        try {
            IvParameterSpec ivp = new IvParameterSpec(key.getBytes("UTF-8"));
            Cipher cipher = Cipher.getInstance(alg + "/CBC/PKCS5Padding");
            cipher.init(mode, secretKey, ivp);

            return cipher;
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    public static byte[] encrypt(byte[] rawValue, Cipher cipher) {
        if (cipher == null) {
            return null;
        }
        byte[] encrypted = null;
        try {
            encrypted = cipher.doFinal(rawValue);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return encrypted;
    }

}

其中AES 的加密模式是CBC,填充模式的PKCS5Padding。最终通过cipher.doFinal 进入加密、解密功能。

另外,这里用到的Cipher 是针对部分双向加密的算法接口,具体的双向加密可以看  数据加密 ---- 总篇

 

算法处理的过程提炼到BlockEncryption 类中:

public abstract class BlockEncryption {
    private static final String TAG = "BlockEncryption";

    private Cipher mEncrytCipher = null;
    private Cipher mDecrytCipher = null;

    private Context mContext;
    private EncryptionCallback mCallback;

    // whether delete the source file.
    private boolean mDeleteSrcFile = true;

    private static final int BUFFER_SIZE = 4096;

    public BlockEncryption() {
    }

    public BlockEncryption(Context context, boolean deleteSrcFile, EncryptionCallback callback) {
        mContext = context;
        mDeleteSrcFile = deleteSrcFile;
        mCallback = callback;
    }

    private int roundUpBlockSize(int bufferSize) {
        return bufferSize + getBlockSize();
    }

    public abstract String getAlgorithm();
    public abstract Cipher getCipher(int mode);

    /**
     * Set the block size for padding.
     * Different size for different algorithm. e.g 16 bytes for AES.
     */
    protected abstract int getBlockSize();

    /**
     * Different algorithms may use different buffer size.
     */
    protected int getBufferSize() {
        return BUFFER_SIZE;
    }

    private byte[] encrypt(byte[] rawData, Cipher cipher) {
        return BlockEncryptionHelper.encrypt(rawData, cipher);
    }

    /**
     * Decrypt files with algorithm AES or DES. When {@value destPath} is null or empty, save the file
     * into current directory or into original directory.
     */
    public boolean decryptFile(String srcPath, String destPath) {
        Log.d(TAG, "==== decryptFile, srcPath = " + srcPath + ", destPath = " + destPath);
        File srcFile = new File(srcPath);
        if (!FileOperation.isFileExists(srcFile)) {
            return false;
        }

        if (mDecrytCipher == null) {
            mDecrytCipher = getCipher(Cipher.DECRYPT_MODE);
        }

        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        File destFile = null;

        ...

        try {
            fileInputStream = new FileInputStream(srcFile);

            ...

            // decrypt file
            int cnt;
            byte[] buf = new byte[roundUpBlockSize(getBufferSize())];
            while ((cnt = fileInputStream.read(buf)) >= 0) {
                byte[] rawData = buf;
                if (cnt != buf.length)
                    rawData = FileOperation.subBytes(buf, 0, cnt);
                byte[] decryptData = encrypt(rawData, mDecrytCipher);
                if (decryptData != null)
                    fileOutputStream.write(decryptData, 0, decryptData.length);
                else
                    return false;
            }

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            ...
            ...
        }

        ...

        return true;
    }

    /**
     * Encrypt file with algorithm AES or DES. When {@value destPath} is null or empty, create a new
     * file in current directory, and the file name is random with 32 characters.
     */
    public boolean encryptFile(String srcPath, String destPath) {
        Log.d(TAG, "==== encryptFile, srcPath = " + srcPath + ", destPath = " + destPath);
        File srcFile = new File(srcPath);
        if (!FileOperation.isFileExists(srcFile)) {
            return false;
        }

        if (mEncrytCipher == null) {
            mEncrytCipher = getCipher(Cipher.ENCRYPT_MODE);
        }

        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;

        ...

        try {
            fileInputStream = new FileInputStream(srcFile);
            fileOutputStream = new FileOutputStream(destFile);

            // encrypt file
            int cnt;
            byte[] buf = new byte[getBufferSize()];
            while ((cnt = fileInputStream.read(buf)) >= 0) {
                byte[] rawData = buf;
                if (cnt != buf.length)
                    rawData = FileOperation.subBytes(buf, 0, cnt);
                byte[] encryptData = encrypt(rawData, mEncrytCipher);
                fileOutputStream.write(encryptData, 0, encryptData.length);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        } finally {
            ...
            ...
        }

        ...

        return true;
    }

    public String strEncryption(String src) {
        byte[] plainText = src.getBytes();
        if (mEncrytCipher == null) {
            mEncrytCipher = getCipher(Cipher.ENCRYPT_MODE);
        }

        byte[] cipherText = encrypt(plainText, mEncrytCipher);

        try {
            return new String(Base64.encode(cipherText, Base64.DEFAULT), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    public String strDecryption(String src) {
        byte[] cipherText = null;
        try {
            cipherText = Base64.decode(src.getBytes("UTF-8"), Base64.DEFAULT);
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        if (mDecrytCipher == null) {
            mDecrytCipher = getCipher(Cipher.DECRYPT_MODE);
        }

        byte[] plainText = encrypt(cipherText, mDecrytCipher);

        try {
            return new String(plainText, "utf-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }
}

注意其中的几个抽象函数,都是跟算法相关的,所以抽象出来再具体的算法中具体定义。

 

针对AES 算法,抽象函数实现的定义在AESEncryption 类中:

public class AESEncryption extends BlockEncryption{
    private static final String DEFAULT_KEY = "shiftencrypt0926";
    public static final String ALGORITHM = "AES";
    private static final int AES_BLOCK_SIZE = 16;

    public AESEncryption(Context context, boolean deleteSrcFile, EncryptionCallback callback) {
        super(context, deleteSrcFile, callback);
    }

    @Override
    public Cipher getCipher(int mode) {
        return BlockEncryptionHelper.getCipher(mode, ALGORITHM, DEFAULT_KEY);
    }

    @Override
    public String getAlgorithm() {
        return ALGORITHM;
    }

    @Override
    protected int getBlockSize() {
        return AES_BLOCK_SIZE;
    }
}

这里的block size 很重要,在算法中需要引进数据的取整,在数据不够块大小(AES 块大小为128位,详细看AES加密算法的详细介绍与实现)需要填充,所以最后的数据加密都是以128位(16字节)为单位。

 

测试用的Activity (Button 实现onClick 监听)中实现如下:

private void testEncryptionAES() {
        Button eaes = (Button) findViewById(R.id.encrypt_aes);
        eaes.setOnClickListener(this);

        Button daes = (Button) findViewById(R.id.decrypt_aes);
        daes.setOnClickListener(this);
        
    }

    private void aesEncryption() {
        if (mFileEncryption != null) {
            Log.d(TAG, "==== test file, encryption with aes ...");
            String filePath = "/storage/emulated/0/hehe.png";
            String destPath = "/storage/emulated/0/2.png";
            mFileEncryption.setAlgorithm(FileEncryption.ALG_MODE_AES);
            mFileEncryption.start(FileEncryption.MODE_ENCRYPT, filePath, destPath);
            Log.d(TAG, "==== test file, encryption end");
        }
    }

    private void aesDecryption() {
        if (mFileEncryption != null) {
            Log.d(TAG, "==== test file, decryption with aes ...");
            String filePath = "/storage/emulated/0/hehe.png";
            String destPath = "/storage/emulated/0/2.png";
            mFileEncryption.setAlgorithm(FileEncryption.ALG_MODE_AES);
            mFileEncryption.start(FileEncryption.MODE_DECRYPT, destPath, filePath);
            Log.d(TAG, "==== test file, decryption end");
        }
    }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值