支持Android7.0的AES加密文件

转载请注明 作者:jqorz
来源:https://blog.csdn.net/baidu_27419681/article/details/81587232

本来之前整理过一个Android端使用AES方式进行文件加密的方法,参见 Android端可用的AES加密/解密,已直接封装为文件加密 ,但是近日使用的时候,发现编译器会报错,提示
这里写图片描述
然后在网上找了一下资料,参考了
Android:7.0 后加密库 Crypto 被废弃后的爬坑指南
,与自己之前的代码相结合,整理了Android7.0以后可用的AES文件加密。
其中,将随机生成密钥的部分,为了省事,直接改为了密码得到的字节密钥,详情可以查看注释。

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

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;

/**
 * @author jqorz
 * @since 2018/8/4
 */
public class AES {
    private static final String TAG = AES.class.getSimpleName();
    private static String mSeed = "dfdas7894513xc21asd878ds4c5x1v32df4g56wr7qw89d43c1324165wef4w";


    /**
     * 将文件加密并更改后缀为ConsValue.LOCK_EXT
     */
    public static boolean lock(File file) {
        String sourcePath = file.getPath();
        return AESCipher(Cipher.ENCRYPT_MODE, sourcePath, file.getPath(), mSeed);
    }

    public static boolean unlock(File file) {
        String sourcePath = file.getPath();
        return AESCipher(Cipher.DECRYPT_MODE, sourcePath, file.getPath(), mSeed);
    }

    public static boolean AESCipher(int cipherMode, String sourceFilePath,
                                    String targetFilePath, String seed) {
        boolean result = false;
        FileChannel sourceFC = null;
        FileChannel targetFC = null;

        try {

            if (cipherMode != Cipher.ENCRYPT_MODE
                    && cipherMode != Cipher.DECRYPT_MODE) {
                return false;
            }

            Cipher mCipher = Cipher.getInstance("AES/CFB/NoPadding");

            byte[] rawkey = getRawKey(seed);
            File sourceFile = new File(sourceFilePath);
            File targetFile = new File(targetFilePath);

            sourceFC = new RandomAccessFile(sourceFile, "r").getChannel();
            targetFC = new RandomAccessFile(targetFile, "rw").getChannel();

            SecretKeySpec secretKey = new SecretKeySpec(rawkey, "AES");

            mCipher.init(cipherMode, secretKey, new IvParameterSpec(
                    new byte[mCipher.getBlockSize()]));

            ByteBuffer byteData = ByteBuffer.allocate(1024);
            while (sourceFC.read(byteData) != -1) {
                // 通过通道读写交叉进行。
                // 将缓冲区准备为数据传出状态
                byteData.flip();

                byte[] byteList = new byte[byteData.remaining()];
                byteData.get(byteList, 0, byteList.length);
                //此处,若不使用数组加密解密会失败,因为当byteData达不到1024个时,加密方式不同对空白字节的处理也不相同,从而导致成功与失败。
                byte[] bytes = mCipher.doFinal(byteList);
                targetFC.write(ByteBuffer.wrap(bytes));
                byteData.clear();
            }

            result = true;
        } catch (IOException | NoSuchAlgorithmException | InvalidKeyException
                | InvalidAlgorithmParameterException
                | IllegalBlockSizeException | BadPaddingException
                | NoSuchPaddingException | InvalidKeySpecException e) {
            e.printStackTrace();
        } finally {
            try {
                if (sourceFC != null) {
                    sourceFC.close();
                }
                if (targetFC != null) {
                    targetFC.close();
                }
            } catch (IOException e) {
                e.printStackTrace();

            }
        }

        return result;
    }

    /**
     * 使用一个安全的随机数来产生一个密匙,密匙加密使用的
     *
     * @param seed 密钥
     * @return 得到的安全密钥
     */
    private static byte[] getRawKey(String seed) throws NoSuchAlgorithmException, InvalidKeySpecException {

        // 密钥的比特位数,注意这里是比特位数
        // AES 支持 128、192 和 256 比特长度的密钥
        int keyLength = 256;
        // 盐值的字节数组长度,注意这里是字节数组的长度
        // 其长度值需要和最终输出的密钥字节数组长度一致
        // 由于这里密钥的长度是 256 比特,则最终密钥将以 256/8 = 32 位长度的字节数组存在
        // 所以盐值的字节数组长度也应该是 32

        // 先获取一个随机的盐值
        // 你需要将此次生成的盐值保存到磁盘上下次再从字符串换算密钥时传入
        // 如果盐值不一致将导致换算的密钥值不同
        // 保存密钥的逻辑官方并没写,需要自行实现
//        int saltLength = 32;
//        SecureRandom random = new SecureRandom();
//        byte[] salt = new byte[saltLength];
//        random.nextBytes(salt);

        //为了省事,直接用密码的字节
        byte[] salt = seed.getBytes();
        // 将密码明文、盐值等使用新的方法换算密钥
        int iterationCount = 1000;
        KeySpec keySpec = new PBEKeySpec(seed.toCharArray(), salt,
                iterationCount, keyLength);
        SecretKeyFactory keyFactory = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        // 到这里你就能拿到一个安全的密钥了
        byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
        SecretKey key = new SecretKeySpec(keyBytes, "AES");
        return key.getEncoded();
    }


}

经过测试,可以正常使用

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值