Mã hóa cơ sở dữ liệu Android


79

Android sử dụng cơ sở dữ liệu SQLite để lưu trữ dữ liệu, tôi cần mã hóa cơ sở dữ liệu SQLite, điều này có thể được thực hiện như thế nào? Tôi hiểu rằng dữ liệu ứng dụng là riêng tư. Tuy nhiên, tôi cần mã hóa cơ sở dữ liệu SQLite mà ứng dụng của tôi đang sử dụng.

Câu trả lời:


68

SQLCipher là một phần mở rộng SQLite cung cấp mã hóa AES 256-bit minh bạch của các tệp cơ sở dữ liệu.

Trước đó sqlcipher là Mã hóa cơ sở dữ liệu đầy đủ nguồn mở cho SQLite không có sẵn cho Android. Nhưng bây giờ nó có sẵn dưới dạng bản phát hành alpha cho nền tảng Android. Các nhà phát triển đã cập nhật ứng dụng Android tiêu chuẩn 'Notepadbot' để sử dụng SQLCipher.

Vì vậy, đây chắc chắn là lựa chọn tốt nhất và đơn giản nhất tính đến thời điểm hiện tại.


2
SQLCIpher cho Android hiện là một phần của dự án SQLCipher chính thức: sqlcipher.net/sqlcipher-for-android
Tên hiển thị

1
Thông tin về giấy phép có sẵn trên trang github github.com/sqlcipher/android-database-sqlcipher/blob/master/…
vaichicedar

2
@vaichi domainsar Bạn sẽ thấy rằng tệp giấy phép cụ thể đó chỉ áp dụng cho phần hỗ trợ Android, có các tệp giấy phép bổ sung cho nội dung SQLCIPHER ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ) cũng như Nội dung của IBM ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ).
Hamid

1
Ví dụ đơn giản về SQLCipher trong android, đây là liên kết myownandroid.blogspot.in/2013/09/sqlcipher-in-android.html
jrhamza

SQLCipher làm chậm ứng dụng giải pháp nào cho @vaichiosystemar đó ??
Arsh Kaushal

28

Cơ sở dữ liệu được mã hóa để ngăn chặn INDIRECT ATTACKS. Thuật ngữ này và các lớp: KeyManager.java , Crypto.java được lấy từ cuốn sách Bảo mật ứng dụng Android của Sheran Gunasekera . Tôi giới thiệu tất cả cuốn sách này để đọc.

INDIRECT ATTACKSđược đặt tên như vậy, bởi vì vi-rút không truy cập trực tiếp vào ứng dụng của bạn. Thay vào đó, nó đi theo hệ điều hành Android. Mục đích là sao chép tất cả cơ sở dữ liệu SQLite với hy vọng tác giả của virus có thể sao chép bất kỳ thông tin nhạy cảm nào được lưu trữ ở đó. Tuy nhiên, nếu bạn đã thêm một lớp bảo vệ khác, thì tất cả những gì tác giả vi rút sẽ thấy là dữ liệu bị cắt xén. Hãy xây dựng một thư viện mật mã mà chúng ta có thể sử dụng lại trong tất cả các ứng dụng của mình. Hãy bắt đầu bằng cách tạo một bộ thông số kỹ thuật ngắn gọn:

  • Sử dụng thuật toán đối xứng: Thư viện của chúng tôi sẽ sử dụng thuật toán đối xứng, hoặc mật mã khối, để mã hóa và giải mã dữ liệu của chúng tôi. Chúng tôi sẽ giải quyết trên AES, mặc dù chúng tôi có thể sửa đổi điều này vào một ngày sau đó.

  • Sử dụng khóa cố định: Chúng tôi cần có một khóa mà chúng tôi có thể lưu trữ trên thiết bị sẽ được sử dụng để mã hóa và giải mã dữ liệu.

  • Khóa được lưu trữ trên thiết bị: Khóa sẽ nằm trên thiết bị. Mặc dù đây là một rủi ro đối với ứng dụng của chúng tôi từ quan điểm của các cuộc tấn công trực tiếp, nhưng nó đủ để bảo vệ chúng tôi trước các cuộc tấn công gián tiếp.

