HarmonyOS 鸿蒙Next中如何实现数据加密、安全存储和权限管理?

HarmonyOS 鸿蒙Next中如何实现数据加密、安全存储和权限管理? 在HarmonyOS应用中如何保护用户数据安全?如何实现数据加密、安全存储和权限管理?

3 回复

解决方案

1. 密码加密存储

import cryptoFramework from '@ohos.security.cryptoFramework'
import { BusinessError } from '@ohos.base'
import util from '@ohos.util'

export class PasswordUtil {
  /**
   * MD5哈希
   */
  static async md5(data: string): Promise<string> {
    try {
      const mdAlg = cryptoFramework.createMd('MD5')
      await mdAlg.update({ data: new Uint8Array(this.stringToArray(data)) })
      const result = await mdAlg.digest()
      return this.arrayToHexString(new Uint8Array(result.data))
    } catch (error) {
      const err = error as BusinessError
      console.error('MD5加密失败:', err.message)
      return ''
    }
  }

  /**
   * SHA256哈希(推荐)
   */
  static async sha256(data: string): Promise<string> {
    try {
      const mdAlg = cryptoFramework.createMd('SHA256')
      await mdAlg.update({ data: new Uint8Array(this.stringToArray(data)) })
      const result = await mdAlg.digest()
      return this.arrayToHexString(new Uint8Array(result.data))
    } catch (error) {
      const err = error as BusinessError
      console.error('SHA256加密失败:', err.message)
      return ''
    }
  }

  /**
   * 加盐哈希(更安全)
   */
  static async hashWithSalt(password: string, salt?: string): Promise<{ hash: string, salt: string }> {
    // 生成盐值
    if (!salt) {
      salt = this.generateSalt()
    }

    // 组合密码和盐值
    const combined = password + salt
    const hash = await this.sha256(combined)

    return { hash, salt }
  }

  /**
   * 验证密码
   */
  static async verifyPassword(
    password: string,
    hash: string,
    salt: string
  ): Promise<boolean> {
    const result = await this.hashWithSalt(password, salt)
    return result.hash === hash
  }

  /**
   * 生成随机盐值
   */
  private static generateSalt(length: number = 16): string {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    let salt = ''
    for (let i = 0; i < length; i++) {
      salt += chars.charAt(Math.floor(Math.random() * chars.length))
    }
    return salt
  }

  private static stringToArray(str: string): number[] {
    const arr: number[] = []
    for (let i = 0; i < str.length; i++) {
      arr.push(str.charCodeAt(i))
    }
    return arr
  }

  private static arrayToHexString(arr: Uint8Array): string {
    let hexString = ''
    for (let i = 0; i < arr.length; i++) {
      const hex = arr[i].toString(16)
      hexString += hex.length === 1 ? '0' + hex : hex
    }
    return hexString
  }
}

// 使用示例
@Entry
@Component
struct PasswordDemo {
  @State password: string = ''
  @State hashedPassword: string = ''
  @State salt: string = ''

  build() {
    Column({ space: 16 }) {
      Text('密码加密')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      TextInput({
          text: this.password,
          placeholder: '请输入密码'
        })
        .width('100%')
        .type(InputType.Password)
        .onChange((value) => {
            this.password = value
        })

      Button('加密密码')
        .width('100%')
        .onClick(async () => {
            const result = await PasswordUtil.hashWithSalt(this.password)
            this.hashedPassword = result.hash
            this.salt = result.salt
        })

      if (this.hashedPassword) {
        Column({ space: 8 }) {
          Text('哈希值:')
            .fontSize(14)
            .fontWeight(FontWeight.Bold)
          Text(this.hashedPassword)
            .fontSize(12)
            .fontColor('#666666')
            .maxLines(2)

          Text('盐值:')
            .fontSize(14)
            .fontWeight(FontWeight.Bold)
          Text(this.salt)
            .fontSize(12)
            .fontColor('#666666')
        }
        .width('100%')
        .padding(12)
        .backgroundColor('#f5f5f5')
        .borderRadius(8)
        .alignItems(HorizontalAlign.Start)
      }

      Button('验证密码')
        .width('100%')
        .enabled(this.hashedPassword.length > 0)
        .onClick(async () => {
            const isValid = await PasswordUtil.verifyPassword(
              this.password,
              this.hashedPassword,
              this.salt
            )
            console.log('密码验证结果:', isValid)
        })
    }
    .padding(16)
  }
}

