How to encrypt sqlite texts without 3rd party libraries (Android)

混江龙づ霸主 提交于 2021-02-11 18:11:35

问题


I'm using an external database inside my android application and it directly embeds inside the apk package after compiling. As I want to implement in app purchase in order to access some of its data I don't want to leave it without any encryption. I used Sqlcipher library but it makes the app too big and slow. Isn't there any other way to do this? For example an algorithm to encrypt the strings so I put the encrypted text in database and decrypt it inside application code?


回答1:


The following is an example App that Encrypts part of the data that can then be turned on. It is based upon the code in my comment.

Stage 1 - The Master Database.

To start with you need the database that is to be the basis of the encrypted database (i.e. the MASTER database that IS NOT include in the App, it's use is to create the Encrypted database (or databases, perhaps a library, each database with a unique password/secret key if you wanted greater security)) in part consider this (as is used throughout the example) :-

As you can see this one will work by having a table called FreeData and another called PaidData. The tables definitions are the same EXCEPT that for the PaidData there is no ID column (the intention of this method is to decrypt the rows in the PaidData into the FreeData when/if the requested and the SecretKey (password) is valid.).

So The FreeData table looks like :-

The PaidData table looks like :-

  • So the only difference between the tables is the actual data contained within and that the id column is missing.
  • The id's will be generated when the encrypted data is extracted from the PaidData table, decrypted and the inserted into the FreeData table. Thus just one decryption is required to get access to the data.

Stage 2 - Generating the Encrypted Database for distribution with the App

This is done by a App just for this purpose using the EncryptDecrypt class very similar to the one at Encrypt data in SQLite

as per 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 this was designed for user login and multiple users where a salt was part of the database the salt has been hard coded using :- String saltasString = "there is no dark side of the moon it is all dark.";, The phrase can be changed as long as it is at least 16 characters in length (only the first 16 bytes are used).

A class is used to cater for potential flexibility/expansion where multiple tables can be specified or not for encryption and for multiple columns that can be encrypted, copied asis or skipped (e.g. id's would probably be skipped (in the example it's not even defined as a column).).

This class is TableColumnConvertList.java and is :-

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

The rest of this basic App, at present, is all in a single activity that uses two input's (EditTexts) :-

  • The secret key used to encrypt
  • The database name (file name) of the encrypted database.
    • Code in the App prevents using the same name as the base database, which needs to be copied into the assets folder. and a Button, to initiate the Encryption if the input is good (to a fashion aka with limited validation).

This the layout 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 where the work is done is :-

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

Of importance is this line/code :-

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

This adds a table to the List of tables to be encrypted. It's parameters are :-

  • The name of the table that is to be included in Encryption.
  • the name of the table into which the encrypted data is to be stored.
    • Note this functionality is not present but could be added. As such the value is ignored.
  • The list of columns that are to be encrypted.
  • The list of columns that are to be copied asis.
    • This functionality is not present but could be added. As such the list is ignored.
  • The list of columns that are to be skipped (e.g. id columns).
    • Although coded, the functionality is not present. As such the list is ignored.

What the App does.

The final result is an database as per the database in the assets folder (named basedb.db) that has the data in the theData column of the PaidData table encrypted, but the the FreeData table is unchanged. This database could then be copied (e.g. using device explorer) and then included as an asset in the App that is to be distributed. That App could include a reversal of the Encryption using the secret key and the decryption part of the EncryptDecrypt class.

e.g.

The FreeData table :-

The PaidData table :-

When the App is started if copies the database (hard coded as basedb.db) from the assets folder it it exists and makes the Encrypt button visible.

  • If the Encrypt button isn't visible then the asset file was not located. So it's time to correct the issue (provide the correct database file).
    • Note as this is just a demo many checks/options that could/should be done or added are skipped for brevity.

If the Encrypt button appears then encryption is just a matter of hitting the button.

After hitting the button createEncryptedDatabase method is called.

This creates a copy, this will be the encrypted database, of the database copied from the assets folder by copying the file to it's new name (as per the given database name which MUST be different to the asset's file name).

Using the copied database it queries the table(s) defined in mTCCL (an instance of the TableColumnConvertList class) .

The query will extract data only for the columns that have been specified as those to be encrypted. The query only obtain distinct rows (i.e if multiple rows exist that has the same data in the columns then only one of the rows is extracted).

For each extracted row :-

  • The commonly used ContentValues instance is cleared.
  • The whereClause StringBuilder is cleared.
  • Each column in the Cursor is checked to see if it is a column, defined in the table being processed (it should be as only column t be encrypted are extracted).
    • if not then it is skipped.
  • The original value is saved in the appropriate element of the string array columnOriginalValues (this to be used as the bind parameters for the WHERE clause of the UPDATE)
  • An element of the ContentValues instance is added with the current column name and the encrypted data.
    • This is done as per cv.put(s,ed.encrypt(c.getString(c.getColumnIndex(s))));
  • If the length of the whereClause is greater than 0 then AND is added to the whereClause, then the column name suffixed with =? is added to the whereClause being built. After all columns have been processed the SQLiteDatabase update method is called to update the columns setting the values to the encrypted values WHERE all the column match the original data.
  • After all rows have been processed the Cursor is closed and the next table processed.
  • If after all tables have been processed then error count is 0 then the transaction is set as successful, otherwise a message is Toasted Errors encountered Encrypting Database. Rolled back (not changed).
  • The transaction is then ended (if not set as successful then the data is not updated but rolled back).

The database will be in the data/data/package_name/databases folder e.g. :-




回答2:


Example Decryption App for Answer 1

Note that the database (MyDatabase) was copied from data/data/package_name/databases of the App from the previous answer (i.e. the encrypted database) into the assets folder of this app

The following is a very basic App that initially only has the Free Data, but as an Edit Text and a Button that allows the Paid Data to be decrypted and retrieved. The available data (Free Data initially) is listed in a ListView; after decryption the PaidData having been copied to the FreeData is then avialable and listed.

  • Notes
    • the data can be decrypted numerous times and each such successful attempt will add and display more rows.

EncryptDEcrypt.java is identical to the one used in the Encrypt tool.

The Database Helper is :-

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 is :-

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

Result

When first run the App only shows the Free Data as per :-

If the correct password/secret key is input and the Get Paid Data button is pressed then the extra data is added :-

If an incorrect password is provided, then the data is not loaded and a toast appears indicating the wrong password.



来源:https://stackoverflow.com/questions/58254879/how-to-encrypt-sqlite-texts-without-3rd-party-libraries-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!