HarmonyOS鸿蒙Next中ArkData关系型数据库Rdb加密问题

HarmonyOS鸿蒙Next中ArkData关系型数据库Rdb加密问题

  1. 本地数据库创建的时候,配置有必须需要密码才能打开数据库文件;那么该密码是否能支持自定义加密方式?
  2. rdb StoreConfig -->encrypt, 只是个开关,这是怎么工作的呢?
  3. 从安全角度讲,数据库防止暴力导出db文件后,直接外部可以读取,这点 鸿蒙是怎么考虑的?
3 回复

1、不支持自定义加密
2、加密机制:HarmonyOS数据库加密时,应用无需传入密钥,只需要设置数据库加密的状态即可。系统会自动帮助将数据库加密,使用huks通用密钥库系统,完成数据库密钥的生成及加密保护
数据库加密机制:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/data-reliability-security-overview-V5#
数据库默认在本应用沙箱目录下创建RdbStore示例,因此当前无法导出数据库查看
3、加密后的数据库只能通过接口进行访问,无法通过其它方式打开数据库文件
demo参考:https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NEXT-Rdb
简易demo参考:

import fs from '@ohos.file.fs';
import buffer from '@ohos.buffer';
import { ValuesBucket } from '@ohos.data.ValuesBucket';
import common from '@ohos.app.ability.common';
import relationalStore from '@ohos.data.relationalStore'; // 导入模块
import { BusinessError } from '@ohos.base';
import hilog from '@ohos.hilog';
import promptAction from '@ohos.promptAction';
import { File } from '@system.file';

let store: relationalStore.RdbStore | undefined = undefined;

let predicates = new relationalStore.RdbPredicates("EMPLOYEE");

const table_name: string = 'EMPLOYEE'

