如何使用 aes 算法加密 sqlite 文件?

Posted

技术标签:

【中文标题】如何使用 aes 算法加密 sqlite 文件?【英文标题】:How to encrypt sqlite file using aes algorithm? 【发布时间】:2022-01-21 08:58:20 【问题描述】:

我正在尝试使用 aes 算法加密 sql 文件。我在整个互联网上看到的是只加密字符串数据或使用 sqlite 密码加密数据库中的每个数据。我想使用带有AES算法的android密码术加密数据库中的每个数据,但不添加像SQLiteCipher这样的依赖项。并将密码短语保存在外部存储中的新文件中。可以做到吗?帮帮我


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;


import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import com.example.sqlitepractise.model.Contact;
import com.example.sqlitepractise.params.Params;


import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import javax.crypto.NoSuchPaddingException;


public class MyDbHandler extends SQLiteOpenHelper 

    public MyDbHandler(Context context)
        super(context, Params.DB_NAME, null, Params.DB_VERSION);
    

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) 
        String create =  "CREATE TABLE " + Params.TABLE_NAME + "("
                + Params.KEY_ID + " INTEGER PRIMARY KEY," + Params.KEY_NAME
                + " TEXT, " + Params.KEY_PHONE + " TEXT, " + Params.KEY_SECRET + " TEXT, " + Params.KEY_IV + " TEXT" + ")";

        Log.d("practise", "Query being run is : " + create);
        sqLiteDatabase.execSQL(create);

    

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) 

    

    public void addContact(Contact contact)
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        try 
            values.put(Params.KEY_NAME, contact.getName());
            values.put(Params.KEY_PHONE, contact.getPhone_number());
            db.insert(Params.TABLE_NAME, null,values);
            Log.d("practise", "Successfully inserted");
            db.close();

            // I wanted the encryption to be executed in this method

            
            catch (Exception e) 
            
               e.printStackTrace();
            
    
    public List<Contact> getAllContact()

        // I wanted the decryption to be executed in this method        

        List<Contact> contactList = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();

        //Generate the query to read from the database
        String select = "SELECT * FROM " + Params.TABLE_NAME;
        Cursor cursor = db.rawQuery(select, null);

        //Loop Through now
        if (cursor.moveToFirst())
            do 
                Contact contact = new Contact();
                contact.setId(Integer.parseInt(cursor.getString(0)));
                contact.setName(cursor.getString(1));
                contact.setPhone_number(cursor.getString(2));
                contactList.add(contact);
            while (cursor.moveToNext());
        
        return contactList;
    

    private static int READ_WRITE_BLOCK_BUFFER = 1024;
    private static String ALGO_FILE_ENCRYPTOR = "AES/CBC/PKCS5Padding";
    private final static String ALGO_SECRET_KEY = "AES";

    public static void encryptFile(String keyStr, String specStr, 
    InputStream in, OutputStream out)
    throws NoSuchPaddingException, NoSuchAlgorithmException,
    InvalidAlgorithmParameterException, InvalidKeyException, 
    IOException 
        try
            IvParameterSpec iv = new 
            IvParameterSpec(specStr.getBytes("UTF-8"));
            SecretKeySpec keySpec = new 
            SecretKeySpec(keyStr.getBytes("UTF-8"), ALGO_SECRET_KEY);
            Cipher c = Cipher.getInstance(ALGO_FILE_ENCRYPTOR);
            c.init(Cipher.ENCRYPT_MODE,keySpec,iv);
            out = new CipherOutputStream(out, c);
            int count = 0;
            byte[] buffer = new byte[READ_WRITE_BLOCK_BUFFER];
            while ((count = in.read(buffer))>0)
                out.write(buffer,0,count);
        finally 
            out.close();
        
    


【问题讨论】:

I am trying to encrypt the sql file using aes algorithm. 文件就是文件。文件包含什么无关紧要。 encrypting only the string data or encrypting every data inside the database 好吧,您可以这样做,但是您不是在加密文件,而是在加密文件中的数据。加密文件!容易得多。 也许您应该考虑一下您的需求 - 以加密形式存储数据很简单,但数据库旨在将存储的数据返回给用户。如果您尝试找到一个数据集,则需要解密每个数据集,将其与您的搜索模式进行比较,依此类推。通常只有敏感数据(列)需要加密(例如电话号码)。在 Android 上,如果您需要其他依赖项,它取决于目标 sdk-version。 【参考方案1】:

我想加密数据库中的每一个数据......

    使用你的数据库是不可能的,因为表中包含Params.KEY_ID + " INTEGER PRIMARY KEY,"
此列将是 rowid 列的别名。 rowid 或其别名必须 是整数,否则将导致 SQLITE_MISIMATCH 错误。因此加密该列将不起作用。如果不加密,值本身几乎没有用处,它只是唯一标识行。
    您包含注释代码(包含在下面),这表明理论上您要按行加密。

:-

