Android 数据库加密

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2203987/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 04:56:49  来源:igfitidea点击:

Android database encryption

androiddatabasesqliteencryption

提问by user121196

Android uses SQLite database to store data, I need to encrypt the SQLite database, how can this be done? I understand that application data is private. However I need to explictly encrypt the SQLite database that my app is using.

Android 使用 SQLite 数据库来存储数据,我需要对 SQLite 数据库进行加密,这怎么做?我了解应用程序数据是私有的。但是我需要显式加密我的应用程序正在使用的 SQLite 数据库。

回答by vaichidrewar

SQLCipheris an SQLite extension that provides transparent 256-bit AES encryption of database files.

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

Earlier sqlcipher which is Open Source Full Database Encryption for SQLite was not available for android. But now it's available as alpha release for android platform. Developers have updated the standard android application 'Notepadbot' to use SQLCipher.

早期的 sqlcipher 是用于 SQLite 的开源完整数据库加密,不适用于 android。但现在它可以作为 android 平台的 alpha 版本使用。开发人员已更新标准 android 应用程序“Notepadbot”以使用 SQLCipher。

So this is definitely the best and simplest option as of now.

所以这绝对是目前最好和最简单的选择。

回答by Plo_Koon

Databases are encrypted in order to prevent INDIRECT ATTACKS. This term and classes: KeyManager.java, Crypto.javaare taken from Sheran Gunasekerabook Android Apps Security. I recommend all this book to reading.

数据库被加密以防止INDIRECT ATTACKS. 这个术语和类:KeyManager.javaCrypto.java取自Sheran Gunasekera 的Android Apps Security。我推荐所有这本书阅读。

INDIRECT ATTACKSare so named, because the virus does not go after your application directly. Instead, it goes after the Android OS. The aim is to copy all SQLite databases in the hopes that the virus author can copy any sensitive information stored there. If you had added another layer of protection, however, then all the virus author would see is garbled data. Let's build a cryptographic library that we can reuse in all our applications. Let's start by creating a brief set of specifications:

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

  • Uses symmetric algorithms: Our library will use a symmetric algorithm, or block cipher, to encrypt and decrypt our data. We will settle on AES, although we should be able to modify this at a later date.

  • Uses a fixed key: We need to be able to include a key that we can store on the device that will be used to encrypt and decrypt data.

  • Key stored on device: The key will reside on the device. While this is a risk to our application from the perspective of direct attacks, it should suffice in protecting us against indirect attacks.

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

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

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

Let's start with our key management module (see Listing 1). Because we plan to use a fixed key, we won't need to generate a random one as we did in the past examples. The KeyManagerwill therefore perform the following tasks:

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

  1. Accept a key as a parameter (the setId(byte[] data)method)
  2. Accept an initialization vector as a parameter (the setIv(byte[] data)method)
  3. Store the key inside a file in the internal store
  4. Retrieve the key from a file in the internal store (the getId(byte[] data)method)
  5. Retrieve the IV from a file in the internal store (the getIv(byte[] data)method)
  1. 接受一个键作为参数(setId(byte[] data)方法)
  2. 接受一个初始化向量作为参数(setIv(byte[] data)方法)
  3. 将密钥存储在内部存储的文件中
  4. 从内部存储中的文件中检索密钥(getId(byte[] data)方法)
  5. 从内部存储中的文件中检索 IV(getIv(byte[] data)方法)

(Listing 1. The KeyManager Module KeyManager.java)

(清单 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());
         }
     }
}

Next, we do the Cryptomodule (see Listing 2). This module takes care of the encryption and decryption. We have added an armorEncrypt()and armorDecrypt()method to the module to make it easier to convert the byte array data into printable Base64data and vice versa. We will use the AESalgorithm with Cipher Block Chaining (CBC) encryption modeand PKCS#5 padding.

接下来,我们执行Crypto模块(参见清单 2)。该模块负责加密和解密。我们在模块中添加了一个armorEncrypt()andarmorDecrypt()方法,以便更容易地将字节数组数据转换为可打印的Base64数据,反之亦然。我们将使用具有密码块链接 (CBC) 加密模式PKCS#5 填充AES算法。