// 建表Sql语句
const SQL_CREATE_TABLE =
  `CREATE TABLE IF NOT EXISTS ${table_name} (`
    + 'ID INTEGER PRIMARY KEY AUTOINCREMENT, '
    + 'NAME TEXT NOT NULL, AGE INTEGER, '
    + 'SALARY REAL, '
    + 'CODES BLOB'
    + ')';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
  @State database_version: number = 0;
  @State database_name: string = '未创建';
  @State database_flag: number = -1;
  @State create_flag: number = -1
  @State table_name1: string = 'EMPLOYEE'

  context = getContext(this) as common.UIAbilityContext;

  build() {
    Column() {
      //1、数据库创建
      Row() {
        Text(`数据库名:${this.database_name}`)
          .textAlign(TextAlign.Start)
          .fontSize(12)//.border({ width: 1 })
          .padding(10)
          .width('50%')
      }
      .width('90%')
      .border({ width: 1 })
      //2、建表
      Row() {
        if (this.create_flag === 1) {
          Text(`表名:${this.table_name1}`)
            .textAlign(TextAlign.Start)
            .fontSize(12)
            .padding(10)
            .width('50%')
        } else {
          Text(`表名:未建表`)
            .textAlign(TextAlign.Start)
            .fontSize(12)
            .padding(10)
            .width('50%')
        }
      }
      .width('90%')
      .border({ width: 1 })
      Button('1、创建数据库')
        .onClick(() => {
          if (this.database_flag === -1) {
            this.getDataStore()
          } else {
            if (this.database_flag === 1) {
              promptAction.showToast({
                message: '数据库已创建'
              });
            }
          }
        })
      Button('2、建表')
        .onClick(() => {
          if (this.database_flag === -1) {
            promptAction.showToast({
              message: '数据库未创建,请先创建数据库'
            });
            return;
          }
          this.createDatabase()
        })
      Button('3、插入数据')
        .onClick(() => {
          this.insertDate()
        })
      Button('4、删除数据')
        .onClick(() => {
          this.deleteData()
        })
      Button('5、修改数据')
        .onClick(() => {
          this.updateData()
        })
      Button('6、查询数据表')
        .onClick(() => {
          this.queryData()
        })
    }
  }

  //获取store,创建数据库
  getDataStore() {
    //设置数据库基本配置信息
    const STORE_CONFIG: relationalStore.StoreConfig = {
      name: "RdbTest.db", // 数据库文件名
      securityLevel: relationalStore.SecurityLevel.S1 // 数据库安全级别
      // encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
      // dataGroupId: 'dataGroupID', // 可选参数,仅可在Stage模型下使用,表示为应用组ID,需要向应用市场获取。指定在此Id对应的沙箱路径下创建实例,当此参数不填时,默认在本应用沙箱目录下创建。
      // customDir: 'customDir/subCustomDir' // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例
    };
    relationalStore.getRdbStore(this.context, STORE_CONFIG).then(async (rdbStore: relationalStore.RdbStore) => {
      store = rdbStore; //操作关系型数据库
      console.info('成功获取RdbStore:' + store)
      this.database_name = STORE_CONFIG.name
      this.database_flag = 1
    }).catch((err: BusinessError) => {
      console.error(`获取RdbStore失败, code is ${err.code},message is ${err.message}`);
    })
  }

  //建表
  createDatabase() {
    if (store != undefined) {
      (store as relationalStore.RdbStore).executeSql(SQL_CREATE_TABLE, (err) => {
        if (err) {
          console.error(`数据表创建失败, code is ${err.code},message is ${err.message}`);
          return;
        }
        console.info('数据表创建成功');
        this.create_flag = 1
        // 设置数据库版本
        if(store != undefined) {
          // this.database_version=(store as relationalStore.RdbStore).version+1;
          (store as relationalStore.RdbStore).version = 3;
          // 获取数据库版本
          console.info(`RdbStore version is ${store.version}`);
        }
      })
    }
  }

  //插入数据
  insertDate() {
    //数据初始化
    let value1 = 'Lisa';
    let value2 = 18;
    let value3 = 100.5;
    let value4 = new Uint8Array([1, 2, 3, 4, 5]);
    // 以下三种方式可用
    const valueBucket1: ValuesBucket = {
      'NAME': value1,
      'AGE': value2,
      'SALARY': value3,
      'CODES': value4,
    };
    if (store != undefined) {
      (store as relationalStore.RdbStore).insert("EMPLOYEE", valueBucket1, (err: BusinessError, rowId: number) => {
        if (err) {
          console.error(`插入失败, code is ${err.code},message is ${err.message}`);
          return;
        }
        console.info(`插入成功, rowId是:${rowId}`);
        promptAction.showToast({
          message: `插入成功`
        });
      })
    }
  }

  //删除数据
  deleteData() {
    predicates = new relationalStore.RdbPredicates("EMPLOYEE");
    predicates.equalTo("NAME", "Lisa");
    if (store != undefined) {
      (store as relationalStore.RdbStore).delete(predicates, (err, rows) => {
        if (err) {
          console.error(`删除失败, code is ${err.code},message is ${err.message}`);
          return;
        }
        console.info(`删除成功,Delete rows: ${rows}`);
        promptAction.showToast({
          message: `删除成功`
        });
      })
    }
  }

  //修改数据
  updateData() {
    let value1 = 'Lisa';
    let value2 = 22;
    let value3 = 200.5;
    let value4 = new Uint8Array([1, 2, 3, 4, 5]);
    // 以下三种方式可用
    const valueBucket1: ValuesBucket = {
      'NAME': value1,
      'AGE': value2,
      'SALARY': value3,
      'CODES': value4,
    };
    // 修改数据
    predicates = new relationalStore.RdbPredicates('EMPLOYEE'); // 创建表'EMPLOYEE'的predicates
    predicates.equalTo('NAME', 'Lisa'); // 匹配表'EMPLOYEE'中'NAME'为'Lisa'的字段
    if (store !== undefined) {
      (store as relationalStore.RdbStore).update(valueBucket1, predicates, (err: BusinessError, rows: number) => {
        if (err) {
          console.error(`Failed to update data. Code:${err.code}, message:${err.message}`);
          return;
        }
        console.info(`Succeeded in updating data. row count: ${rows}`);
      })
    }
  }

  queryData() {
    predicates = new relationalStore.RdbPredicates("EMPLOYEE");
    predicates.equalTo("NAME", "Lisa");
    if (store != undefined) {
      (store as relationalStore.RdbStore).query(predicates, (err, resultSet) => {
        if (err) {
          console.error(`查询失败, code is ${err.code},message is ${err.message}`);
          return;
        }
        console.log(`结果集:ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
        if (resultSet.rowCount === 0) {
          console.log(`结果集行数:${resultSet.rowCount},没有查询到数据`)
          return;
        } else {
          console.log(`查询到:${resultSet.rowCount}行数据`)
        }
        let str:string = ''
        // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
        while (resultSet.goToNextRow()) {
          const id = resultSet.getLong(resultSet.getColumnIndex("ID"));
          const name = resultSet.getString(resultSet.getColumnIndex("NAME"));
          const age = resultSet.getLong(resultSet.getColumnIndex("AGE"));
          const salary = resultSet.getDouble(resultSet.getColumnIndex("SALARY"));
          console.log(`查询结果:id=${id}, name=${name}, age=${age}, salary=${salary}`);
          str+=`查询结果:id=${id}, name=${name}, age=${age}, salary=${salary}\n`
        }
        promptAction.showToast({
          message: str
        });
        // 释放数据集的内存
        resultSet.close();
      })
    }
  }
}

更多关于HarmonyOS鸿蒙Next中ArkData关系型数据库Rdb加密问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,ArkData关系型数据库(Rdb)支持数据加密功能。ArkData Rdb使用SQLCipher库来实现加密,SQLCipher是一个开源的SQLite扩展,提供透明的256位AES加密。开发者可以通过设置密码来加密数据库文件,确保数据在存储时的安全性。

在ArkData Rdb中,加密功能通过RdbStoreConfig类进行配置。开发者可以在创建或打开数据库时,通过setEncryptKey方法设置加密密钥。密钥的长度必须为32字节,并且需要妥善保管,因为丢失密钥将导致无法访问加密数据。

以下是一个简单的示例代码:

import relationalStore from '@ohos.data.relationalStore';

let config: relationalStore.RdbStoreConfig = {
    name: 'encrypted_database.db',
    securityLevel: relationalStore.SecurityLevel.S1,
    encryptKey: new Uint8Array([ /* 32-byte encryption key */ ])
};

relationalStore.getRdbStore(context, config, function (err, store) {
    if (err) {
        console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
        return;
    }
    console.info('Succeeded in getting RdbStore.');
});

在以上代码中,encryptKey是用于加密数据库的32字节密钥。开发者需要确保密钥的安全性,并根据应用需求选择合适的加密级别。

ArkData Rdb的加密功能可以有效保护存储在设备上的敏感数据,防止未经授权的访问和数据泄露。

在HarmonyOS鸿蒙Next中,ArkData关系型数据库(Rdb)支持通过SQLCipher进行加密。开发者可以在创建或打开数据库时,通过设置密码参数来启用加密功能,确保数据在存储和传输过程中的安全性。具体实现可参考官方文档中的加密接口和示例代码,合理配置加密策略以保护敏感数据。

回到顶部