2. AES数据加密

import cryptoFramework from '@ohos.security.cryptoFramework'
import { BusinessError } from '@ohos.base'

export class AESUtil {
  private static readonly ALGORITHM = 'AES256|GCM|PKCS7'
  private static readonly KEY_SIZE = 256

  /**
   * 生成密钥
   */
  static async generateKey(): Promise<cryptoFramework.SymKey> {
    try {
      const symKeyGenerator = cryptoFramework.createSymKeyGenerator(this.ALGORITHM)
      return await symKeyGenerator.generateSymKey()
    } catch (error) {
      const err = error as BusinessError
      console.error('生成密钥失败:', err.message)
      throw error
    }
  }

  /**
   * AES加密
   */
  static async encrypt(
    plainText: string,
    key: cryptoFramework.SymKey
  ): Promise<{ cipherText: string, iv: string }> {
    try {
      const cipher = cryptoFramework.createCipher(this.ALGORITHM)

      // 生成随机IV
      const iv = this.generateIV()
      const ivData = new Uint8Array(this.hexStringToArray(iv))

      // 初始化加密器
      await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, { data: ivData })

      // 加密数据
      const plainData = new Uint8Array(this.stringToArray(plainText))
      await cipher.update({ data: plainData })
      const encryptResult = await cipher.doFinal(null)

      const cipherText = this.arrayToHexString(new Uint8Array(encryptResult.data))

      return { cipherText, iv }
    } catch (error) {
      const err = error as BusinessError
      console.error('加密失败:', err.message)
      throw error
    }
  }

  /**
   * AES解密
   */
  static async decrypt(
    cipherText: string,
    key: cryptoFramework.SymKey,
    iv: string
  ): Promise<string> {
    try {
      const cipher = cryptoFramework.createCipher(this.ALGORITHM)

      // 初始化解密器
      const ivData = new Uint8Array(this.hexStringToArray(iv))
      await cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, key, { data: ivData })

      // 解密数据
      const cipherData = new Uint8Array(this.hexStringToArray(cipherText))
      await cipher.update({ data: cipherData })
      const decryptResult = await cipher.doFinal(null)

      return this.arrayToString(new Uint8Array(decryptResult.data))
    } catch (error) {
      const err = error as BusinessError
      console.error('解密失败:', err.message)
      throw error
    }
  }

  private static generateIV(): string {
    const iv = new Uint8Array(16)
    for (let i = 0; i < 16; i++) {
      iv[i] = Math.floor(Math.random() * 256)
    }
    return this.arrayToHexString(iv)
  }

  private static stringToArray(str: string): number[] {
    const arr: number[] = []
    for (let i = 0; i < str.length; i++) {
      arr.push(str.charCodeAt(i))
    }
    return arr
  }

  private static arrayToString(arr: Uint8Array): string {
    return String.fromCharCode(...arr)
  }

  private static arrayToHexString(arr: Uint8Array): string {
    let hexString = ''
    for (let i = 0; i < arr.length; i++) {
      const hex = arr[i].toString(16)
      hexString += hex.length === 1 ? '0' + hex : hex
    }
    return hexString
  }

  private static hexStringToArray(hexString: string): number[] {
    const arr: number[] = []
    for (let i = 0; i < hexString.length; i += 2) {
      arr.push(parseInt(hexString.substr(i, 2), 16))
    }
    return arr
  }
}

// 使用示例
@Entry
@Component
struct AESDemo {
  @State plainText: string = ''
  @State encryptedText: string = ''
  @State decryptedText: string = ''
  private key?: cryptoFramework.SymKey
  private iv: string = ''

  async aboutToAppear() {
    // 生成密钥
    this.key = await AESUtil.generateKey()
  }