Hãy bắt đầu với mô-đun quản lý khóa của chúng ta (xem Liệt kê 1 ). Bởi vì chúng tôi dự định sử dụng một khóa cố định, chúng tôi sẽ không cần tạo một khóa ngẫu nhiên như chúng tôi đã làm trong các ví dụ trước đây. Các KeyManager do đó sẽ thực hiện các nhiệm vụ sau đây:

  1. Chấp nhận một khóa làm tham số ( setId(byte[] data)phương thức)
  2. Chấp nhận một vectơ khởi tạo làm tham số ( setIv(byte[] data) phương thức)
  3. Lưu khóa bên trong tệp trong cửa hàng nội bộ
  4. Lấy khóa từ tệp trong cửa hàng nội bộ ( getId(byte[] data) phương thức)
  5. Lấy IV từ một tệp trong cửa hàng nội bộ ( getIv(byte[] data) phương thức)

(Liệt kê 1. Mô-đun 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());
         }
     }
}

Tiếp theo, chúng tôi thực hiện mô-đun Crypto (xem Liệt kê 2 ). Mô-đun này đảm nhiệm việc mã hóa và giải mã. Chúng tôi đã thêm một armorEncrypt()armorDecrypt()phương thức vào mô-đun để giúp dễ dàng chuyển đổi dữ liệu mảng byte thành dữ liệu Base64 có thể in được và ngược lại. Chúng tôi sẽ sử dụng thuật toán AES với chế độ mã hóa Cipher Block Chaining (CBC) và phần đệm PKCS # 5 .

(Liệt kê 2. Mô-đun mật mã 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)));
               }
}

Bạn có thể đưa hai tệp này vào bất kỳ ứng dụng nào của mình yêu cầu bộ nhớ dữ liệu được mã hóa. Trước tiên, hãy đảm bảo rằng bạn có một giá trị cho khóa và vectơ khởi tạo, sau đó gọi bất kỳ một trong các phương thức mã hóa hoặc giải mã trên dữ liệu của bạn trước khi bạn lưu trữ. Liệt kê 3Liệt kê 4 chứa một ví dụ ứng dụng đơn giản của các lớp này bằng cách sử dụng. Chúng tôi tạo một Hoạt động với 3 Nút Mã hóa, Giải mã, Xóa; 1 EditText để nhập dữ liệu; 1 TextView cho đầu ra dữ liệu.

(Ví dụ 3. Một ví dụ. 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 ");
            }
        });

    }

}

(Ví dụ 4. Một ví dụ. 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>

8
nếu khóa được lưu trong thiết bị thì lợi ích của việc mã hóa, mã hóa dữ liệu bằng khóa đó là gì?
minhaz

Làm thế nào để đặt và lấy khóa từ một tệp khác..có thể cho một ví dụ làm việc không ?? nhận NPE khi đọc (tệp)
Gaju Kollur

13

Nếu cơ sở dữ liệu nhỏ, thì bạn có thể đạt được một lượng bảo mật nhỏ bằng cách giải mã toàn bộ tệp đến một vị trí tạm thời (không phải trên thẻ sd), sau đó mã hóa lại khi bạn đã đóng nó. Các vấn đề: ứng dụng chết sớm, hình ảnh ma trên phương tiện.

Một giải pháp tốt hơn một chút để mã hóa các trường dữ liệu. Điều này gây ra sự cố cho mệnh đề WHERE và ORDER BY. Nếu các trường được mã hóa cần được lập chỉ mục để tìm kiếm tương đương, thì bạn có thể lưu trữ một hàm băm mật mã của trường và tìm kiếm trường đó. Nhưng điều đó không giúp ích cho việc tìm kiếm phạm vi hoặc đặt hàng.

Nếu bạn muốn có được những điều kỳ diệu, bạn có thể tìm hiểu kỹ về NDK của Android và hack một số tiền điện tử vào mã C cho SQLite.

Xem xét tất cả các vấn đề này và các giải pháp từng phần, bạn có chắc mình thực sự cần một cơ sở dữ liệu SQL cho ứng dụng không? Bạn có thể tốt hơn với một cái gì đó giống như một tệp chứa một đối tượng tuần tự được mã hóa.


3

Bạn chắc chắn có thể có một cơ sở dữ liệu SQLite được mã hóa trên Android. Tuy nhiên, bạn không thể làm điều đó với các lớp học được cung cấp bởi Google.

Một vài lựa chọn thay thế:

  • Biên dịch SQLite của riêng bạn thông qua NDK và bao gồm codec mã hóa từ wxSQLite3 chẳng hạn (một codec miễn phí đẹp được bao gồm trong gói)
  • SQLCipher hiện bao gồm hỗ trợ cho Android

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.