Android 데이터베이스 암호화


79

Android는 SQLite 데이터베이스를 사용하여 데이터를 저장합니다. SQLite 데이터베이스를 암호화해야합니다. 어떻게 할 수 있습니까? 애플리케이션 데이터가 비공개임을 이해합니다. 그러나 내 앱에서 사용하는 SQLite 데이터베이스를 명시 적으로 암호화해야합니다.

답변:


68

SQLCipher 는 데이터베이스 파일의 투명한 256 비트 AES 암호화를 제공하는 SQLite 확장입니다.

SQLite 용 오픈 소스 전체 데이터베이스 암호화 인 이전 sqlcipher는 Android에서 사용할 수 없었습니다. 하지만 이제는 Android 플랫폼 용 알파 릴리스로 사용할 수 있습니다. 개발자는 SQLCipher를 사용하도록 표준 안드로이드 애플리케이션 'Notepadbot'을 업데이트했습니다.

따라서 이것은 현재 가장 좋고 가장 간단한 옵션입니다.


2
Android 용 SQLCIpher는 이제 공식 SQLCipher 프로젝트의 일부입니다. sqlcipher.net/sqlcipher-for-android
표시 이름


2
@vaichidrewar 특정 라이선스 파일은 Android 지원 부분에만 적용되며 SQLCIPHER 항목 ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… )에 대한 추가 라이선스 파일 이 있습니다. IBM 물건 ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ).
Hamid

1
Android의 SQLCipher에 대한 간단한 예제를 보려면 myownandroid.blogspot.in/2013/09/sqlcipher-in-android.html
jrhamza

SQLCipher는 @vaichidrewar ??
Arsh Kaushal

28

를 방지하기 위해 데이터베이스가 암호화됩니다 INDIRECT ATTACKS. 이 용어 및 클래스 : KeyManager.java , Crypto.java가 에서 가져 Sheran Gunasekera안드로이드 앱 보안 . 나는이 모든 책을 읽을 것을 권합니다.

INDIRECT ATTACKS바이러스가 귀하의 응용 프로그램을 직접 따르지 않기 때문에 그렇게 명명되었습니다. 대신 Android OS를 따릅니다. 목표는 바이러스 작성자가 저장된 민감한 정보를 복사 할 수 있기를 바라면서 모든 SQLite 데이터베이스를 복사하는 것입니다. 그러나 다른 보호 계층을 추가 한 경우 바이러스 작성자가 볼 수있는 모든 데이터는 왜곡됩니다. 모든 애플리케이션에서 재사용 할 수있는 암호화 라이브러리를 구축해 보겠습니다. 간단한 사양 세트를 작성하여 시작하겠습니다.

  • 대칭 알고리즘 사용 : 우리 도서관은 대칭 알고리즘 또는 블록 암호를 사용하여 데이터를 암호화하고 해독합니다. 나중에 수정할 수 있지만 AES에 합의 할 것입니다.

  • 고정 키 사용 : 데이터를 암호화하고 해독하는 데 사용할 장치에 저장할 수있는 키를 포함 할 수 있어야합니다.

  • 장치에 저장된 키 : 키는 장치에 있습니다. 이것은 직접적인 공격의 관점에서 우리 애플리케이션에 대한 위험이지만 간접 공격으로부터 우리를 보호하는 데 충분할 것입니다.

키 관리 모듈부터 시작하겠습니다 ( 목록 1 참조 ). 고정 키를 사용할 계획이므로 이전 예제에서했던 것처럼 임의의 키를 생성 할 필요가 없습니다. 따라서 KeyManager 는 다음 작업을 수행합니다.

  1. 키를 매개 변수로 허용 ( setId(byte[] data)방법)
  2. 초기화 벡터를 매개 변수 ( setIv(byte[] data) 메소드)로 허용
  3. 내부 저장소의 파일에 키 저장
  4. 내부 저장소의 파일에서 키 검색 ( getId(byte[] data) 방법)
  5. 내부 저장소의 파일에서 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 모듈 을 수행합니다 ( Listing 2 참조 ). 이 모듈은 암호화 및 암호 해독을 처리합니다. 바이트 배열 데이터를 인쇄 가능한 Base64 데이터로 또는 그 반대로 쉽게 변환 할 수 있도록 모듈에 armorEncrypt()armorDecrypt()메서드를 추가했습니다 . CBC (Cipher Block Chaining) 암호화 모드PKCS # 5 패딩 과 함께 AES 알고리즘을 사용합니다 .

(목록 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; 데이터 출력용 TextView 1 개.

(목록 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. 예. 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
키가 장치에 저장되어있는 경우 해당 키를 사용하여 데이터를 암호화하고 암호화하면 어떤 이점이 있습니까?
minhaz

다른 파일에서 키를 설정하고 가져 오는 방법 .. 작업 예제를 제공 할 수 있습니까 ?? 읽기에서 NPE를 받고 (파일)
Gaju Kollur

13

데이터베이스가 작을 경우 전체 파일을 임시 위치 (SD 카드가 아님)로 복호화 한 다음 닫은 후 다시 암호화하여 약간의 보안을 얻을 수 있습니다. 문제 : 조기 앱 종료, 미디어의 고스트 이미지.

데이터 필드를 암호화하는 약간 더 나은 솔루션입니다. 이로 인해 WHERE 및 ORDER BY 절에 문제가 발생합니다. 동등성 검색을 위해 암호화 된 필드를 인덱싱해야하는 경우 필드의 암호화 해시를 저장하고 검색 할 수 있습니다. 그러나 그것은 범위 검색이나 순서에 도움이되지 않습니다.

더 멋지게 만들고 싶다면 Android NDK를 조사하고 SQLite 용 C 코드로 암호화를 해킹 할 수 있습니다.

이러한 모든 문제와 부분적인 솔루션을 고려할 때 응용 프로그램을위한 SQL 데이터베이스가 정말로 필요합니까? 암호화 된 직렬화 된 객체를 포함하는 파일과 같은 것이 더 나을 수 있습니다.


3

Android에서 암호화 된 SQLite 데이터베이스를 사용할 수 있습니다. 그러나 Google에서 제공하는 기본 수업으로는 할 수 없습니다.

몇 가지 대안 :

  • NDK를 통해 자신의 SQLite를 컴파일하고 예를 들어 wxSQLite3 의 암호화 코덱을 포함합니다 (멋진 무료 코덱이 패키지에 포함되어 있음).
  • SQLCipher 이제 Android에 대한 지원을 포함합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.