public void addContact(Contact contact)
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues values = new ContentValues();
    try 
        values.put(Params.KEY_NAME, contact.getName());
        values.put(Params.KEY_PHONE, contact.getPhone_number());
        db.insert(Params.TABLE_NAME, null,values);
        Log.d("practise", "Successfully inserted");
        db.close();

        // I wanted the encryption to be executed in this method

        
        catch (Exception e) 
        
           e.printStackTrace();
        

除非您完全加密数据库文件,否则插入一行然后加密是没有用的。因此,考虑到第 1 点以及在行级别加密的明显希望,那么您要么

    将数据作为一个整体加密并插入单行或

    您单独加密每一列,然后插入加密数据。

      减少开销并仅加密敏感列是有意义的

选项 2 将是最简单的选项 1,您不仅需要加密/解密的方法,而且还需要连接和拆分多个数据项的方法。 p>

您的加密方法假定加密文件,各个行不是文件,它们是文件的一部分。这种方法只适合加密整个数据库,需要在将数据库打开到另一个文件之前解密整个数据库文件,并将整个数据库加密到加密文件中,这有其自身的复杂性。

基于选项 2 的示例

获取您的代码并将其修改为(用于演示原理):-

那么 MyDbHandler 可能是:-

@SuppressLint("Range")
public class MyDbHandler extends SQLiteOpenHelper 

    EncryptDecrypt ed;
    String KEYSTR = "this is my key string";
    String SPECSTR = "this is my specstring";

    public MyDbHandler(Context context)
        super(context, Params.DB_NAME, null, Params.DB_VERSION);
        ed = new EncryptDecrypt();
        EncryptDecrypt.setIvParameterSpec(new
                IvParameterSpec(SPECSTR.substring(0,16).getBytes(StandardCharsets.UTF_8)));
        EncryptDecrypt.setSecretKeySpec(new
                SecretKeySpec(KEYSTR.substring(0,16).getBytes(StandardCharsets.UTF_8), ALGO_SECRET_KEY));
        try 
            ed.setCipher(Cipher.getInstance(ALGO_FILE_ENCRYPTOR));
        
        catch (Exception e) 
            e.printStackTrace();
        
    

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) 
        String create =  "CREATE TABLE " + Params.TABLE_NAME + "("
                + Params.KEY_ID + " INTEGER PRIMARY KEY," + Params.KEY_NAME
                + " TEXT, " + Params.KEY_PHONE + " TEXT, " + Params.KEY_SECRET + " TEXT, " + Params.KEY_IV + " TEXT" + ")";

        Log.d("practise", "Query being run is : " + create);
        sqLiteDatabase.execSQL(create);
    

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) 

    

    public void addContact(Contact contact)
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        try 
            values.put(Params.KEY_NAME,ed.encrypt(contact.getName()));
            values.put(Params.KEY_PHONE,ed.encrypt(contact.getPhone_number()));
            db.insert(Params.TABLE_NAME, null,values);
            Log.d("practise", "Successfully inserted");
            db.close();
        
        catch (Exception e)
        
            e.printStackTrace();
        
    
    public List<Contact> getAllContact()
        List<Contact> contactList = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();

        //Generate the query to read from the database
        String select = "SELECT * FROM " + Params.TABLE_NAME;
        Cursor cursor = db.rawQuery(select, null);

        //Loop Through now
        if (cursor.moveToFirst())
            do 
                Contact contact = new Contact();
                contact.setId(cursor.getInt(cursor.getColumnIndex(Params.KEY_ID)));
                contact.setName(ed.decrypt(cursor.getString(cursor.getColumnIndex(Params.KEY_NAME))));
                contact.setPhone_number(ed.decrypt(cursor.getString(cursor.getColumnIndex(Params.KEY_PHONE))));
                contactList.add(contact);
            while (cursor.moveToNext());
        
        cursor.close(); //<<<< SHOULD ALWAYS CLOSE CURSORS
        return contactList;
    

    private static int READ_WRITE_BLOCK_BUFFER = 1024;
    private static String ALGO_FILE_ENCRYPTOR = "AES/CBC/PKCS5Padding";
    private final static String ALGO_SECRET_KEY = "AES";

    /*

    public static void encryptFile(String keyStr, String specStr,
                                   InputStream in, OutputStream out)
            throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException,
            IOException 
        try
            IvParameterSpec iv = new
                    IvParameterSpec(specStr.getBytes("UTF-8"));
            SecretKeySpec keySpec = new
                    SecretKeySpec(keyStr.getBytes("UTF-8"), ALGO_SECRET_KEY);
            Cipher c = Cipher.getInstance(ALGO_FILE_ENCRYPTOR);
            c.init(Cipher.ENCRYPT_MODE,keySpec,iv);
            out = new CipherOutputStream(out, c);
            int count = 0;
            byte[] buffer = new byte[READ_WRITE_BLOCK_BUFFER];
            while ((count = in.read(buffer))>0)
                out.write(buffer,0,count);
        finally 
            out.close();
        
    

     */

