last updated on November 6, 2023 by Quéau Jean Pierre

In that tutorial we will learned how to encrypt existing or new SQLite databases using the @capacitor-community/sqlite plugin.

The process flow described here does not depend on the framework you are using to develop your application.

The @capacitor-community/sqlite plugin enables database encryption for applications that run natively on iOS, Android, and Electron devices.

Table of Contents



Process Flow

IsEncryption

  • Set the deviceIsEncryption to true in capacitor.config.ts file.
     const config: CapacitorConfig = {
     ...
         plugins: {
             CapacitorSQLite: {
                 ...
                 iosIsEncryption: true,
                 ...
                 androidIsEncryption: true,
                 ...
                 electronIsEncryption: true,
                 ...
             }
         }
     ...
     }
    
  • Add isDeviceEncryption method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         isDeviceEncryption(): Promise<boolean>
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async isDeviceEncryption(): Promise<boolean> { 
             // Is platform allow for encryption
             const isPlatformEncryption = this.platform === 'web' ? false : true;
             // Check if isEncryption in the capacitor.config.ts
             const isEncryptInConfig = (await this.sqliteConnection.isInConfigEncryption()).result
    
             return isPlatformEncryption && isEncryptInConfig ? true : false;
         }
     }
    

Set Encryption Passphrase

  • This methods allows to set the passphrase which is required to encrypt databases.

  • Add setEncryptionPassphrase method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         setEncryptionPassphrase(passphrase: string): Promise<void>
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async setEncryptionPassphrase(passphrase: string): Promise<void> {
             // check if a passphrase is already stored
             let isSetPassphrase = await this.isSecretStored();
             if(isSetPassphrase) {
                 const msg = "Passphrase already stored";
                 throw new Error(`sqliteService.setEncryptionPassphrase: ${msg}`);
             }
             return Promise.resolve(await this.sqliteConnection.setEncryptionSecret(passphrase));
         }
         ... 
     }
    

OpenDatabase Method in sqliteService

  • This method will allow to:
    • create/open a non-encrypted database
    • create/open an encrypted database
    • open a non-encrypted database and encrypt it
    • open an encrypted database and decrypt it
  • create or modify openDatabase method in the sqliteService.ts file as followed:
     export interface ISQLiteService {
         ...
     openDatabase(dbName:string, version: number,
                  readOnly: boolean, decrypt?: boolean)
                 : Promise<SQLiteDBConnection>;
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
     async openDatabase(dbName:string, version: number,
                        readOnly: boolean,
                        decrypt: boolean = false)
                 : Promise<SQLiteDBConnection>  {
         this.dbNameVersionDict.set(dbName, version);
         let db: SQLiteDBConnection;
         const retCC = (await this.sqliteConnection.checkConnectionsConsistency()).result;
         let isConn = (await this.sqliteConnection.isConnection(dbName, readOnly)).result;
         if(retCC && isConn) {
             db = await this.sqliteConnection.retrieveConnection(dbName, readOnly);
         } else {
             let encrypted = false;
             let mode = "no-encryption";
             if(this.platform !== "web") {
                 const isDbExists = (await this.sqliteConnection.isDatabase(dbName)).result!;
                 const isDevEncrypt = await this.isDeviceEncryption();
                 const isDbEncrypt = isDevEncrypt && isDbExists
                         ? (await this.sqliteConnection.isDatabaseEncrypted(dbName)).result!
                         : false;
                 const isPassphrase = isDevEncrypt 
                         ? await this.isSecretStored()
                         : false;
                 encrypted = (!isDbExists && isDevEncrypt && isPassphrase) ||
                              (isDbExists && isDevEncrypt && isDbEncrypt && isPassphrase) ||
                              (isDbExists && isDevEncrypt && !isDbEncrypt && isPassphrase ) ? true : false;
                 mode =  isDbExists && isDevEncrypt && !isDbEncrypt && isPassphrase
                             ? "encryption" 
                             : encrypted && decrypt ? "decryption"
                             : encrypted ? "secret" : "no-encryption";
             }
             db = await this.sqliteConnection
                     .createConnection(dbName, encrypted, mode, version, readOnly);
         }
        
         await db.open();
         const res = (await db.isDBOpen()).result;
         if(res) {
             return db;
         } else {
             const msg = "database not opened";
             throw new Error(`sqliteService.openDatabase: ${msg}`);
         }
    
     }
    

Your Database Service

  • In initializeDatabase method or whatever you have call them, replace the call of sqliteServ.openDatabase with:

     this.db = await this.sqliteServ.openDatabase(this.database,
                                                  this.loadToVersion,
                                                  false);
    

    here understand this.database as the database name and this.loadToVersion as the database version.

Others Methods When Necessary

Is Secret/Passphrase Stored (Required)

  • This method allows to know if a passphrase is already stored for your application.

  • Add isSecretStored method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         isSecretStored(): Promise<boolean>;
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async isSecretStored(): Promise<boolean> {
             const res = await this.sqliteConnection.isSecretStored()
             return  res.result ?? false;
         }
    
         ... 
    
     }
    

