在Android中,通常使用SQLite来管理本地数据。但是如果手机被ROOT了,用户能够轻易查看到SQLite数据库中的信息。如果数据库中包含用户私密信息或者APP的关键信息,那么也就能够轻易被其他人访问。现在这是所有开发不希望看见的。

       这里讲两种数据加密方法。分别采用SQLCipher和ConCeal。并分别讲述这两种方法的优缺点。


       一. SQLCipher

       SQLCipher是在SQLite基础上进行扩展的开源数据库,它增加了数据加密功能,并且支持多种不同的平台。

首先需要下载Android项目所依赖的SQLCipher包,下载地址是:

      https://s3.amazonaws.com/sqlcipher/SQLCipher+for+Android+v2.2.2.zip

 解压压缩包,有assets和libs这两个目录,assets文件夹中有icudt461.zip,libs文件夹中有armeabi文件夹,下面有三个.so的文件。需要将这两个目录中的内容复制添加到需要加密数据库的Android项目的assets和libs文件夹中。



         到此准备工作完毕,,我们需要建立一个SQLCipherDatabaseHelper继承自SQLiteOpenHelper,这里使用的不是Android API中的SQLiteOpenHelper,而是net.sqlcipher.database包下的SQLiteOpenHelper。代码如下:

import android.content.Context;import net.sqlcipher.database.SQLiteDatabase;import net.sqlcipher.database.SQLiteDatabase.CursorFactory;import net.sqlcipher.database.SQLiteOpenHelper;public class MyDatabaseHelper extends SQLiteOpenHelper{ public static final String CREATE_TABLE = "create table Book(name text, pages integer)";        public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) {          super(context, name, factory, version);      }        @Override      public void onCreate(SQLiteDatabase db) {          db.execSQL(CREATE_TABLE);      }        @Override      public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {        }}
      我们引入的是net.sqlcipher.database.SQLiteOpenHelper在这段代码中,建立了一个书籍的表,分别记录书名name和页数pages。

     除了引入的包不一样,其他的用法和SQLite都是完全相同。 添加和查询数据的代码和SQLite是相同的。

public class MainActivity extends Activity {            private SQLiteDatabase db;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          SQLiteDatabase.loadLibs(this);          MyDatabaseHelper dbHelper = new MyDatabaseHelper(this, "demo.db", null, 1);          db = dbHelper.getWritableDatabase("secret_key");          Button addData = (Button) findViewById(R.id.add_data);          Button queryData = (Button) findViewById(R.id.query_data);          addData.setOnClickListener(new OnClickListener() {              @Override              public void onClick(View v) {                  ContentValues values = new ContentValues();                  values.put("name", "达芬奇密码");                  values.put("pages", 566);                  db.insert("Book", null, values);              }          });          queryData.setOnClickListener(new OnClickListener() {              @Override              public void onClick(View v) {                  Cursor cursor = db.query("Book", null, null, null, null, null, null);                  if (cursor != null) {                      while (cursor.moveToNext()) {                          String name = cursor.getString(cursor.getColumnIndex("name"));                          int pages = cursor.getInt(cursor.getColumnIndex("pages"));                          Log.d("TAG", "book name is " + name);                          Log.d("TAG", "book pages is " + pages);                      }                  }                  cursor.close();              }          });      }  }  

     在OnCreate()中,调用了SQLiteDatabase的loadLibs()静态方法将SQLCipher所以来的.so加载进来,注意引入的是net.sqlcipher.database下的SQLiteDatabase。然后我们创建了MyDatabaseHelper的实例,并调用getWritableDatabase()方法去获取SQLiteDatabase对象。这里在调用getWritableDatabase()方法的时候传入了一个字符串参数,它就是SQLCipher所依赖的key,在对数据库进行加解密的时候SQLCipher都将使用这里指定的key。

     在添加数据按钮的点击事件里面,我们通过ContentValues构建了一条数据,然后调用SQLiteDatabase的insert()方法将这条数据插入到Book表中。

     在查询数据按钮的点击事件里面,我们调用SQLiteDatabase的query()方法来查询Book表中的数据,查询到的结果会存放在Cursor对象中,注意这里使用的是net.sqlcipher包下的Cursor。然后对Cursor对象进行遍历,并将查询到的结果打印出来。

     现在运行一下程序,先点击添加数据按钮,再点击查询数据按钮,刚刚添加的那条数据就应该在控制台里打印出来了。

     SQLCipher提供的API与Android原生的API操作起来是几乎一模一样的,因为SQLCipher对Android SDK中所有与数据库相关的API做了一份镜像。这样开发者可以向操作普通数据库文件一样来操作SQLCipher,而所有的加密解密操作,对开发人员而言都是黑盒的,SQLCipher在背后帮我们做好了。

      二。ConCeal

     ConCeal是Facebook推出的用于对数据进行快速加密和认证的开发包,Facebook用它来加密手机、平板电脑SD卡中的数据和图片。能够高效快速地在磁盘上加密大文件,对于低版本的android系统、低内存、较慢的处理器,都有很好的效率。ConCeal的加密算法基于OpenSSL,经过编译之后,apk包大小只增加0.1MB左右,能够兼容众多Android版本,减少后期维护。

      项目主页是:http://www.open-open.com/lib/view/home/1394779937806

     Android的jar包下载地址:http://facebook.github.io/conceal/documentation/

     ConCeal的加密代码判断是:

