当前位置:  开发笔记 > 编程语言 > 正文

Android数据库加密

如何解决《Android数据库加密》经验,为你挑选了3个好方法。

Android使用SQLite数据库来存储数据,我需要加密SQLite数据库,怎么做呢?我知道应用程序数据是私有的.但是,我需要明确加密我的应用程序正在使用的SQLite数据库.



1> vaichidrewar..:

SQLCipher 是一个SQLite扩展,为数据库文件提供透明的256位AES加密.

早期的sqlcipher是SQLite的开源全数据库加密,不适用于Android.但现在它可用作Android平台的alpha版本.开发人员更新了标准的Android应用程序'Notepadbot'以使用SQLCipher.

所以这绝对是目前最好,最简单的选择.


适用于Android的SQLCIpher现在是官方SQLCipher项目的一部分:http://sqlcipher.net/sqlcipher-for-android/
@vaichidrewar你会发现,特定的许可文件仅适用于Android的支撑部分,有对SQLCIPHER东西额外的许可文件(https://github.com/sqlcipher/android-database-sqlcipher/blob/master/SQLCIPHER_LICENSE)以及IBM的东西(https://github.com/sqlcipher/android-database-sqlcipher/blob/master/ICU_LICENSE).

2> Plo_Koon..:

数据库经过加密以防止INDIRECT ATTACKS.这个术语和类:KeyManager.java,Crypto.java取自Sheran Gunasekera的书籍Android Apps Security.我推荐阅读这本书.

INDIRECT ATTACKS如此命名,因为病毒不会直接在您的应用程序之后.相反,它追随Android操作系统.目的是复制所有SQLite数据库,希望病毒作者可以复制存储在那里的任何敏感信息.但是,如果您添加了另一层保护,那么所有病毒作者都会看到乱码数据.让我们构建一个加密库,我们可以在所有应用程序中重用它.让我们首先创建一组简短的规范:

使用对称算法:我们的库将使用对称算法或分组密码来加密和解密我们的数据.我们将以AES为基础,尽管我们应该能够在以后修改它.

使用固定密钥:我们需要能够包含一个密钥,我们可以将其存储在将用于加密和解密数据的设备上.

存储在设备上的密钥:密钥将驻留在设备上.虽然从直接攻击的角度来看这对我们的应用程序是一种风险,但它应该足以保护我们免受间接攻击.

让我们从我们的密钥管理模块开始(参见清单1).因为我们计划使用固定密钥,所以我们不需要像过去的示例那样生成随机密钥.因此,KeyManager将执行以下任务:

    接受一个键作为参数(setId(byte[] data)方法)

    接受初始化向量作为参数(setIv(byte[] data) 方法)

    将密钥存储在内部存储中的文件中

    从内部存储中的文件中检索密钥(getId(byte[] data) 方法)

    从内部存储中的文件中检索IV(getIv(byte[] data) 方法)

(清单1. KeyManager模块KeyManager.java)

    package com.yourapp.android.crypto;

    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import android.content.Context;
    import android.util.Log;

    public class KeyManager {

       private static final String TAG = "KeyManager";
       private static final String file1 = "id_value";
       private static final String file2 = "iv_value";
       private static Context ctx;

       public KeyManager(Context cntx) {
         ctx = cntx;
       }

       public void setId(byte[] data){
         writer(data, file1);
       }

       public void setIv(byte[] data){
         writer(data, file2);
       }

       public byte[] getId(){
         return reader(file1);
       }

       public byte[] getIv(){
         return reader(file2);
       }

       public byte[] reader(String file){
         byte[] data = null;
         try {
           int bytesRead = 0;
           FileInputStream fis = ctx.openFileInput(file);
           ByteArrayOutputStream bos = new ByteArrayOutputStream();
           byte[] b = new byte[1024];
           while ((bytesRead = fis.read(b)) != -1){
             bos.write(b, 0, bytesRead);
           }
           data = bos.toByteArray();
         } catch (FileNotFoundException e) {
           Log.e(TAG, "File not found in getId()");
         } catch (IOException e) {
           Log.e(TAG, "IOException in setId(): " + e.getMessage());
         }
         return data;
       }

       public void writer(byte[] data, String file) {
         try {
           FileOutputStream fos = ctx.openFileOutput(file,
           Context.MODE_PRIVATE);
           fos.write(data);
           fos.flush();
           fos.close();
         } catch (FileNotFoundException e) {
           Log.e(TAG, "File not found in setId()");
         } catch (IOException e) {
           Log.e(TAG, "IOException in setId(): " + e.getMessage());
         }
     }
}

接下来,我们执行Crypto模块(参见清单2).该模块负责加密和解密.我们增加了一个armorEncrypt()armorDecrypt()方法的模块,使其更容易的字节数组数据转换成可打印Base64编码数据,反之亦然.我们将使用AES算法与密码块链接(CBC)加密模式和PKCS#5填充.

(清单2.加密模块Crypto.java)

        package com.yourapp.android.crypto;

        import java.security.InvalidAlgorithmParameterException;
        import java.security.InvalidKeyException;
        import java.security.NoSuchAlgorithmException;
        import javax.crypto.BadPaddingException;
        import javax.crypto.Cipher;
        import javax.crypto.IllegalBlockSizeException;
        import javax.crypto.NoSuchPaddingException;
        import javax.crypto.spec.IvParameterSpec;
        import javax.crypto.spec.SecretKeySpec;
        import android.content.Context;
        import android.util.Base64;

        public class Crypto {

           private static final String engine = "AES";
           private static final String crypto = "AES/CBC/PKCS5Padding";
           private static Context ctx;
           public Crypto(Context cntx) {
             ctx = cntx;
           }

           public byte[] cipher(byte[] data, int mode) throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IllegalBlockSizeException,BadPaddingException,InvalidAlgorithmParameterException {
             KeyManager km = new KeyManager(ctx);
             SecretKeySpec sks = new SecretKeySpec(km.getId(), engine);
             IvParameterSpec iv = new IvParameterSpec(km.getIv());
             Cipher c = Cipher.getInstance(crypto);
             c.init(mode, sks, iv);
             return c.doFinal(data);
           }

           public byte[] encrypt(byte[] data) throws InvalidKeyException,
        NoSuchAlgorithmException, NoSuchPaddingException,
        IllegalBlockSizeException, BadPaddingException,
        InvalidAlgorithmParameterException {
             return cipher(data, Cipher.ENCRYPT_MODE);
           }

           public byte[] decrypt(byte[] data) throws InvalidKeyException,
        NoSuchAlgorithmException, NoSuchPaddingException,
        IllegalBlockSizeException, BadPaddingException,
        InvalidAlgorithmParameterException {
             return cipher(data, Cipher.DECRYPT_MODE);
           }

        public String armorEncrypt(byte[] data) throws InvalidKeyException,NoSuchAlgorithmException,
    NoSuchPaddingException,IllegalBlockSizeException,
    BadPaddingException,InvalidAlgorithmParameterException {
                 return Base64.encodeToString(encrypt(data), Base64.DEFAULT);
               }

         public String armorDecrypt(String data) throws InvalidKeyException,NoSuchAlgorithmException,
    NoSuchPaddingException,IllegalBlockSizeException,
    BadPaddingException,InvalidAlgorithmParameterException {
                 return new String(decrypt(Base64.decode(data, Base64.DEFAULT)));
               }
}

您可以将这两个文件包含在需要加密数据存储的任何应用程序中.首先,确保您的密钥和初始化向量具有值,然后在存储数据之前调用数据中的任何一种加密或解密方法.清单3清单4包含这些类的简单应用示例.我们使用3个按钮加密,解密,删除创建一个活动; 1 EditText用于数据输入; 1 TextView用于数据输出.

(清单3.示例.MainActivity.java)

package com.yourapp.android.crypto;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;


public class MainActivity extends Activity {
    TextView encryptedDataView;
    EditText editInputData;
    private Context cntx;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.cntx = getApplicationContext();
        Button btnEncrypt = (Button) findViewById(R.id.buttonEncrypt);
        Button btnDecrypt = (Button) findViewById(R.id.buttonDecrypt);
        Button btnDelete = (Button) findViewById(R.id.buttonDelete);
        editInputData = (EditText)findViewById(R.id.editInputData) ;
        encryptedDataView = (TextView) findViewById(R.id.encryptView);

        /**********************************************/
            /** INITIALIZE KEY AND INITIALIZATION VECTOR **/
        String key = "12345678909876543212345678909876";
        String iv = "1234567890987654";
        KeyManager km = new KeyManager(getApplicationContext());
        km.setIv(iv.getBytes());
        km.setId(key.getBytes());
        /**********************************************/

        btnEncrypt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String Data = editInputData.getText().toString();
                String Encrypted_Data = "data";
                try {
                    Crypto crypto = new Crypto(cntx);
                    Encrypted_Data = crypto.armorEncrypt(Data.getBytes());
                }   catch (InvalidKeyException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchAlgorithmException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (IllegalBlockSizeException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (BadPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (InvalidAlgorithmParameterException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    }
                encryptedDataView.setText(Encrypted_Data);
            }
        });

        btnDecrypt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String Data = encryptedDataView.getText().toString();
                String Decrypted_Data = "data";
                try {
                    Crypto crypto = new Crypto(cntx);
                    Decrypted_Data = crypto.armorDecrypt(Data);
                }   catch (InvalidKeyException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchAlgorithmException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (IllegalBlockSizeException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (BadPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (InvalidAlgorithmParameterException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    }
                encryptedDataView.setText(Decrypted_Data);
            }
        });

        btnDelete.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                encryptedDataView.setText(" Deleted ");
            }
        });

    }

}

(清单4.示例.activation_main.xml)



    

        
    

    

    


如果密钥存储在设备中,使用该密钥加密,加密数据有什么好处?

3> Mark Borgerd..:

如果数据库很小,那么您可以通过将整个文件解密到临时位置(而不是SD卡)来获得少量安全性,然后在关闭它时重新加密.问题:应用程序过早死亡,媒体上出现鬼影.

加密数据字段的稍好的解决方案.这会导致WHERE和ORDER BY子句出现问题.如果需要将加密字段编入索引以进行等效搜索,则可以存储该字段的加密哈希并搜索该字段.但这对范围搜索或订购没有帮助.

如果你想获得更好的体验,你可以深入研究Android NDK并将一些加密内容加入到SQLite的C代码中.

考虑到所有这些问题和部分解决方案,您确定您确实需要一个SQL数据库用于该应用程序吗?使用包含加密序列化对象的文件之类的东西可能会更好.

推荐阅读
喜生-Da
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有