Как зашифровать sqlite тексты без сторонних библиотек (Android) - PullRequest
0 голосов
/ 06 октября 2019

Я использую внешнюю базу данных внутри своего приложения для Android, и после компиляции она напрямую встраивается в пакет apk. Поскольку я хочу реализовать покупку приложения для доступа к некоторым его данным, я не хочу оставлять их без шифрования. Я использовал библиотеку Sqlcipher, но она делает приложение слишком большим и медленным. Нет ли другого способа сделать это? Например, алгоритм для шифрования строк, поэтому я помещаю зашифрованный текст в базу данных и расшифровываю его внутри кода приложения?

Ответы [ 2 ]

1 голос
/ 09 октября 2019

Пример приложения расшифровки для ответа 1

Обратите внимание, что база данных (MyDatabase) была скопирована из data / data / package_name / database приложения из предыдущего ответа (то есть зашифрованной базы данных) впапка активов этого приложения

Ниже приведено очень простое приложение, которое изначально имеет только бесплатные данные, но представляет собой текст редактирования и кнопку, позволяющую расшифровывать и извлекать платные данные. Доступные данные (изначально бесплатные данные) перечислены в ListView;после дешифрования PaidData, скопированные во FreeData, становятся доступными и отображаются в списке.

  • Примечания
    • данные могут быть расшифрованы много раз, и каждая такая успешная попыткадобавит и отобразит больше строк.

EncryptDEcrypt.java идентичен тому, который используется в инструменте шифрования.

Помощник по базам данных: -

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;
    }
}

MainActivity.java - это: -

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();
                }
            }
        });
    }
}

Результат

При первом запуске приложение отображает только бесплатные данные согласно: -

enter image description here

Если введен правильный пароль / секретный ключ и нажата кнопка «Получить оплаченные данные», то добавляются дополнительные данные: -

enter image description here

Если указан неверный пароль, данные не загружаются, и появляется всплывающее окно с указанием неверного пароля.

1 голос
/ 07 октября 2019

Ниже приведен пример приложения, которое шифрует часть данных, которую затем можно включить. Он основан на коде в моем комментарии.

Этап 1 - Основная база данных.

Для начала вам нужна база данных, которая должна быть основой зашифрованной базы данных (то есть MASTER база данных, которая НЕ включена в приложение, она используется для создания зашифрованной базы данных (или баз данных, возможно, библиотеки, каждая база данных с уникальным паролем / секретным ключом, если вы хотите большей безопасности)), частично рассматривайтеэто (как используется во всем примере): -

enter image description here

Как вы можете видеть, этот будет работать, имея таблицу с именем FreeData, а другую - с именем PaidData. ,Определения таблиц те же, КРОМЕ того, что для PaidData нет столбца ID (цель этого метода - расшифровать строки в PaidData во FreeData, когда / если запрошенный и SecretKey (пароль) действительны.).

Итак, таблица FreeData выглядит следующим образом: -

enter image description here

Таблица PaidData выглядит так:как: -

enter image description here

  • Таким образом, единственное различие между таблицами заключается в фактических данных, содержащихся внутри, и в том, что id столбец отсутствует.
  • Идентификаторы будут сгенерированы, когда зашифрованные данные будут извлечены из таблицы PaidData , расшифрованы и вставлены в таблицу FreeData . Таким образом, для доступа к данным требуется всего одна расшифровка.

Этап 2 - Генерация зашифрованной базы данных для распространения с приложением

Это делается приложением только для этой целииспользуя класс EncryptDecrypt, очень похожий на класс Шифрование данных в SQLite

согласно EncryptDecrypt.java

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);
    }
}
  • Asэто было разработано для входа пользователя и нескольких пользователей, где соль была частью базы данных, соль была жестко запрограммирована с помощью: - String saltasString = "there is no dark side of the moon it is all dark.";, фразу можно изменить, если она имеет длину не менее 16 символов (только перваяИспользуется 16 байтов).

Класс используется для обеспечения потенциальной гибкости / расширения, где можно указать несколько таблиц или нет для шифрования, а также для нескольких столбцов, которые могут быть зашифрованы, скопированы asis или пропущены (например, идентификаторы, вероятно, будут пропущены (в этом примере он даже не определен как столбец).).

Этот класс TableColumnConvertList.java и это: -

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;
        }
    }
}