Is Passphrase Valid (Optional)

  • This method allow to check if a given passphrase is equal to the one stored

  • Add isPassphraseValid method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         isPassphraseValid(passphrase: string): Promise<boolean>;
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async isPassphraseValid(passphrase: string): Promise<boolean> {
             const res = await this.sqliteConnection.checkEncryptionSecret(passphrase);
             return  res.result ?? false;
         }
    
         ... 
    
     }
    

Change Encryption Secret (Optional)

  • This method allow to change the stored passphrase with a new one

  • Add changeEncryptionSecret method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         changeEncryptionSecret(passphrase: string, oldpassphrase: string)
                                                         : Promise<void>
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async changeEncryptionSecret(passphrase: string,
                                      oldpassphrase: string)
                                      : Promise<void> {
             return await this.sqliteConnection.changeEncryptionSecret(
                                             passphrase, oldpassphrase);
         }
        
         ... 
    
     }
    

Clear Encryption Passphrase (Optional)

  • This method allow to clear the stored passphrase

  • Add clearEncryptionPassphrase method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         clearEncryptionPassphrase(): Promise<void>;
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async clearEncryptionPassphrase(): Promise<void> {
             // close all open connections
             await this.sqliteConnection.closeAllConnections();
             return await this.sqliteConnection.clearEncryptionSecret();
         }
       
         ... 
    
     }
    

Decrypt All Databases (Optional)

  • This method allow to decrypt all databases in the database folder

  • Add decryptAllDatabases method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         decryptAllDatabases(dbVerDict: Map<string, number>): Promise<void>;
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async decryptAllDatabases(dbVerDict: Map<string, number>): Promise<void> {
    
             // close all open connections
             await this.sqliteConnection.closeAllConnections();
             // get the database list
             const dbList = await this.getDatabaseList();
             const encryptNames = dbList.filter(item => item.encrypted === true).map(item => item.name);
             for (const name of encryptNames) {
                 const version = dbVerDict.get(name) ?? 1;
                 // decrypt the database
                 await this.openDatabase(name, version, false, true);
             }
             // close all open connections
             await this.sqliteConnection.closeAllConnections();
             // clear the passphrase
             await this.sqliteConnection.clearEncryptionSecret();
         }
        
         ... 
    
     }
    

Get Database List (Optional)

  • This method allow to get the list of all databases in the database folder

  • Add getDatabaseList method in the sqliteService.ts file:

     export interface ISQLiteService {
         ...
         getDatabaseList(): Promise<any[]>;
     };    
    
     class SQLiteService implements ISQLiteService {
         platform = Capacitor.getPlatform();
         sqlitePlugin = CapacitorSQLite;
         sqliteConnection = new SQLiteConnection(CapacitorSQLite);
         dbNameVersionDict: Map<string, number> = new Map();
    
         ... 
    
         async getDatabaseList(): Promise<any[]> {
             const retDbList: TDatabase[] = [];
             const dbList = (await this.sqliteConnection.getDatabaseList()).values!;
             if(dbList.length > 0) {
                 for (let idx:number = 0; idx < dbList.length; idx++) {
                     const dbName = dbList[idx].split("SQLite.db")[0];
                     const isEncrypt = (await this.sqliteConnection.isDatabaseEncrypted(dbName)).result!;
                     const data: TDatabase = {name: dbName, encrypted: isEncrypt};
                     retDbList.push(data);
                 }
             }
             return retDbList;
         };
            
         ... 
    
     }
    

Is Database Encrypted

  • This methods allows to check if a database exists and is encrypted

  • Add isDatabaseEncrypted method in the sqliteService.ts file:

     async isDatabaseEncrypted(dbName: string): Promise<boolean> {
         if((await this.sqliteConnection.isDatabase(dbName)).result)  {
             const isDBEncrypted = (await this.sqliteConnection.isDatabaseEncrypted(dbName)).result;
             return isDBEncrypted!
         } else {
             const msg = `database ${dbName} does not exist` ;
             throw new Error(`sqliteService.isDatabaseEncrypted: ${msg}`);
         }       
     }
    

Conclusion

We have completed SQLite Database Encryption using the @capacitor-community/sqlite plugin.

We learned about the process flow required to encrypt the database

  • by first modifying the capacitor.config.ts file,
  • set the encryption passphrase in the sqliteService
  • then how to open the database in the sqliteService
  • call the sqliteService openDatabase from your own service or compon ent.

We learned how to enhance the sqliteService with some methods that you may need during your application Development.

All this learning is not Framework dependent

An example for the Vue Framework can be found at Encryption/ionic7-vue-sqlite-app-encryption


<
Previous Post
Ionic 7 SQLite Database CRUD App Example Tutorial using Vue3 and @capacitor-community/sqlite
>
Next Post
Vant Nuxt Capacitor Video Player Example Tutorial using Nuxt, Vant and the capacitor-video-player plugin