  build() {
    Column({ space: 16 }) {
      Text('AES加密')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      TextInput({
          text: this.plainText,
          placeholder: '请输入要加密的文本'
        })
        .width('100%')
        .onChange((value) => {
            this.plainText = value
        })

      Button('加密')
        .width('100%')
        .onClick(async () => {
            if (this.key) {
              const result = await AESUtil.encrypt(this.plainText, this.key)
              this.encryptedText = result.cipherText
              this.iv = result.iv
            }
        })

      if (this.encryptedText) {
        Text('加密结果:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
        Text(this.encryptedText)
          .fontSize(12)
          .fontColor('#666666')
          .maxLines(3)
      }

      Button('解密')
        .width('100%')
        .enabled(this.encryptedText.length > 0)
        .onClick(async () => {
            if (this.key) {
              this.decryptedText = await AESUtil.decrypt(
                this.encryptedText,
                this.key,
                this.iv
              )
            }
        })

      if (this.decryptedText) {
        Text('解密结果:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
        Text(this.decryptedText)
          .fontSize(12)
          .fontColor('#666666')
      }
    }
    .padding(16)
  }
}

3. 安全存储工具类

import preferences from '@ohos.data.preferences'
import cryptoFramework from '@ohos.security.cryptoFramework'

export class SecureStorage {
  private static pref?: preferences.Preferences
  private static key?: cryptoFramework.SymKey

  /**
   * 初始化
   */
  static async init(): Promise<void> {
    this.pref = await preferences.getPreferences(
      getContext(),
      'secure_storage'
    )
    this.key = await AESUtil.generateKey()
  }

  /**
   * 安全存储数据
   */
  static async setSecure(key: string, value: string): Promise<void> {
    if (!this.pref || !this.key) {
      await this.init()
    }

    try {
      // 加密数据
      const encrypted = await AESUtil.encrypt(value, this.key!)

      // 存储加密数据和IV
      await this.pref!.put(key, encrypted.cipherText)
      await this.pref!.put(`${key}_iv`, encrypted.iv)
      await this.pref!.flush()
    } catch (error) {
      console.error('安全存储失败:', error)
      throw error
    }
  }

  /**
   * 安全读取数据
   */
  static async getSecure(key: string): Promise<string | null> {
    if (!this.pref || !this.key) {
      await this.init()
    }

    try {
      const cipherText = await this.pref!.get(key, '') as string
      const iv = await this.pref!.get(`${key}_iv`, '') as string

      if (!cipherText || !iv) {
        return null
      }

      // 解密数据
      return await AESUtil.decrypt(cipherText, this.key!, iv)
    } catch (error) {
      console.error('安全读取失败:', error)
      return null
    }
  }

  /**
   * 删除数据
   */
  static async remove(key: string): Promise<void> {
    if (!this.pref) {
      await this.init()
    }

    await this.pref!.delete(key)
    await this.pref!.delete(`${key}_iv`)
    await this.pref!.flush()
  }

  /**
   * 清空所有数据
   */
  static async clear(): Promise<void> {
    if (!this.pref) {
      await this.init()
    }

    await this.pref!.clear()
    await this.pref!.flush()
  }
}

// 使用示例
@Entry
@Component
struct SecureStorageDemo {
  @State username: string = ''
  @State password: string = ''
  @State storedPassword: string = ''

  async aboutToAppear() {
    await SecureStorage.init()
  }

  build() {
    Column({ space: 16 }) {
      Text('安全存储')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      TextInput({
          text: this.username,
          placeholder: '用户名'
        })
        .width('100%')
        .onChange((value) => {
            this.username = value
        })

      TextInput({
          text: this.password,
          placeholder: '密码'
        })
        .width('100%')
        .type(InputType.Password)
        .onChange((value) => {
            this.password = value
        })

      Button('安全保存')
        .width('100%')
        .onClick(async () => {
            await SecureStorage.setSecure('username', this.username)
            await SecureStorage.setSecure('password', this.password)
            console.log('数据已安全保存')
        })

      Button('读取密码')
        .width('100%')
        .onClick(async () => {
            this.storedPassword = await SecureStorage.getSecure('password') || ''
            console.log('读取的密码:', this.storedPassword)
        })

      if (this.storedPassword) {
        Text(`存储的密码: ${this.storedPassword}`)
          .fontSize(14)
      }
    }
    .padding(16)
  }
}

4. Token安全管理

export interface TokenInfo {
  accessToken: string
  refreshToken: string
  expiresAt: number
}

export class TokenManager {
  private static readonly TOKEN_KEY = 'auth_token'
  private static readonly REFRESH_KEY = 'refresh_token'
  private static readonly EXPIRES_KEY = 'token_expires'

  /**
   * 保存Token
   */
  static async saveToken(token: TokenInfo): Promise<void> {
    await SecureStorage.setSecure(this.TOKEN_KEY, token.accessToken)
    await SecureStorage.setSecure(this.REFRESH_KEY, token.refreshToken)
    await SecureStorage.setSecure(this.EXPIRES_KEY, token.expiresAt.toString())
  }

  /**
   * 获取Token
   */
  static async getToken(): Promise<TokenInfo | null> {
    const accessToken = await SecureStorage.getSecure(this.TOKEN_KEY)
    const refreshToken = await SecureStorage.getSecure(this.REFRESH_KEY)
    const expiresStr = await SecureStorage.getSecure(this.EXPIRES_KEY)

    if (!accessToken || !refreshToken || !expiresStr) {
      return null
    }

    return {
      accessToken,
      refreshToken,
      expiresAt: parseInt(expiresStr)
    }
  }

  /**
   * 检查Token是否过期
   */
  static async isTokenExpired(): Promise<boolean> {
    const token = await this.getToken()
    if (!token) {
      return true
    }

    return Date.now() >= token.expiresAt
  }

  /**
   * 清除Token
   */
  static async clearToken(): Promise<void> {
    await SecureStorage.remove(this.TOKEN_KEY)
    await SecureStorage.remove(this.REFRESH_KEY)
    await SecureStorage.remove(this.EXPIRES_KEY)
  }

  /**
   * 获取有效Token
   */
  static async getValidToken(): Promise<string | null> {
    if (await this.isTokenExpired()) {
      // Token已过期,需要刷新
      await this.refreshToken()
    }

    const token = await this.getToken()
    return token?.accessToken || null
  }

  /**
   * 刷新Token
   */
  private static async refreshToken(): Promise<void> {
    // 实现Token刷新逻辑
    console.log('刷新Token')
  }
}

关键要点

  1. 密码安全: 使用SHA256+盐值哈希,不存储明文
  2. 数据加密: 使用AES加密敏感数据
  3. 密钥管理: 安全生成和存储加密密钥
  4. Token管理: 加密存储、过期检查、自动刷新
  5. 权限最小化: 只申请必要权限

最佳实践

  1. 不信任输入: 验证所有用户输入
  2. HTTPS通信: 网络传输使用HTTPS
  3. 敏感数据: 内存中不长期保留
  4. 日志安全: 不在日志中输出敏感信息
  5. 安全审计: 定期检查安全漏洞

更多关于HarmonyOS 鸿蒙Next中如何实现数据加密、安全存储和权限管理?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next通过分布式数据管理框架实现数据加密,支持端到端加密传输。安全存储基于密钥管理系统和硬件级安全芯片,提供文件级加密存储。权限管理采用最小权限原则,通过动态权限申请和细粒度访问控制确保应用权限安全。

在HarmonyOS Next中,数据安全通过系统级能力与应用层开发结合实现,以下是核心方案:

1. 数据加密

  • 使用@ohos.security.cryptoFramework加密框架,支持AES、RSA、ECC等算法。
  • 通过crypto.createCipher创建加密实例,结合密钥生成器(KeyGenerator)保障数据加密强度。
  • 敏感数据(如密码)推荐使用非对称加密,本地存储结合密钥库(KeyStore)保护密钥。

2. 安全存储

  • 用户数据:使用@ohos.data.preferences持久化存储,数据以加密形式保存在应用沙箱内。
  • 敏感信息:通过@ohos.security.keystore密钥系统管理,密钥由TEE(可信执行环境)保护,避免明文泄露。
  • 数据库加密@ohos.data.relationalStore支持数据库级加密,通过encrypt参数开启,数据文件自动加密。

3. 权限管理

  • 声明权限:在module.json5中按需声明权限(如ohos.permission.READ_CONTACTS)。
  • 动态授权:使用@ohos.abilityAccessCtrlrequestPermissionsFromUser接口触发动态授权弹窗。
  • 最小化权限原则:仅申请必要权限,通过verifyAccessToken接口校验权限状态。

实践建议

  • 加密密钥与设备绑定,避免导出。
  • 定期清理内存中的敏感数据临时对象。
  • 使用@ohos.security.huks统一密钥系统管理全生命周期密钥。

以上能力均需在ArkTS中调用对应API,并遵循HarmonyOS安全开发规范。

回到顶部