Glide加载AES加密的网络图片

Glide加载AES加密的网络图片

1. AES加密工具

public class CXAESUtil {

    public static final String AES_KEY ="thisisaeskey";

    private final static String HEX = "0123456789ABCDEF";
    private static final int keyLenght = 16;
    private static final String defaultV = "0";

    /**
     * 加密
     *
     * @param key
     *            密钥
     * @param src
     *            加密文本
     * @return
     * @throws Exception
     */
    public static String encrypt(String key, String src) throws Exception {
        // /src = Base64.encodeToString(src.getBytes(), Base64.DEFAULT);
        byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes();
        byte[] result = encrypt(rawKey, src.getBytes("utf-8"));
        // result = Base64.encode(result, Base64.DEFAULT);
        return toHex(result);
    }

    /**
     * 加密
     *
     * @param key
     *            密钥
     * @param src
     *            加密文本
     * @return
     * @throws Exception
     */
    public static String encrypt2Java(String key, String src) throws Exception {
        // /src = Base64.encodeToString(src.getBytes(), Base64.DEFAULT);
        byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes();
        byte[] result = encrypt2Java(rawKey, src.getBytes("utf-8"));
        // result = Base64.encode(result, Base64.DEFAULT);
        return toHex(result);
    }

    /**
     * 解密
     *
     * @param key
     *            密钥
     * @param encrypted
     *            待揭秘文本
     * @return
     * @throws Exception
     */
    public static String decrypt(String key, String encrypted) throws Exception {
        byte[] rawKey = toMakekey(key, keyLenght, defaultV).getBytes();// key.getBytes();
        byte[] enc = toByte(encrypted);
        // enc = Base64.decode(enc, Base64.DEFAULT);
        byte[] result = decrypt(rawKey, enc);
        // /result = Base64.decode(result, Base64.DEFAULT);
        return new String(result, "utf-8");
    }

    /**
     * 密钥key ,默认补的数字,补全16位数,以保证安全补全至少16位长度,android和ios对接通过
     * @param str
     * @param strLength
     * @param val
     * @return
     */
    private static String toMakekey(String str, int strLength, String val) {

        int strLen = str.length();
        if (strLen < strLength) {
            while (strLen < strLength) {
                StringBuffer buffer = new StringBuffer();
                buffer.append(str).append(val);
                str = buffer.toString();
                strLen = str.length();
            }
        }
        return str;
    }

    /**
     * 真正的加密过程
     * 1.通过密钥得到一个密钥专用的对象SecretKeySpec
     * 2.Cipher 加密算法,加密模式和填充方式三部分或指定加密算 (可以只用写算法然后用默认的其他方式)Cipher.getInstance("AES");
     * @param key
     * @param src
     * @return
     * @throws Exception
     */
    private static byte[] encrypt(byte[] key, byte[] src) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        // TODO: 2021/7/19
//        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(src);
        return encrypted;
    }

    /**
     * 真正的加密过程
     *
     * @param key
     * @param src
     * @return
     * @throws Exception
     */
    private static byte[] encrypt2Java(byte[] key, byte[] src) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        byte[] encrypted = cipher.doFinal(src);
        return encrypted;
    }

    /**
     * 真正的解密过程
     *
     * @param key
     * @param encrypted
     * @return
     * @throws Exception
     */
    private static byte[] decrypt(byte[] key, byte[] encrypted) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        // TODO: 2021/7/19
