Secure Storage - Encryption Key Management with Identity Vault
Encryption key management is critical when building secure apps, as encryption is only as good as the management of the encryption key.
Managing encryption keys client-side is an incredibly complicated and risky task, as apps will need to persist and use an encryption key client-side to enable secure offline apps.
Historically, secure client-side key management was impossible. However, thanks to advances in mobile device hardware and APIs, there is a way to securely manage sensitive values such as encryption keys on the client, and the companion Ionic Identity Vault product was built to correctly and securely implement those capabilities.
Managing Encryption Keys with Identity Vault
Ionic Identity Vault is a powerful solution that provides a cross-platform layer on top of modern mobile security hardware and biometric authentication. Sensitive values, such as encryption keys or auth tokens, can be stored securely on-device or at rest in specialty hardware developed by Apple and vendors in the Android ecosystem.
When used in tandem with Ionic Identity Vault, developers can safely store and management their encryption key on device, and enable biometric authentication to secure sensitive data against theft, loss, or jailbreaking.
In practice, once the app user authenticates, the app obtains an encryption key following one of several strategies. One strategy is to auto-generate a unique encryption key on demand, tied to the authenticated user. Another is to retrieve it from a server backend through an API call. Next, store the encryption key securely using Identity Vault. Finally, configure Secure Storage with the encryption key to encrypt all data.
Installation
To get started, follow the Identity Vault installation instructions.
Obtain an Encryption Key
There are several approaches to obtain an encryption key. It's recommended to use a Service that encapsulates all key retrieval logic.
Autogenerated
Auto-generate a unique encryption key on demand, tied to the authenticated user. Note that the key is not retrievable.
Angular
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class KeyService {
// Generate an encryption key on the fly unique to the current app user
// Reference: https://stackoverflow.com/a/2117523/180424
async get(): Promise<string> {
// generate a UUID v4
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}
React, Vue, Vanilla JavaScript
Coming soon!
Retrieve from a Server
Retrieve the key from a server backend using an API call and your tooling of choice. The key may be retrievable depending on the backend configuration.
Store the Key in Identity Vault
Secure the encryption key on-device using Ionic Identity Vault. It's recommended to create a Service that encapsulates all key storage logic.
Begin by injecting the KeyService
created above and creating a key
that represents the encryption key to be stored in IV.
Angular
import { Injectable } from '@angular/core';
import { AuthMode, IonicIdentityVaultUser, IonicNativeAuthPlugin, DefaultSession} from '@ionic-enterprise/identity-vault';
import { Platform } from '@ionic/angular';
import { KeyService } from './key.service';
@Injectable({
providedIn: 'root'
})
export class IdentityService extends IonicIdentityVaultUser<DefaultSession> {
private key = 'encryption-key';
constructor(public platform: Platform, private keyService: KeyService) {
// Identity Vault configuration will vary; see IV docs
super(platform, {
restoreSessionOnReady: false,
unlockOnReady: false,
// Automatically trigger Face Id
unlockOnAccess: true,
lockAfter: 1000,
hideScreenOnBackground: true,
authMode: AuthMode.BiometricAndPasscode
});
}
Next, create a generic function that stores values in Identity Vault.
private async set(value: string): Promise<void> {
const vault = await this.getVault();
vault.storeValue(this.key, value);
}
Next, create a function to retrieve the encryption key from on-device storage. If the key was previously stored in Identity Vault, return it. Otherwise,
use the KeyService
to obtain an encryption key, then save it into Identity Vault using the set
function.
async getEncryptionKey(): Promise<string> {
const vault = await this.getVault();
let dbKey = await vault.getValue(this.key);
if (!dbKey) {
dbKey = await this.keyService.get();
this.set(dbKey);
}
return dbKey;
}
React, Vue, Vanilla JavaScript
Coming soon!
Configure Ionic Secure Storage with the Encryption Key
Configure Secure Storage with the newly generated encryption key. It's recommended to create a Service that encapsulates all storage logic.
Begin by injecting the IdentityService
created above then call an initialization function in the constructor. Within the init function,
obtain the encryption key from the IdentityService
, then set it to the key
property in a call to sqlite.create()
.
Angular
import { Injectable } from '@angular/core';
import { SQLite, SQLiteObject } from '@ionic-enterprise/secure-storage/ngx';
import { IdentityService } from './identity.service';
@Injectable({
providedIn: 'root'
})
export class StorageService {
private _database: SQLiteObject | null = null;
constructor(private sqlite: SQLite, private identityService: IdentityService) {
this.init();
}
async init() {
// Obtain encryption key
const encryptionKey = await this.identityService.getEncryptionKey();
// Create the ionifits database
try {
this._database = await this.sqlite.create({
name: "my_database.db",
location: "default",
// Key used to encrypt the database
key: encryptionKey
});
// Create database tables, other setup tasks, etc.
} catch (e) {
console.error('Unable to initialize database', e);
}
}
}
React, Vue, Vanilla JavaScript
Coming soon!
Now that Secure Storage has been configured with an encryption key, all data will be encrypted at-rest.