Android 데이터베이스 암호화
Android는 SQLite 데이터베이스를 사용하여 데이터를 저장합니다. SQLite 데이터베이스를 암호화해야합니다. 어떻게 할 수 있습니까? 애플리케이션 데이터가 비공개임을 이해합니다. 그러나 내 앱에서 사용하는 SQLite 데이터베이스를 명시 적으로 암호화해야합니다.
SQLCipher 는 데이터베이스 파일의 투명한 256 비트 AES 암호화를 제공하는 SQLite 확장입니다.
SQLite 용 오픈 소스 전체 데이터베이스 암호화 인 이전 sqlcipher는 Android에서 사용할 수 없었습니다. 하지만 이제는 Android 플랫폼 용 알파 릴리스로 사용할 수 있습니다. 개발자는 SQLCipher를 사용하도록 표준 안드로이드 애플리케이션 'Notepadbot'을 업데이트했습니다.
따라서 이것은 현재 가장 좋고 가장 간단한 옵션입니다.
를 방지하기 위해 데이터베이스가 암호화됩니다 INDIRECT ATTACKS
. 이 용어 및 클래스 : KeyManager.java , Crypto.java가 에서 가져 Sheran Gunasekera 책 안드로이드 앱 보안 . 나는이 모든 책을 읽을 것을 권합니다.
INDIRECT ATTACKS
바이러스가 귀하의 응용 프로그램을 직접 따르지 않기 때문에 그렇게 명명되었습니다. 대신 Android OS를 따릅니다. 목표는 바이러스 작성자가 저장된 민감한 정보를 복사 할 수 있기를 바라면서 모든 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 모듈 을 수행합니다 ( 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>
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.
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.
If you want to get fancier, you could delve into the Android NDK and hack some crypto into C code for SQLite.
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.
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.
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)
- SQLCipher now includes support for Android
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.
참고URL : https://stackoverflow.com/questions/2203987/android-database-encryption
'developer tip' 카테고리의 다른 글
WPF 바인딩에서 "{Binding Path =.}"는 무엇을 의미합니까? (0) | 2020.10.17 |
---|---|
jQuery 플러그인을 확장하는 가장 좋은 방법 (0) | 2020.10.17 |
C #은 System.Type을 Generic 매개 변수로 사용합니다. (0) | 2020.10.17 |
Facebook API 오류 191 (0) | 2020.10.17 |
REST API-단일 요청으로 대량 생성 또는 업데이트 (0) | 2020.10.17 |