//        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }

    public static String toHex(String txt) {
        return toHex(txt.getBytes());
    }

    public static String fromHex(String hex) {
        return new String(toByte(hex));
    }


    /**
     * 把16进制转化为字节数组
     * @param hexString
     * @return
     */
    public static byte[] toByte(String hexString) {
        int len = hexString.length() / 2;
        byte[] result = new byte[len];
        for (int i = 0; i < len; i++)
            result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
        return result;
    }


    /**
     * 二进制转字符,转成了16进制
     * 0123456789abcdefg
     * @param buf
     * @return
     */
    public static String toHex(byte[] buf) {
        if (buf == null)
            return "";
        StringBuffer result = new StringBuffer(2 * buf.length);
        for (int i = 0; i < buf.length; i++) {
            appendHex(result, buf[i]);
        }
        return result.toString();
    }

    private static void appendHex(StringBuffer sb, byte b) {
        sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
    }

    /**
     * 初始化 AES Cipher
     * @param sKey
     * @param cipherMode
     * @return
     */
    public static Cipher initAESCipher(String sKey, int cipherMode) {
        // 创建Key gen
        // KeyGenerator keyGenerator = null;
        Cipher cipher = null;
        try {
            /*
             * keyGenerator = KeyGenerator.getInstance("AES");
             * keyGenerator.init(128, new SecureRandom(sKey.getBytes()));
             * SecretKey secretKey = keyGenerator.generateKey(); byte[]
             * codeFormat = secretKey.getEncoded(); SecretKeySpec key = new
             * SecretKeySpec(codeFormat, "AES"); cipher =
             * Cipher.getInstance("AES"); //初始化 cipher.init(cipherMode, key);
             */
            byte[] rawKey = toMakekey(sKey, keyLenght, defaultV).getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
            cipher = Cipher.getInstance("AES");
            // TODO: 2021/7/19
//            cipher.init(cipherMode, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
            cipher.init(cipherMode, skeySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();  // To change body of catch statement use File |
            // Settings | File Templates.
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();  // To change body of catch statement use File |
            // Settings | File Templates.
        } catch (InvalidKeyException e) {
            e.printStackTrace();  // To change body of catch statement use File |
            // Settings | File Templates.
        }
//        catch (InvalidAlgorithmParameterException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
        return cipher;
    }

    /**
     * 对文件进行AES加密
     * @param sourceFile
     * @param toFilePath 加密到文件的全路径
     * @param sKey
     * @return
     */
    public static File encryptFile(File sourceFile, String toFilePath, String sKey) {
        // 新建临时加密文件
        File encrypfile = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(sourceFile);
            encrypfile = new File(toFilePath);
            outputStream = new FileOutputStream(encrypfile);
            Cipher cipher = initAESCipher(sKey, Cipher.ENCRYPT_MODE);
            // 以加密流写入文件
            CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
            byte[] cache = new byte[1024];
            int nRead = 0;
            while ((nRead = cipherInputStream.read(cache)) != -1) {
                outputStream.write(cache, 0, nRead);
                outputStream.flush();
            }
            cipherInputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();  // To change body of catch statement use File |
            // Settings | File Templates.
        } catch (IOException e) {
            e.printStackTrace();  // To change body of catch statement use File |
            // Settings | File Templates.
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();  // To change body of catch statement use
                // File | Settings | File Templates.
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();  // To change body of catch statement use
                // File | Settings | File Templates.
            }
        }
        return encrypfile;
    }

    public static InputStream decryptFileToInputStream(File sourceFile,String sKey){
        InputStream inputStream = null;
        //创建cipher对象,
        Cipher cipher = initAESCipher(sKey, Cipher.DECRYPT_MODE);
        //从源文件中读取,生成输入流
        try {
            inputStream = new FileInputStream(sourceFile);
            //根据输出流创建可以加密的输出流
            CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
            return cipherInputStream;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return inputStream;
    }

    /**
     * AES方式解密文件
     * @param sourceFile
     * @param toFilePath
     * @return
     */
    public static File decryptFile(File sourceFile, String toFilePath, String sKey) {
        File decryptFile = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            //解密后存放的目标文件
            decryptFile = new File(toFilePath);
            //创建cipher对象,
            Cipher cipher = initAESCipher(sKey, Cipher.DECRYPT_MODE);
            //从源文件中读取,生成输入流
            inputStream = new FileInputStream(sourceFile);
            //从目标文件中获取输出流
            outputStream = new FileOutputStream(decryptFile);
            //根据输出流创建可以加密的输出流
            CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
            //创建缓存
            byte[] buffer = new byte[1024];
            int r;
            //从输入流读取
            while ((r = inputStream.read(buffer)) >= 0) {
                //写入到输出流中
                cipherOutputStream.write(buffer, 0, r);
            }
            cipherOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();  // To change body of catch statement use File |
            // Settings | File Templates.
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();  // To change body of catch statement use
                // File | Settings | File Templates.
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();  // To change body of catch statement use
                // File | Settings | File Templates.
            }
        }
        return decryptFile;
    }

}

2. 自定义MyGlideModule

  • 前提是安装了glide依赖
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
  • 开始自定义
@GlideModule
public class MyGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        super.registerComponents(context, glide, registry);
        //这是关键
        registry.append(MyDptFile.class, InputStream.class,new DptModelLoader.DptLoaderFactory());
    }

    /**
     * 这里不开启,避免添加相同的modules两次
     * @return
     */
    @Override
    public boolean isManifestParsingEnabled() {
        return false;
    }
}

3. 自定义模型 MyDptFile

public class MyDptFile {

    private File file;

    public MyDptFile(File file) {
        this.file = file;
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }
}

4. 自定义 DptModelLoader

public class DptModelLoader implements ModelLoader<MyDptFile, InputStream> {

    private static final String TAG="mylog_DptModelLoader";

    @Nullable
    @Override
    public LoadData<InputStream> buildLoadData(@NonNull MyDptFile myDptFile, int width, int height, @NonNull Options options) {
//        Log.i(TAG,"buildLoadData:"+myDptFile.getFile().getAbsolutePath());
        return new LoadData<InputStream>(new ObjectKey(myDptFile),new DptModelLoader.DPtDataFetcher(myDptFile));
    }

