如何在没有第三方库(Android)的情况下加密sqlite文本
我在android应用程序中使用了一个外部数据库,它在编译后直接嵌入到apk包中。 因为我想实现应用内购买,以便访问它的一些数据,我不想让它没有任何加密。 我使用了Sqlcipher库,但它使应用程序太大、太慢。如何在没有第三方库(Android)的情况下加密sqlite文本,android,sqlite,android-studio,Android,Sqlite,Android Studio,我在android应用程序中使用了一个外部数据库,它在编译后直接嵌入到apk包中。 因为我想实现应用内购买,以便访问它的一些数据,我不想让它没有任何加密。 我使用了Sqlcipher库,但它使应用程序太大、太慢。 没有别的办法吗?例如,加密字符串的算法,因此我将加密文本放入数据库,并在应用程序代码中解密?下面是一个示例应用程序,它加密了部分数据,然后可以打开。它基于我评论中的代码 第一阶段-主数据库。 首先,您需要数据库是加密数据库的基础(即,在应用程序中不包括的主/强>数据库,它的用途是创建加
没有别的办法吗?例如,加密字符串的算法,因此我将加密文本放入数据库,并在应用程序代码中解密?下面是一个示例应用程序,它加密了部分数据,然后可以打开。它基于我评论中的代码 第一阶段-主数据库。
首先,您需要数据库是加密数据库的基础(即,在应用程序中不包括的<强>主/强>数据库,它的用途是创建加密的数据库(或数据库,也许是一个库,每个数据库有一个唯一的密码/秘密密钥,如果您想要更大的安全性))部分地考虑这一点。(在整个示例中使用):-
如您所见,此方法将通过一个名为FreeData的表和另一个名为PaidData的表来工作。除了PaidData没有ID列之外,表的定义是相同的(此方法的目的是在请求的数据和SecretKey(密码)有效时,将PaidData中的行解密为FreeData) 因此,FreeData表如下所示:- PaidData表如下所示:-- 因此,表之间的唯一区别是其中包含的实际数据,id列丢失
- 当加密数据从PaidData表中提取、解密并插入FreeData表时,将生成id。因此,只需一次解密即可访问数据
class EncryptDecrypt {
private Cipher cipher;
private static SecretKeySpec secretKeySpec;
private static IvParameterSpec ivParameterSpec;
private boolean do_encrypt = true;
/**
* Construct EncryptDecrypt instance that does not check user login-in
* mode, thus the assumption is that this user is NOT the special user
* NOUSER that doesn't require a password to login; this constructor
* is designed to ONLY be used when a user has been added by NOUSER,
* and to then encrypt the data using the enccryptForced method solely
* to encrypt any existing card data for the new user that has a password.
*
* @param context The context, required for database usage (user)
* @param skey The secret key to be used to encrypt/decrypt
*/
EncryptDecrypt(Context context, String skey) {
//DBUsersMethods users = new DBUsersMethods(context);
String saltasString = "there is no dark side of the moon it is all dark.";
String paddedskey = (skey + saltasString).substring(0,16);
secretKeySpec = new SecretKeySpec(paddedskey.getBytes(),"AES/CBC/PKCS5Padding");
ivParameterSpec = new IvParameterSpec((saltasString.substring(0,16)).getBytes());
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (Exception e){
//e.printStackTrace();
}
}
/**
* Normal encryption routine that will not encrypt data if the user is
* the special case NOUSER (i.e LOGIN mode is NOUSER), otherwise data
* is encrypted.
*
* @Param toEncrypt The string to be encrypted
* @return The encryted (or not if NOUSER) data as a string
*/
String encrypt(String toEncrypt) {
if (!do_encrypt) {
return toEncrypt;
}
byte[] encrypted;
try {
cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec,ivParameterSpec);
encrypted = cipher.doFinal(toEncrypt.getBytes());
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return Base64.encodeToString(encrypted, Base64.DEFAULT);
}
/**
* Encryption, irrespective of the USER type, noting that this should
* only be used in conjunction with an EncryptDecrypt instance created
* using the 2nd/extended constructor
*
* @param toEncrypt The string to be encrypted
* @return The encrypted data as a string
*/
String encryptForced(String toEncrypt) {
byte[] encrypted;
try {
cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec,ivParameterSpec);
encrypted = cipher.doFinal(toEncrypt.getBytes());
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return Base64.encodeToString(encrypted,Base64.DEFAULT);
}
/**
* Decrypt an encrypted string
* @param toDecrypt The encrypted string to be decrypted
* @return The decrypted string
*/
String decrypt(String toDecrypt) {
if (!do_encrypt) {
return toDecrypt;
}
byte[] decrypted;
try {
cipher.init(Cipher.DECRYPT_MODE,secretKeySpec,ivParameterSpec);
decrypted = cipher.doFinal(Base64.decode(toDecrypt,Base64.DEFAULT));
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return new String(decrypted);
}
}
- 由于这是为用户登录和多个用户设计的,其中salt是数据库的一部分,因此salt已使用以下硬编码:-
,该短语可以更改,只要长度至少为16个字符(仅使用前16个字节)String saltasString=“月球没有黑暗面,一切都是黑暗的。”;
public class TableColumnConvertList {
private ArrayList<TableEntry> tables;
public TableColumnConvertList() {
this.tables = new ArrayList<>();
}
public String[] getTables() {
String[] tableList = new String[tables.size()];
int ix = 0;
for (TableEntry te: this.tables) {
tableList[ix++] = te.getSourceTableName();
}
return tableList;
}
public String[] getTableColumnNamesToEncrypt(String tableName) {
String[] rv = null;
for(TableEntry te: this.tables) {
if (te.getSourceTableName().equals(tableName)) {
rv = new String[te.getColumnNamesToEncrypt().size()];
int ix=0;
for (String s: te.getColumnNamesToEncrypt()) {
rv[ix++] = s;
}
}
}
return rv;
}
public String[] getTableColumnNamesToCopyAsis(String tableName) {
String[] rv = null;
for (TableEntry te: this.tables) {
if (te.getSourceTableName().equals(tableName)) {
rv = new String[te.getColumnNamesToCopyAsis().size()];
int ix=0;
for (String s: te.getColumnNamesToCopyAsis()) {
rv[ix++] = s;
}
}
}
return rv;
}
public String[] getTableColumnNamesToSkip(String tableName) {
String[] rv = null;
for (TableEntry te: this.tables) {
if (te.sourceTableName.equals(tableName)) {
rv = new String[te.getColumnNamesToSkip().size()];
int ix =0;
for (String s: te.getColumnNamesToSkip()) {
rv[ix++] = s;
}
}
}
return rv;
}
public void addTable(
String sourceTableName,
String destinationTableName,
String[] columnNamesToEncrypt,
String[] columnNamesToCopyAsis,
String[] columnNamesToSkip
) {
tables.add(
new TableEntry(
sourceTableName,
destinationTableName,
columnNamesToEncrypt,
columnNamesToCopyAsis,
columnNamesToSkip
)
);
}
private class TableEntry {
private String sourceTableName;
private String destinationTableName;
private ArrayList<String> columnNamesToEncrypt;
private ArrayList<String> columnNamesToCopyAsis;
private ArrayList<String> columnNamesToSkip;
private TableEntry() {}
private TableEntry(String sourceTableName,
String destinationTableName,
String[] columnNamesToEncrypt,
String[] columnNamesToCopyAsis,
String[] columnNamesToSkip
) {
this.sourceTableName = sourceTableName;
this.destinationTableName = destinationTableName;
this.columnNamesToEncrypt = new ArrayList<>();
if (columnNamesToEncrypt != null && columnNamesToEncrypt.length > 0) {
for (String s: columnNamesToEncrypt) {
addColumn(s);
}
}
}
private void addColumn(String s) {
this.columnNamesToEncrypt.add(s);
}
private String getSourceTableName() {
return sourceTableName;
}
public String getDestinationTableName() {
return destinationTableName;
}
public void setSourceTableName(String sourceTableName) {
this.sourceTableName = sourceTableName;
}
public void setDestinationTableName(String destinationTableName) {
this.destinationTableName = destinationTableName;
}
private ArrayList<String> getColumnNamesToEncrypt() {
return columnNamesToEncrypt;
}
public void setColumnNamesToEncrypt(ArrayList<String> columnNamesToEncrypt) {
this.columnNamesToEncrypt = columnNamesToEncrypt;
}
private ArrayList<String> getColumnNamesToCopyAsis() {
return columnNamesToCopyAsis;
}
public void setColumnNamesToCopyAsis(ArrayList<String> columnNamesToCopyAsis) {
this.columnNamesToCopyAsis = columnNamesToCopyAsis;
}
public ArrayList<String> getColumnNamesToSkip() {
return columnNamesToSkip;
}
public void setColumnNamesToSkip(ArrayList<String> columnNamesToSkip) {
this.columnNamesToSkip = columnNamesToSkip;
}
}
}
公共类表格列表{
私有ArrayList表;
公共表列表(){
this.tables=新的ArrayList();
}
公共字符串[]getTables(){
String[]tableList=新字符串[tables.size()];
int ix=0;
for(TableEntry te:this.tables){
tableList[ix++]=te.getSourceTableName();
}
返回表格列表;
}
公共字符串[]getTableColumnNamesToEncrypt(字符串表名){
字符串[]rv=null;
for(TableEntry te:this.tables){
if(te.getSourceTableName().equals(tableName)){
rv=新字符串[te.getColumnNamesToEncrypt().size()];
int ix=0;
for(字符串s:te.getColumnNamesToEncrypt()){
rv[ix++]=s;
}
}
}
返回rv;
}
公共字符串[]getTableColumnNamesToCopyAsis(字符串表名){
字符串[]rv=null;
for(TableEntry te:this.tables){
if(te.getSourceTableName().equals(tableName)){
rv=新字符串[te.getColumnNamesToCopyAsis().size()];
int ix=0;
for(字符串s:te.getColumnNamesToCopyAsis()){
rv[ix++]=s;
}
}
}
返回rv;
}
公共字符串[]getTableColumnNamesToSkip(字符串表名){
字符串[]rv=null;
for(TableEntry te:this.tables){
if(te.sourceTableName.equals(tableName)){
rv=新字符串[te.getColumnNamesToSkip().size()];
int ix=0;
for(字符串s:te.getColumnNamesToSkip()){
rv[ix++]=s;
}
}
}
返回rv;
}
公共无效添加表(
字符串sourceTableName,
字符串destinationTableName,
字符串[]列名称加密,
String[]columnNamesToCopyAsis,
字符串[]columnNamesToSkip
) {
tables.add(
新表格条目(
sourceTableName,
destinationTableName,
列名为Encrypt,
链球菌病,
托斯基普酒店
)
);
}
私有类表格条目{
私有字符串sourceTableName;
私有字符串destinationTableName;
私有ArrayList列名称加密;
private ArrayList columnNamesToCopyAsis;
私有ArrayList columnNamesToSkip;
私有TableEntry(){}
private TableEntry(字符串sourceTableName,
字符串destinationTableName,
字符串[]列名称加密,
String[]columnNamesToCopyAsis,
字符串[]columnNamesToSkip
) {
this.sourceTableName=sourceTableName;
this.destinationTableName=destinationTableName;
this.columnNamesToEncrypt=n
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Database EncryptTool" />
<EditText
android:id="@+id/secretkey"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Secret Key to use to Encrypt the Database."
>
</EditText>
<EditText
android:id="@+id/databasename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MyDatabase"
android:hint="Database Name"
>
</EditText>
<Button
android:id="@+id/encrypt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ENCRYPT"
android:visibility="gone"
>
</Button>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
public static final String ASSETDB_NAME = "basedb.db";
public static final int ASSETDB_NOT_FOUND = -10;
public static final int ASSETFILE_OPEN_ERROR = -11;
public static final int ASSETDB_OPEN_ERROR = -12;
public static final int ASSETDB_COPY_ERROR = -13;
public static final int ASSETDB_FLUSH_ERROR = -14;
public static final int ASSETDB_CLOSE_ERROR = -15;
public static final int ASSETFILE_CLOSE_ERROR = -16;
public static final int ASSETDB_CREATED_SUCCESSFULLY = 0;
public static final int BUFFERSIZE = 1024 * 4;
EditText mSecretKey, mDBName;
Button mEncryptButton;
TableColumnConvertList mTCCL = new TableColumnConvertList();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDBName = this.findViewById(R.id.databasename);
mSecretKey = this.findViewById(R.id.secretkey);
mEncryptButton = this.findViewById(R.id.encrypt);
//<<<<<<<<< set what data to encrypt i.e. table(s) and the column(s) in the table >>>>>>>>>
mTCCL.addTable(
"PaidData",
"FreeData",
new String[]{"theData"},
new String[]{},
new String[]{"id"}
);
if (getDBFromAsset() >= 0) {
mEncryptButton.setVisibility(View.VISIBLE);
mEncryptButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDBName.getText().toString().length() < 1) {
Toast.makeText(
v.getContext(),
"The Database Name cannot be blank.",
Toast.LENGTH_LONG
).show();
mDBName.requestFocus();
return;
}
if (mDBName.getText().toString().equals(ASSETDB_NAME)) {
Toast.makeText(
v.getContext(),
"Database Name cannot be "
+ ASSETDB_NAME
+ ". Please change the name.",
Toast.LENGTH_LONG
).show();
mDBName.requestFocus();
return;
}
if (mSecretKey.getText().toString().length() < 1) {
Toast.makeText(
v.getContext(),
"The Secret Key cannot be blank.",
Toast.LENGTH_LONG
).show();
mSecretKey.requestFocus();
return;
}
if (createEncryptedDatabase(mTCCL,
mDBName.getText().toString(),
mSecretKey.getText().toString()
) == 0) {
Toast.makeText(v.getContext(),"Successfully Encrypted Database " + mDBName + " using Secret Key " + mSecretKey,Toast.LENGTH_LONG).show();
}
}
});
}
}
private boolean checkIfDataBaseExists(String databaseName) {
File dbFile = new File(this.getDatabasePath(databaseName).getPath());
if (dbFile.exists()) {
return true;
} else {
if (!dbFile.getParentFile().exists()) {
dbFile.getParentFile().mkdirs();
}
}
return false;
}
private boolean checkIfAssetDBExists() {
try {
InputStream is = this.getAssets().open(ASSETDB_NAME);
is.close();
return true;
} catch (IOException e) {
return false;
}
}
private int getDBFromAsset() {
int rv = ASSETDB_NOT_FOUND;
File dbFile = new File(this.getDatabasePath(ASSETDB_NAME).getPath());
InputStream is;
FileOutputStream os;
int read_length;
byte[] buffer = new byte[BUFFERSIZE];
if (!checkIfAssetDBExists()) {
return ASSETDB_NOT_FOUND;
}
if (checkIfDataBaseExists(ASSETDB_NAME)) {
dbFile.delete();
}
try {
rv = ASSETFILE_OPEN_ERROR;
is = this.getAssets().open(ASSETDB_NAME);
rv = ASSETDB_OPEN_ERROR;
os = new FileOutputStream(dbFile);
rv = ASSETDB_COPY_ERROR;
while ((read_length = is.read(buffer)) > 0) {
os.write(buffer,0,read_length);
}
rv = ASSETDB_FLUSH_ERROR;
os.flush();
rv = ASSETDB_CLOSE_ERROR;
os.close();
rv = ASSETFILE_CLOSE_ERROR;
is.close();
rv = ASSETDB_CREATED_SUCCESSFULLY;
} catch (IOException e) {
e.printStackTrace();
}
return rv;
}
private int createEncryptedDatabase(TableColumnConvertList tableColumnConvertList, String databaseName, String key) {
File copiedAssetDB = new File(this.getDatabasePath(ASSETDB_NAME).getPath());
File encryptedDB = new File(this.getDatabasePath(databaseName).getPath());
if (encryptedDB.exists()) {
encryptedDB.delete();
}
try {
byte[] buffer = new byte[BUFFERSIZE];
int read_length;
InputStream is = new FileInputStream(copiedAssetDB);
OutputStream os = new FileOutputStream(encryptedDB);
while ((read_length = is.read(buffer)) > 0) {
os.write(buffer,0,read_length);
}
os.flush();
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
return -1;
}
SQLiteDatabase db = SQLiteDatabase.openDatabase(encryptedDB.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
EncryptDecrypt ed = new EncryptDecrypt(this,key);
int errorcount = 0;
db.beginTransaction();
for (String t: tableColumnConvertList.getTables()) {
ContentValues cv = new ContentValues();
String[] columnsToEncrypt = tableColumnConvertList.getTableColumnNamesToEncrypt(t);
String[] columnOriginalValues = new String[columnsToEncrypt.length];
Cursor c = db.query(true,t,columnsToEncrypt,null,null,null,null,null, null);
int totalRows = c.getCount();
int updatedRows = 0;
while (c.moveToNext()) {
cv.clear();
int ovix=0;
StringBuilder whereClause = new StringBuilder();
for (String s: c.getColumnNames()) {
for (String ec: columnsToEncrypt ) {
if (s.equals(ec)) {
cv.put(s,ed.encrypt(c.getString(c.getColumnIndex(s))));
columnOriginalValues[ovix++] = c.getString(c.getColumnIndex(s));
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
whereClause.append(s).append("=?");
}
}
}
updatedRows += db.update(t,cv,whereClause.toString(),columnOriginalValues);
}
c.close();
Log.d("ENCRYPTRESULT","Read " + totalRows + " DISTINCT ROWS. Updated " + updatedRows);
errorcount += totalRows - updatedRows;
}
if (errorcount == 0) {
db.setTransactionSuccessful();
} else {
Toast.makeText(
this,
"Errors encountered Encrypting Database. Rolled back (not changed)",
Toast.LENGTH_LONG
).show();
}
db.endTransaction();
return errorcount;
}
}
TableColumnConvertList mTCCL = new TableColumnConvertList();
..........
//<<<<<<<<< set what data to encrypt i.e. table(s) and the column(s) in the table >>>>>>>>>
mTCCL.addTable(
"PaidData",
"FreeData",
new String[]{"theData"},
new String[]{},
new String[]{"id"}
);
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "MyDatabase";
public static final int DBVERSION = 1;
public static final String TBL_FREEDATA = "FreeData";
public static final String COL_FREEDATA_ID = "id";
public static final String COL_THEDATA = "theData";
SQLiteDatabase mDB;
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
loadDBFromAssets(context);
mDB = this.getWritableDatabase();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long insertFreeDataRow(String theData) {
ContentValues cv = new ContentValues();
cv.put(COL_THEDATA,theData);
return mDB.insert(TBL_FREEDATA,null,cv);
}
public Cursor getAllAvialableData() {
return mDB.query(TBL_FREEDATA,new
String[]{"*",COL_FREEDATA_ID + " AS " + BaseColumns._ID},
null,null,null,null,null
);
}
public void decryptAndLoadPaidData(Context context, String secretKey) {
EncryptDecrypt ed = new EncryptDecrypt(context,secretKey);
mDB.beginTransaction();
Cursor c = mDB.query("PaidData",null,null,null,null,null,null);
while (c.moveToNext()) {
String decrypted_data = ed.decrypt(c.getString(c.getColumnIndex(COL_THEDATA)));
if (decrypted_data != null) {
insertFreeDataRow(decrypted_data);
} else {
Toast.makeText(context,"Naughty, that's not the password.",Toast.LENGTH_LONG).show();
}
}
c.close();
mDB.setTransactionSuccessful();
mDB.endTransaction();
}
private boolean loadDBFromAssets(Context context) {
File dbFile = new File(context.getDatabasePath(DBNAME).getPath());
byte[] buffer = new byte[1024 * 4];
int read_length = 0;
if (dbFile.exists()) return true;
if (!dbFile.getParentFile().exists()) {
dbFile.getParentFile().mkdirs();
}
try {
InputStream assetdb = context.getAssets().open(DBNAME);
OutputStream realdb = new FileOutputStream(dbFile);
while ((read_length = assetdb.read(buffer)) > 0) {
realdb.write(buffer,0,read_length);
}
realdb.flush();
realdb.close();
assetdb.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}
public class MainActivity extends AppCompatActivity {
ListView mListView;
EditText mSecretKeyInput;
Button mDecrypt;
SimpleCursorAdapter mSCA;
Cursor mAllTheData;
DBHelper mDBhlpr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = this.findViewById(R.id.list);
mSecretKeyInput = this.findViewById(R.id.secretKey);
mDecrypt = this.findViewById(R.id.decrypt);
mDBhlpr = new DBHelper(this);
manageListView();
manageDecryptButton();
}
private void manageListView() {
mAllTheData = mDBhlpr.getAllAvialableData();
if (mSCA == null) {
mSCA = new SimpleCursorAdapter(
this,android.R.layout.simple_list_item_1,mAllTheData,new String[]{DBHelper.COL_THEDATA},new int[]{android.R.id.text1},0);
mListView.setAdapter(mSCA);
} else {
mSCA.swapCursor(mAllTheData);
}
}
private void manageDecryptButton() {
mDecrypt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mSecretKeyInput.getText().toString().length() > 0) {
mDBhlpr.decryptAndLoadPaidData(v.getContext(),mSecretKeyInput.getText().toString());
manageListView();
}
}
});
}
}