//使用秘钥链和原生库的默认实现,来创建一个新的加密对象Crypto crypto = new Crypto(  new SharedPrefsBackedKeyChain(context),  new SystemNativeCryptoLibrary()); //检查加密功能是否可用//如果Android没有正确载入库,则此步骤可能失败if (!crypto.isAvailable()) {  return;} OutputStream fileStream = new BufferedOutputStream(  new FileOutputStream(file)); //创建输出流,当数据写入流的时候进行加密,并将加密后的数据输出到文件OutputStream outputStream = crypto.getCipherOutputStream(  fileStream,  entity); //将纯文本写入其中outputStream.write(plainText);outputStream.close();
      ConCeal的解密代码段是:

// 打开用ConCeal加密的文件FileInputStream fileStream = new FileInputStream(file);// 创建一个输入流,当数据读入的时候用ConCeal进行解密InputStream inputStream = crypto.getCipherInputStream(  fileStream,entity);// 变为字节int read;byte[] buffer = new byte[1024];
// 读入整个输入流,在输入流结束时进行验证,如果不一直验证到流结束,将有安全漏洞while ((read = inputStream.read(buffer)) != -1) {  out.write(buffer, 0, read);}inputStream.close();
      创建新的KeyChain。程序中有默认的KeyChain,但是也可以继承KeyChain以实现一个自己的KeyChain。

public class CustomKeyChain implements KeyChain {  ...}
      下面讲述ConCeal的具体实现

      1. 从http://facebook.github.io/conceal/documentation/中下载工程所需要的两个Jar包和so文件,分别点击ConCeal Jar、Android Jar与Native Binaries下载即可。将libconceal.jar与conceal_android.jar放入工程的libs文件夹下。下载的libs.rar解压后,把armeabi文件夹下的libconceal.so文件翻入自己工程libs文件夹下的armeabi文件夹下即可。





      2. 加密

private void encryption(){// Creates a new Crypto object with default implementations of // a key chain as well as native library.Crypto crypto = new Crypto(  new SharedPrefsBackedKeyChain(context),  new SystemNativeCryptoLibrary());// Check for whether the crypto functionality is available// This might fail if Android does not load libaries correctly.if (!crypto.isAvailable()) {Toast.makeText(context, "ENCRYPTION FAIL!", Toast.LENGTH_SHORT).show();return;}try {//加密后的文件路径File file = new File(GPUtils.getSharePicPath() + "/text.txt");OutputStream fileStream = new BufferedOutputStream(new FileOutputStream(file));com.facebook.crypto.Entity entity = new com.facebook.crypto.Entity("text");// Creates an output stream which encrypts the data as// it is written to it and writes it out to the file.OutputStream outputStream;outputStream = crypto.getCipherOutputStream(fileStream, entity);//需要加密的textString plainString = "TEST!!!测试!!!";//将String变为byt[]类型byte[] plainText = plainString.getBytes();// Write plaintext to it.outputStream.write(plainText);outputStream.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (CryptoInitializationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (KeyChainException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
          Entity应该是加密所使用秘钥。但是具体没有研究。

      3. 解密

private void decryption(){// Creates a new Crypto object with default implementations of // a key chain as well as native library.Crypto crypto = new Crypto(  new SharedPrefsBackedKeyChain(context),  new SystemNativeCryptoLibrary());try {//需要解密的文件路径File file = new File(GPUtils.getSharePicPath() + "/text.txt");// Get the file to which ciphertext has been written.FileInputStream fileStream = new FileInputStream(file);com.facebook.crypto.Entity entity = new com.facebook.crypto.Entity("text");// Creates an input stream which decrypts the data as// it is read from it.InputStream inputStream;inputStream = crypto.getCipherInputStream(fileStream,entity);// Read into a byte array.int read;byte[] buffer = new byte[1024];//解密后的文本String plainString = new String();// You must read the entire stream to completion.// The verification is done at the end of the stream.// Thus not reading till the end of the stream will cause// a security bug. StringBuilder stringBuilder = new StringBuilder();    while((read = inputStream.read(buffer)) > 0){        stringBuilder.append(new String(buffer, 0, read));    }    plainString = stringBuilder.toString();Toast.makeText(context, plainString, Toast.LENGTH_SHORT).show();inputStream.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (CryptoInitializationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (KeyChainException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
       在解密时,new Entity时使用的String必须与解密时相同,否则不能正确解密。

       4. 扩展

       目前只是实现了加密String类型。其余类型没有实现。猜测JSON类型的应该也是可以的。这样的话,对于结构化的数据也能较好得加解密了。

     三。总结

       总结一下就是,SQLite对程序猿而言,只需要选择秘钥,其余查询、插入等数据库操作方法与SQLite几乎一模一样,以为它是SQLite的镜像,容易上手,加密结构化数据很便利。但是导入的jar包较大,会增加4MB左右apk大小。Conceal上手相对于SQLite而言,稍微慢一点点,加密大型的结构化的数据较为麻烦。但是体量小,对apk包大小影响很小,只增加0.1MB左右。如果进行快速小型的加密时,使用ConCeal较为方便。

更多相关文章

  1. 一款常用的 Squid 日志分析工具
  2. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  3. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  4. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  5. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  6. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  7. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  8. android studio NDK编程
  9. Android(安卓)Handler的详细介绍

随机推荐

  1. 【javascript】js中replace替换失败的问
  2. TV端影视APP开发运营需要考虑的几个问题
  3. Java中的对象和引用详解
  4. Google 大佬们为什么要开发 Go 这门新语
  5. Vue3 路由与状态管理
  6. SpringBoot使用前缀树过滤敏感词的方法实
  7. SpringBoot使用前缀树过滤敏感词的方法实
  8. 学绘画如何入门?绘画新手入门教程
  9. 5G时代,中国招标采购网如何推动大数据招投
  10. 华纳小陈z18788371515