Остальная часть этого базового приложения, в настоящее время, находится в одном действии, которое использует два входа (EditTexts): -

  • Секретключ, используемый для шифрования
  • Имя базы данных (имя файла) зашифрованной базы данных.
    • Код в приложении не позволяет использовать то же имя, что и базовая база данных, которую необходимо скопировать в папку ресурсов. и кнопка, чтобы инициировать шифрование, если вход является хорошим (в моде, иначе ограниченная проверка).

Это макет xml activiy_main.xml is: -

<?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>

MainActivity.java , где работа выполнена: -

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"}
            );

Это добавляет таблицу в список таблиц для шифрования. Это параметры: -

  • Имя таблицы, которая должна быть включена в шифрование.
  • имя таблицы, в которой должны храниться зашифрованные данные.
    • Обратите внимание, что эта функция отсутствует, но может быть добавлена. Как таковое значение игнорируется.
  • Список столбцов, которые должны быть зашифрованы.
  • Список столбцов, которые должны быть скопированы asis.
    • Эта функциональность отсутствует, но может быть добавлена. Таким образом, список игнорируется.
  • Список столбцов, которые должны быть пропущены (например, столбцы идентификаторов).
    • Хотя и закодировано, функциональность отсутствует. Таким образом, список игнорируется.

Что делает приложение.

Окончательный результат - это база данных в соответствии с базой данных в папке ресурсов (названной basedb.db), которая содержит данные в столбце theData Таблица PaidData зашифрована, но таблица FreeData не изменилась. Затем эту базу данных можно скопировать (например, с помощью обозревателя устройств) и затем включить в качестве ресурса в приложение, которое должно быть распространено. Это приложение может включать в себя сторнирование шифрования с использованием секретного ключа и части расшифровки класса EncryptDecrypt.

например,

Таблица FreeData: -

enter image description here

Таблица PaidData: -

enter image description here

При запуске приложения, если копируется база данных ( жестко запрограммирован как basedb.db ) из папки ресурсов, в которой он существует, и делает кнопку «Шифрование» видимой.

  • Если кнопка «Шифрование» не видна, тогда активфайл не найденИтак, пришло время исправить проблему (укажите правильный файл базы данных).
    • Обратите внимание, что это просто демонстрация, многие проверки / опции, которые можно / нужно сделать или добавить, пропущены для краткости.

Если появляется кнопка шифрованиятогда для шифрования достаточно нажать кнопку.

После нажатия кнопки вызывается метод createEncryptedDatabase .

Это создает копию (это будет зашифрованная база данных) базы данных, скопированной из папки ресурсов путем копирования файла с новым именем (согласно заданному имени базы данных, которое ДОЛЖНО отличаться от имени файла ресурса). ).

Используя скопированную базу данных, он запрашивает таблицы, определенные в mTCCL (экземпляр класса TableColumnConvertList ).

Запрос извлечет данные только для столбцов, которые были указаны как те, которые должны быть зашифрованы. Запрос получает только отдельные строки (т. Е. Если существует несколько строк с одинаковыми данными в столбцах, извлекается только одна строка).

Для каждой извлеченной строки: -

  • Обычно используемый экземпляр ContentValues ​​очищается.
  • Очистка whereClause StringBuilder.
  • Каждый столбец в курсоре проверяется, чтобы увидеть, является ли он столбцом, определенным в обрабатываемой таблице (ондолжен быть извлечен только столбец t).
    • если нет, то оно пропускается.
  • Исходное значение сохраняется в соответствующем элементе массива строк columnOriginalValues ​​ (это будет использоватьсяв качестве параметров связывания для предложения WHERE ОБНОВЛЕНИЯ)
  • Элемент экземпляра ContentValues ​​добавляется с именем текущего столбца и зашифрованными данными.
    • Это делается согласно cv.put(s,ed.encrypt(c.getString(c.getColumnIndex(s))));
  • Если длина whereClause больше 0, тогда AND добавляется к whereClause, тогда к имени столбца добавляется суффикс сзнак равнодобавляется к создаваемому whereClause. После обработки всех столбцов вызывается метод SQLiteDatabase update для обновления столбцов, устанавливающих значения в зашифрованные значения. ГДЕ все столбцы соответствуют исходным данным.
  • После обработки всех строкКурсор закрывается и следующая таблица обрабатывается.
  • Если после обработки всех таблиц количество ошибок равно 0, тогда транзакция считается успешной, в противном случае отправляется сообщение Toasted Обнаружены ошибки Шифрование базы данных. Откат (без изменений) .
  • После этого транзакция заканчивается (если она не установлена ​​как успешная, данные не обновляются, а откатывается).

База данных будетнаходиться в папке data / data / package_name / database, например: -

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...