    @Override
    public boolean handles(@NonNull MyDptFile myDptFile) {
        return true;
    }

    public static class DptLoaderFactory implements ModelLoaderFactory<MyDptFile, InputStream> {

        @NonNull
        @Override
        public ModelLoader<MyDptFile, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
            return new DptModelLoader();
        }

        @Override
        public void teardown() {
        }
    }

    public static class DPtDataFetcher implements DataFetcher<InputStream> {
        private MyDptFile file;

        public DPtDataFetcher(MyDptFile file) {
            this.file = file;
        }

        @Override
        public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
//            Log.i(TAG,"图片解密--1, file= "+file.getFile().getAbsolutePath());
            if (file==null){
                return;
            }
            //读取文件流,直接把文件流AES解密
            callback.onDataReady(CXAESUtil.decryptFileToInputStream(file.getFile(),CXAESUtil.AES_KEY));
        }
        /** 这里是用来释放IO流的*/
        @Override
        public void cleanup() {

        }

        @Override
        public void cancel() {

        }

        @NonNull
        @Override
        public Class<InputStream> getDataClass() {
            return InputStream.class;
        }

        @NonNull
        @Override
        public DataSource getDataSource() {
            return DataSource.LOCAL;
        }
    }

}

5. 图片下载模型 DptDownload

前提是安装了OkHttp的依赖,注意:这里可以替换任意的文件下载框架,只要能把文件下载就好了。

public class DptDownload {

    /**
     * 下载文件
     * @param url      文件网络路径
     * @param fileDir 文件下载本地路径(肯定是存储在的)
     * @param fileName 文件名称(包含后缀)
     * @param callBack 回调
     */
    public static void okHttpDownloadFile(String url, String fileDir, String fileName, final CallBackDownloadFile callBack) {
        final File incompleteFile = new File(fileDir, fileName + ".ysy");
        final File completeFile = new File(fileDir, fileName);
        if (!incompleteFile.exists()) {
            try {
                incompleteFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Request request = new Request.Builder().url(url).build();
        new OkHttpClient().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                incompleteFile.delete();
                callBack.onFailure(e);
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                Sink sink = Okio.sink(incompleteFile);
                BufferedSink bufferedSink = Okio.buffer(sink);
                bufferedSink.writeAll(response.body().source());
                bufferedSink.close();
                incompleteFile.renameTo(completeFile);
                if (bufferedSink != null) {
                    bufferedSink.close();
                }
//                callBack.onDownloadOk(response.body().string());
                callBack.onDownloadOk(completeFile.getAbsolutePath());
            }
        });
    }

    public interface CallBackDownloadFile{
        void onDownloadOk(String str);
        void onFailure(IOException e);
    }

}

6. 定义图片加载工具DptLoadImage

public class DptLoadImage {

    private static final String TAG="mylog_DptLoadImage";
    /** 缓存文件夹的名称,清除缓存时可以指定这个文件夹*/
    private static final String DPT_CACHE_DIR="dptCache";