以及支持的 EncryptDecrypt 类:-

class EncryptDecrypt 
    private Cipher cipher;
    private static SecretKeySpec secretKeySpec;
    private static IvParameterSpec ivParameterSpec;


    /**
     *
     * @Param toEncrypt     The string to be encrypted
     * @return              The encrypted data as a string
     */
    String encrypt(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)  
        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);
    

    public void setCipher(Cipher cipher) 
        this.cipher = cipher;
    

    public static IvParameterSpec getIvParameterSpec() 
        return ivParameterSpec;
    

    public static void setIvParameterSpec(IvParameterSpec ivParameterSpec) 
        EncryptDecrypt.ivParameterSpec = ivParameterSpec;
    

    public static SecretKeySpec getSecretKeySpec() 
        return secretKeySpec;
    

    public static void setSecretKeySpec(SecretKeySpec secretKeySpec) 
        EncryptDecrypt.secretKeySpec = secretKeySpec;
    

并将 MainActivity 演示为:-

public class MainActivity extends AppCompatActivity 

    MyDbHandler db;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = new MyDbHandler(this);

        db.addContact(new Contact(0,"Fred Bloggs","1111111111"));
        db.addContact(new Contact(0,"Jane Doe","2222222222"));
        db.addContact(new Contact(0,"Miss Scarlet","3333333333"));

        Cursor csr = db.getWritableDatabase().query(Params.TABLE_NAME,null,null,null,null,null,"(random())");
        DatabaseUtils.dumpCursor(csr);
        csr.close();

        for(Contact c : db.getAllContact()) 
            Log.d("DBINFO","Name is " + c.getName() + " Phone is " + c.getPhone_number());
        

    

日志的输出将是(作为新安装运行时):-

2021-12-21 09:54:30.083 D/practise: Query being run is : CREATE TABLE mytable(_id INTEGER PRIMARY KEY,_name TEXT, _phone TEXT, _secret TEXT, _iv TEXT)
2021-12-21 09:54:30.086 D/practise: Successfully inserted
2021-12-21 09:54:30.100 I/chatty: uid=10164(a.a.so70410018javasqliteencryptdb) identical 1 line
2021-12-21 09:54:30.113 D/practise: Successfully inserted

2021-12-21 09:54:30.121 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@ec73c85
2021-12-21 09:54:30.121 I/System.out: 0 
2021-12-21 09:54:30.121 I/System.out:    _id=2
2021-12-21 09:54:30.121 I/System.out:    _name=at3jvwWB4LcYZHFWcdVhbw==
2021-12-21 09:54:30.121 I/System.out:    _phone=qqypV3kzmDrj6MruN1npvA==
2021-12-21 09:54:30.122 I/System.out:    _secret=null
2021-12-21 09:54:30.122 I/System.out:    _iv=null
2021-12-21 09:54:30.122 I/System.out: 
2021-12-21 09:54:30.122 I/System.out: 1 
2021-12-21 09:54:30.122 I/System.out:    _id=3
2021-12-21 09:54:30.122 I/System.out:    _name=DLeinhF4EoxeNZCRUZyOFw==
2021-12-21 09:54:30.122 I/System.out:    _phone=V1ZkbeIPV3Rqm6Uajc93Sg==
2021-12-21 09:54:30.122 I/System.out:    _secret=null
2021-12-21 09:54:30.122 I/System.out:    _iv=null
2021-12-21 09:54:30.122 I/System.out: 
2021-12-21 09:54:30.122 I/System.out: 2 
2021-12-21 09:54:30.122 I/System.out:    _id=1
2021-12-21 09:54:30.122 I/System.out:    _name=lRGUesUDAZegVCgfIKVDIg==
2021-12-21 09:54:30.122 I/System.out:    _phone=Q/CNAvw/xV1+LzWWSw8SKg==
2021-12-21 09:54:30.122 I/System.out:    _secret=null
2021-12-21 09:54:30.122 I/System.out:    _iv=null
2021-12-21 09:54:30.122 I/System.out: 
2021-12-21 09:54:30.122 I/System.out: <<<<<

2021-12-21 09:54:30.127 D/DBINFO: Name is Fred Bloggs Phone is 1111111111
2021-12-21 09:54:30.127 D/DBINFO: Name is Jane Doe Phone is 2222222222
2021-12-21 09:54:30.127 D/DBINFO: Name is Miss Scarlet Phone is 3333333333

使用 App Inspection 查看数据库显示:-

以上代码基于您可能希望考虑的以下更全面的答案:-

Encrypt data in SQLite

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

【讨论】:

以上是关于如何使用 aes 算法加密 sqlite 文件?的主要内容,如果未能解决你的问题,请参考以下文章

PHP如何实现AES加解密

java加密用PHP解密

正确使用AES对称加密

正确使用AES对称加密

aes加密安全吗

如何使用AES在一个程序中加密,在另一个程序中解密