Android中使用加密图片的解决方案
来源:互联网 发布:广州525孕妇摄影淘宝网 编辑:程序博客网 时间:2024/04/30 10:22
Android中使用加密图片的解决方案
产品要求资源zip包中的图片资源要加密,于是在需求的驱动下,在网上搜寻解决方案。
加密方式
加密方式很多种,从最简单的交换字节顺序,到各种加密算法。下面简单列一下网上摘录的加密算法的对比。
- AES/DES加密速度快,适合大量数据,DES容易破解,一般用3重DES,后来又出现了更快更安全的AES
- RSA是公钥加密,速度慢,只能处理少量数据,优点是公钥即使在不安全的网络上公开,也能保证安全
由此加密算法选定了AES加密算法,在网上搜索AES加密实现的时候发现几乎都是对byte[]的加密解密操作,考虑到Android机上对图片做解密操作可能对内存消耗大,尝试找有没有基于Stream的加密解密方式,经过了一番资料查找找到了支持AES加密解密的CipherInputStream
和CipherOutputStream
借助这两个Stream可以实现将加密的图片文件读取成解密后的Bitmap。方法找到了,下面来看下关键代码
加密关键代码
/*** 加密** @param file 待加密数据* @param key 密钥* @param cipherAlgorithm 加密算法/工作模式/填充方式* @return byte[] 加密数据* @throws Exception*/private static OutputStream encrypt(File file, Key key) throws Exception{ //实例化 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); //使用密钥初始化,设置为加密模式 cipher.init(Cipher.ENCRYPT_MODE, key); //执行操作 CipherOutputStream cos = new CipherOutputStream(new FileOutputStream(file), cipher); return cos;}
解密关键代码
/*** 解密** @param file 待解密文件* @param key 密钥* @param cipherAlgorithm 加密算法/工作模式/填充方式* @return byte[] 解密数据* @throws Exception*/private static InputStream decrypt(File file, Key key) throws Exception{ //实例化 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); //使用密钥初始化,设置为解密模式 cipher.init(Cipher.DECRYPT_MODE, key); //执行操作 CipherInputStream cis = new CipherInputStream(new FileInputStream(file), cipher); return cis;}
构造Key
/*** 转换密钥** @param key 二进制密钥* @return 密钥*/private static Key toKey(byte[] key){ //生成密钥 return new SecretKeySpec(key, "AES");}
生成128位二进制密钥
/*** 初始化密钥** @return byte[] 密钥 * @throws Exception*/public static byte[] initSecretKey() { //返回生成指定算法的秘密密钥的 KeyGenerator 对象 KeyGenerator kg = null; try { kg = KeyGenerator.getInstance("AES"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return new byte[0]; } //初始化此密钥生成器,使其具有确定的密钥大小 //AES 要求密钥长度为 128 kg.init(128); //生成一个密钥 SecretKey secretKey = kg.generateKey(); return secretKey.getEncoded();}
至此,就可以通过生成的128为密钥加密解密文件了。那么接下来要解决下一个问题,将加密解密融入到项目中去。
与现有项目融合
项目的图片加载采用的是Universal-Image-Loader,最开始想到的方式是,下载U-I-L的源码将加密解密代码插入其中,问题可以解决。单后来考虑到U-I-L是一个相当成熟的框架,一定有通过扩展的方式来实现目的。
解密图片
U-I-L有提供设置自定义的ImageDownloader,让我们来看下ImageDownloader接口
他有一个接口方法getStream
public interface ImageDownloader { InputStream getStream(String uri, Object var2) throws IOException; ······}
接下来让我们实现这个接口
@Overridepublic InputStream getStream(String s, Object o) throws IOException { if (s == null){ return null; } if (s.toLowerCase(Locale.US).startsWith("file://")){ String crop = Scheme.FILE.crop(s); try { return AESCoder.decrypt(new File(crop), AESCoder.get(context)); } catch (Exception e) { e.printStackTrace(); } } return null;}
到此为止就可以实现,App读取加密后的资源图片。但是此时还有一个很大的漏洞,加载图片的时候开启了cacheOnDisk(true)
,这就会导致,解密后的图片缓存到sdcard的时候以解密形态存储,没有达到最初的目的,接下来要解决一下缓存加密的问题。
加密缓存
加密缓存,目的是从手机sdcard中不能拿到解密的图片,这里就要考虑对缓存逻辑动手脚了,此时我发现U-I-L的配置中可以配置DiskCache
。
我们来看一下DiskCache接口
public interface DiskCache { boolean save(String imageUri, InputStream imageStream, CopyListener listener) throws IOException; boolean save(String imageUri, Bitmap bitmap) throws IOException; ......}
我们看到UnlimitedDiskCache
实现啊了DiskCache
接口,而项目中正好用的是这种缓存机制,所以我们可以重写UnlimitedDiskCache
的这两个save方法,将解密的bitmap重新加密。
主要代码如下
@Overridepublic boolean save(String imageUri, Bitmap bitmap) throws IOException { File imageFile = this.getFile(imageUri); File tmpFile = new File(imageFile.getAbsolutePath() + ".tmp"); try { OutputStream encrypt = AESCoder.encrypt(tmpFile, AESCoder.get(context)); BufferedOutputStream os = new BufferedOutputStream(encrypt, this.bufferSize); boolean savedSuccessfully = false; try { savedSuccessfully = bitmap.compress(this.compressFormat, this.compressQuality, os); } finally { IoUtils.closeSilently(os); if(savedSuccessfully && !tmpFile.renameTo(imageFile)) { savedSuccessfully = false; } if(!savedSuccessfully) { tmpFile.delete(); } } bitmap.recycle(); return savedSuccessfully; } catch (Exception e) { e.printStackTrace(); return false; }}@Overridepublic boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException { File imageFile = this.getFile(imageUri); File tmpFile = new File(imageFile.getAbsolutePath() + ".tmp"); boolean loaded = false; try { OutputStream encrypt = AESCoder.encrypt(tmpFile, AESCoder.get(context)); BufferedOutputStream os = new BufferedOutputStream(encrypt, this.bufferSize); try { loaded = IoUtils.copyStream(imageStream, os, listener, this.bufferSize); } finally { IoUtils.closeSilently(os); } } catch (Exception e) { e.printStackTrace(); return false; } finally { if(loaded && !tmpFile.renameTo(imageFile)) { loaded = false; } if(!loaded) { tmpFile.delete(); } } return loaded;}
最后附上AES加密类的实现
package util;import android.content.Context;import javax.crypto.Cipher;import javax.crypto.CipherInputStream;import javax.crypto.CipherOutputStream;import javax.crypto.spec.SecretKeySpec;import java.io.*;import java.security.Key;public class AESCoder { /** * 密钥算法 */ private static final String KEY_ALGORITHM = "AES"; private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding"; public static byte[] get(Context context){ try { InputStream is = context.getAssets().open("aes.key"); byte[] k = new byte[16]; is.read(k); return k; } catch (IOException e) { e.printStackTrace(); } return null; } /** * 加密 * * @param file 待加密数据 * @param key 二进制密钥 * @return byte[] 加密数据 * @throws Exception */ public static OutputStream encrypt(File file, byte[] key) throws Exception{ return encrypt(file, key, DEFAULT_CIPHER_ALGORITHM); } /** * 解密 * * @param file 待解密数据 * @param key 二进制密钥 * @return byte[] 解密数据 * @throws Exception */ public static InputStream decrypt(File file, byte[] key) throws Exception{ return decrypt(file, key,DEFAULT_CIPHER_ALGORITHM); } /** * 转换密钥 * * @param key 二进制密钥 * @return 密钥 */ private static Key toKey(byte[] key){ //生成密钥 return new SecretKeySpec(key, KEY_ALGORITHM); } /** * 加密 * * @param file 待加密数据 * @param key 二进制密钥 * @param cipherAlgorithm 加密算法/工作模式/填充方式 * @return byte[] 加密数据 * @throws Exception */ private static OutputStream encrypt(File file, byte[] key, String cipherAlgorithm) throws Exception{ //还原密钥 Key k = toKey(key); return encrypt(file, k, cipherAlgorithm); } /** * 加密 * * @param file 待加密数据 * @param key 密钥 * @param cipherAlgorithm 加密算法/工作模式/填充方式 * @return byte[] 加密数据 * @throws Exception */ private static OutputStream encrypt(File file, Key key, String cipherAlgorithm) throws Exception{ //实例化 Cipher cipher = Cipher.getInstance(cipherAlgorithm); //使用密钥初始化,设置为加密模式 cipher.init(Cipher.ENCRYPT_MODE, key); //执行操作 CipherOutputStream cos = new CipherOutputStream(new FileOutputStream(file), cipher); return cos; } /** * 解密 * * @param file 待解密数据 * @param key 二进制密钥 * @param cipherAlgorithm 加密算法/工作模式/填充方式 * @return byte[] 解密数据 * @throws Exception */ private static InputStream decrypt(File file, byte[] key,String cipherAlgorithm) throws Exception{ //还原密钥 Key k = toKey(key); return decrypt(file, k, cipherAlgorithm); } /** * 解密 * * @param file 待解密文件 * @param key 密钥 * @param cipherAlgorithm 加密算法/工作模式/填充方式 * @return byte[] 解密数据 * @throws Exception */ private static InputStream decrypt(File file, Key key, String cipherAlgorithm) throws Exception{ //实例化 Cipher cipher = Cipher.getInstance(cipherAlgorithm); //使用密钥初始化,设置为解密模式 cipher.init(Cipher.DECRYPT_MODE, key); //执行操作 CipherInputStream cis = new CipherInputStream(new FileInputStream(file), cipher); return cis; }}
- Android中使用加密图片的解决方案
- android开发中Rsa加密的使用
- android andengine游戏引擎中图片资源的加密
- 关于Android中使用定位的解决方案。
- android中listview,gridview加载图片的线程并发解决方案
- android中listview,gridview加载图片的线程并发解决方案
- android中listview,gridview加载图片的线程并发解决方案
- android中listview,gridview加载图片的线程并发解决方案
- Android 百度地图marker中图片不显示的解决方案
- Android 裁剪图片的解决方案
- 关于Android中使用AES加密解密的问题
- android中9-patch图片的使用
- Android Studio中Svg图片的使用
- java中 使用ImageMagick处理图片(对图片处理要求比较高的解决方案)
- 最全的android图片加密
- Android中图片缩放9path图片的使用
- android RSA加密的使用
- iOS中加密的使用
- 泉水
- listview滑动删除item的一个方案
- 征迈科技USB硬件加密狗
- autoupdate script
- skb-4
- Android中使用加密图片的解决方案
- flume nginx 日志处理异常 JsonParseException: Unexpected character ('(' (code 40)): expected a valid value
- Xcode运行范例程序遇到的问题Command /bin/sh failed with exit code 1
- split 分割 字符串(分隔符如:* ^ : | , .) 及注意点
- 设计模式之:构造者模式
- 朴素贝叶斯
- 西门子S7-200 Smart PLC过程控制工控机数据记录与曲线显示系统
- UIStatusBarStyle PreferredStatusBarStyle does not work
- 可公度线段与欧几里得(Euclid)算法