AndroidはSQLiteデータベースを使用してデータを保存しますが、SQLiteデータベースを暗号化する必要がありますが、これを行うにはどうすればよいですか?アプリケーションデータはプライベートであることを理解しています。ただし、アプリが使用しているSQLiteデータベースを明示的に暗号化する必要があります。
SQLCipher は、データベースファイルの透過的な256ビットAES暗号化を提供するSQLite拡張機能です。
SQLite用のオープンソースの完全データベース暗号化である以前のsqlcipherは、Androidでは使用できませんでした。しかし、今ではAndroidプラットフォームのアルファリリースとして利用可能です。開発者は、標準のAndroidアプリケーション 'Notepadbot'をSQLCipherを使用するように更新しました。
そのため、これは間違いなく現在の最良かつ最も簡単なオプションです。
_INDIRECT ATTACKS
_を防ぐために、データベースは暗号化されます。この用語とクラス:KeyManager.Java、Crypto.Javaは、Sheran Gunasekerabook Android Apps Security 。この本はすべて読むことをお勧めします。
_INDIRECT ATTACKS
_という名前は、ウイルスがアプリケーションを直接追跡しないためです。代わりに、Android OSの後に移動します。目的は、ウイルス作成者がそこに保存されている機密情報をコピーできることを期待して、すべてのSQLiteデータベースをコピーすることです。ただし、ウイルスの作成者はデータが文字化けしているだけなので、すべてのアプリケーションで再利用できる暗号化ライブラリを構築しましょう。
対称アルゴリズムの使用:ライブラリでは、対称アルゴリズムまたはブロック暗号を使用してデータを暗号化および復号化します。後日これを変更できるはずですが、AESで解決します。
固定キーの使用:データを暗号化および復号化するために使用されるデバイスに保存できるキーを含める必要があります。
デバイスに保存されたキー:キーはデバイスに常駐します。これは、直接攻撃の観点から見るとアプリケーションのリスクですが、間接攻撃から保護するには十分です。
鍵管理モジュールから始めましょう(リスト1を参照)。固定キーを使用する予定なので、過去の例で行ったようにランダムなキーを生成する必要はありません。したがって、KeyManagerは次のタスクを実行します。
setId(byte[] data)
メソッド)setIv(byte[] data)
メソッド)getId(byte[] data)
メソッド)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を参照)。このモジュールは暗号化と復号化を処理します。モジュールに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)));
}
}
_
これらの2つのファイルは、データストレージを暗号化する必要があるアプリケーションに含めることができます。最初に、キーと初期化ベクトルの値があることを確認してから、保存する前にデータの暗号化または復号化メソッドのいずれかを呼び出します。 リスト3およびリスト4には、これらのクラスの単純なApp-exampleが含まれています使用しています。暗号化、復号化、削除の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.例。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>
_
データベースが小さい場合は、ファイル全体を一時的な場所(SDカード上ではない)に復号化し、閉じたときに再暗号化することにより、少量のセキュリティを確保できます。問題:アプリの早すぎる死、メディア上のゴースト画像。
データフィールドを暗号化するためのわずかに優れたソリューション。これにより、WHERE句とORDER BY句に問題が発生します。等価検索のために暗号化フィールドにインデックスを付ける必要がある場合は、フィールドの暗号化ハッシュを保存して検索できます。しかし、それは範囲検索や順序付けには役立ちません。
もっと手に入れたいなら、Android NDKを掘り下げ、SQLiteのCコードに暗号をハックすることができます。
これらすべての問題と部分的な解決策を考慮すると、本当にアプリケーションにSQLデータベースが必要ですか?暗号化されたシリアル化されたオブジェクトを含むファイルのようなものをお勧めします。
http://sqlite-crypt.com/ は、暗号化されたデータベースを作成するのに役立つかもしれませんが、Androidはソースコード。