    public static void load(String imgUrl, final ImageView imageView){
        final Context context=imageView.getContext();
        String externalCacheDirPath=context.getExternalCacheDir().getAbsolutePath();
        File dptFileCacheDir=new File(externalCacheDirPath+ File.separator+DPT_CACHE_DIR);
        if (!dptFileCacheDir.exists()){
            Log.i(TAG,"不存在缓存文件夹,进行创建");
            dptFileCacheDir.mkdir();
        }
        //根据图片url获取存储的文件名称,防止地址泄露
        try {
            String fileName= CXAESUtil.encrypt(CXAESUtil.AES_KEY,imgUrl);
            //判断这个文件本地是否存在
            File imageFile=new File(dptFileCacheDir,fileName);
            if (imageFile.exists()){
                //文件存在,直接加载文件,不用网络下载,直接进行解密成文件流
                Glide.with(context).load(new MyDptFile(imageFile)).placeholder(R.drawable.common_placeholder).into(imageView);
            }else{
                DptDownload.okHttpDownloadFile(imgUrl,dptFileCacheDir.getAbsolutePath(),fileName,new DptDownload.CallBackDownloadFile() {
                    @Override
                    public void onDownloadOk(final String str) {
                        ThreadUtils.runOnUI(new Runnable() {
                            @Override
                            public void run() {
                                Glide.with(context).load(new MyDptFile(new File(str))).into(imageView);
                            }
                        });
                    }

                    @Override
                    public void onFailure(IOException e) {
                        showErrorImage(imageView);
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
            showErrorImage(imageView);
        }
    }

    /** 显示错误图片*/
    private static void showErrorImage(ImageView imageView){
        Glide.with(imageView).load(R.drawable.common_placeholder).into(imageView);
    }

    /** 获取缓存存在的路径*/
    public static File getEptCachePath(Context context){
        String externalCacheDirPath=context.getExternalCacheDir().getAbsolutePath();
        return new File(externalCacheDirPath+ File.separator+DPT_CACHE_DIR);
    }

}

7. 使用方法

DptLoadImage.load(imgUrl,imageView);

8. 制作批量加密工具jar包,可命令行操作

  • 使用idea编辑器新建一个项目,添加commons-io-2.6.jar到项目中
  • 代码,只有一个类
public class Main {

    public static void main(String[] args) {
        if (args.length==0){
            helpPrint();
            return;
        }
        if (args[0].equals("-file")){
            if (args.length!=4){
                helpPrint();
                return;
            }
            fileEncrypt(args);
        }else if (args[0].equals("-dir")){
            if (args.length!=4){
                helpPrint();
                return;
            }
            dirEncrypt(args);
        }else if (args[0].equals("-str")){
            if (args.length!=3){
                helpPrint();
                return;
            }
            strEncrypt(args);
        }else{
            helpPrint();
        }
    }

    private static void helpPrint(){
        System.out.println("单文件加密: java -jar <*.jar> -file <AES密码> <源文件路径>   <加密后的文件路径>");
        System.out.println("文件夹加密: java -jar <*.jar> -dir  <AES密码> <源文件夹路径> <加密后文件夹路径>");
        System.out.println("字符串加密: java -jar <*.jar> -str  <AES密码> <源字符串>");
    }

    /**
     * 文件夹内所有文件加密
     * @param args
     */
    public static void dirEncrypt(String[] args){
        //第1个参数是密码, 第2个参数是源文件夹,第3个参数是加密后的文件夹,
        String aesKey=args[1];
        //源文件夹
        String sourceDirPath=args[2];
        //加密后的文件夹
        String encryptDirPath=args[3];
        //目标文件夹是否存在,存在就删除,重新建立
        File encryptDir=new File(encryptDirPath);
        if (encryptDir.exists()){
            //不能随便删除人家的文件夹,很危险,让用户自己去删除
            System.out.println("操作异常! 已存在文件夹:"+encryptDirPath+", 请使用新的文件夹名称!");
            return;
        }
        encryptDir.mkdir();
        //列出源文件中所有的文件
        List<File> files = (List<File>) FileUtils.listFiles(new File(sourceDirPath), EmptyFileFilter.NOT_EMPTY, null);
        if (files.size()==0){
            System.out.println("操作异常! 源文件夹中没有需要加密的文件");
            return;
        }
        for (int i=0;i<files.size();i++) {
            File file=files.get(i);
//            System.out.println(file.getAbsolutePath());
            //开始加密
            //获取文件名称name
            String encryptFilePath=encryptDir.getAbsolutePath()+File.separator+file.getName()+".ept";
            File encryptFile=new File(encryptFilePath);
            if (!encryptFile.exists()){
                try {
                    encryptFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            CXAESUtil.encryptFile(file,encryptFilePath,aesKey);
            System.out.println("正在加密 ( "+i+"/"+files.size()+" ) ...");
        }
        System.out.println("加密完成! 输出文件夹路径:"+encryptDirPath);
    }

    /**
     * 单个文件加密
     * @param args
     */
    public static void fileEncrypt(String[] args) {
        //第1个参数是密码, 第2个参数是源文件,第3个参数是加密后的文件,
        String aesKey=args[1];
        //源文件
        String sourceFilePath=args[2];
        //源文件的大小
        long len = FileUtils.sizeOf(new File(sourceFilePath));
        System.out.println("源文件大小="+len);
        //获取加密后文件路径
        String encryptFilePath=args[3];
        File fileEncrypt=new File(encryptFilePath);
        if (!fileEncrypt.exists()){
            try {
                fileEncrypt.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            System.out.println("操作异常!文件已经存在:"+encryptFilePath);
        }
        CXAESUtil.encryptFile(new File(sourceFilePath),encryptFilePath,aesKey);
        System.out.println("加密完成! 路径 : "+encryptFilePath);
    }

    public static void strEncrypt(String[] args){
        String aesKey=args[1];
        String sourceStr=args[2];
        //加密后的
        try {
            String outStr= CXAESUtil.encrypt(aesKey,sourceStr);
            System.out.println(outStr);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("操作异常! "+e.getLocalizedMessage());
        }
    }

}
  • 生成jar包,<*>.jar

  • 使用方法

单文件加密: java -jar <*.jar> -file <AES密码> <源文件路径>   <加密后的文件路径>
文件夹内多图片加密: java -jar <*.jar> -dir  <AES密码> <源文件夹路径> <加密后文件夹路径>
字符串加密: java -jar <*.jar> -str  <AES密码> <源字符串>
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值