(Listing 2. The Cryptographic Module Crypto.java)

(清单 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)));
               }
}

You can include these two files in any of your applications that require data storage to be encrypted. First, make sure that you have a value for your key and initialization vector, then call any one of the encrypt or decrypt methods on your data before you store it. Listing 3and Listing 4contain an simply App-example of these classes using. We create an Activity with 3 Buttons Encrypt, Decrypt, Delete; 1 EditText for data input; 1 TextView for data output.

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

(Listing 3. An example. MainActivity.java)

(清单 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 ");
            }
        });

    }

}

(Listing 4. An example. activity_main.xml)

(清单 4. 一个例子。activity_main.xml)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#363636"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/editInputData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:ems="10"
        android:textColor="#FFFFFF" >

        <requestFocus />
    </EditText>

    <TextView
        android:id="@+id/encryptView"
        android:layout_width="fill_parent"
        android:layout_height="100dp"
        android:layout_alignLeft="@+id/editInputData"
        android:layout_alignRight="@+id/editInputData"
        android:layout_below="@+id/buttonEncrypt"
        android:layout_marginTop="26dp"
        android:background="#000008"
        android:text="Encrypted/Decrypted Data View"
        android:textColor="#FFFFFF"
        android:textColorHint="#FFFFFF"
        android:textColorLink="#FFFFFF" />

    <Button
        android:id="@+id/buttonEncrypt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/encryptView"
        android:layout_alignRight="@+id/editInputData"
        android:layout_below="@+id/editInputData"
        android:layout_marginTop="26dp"
        android:text="Encrypt" />

    <Button
        android:id="@+id/buttonDelete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/buttonDecrypt"
        android:layout_alignRight="@+id/buttonDecrypt"
        android:layout_below="@+id/buttonDecrypt"
        android:layout_marginTop="15dp"
        android:text="Delete" />

    <Button
        android:id="@+id/buttonDecrypt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/encryptView"
        android:layout_alignRight="@+id/encryptView"
        android:layout_below="@+id/encryptView"
        android:layout_marginTop="21dp"
        android:text="Decrypt" />

</RelativeLayout>

回答by Mark Borgerding

If the database will be small, then you could gain a small amount of security by decrypting the whole file to a temp location (not on sd card), then re-encrypting when you've closed it. Problems: premature app death, ghost image on media.

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

A slightly better solution to encrypt the data fields. This causes a problem for WHERE and ORDER BY clauses. If the encrypted fields need to be indexed for equivalence searching, then you can store a cryptographic hash of the field and search for that. But that doesn't help with range searches or ordering.

加密数据字段的稍微好一点的解决方案。这会导致 WHERE 和 ORDER BY 子句出现问题。如果需要对加密字段进行索引以进行等价搜索,那么您可以存储该字段的加密散列并进行搜索。但这对范围搜索或排序没有帮助。

If you want to get fancier, you could delve into the Android NDK and hack some crypto into C code for SQLite.

如果你想变得更高级,你可以深入研究 Android NDK 并将一些密码破解到 SQLite 的 C 代码中。

Considering all these problems and partial solutions, are you sure you really need a SQL database for the application? You might be better off with something like a file that contains an encrypted serialized object.

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

回答by NuSkooler

You can certainly have a encrypted SQLite database on Android. You can't do it with the out of the box Google provided classes, however.

您当然可以在 Android 上拥有加密的 SQLite 数据库。但是,您无法使用 Google 提供的开箱即用的类来实现。

A couple alternatives:

几个选择:

  • Compile your own SQLite via the NDK and include the encryption codec from for example, wxSQLite3(a nice free codec is included in the package)
  • SQLCiphernow includes support for Android
  • 通过 NDK 编译您自己的 SQLite 并包含来自例如wxSQLite3的加密编解码器(包中包含一个不错的免费编解码器)
  • SQLCipher现在包括对 Android 的支持

回答by Diego Torres Milano

http://sqlite-crypt.com/may help you to create an encrypted database, though I've never used it on android seems to be possible with the source code.

http://sqlite-crypt.com/可能会帮助您创建一个加密的数据库,尽管我从未在 android 上使用过它,但源代码似乎是可能的。