10  第9章 应用安全

10.1 9.1 HarmonyOS安全架构

10.1.1 9.1.1 安全模型概述

HarmonyOS采用了分层安全架构,从硬件到应用层构建了完整的安全防护体系。其安全模型的核心原则是”最小权限”和”纵深防御”。

HarmonyOS安全架构包含以下层次:

  1. 硬件安全层:基于TEE(Trusted Execution Environment)的可信执行环境,提供硬件级别的密钥存储和密码学运算。
  2. 内核安全层:基于微内核设计,实现进程隔离、内存保护和安全调度。
  3. 系统服务层:提供安全认证、权限管理、加密服务等系统级安全能力。
  4. 应用框架层:实现应用沙箱、权限控制和数据安全存储。
  5. 应用层:开发者在此层面实现业务安全逻辑。
// HarmonyOS安全能力概览
// 以下代码展示了如何在应用中调用各层安全能力

import { security } from '@kit.SecurityKit'
import { cryptoFramework } from '@kit.CryptoKit'

// 安全能力使用示例入口
class SecurityDemo {
  // 1. 使用系统级加密服务
  async encryptData(plainText: string): Promise<ArrayBuffer> {
    // 调用系统加密服务(系统服务层)
    const aesCipher = await cryptoFramework.createCipher('AES128|CBC|PKCS7')
    // ... 加密逻辑
    return new ArrayBuffer(0)
  }

  // 2. 使用安全存储
  async storeSecureData(key: string, value: string): Promise<void> {
    // 调用安全存储(应用框架层)
    // 数据会被TEE保护
  }

  // 3. 权限检查
  async checkPermission(permission: string): Promise<boolean> {
    // 调用权限管理(应用框架层)
    return true
  }
}

10.1.2 9.1.2 应用沙箱机制

HarmonyOS通过应用沙箱确保每个应用在隔离的环境中运行,防止应用间的数据泄露和恶意行为。

沙箱机制的核心特性:

  • 文件系统隔离:每个应用拥有独立的数据目录,无法直接访问其他应用的文件。
  • 进程隔离:每个应用运行在独立的进程中,通过内核级进程隔离防止内存越界访问。
  • IPC管控:应用间的进程间通信需要经过系统授权和检查。
  • 资源限制:对每个应用的CPU、内存、存储等资源使用进行限制。
// 应用沙箱目录结构
// 每个应用的数据存储在以下沙箱路径中:
// /data/storage/el2/base/haps/<app_bundle_name>/
//   ├── files/          # 应用文件目录
//   ├── cache/          # 缓存目录
//   ├── preferences/    # 偏好设置
//   ├── database/       # 数据库
//   └── log/            # 日志

import { fileIo } from '@kit.CoreFileKit'

// 在沙箱内操作文件
class SandboxFileDemo {
  // 获取应用沙箱文件路径
  async getFilePath(context: Context, fileName: string): Promise<string> {
    // filesDir 指向应用沙箱的files目录
    return `${context.filesDir}/${fileName}`
  }

  // 在沙箱内写入文件
  async writeFile(context: Context, fileName: string, content: string): Promise<void> {
    const filePath = `${context.filesDir}/${fileName}`
    const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY)
    try {
      fileIo.writeSync(file.fd, content)
    } finally {
      fileIo.closeSync(file.fd)
    }
  }

  // 在沙箱内读取文件
  async readFile(context: Context, fileName: string): Promise<string> {
    const filePath = `${context.filesDir}/${fileName}`
    const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY)
    try {
      const stat = fileIo.statSync(file.fd)
      const buffer = new ArrayBuffer(stat.size)
      fileIo.readSync(file.fd, buffer)
      // 将ArrayBuffer转为字符串
      const decoder = new util.TextDecoder('utf-8')
      const uint8Array = new Uint8Array(buffer)
      return decoder.decodeWithStream(uint8Array)
    } finally {
      fileIo.closeSync(file.fd)
    }
  }
}

import { util } from '@kit.ArkTS'

10.1.3 9.1.3 权限管理体系

HarmonyOS的权限管理体系采用分级授权模式,确保应用仅能访问其所需的系统资源。

权限分类:

权限级别 说明 授权方式
normal 普通权限 安装时自动授权
system_basic 系统基础权限 系统应用专用
system_core 系统核心权限 系统核心应用专用
user_grant 用户授权权限 运行时动态申请
// module.json5 权限声明示例
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",
        "reason": "$string:permission_internet_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:permission_location_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.CAMERA",
        "reason": "$string:permission_camera_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

10.1.4 9.1.4 安全启动链

HarmonyOS实现了从硬件到应用的完整安全启动链,确保系统软件未被篡改。

安全启动流程:

  1. BootROM:芯片内置的不可变代码,验证Bootloader签名。
  2. Bootloader:验证操作系统内核签名。
  3. 内核:验证系统服务和驱动签名。
  4. 系统框架:验证应用框架层签名。
  5. 应用:验证应用安装包签名。

每个环节都通过密码学签名确保下一环节的完整性和可信性,形成信任链(Chain of Trust)。


10.2 9.2 权限管理深入

10.2.1 9.2.1 动态权限申请

对于user_grant类型的权限,应用必须在运行时向用户动态申请。

import { abilityAccessCtrl, common } from '@kit.AbilityKit'

// 权限管理器
class PermissionManager {
  private context: common.UIAbilityContext

  constructor(context: common.UIAbilityContext) {
    this.context = context
  }

  // 检查单个权限
  async checkPermission(permission: string): Promise<boolean> {
    const atManager = abilityAccessCtrl.createAtManager()
    try {
      const result = atManager.checkAccessTokenSync(
        this.context.applicationInfo.accessTokenId,
        permission
      )
      return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
    } catch (error) {
      console.error(`检查权限失败: ${(error as Error).message}`)
      return false
    }
  }

  // 动态申请权限
  async requestPermission(permission: string): Promise<boolean> {
    const atManager = abilityAccessCtrl.createAtManager()
    try {
      const result = await atManager.requestPermissionsFromUser(
        this.context,
        [permission]
      )
      // 检查授权结果
      const grantStatus = result.authResults[0]
      if (grantStatus === 0) {
        console.info(`权限 ${permission} 已授予`)
        return true
      } else {
        console.warn(`权限 ${permission} 被拒绝`)
        // 检查是否可以显示 rationale
        if (result.shouldShowRequestPermissionRationale[0]) {
          // 用户之前拒绝过,需要解释为什么需要该权限
          this.showPermissionRationale(permission)
        }
        return false
      }
    } catch (error) {
      console.error(`申请权限失败: ${(error as Error).message}`)
      return false
    }
  }

  // 批量申请权限
  async requestPermissions(permissions: string[]): Promise<Map<string, boolean>> {
    const atManager = abilityAccessCtrl.createAtManager()
    const resultMap = new Map<string, boolean>()

    try {
      const result = await atManager.requestPermissionsFromUser(
        this.context,
        permissions
      )

      for (let i = 0; i < permissions.length; i++) {
        resultMap.set(permissions[i], result.authResults[i] === 0)
      }
    } catch (error) {
      console.error(`批量申请权限失败: ${(error as Error).message}`)
    }

    return resultMap
  }

  // 显示权限说明
  private showPermissionRationale(permission: string): void {
    // 弹出对话框解释为什么需要该权限
    console.info(`需要向用户解释为什么需要 ${permission} 权限`)
  }
}

// 使用示例
@Component
struct CameraPage {
  private context = getContext(this) as common.UIAbilityContext
  private permissionManager: PermissionManager | null = null

  aboutToAppear(): void {
    this.permissionManager = new PermissionManager(this.context)
  }

  async takePhoto(): Promise<void> {
    if (!this.permissionManager) return

    // 先检查权限
    const hasPermission = await this.permissionManager.checkPermission(
      'ohos.permission.CAMERA'
    )

    if (hasPermission) {
      // 已有权限,直接拍照
      await this.openCamera()
    } else {
      // 申请权限
      const granted = await this.permissionManager.requestPermission(
        'ohos.permission.CAMERA'
      )
      if (granted) {
        await this.openCamera()
      } else {
        // 权限被拒绝,显示提示
        this.showPermissionDeniedDialog()
      }
    }
  }

  async openCamera(): Promise<void> {
    // 打开相机
  }

  showPermissionDeniedDialog(): void {
    // 显示权限被拒绝的对话框
  }

  build() {
    Column() {
      Button('拍照')
        .onClick(() => this.takePhoto())
    }
  }
}

10.2.2 9.2.2 权限分级

HarmonyOS的权限分为多个等级,不同等级有不同的获取条件和保护策略。

// 权限分级示例
class PermissionLevelDemo {
  // normal级别权限 - 安装时自动授权
  // 如:ohos.permission.INTERNET(网络访问)
  // 如:ohos.permission.GET_NETWORK_INFO(获取网络信息)

  // user_grant级别权限 - 需要运行时动态申请
  // 如:ohos.permission.CAMERA(相机)
  // 如:ohos.permission.APPROXIMATELY_LOCATION(粗略定位)
  // 如:ohos.permission.READ_MEDIA(读取媒体文件)

  // system_basic级别权限 - 仅系统应用可获取
  // 如:ohos.permission.GET_WIFI_INFO(获取WiFi信息)

  // system_core级别权限 - 仅核心系统应用可获取
  // 如:ohos.permission.CONFIG_WIFI(配置WiFi)

  // 权限使用场景声明
  getPermissionConfig(): object {
    return {
      // 使用时申请 - 仅在前台使用时可用
      locationInUse: {
        permission: 'ohos.permission.APPROXIMATELY_LOCATION',
        when: 'inuse"  // 仅在使用时
      },
      // 始终允许 - 前后台均可使用
      locationAlways: {
        permission: 'ohos.permission.LOCATION',
        when: 'always'  // 始终可用
      }
    }
  }
}

10.2.3 9.2.3 代理授权(Delegation)

代理授权允许一个应用代表另一个应用执行操作,适用于跨应用协作场景。

// 代理授权配置
class DelegationDemo {
  // 委托方应用(授权方)
  async setupDelegation(context: common.UIAbilityContext): Promise<void> {
    const atManager = abilityAccessCtrl.createAtManager()

    // 将位置权限委托给目标应用
    try {
      await atManager.setAccessTokenDelegation(
        context,
        {
          // 被委托的应用包名
          delegateBundleName: 'com.example.delegateapp',
          // 委托的权限列表
          permissions: ['ohos.permission.APPROXIMATELY_LOCATION'],
          // 委托的有效期
          expiryTime: Date.now() + 24 * 60 * 60 * 1000  // 24小时
        }
      )
      console.info('权限委托设置成功')
    } catch (error) {
      console.error(`权限委托设置失败: ${(error as Error).message}`)
    }
  }

  // 撤销委托
  async revokeDelegation(context: common.UIAbilityContext): Promise<void> {
    const atManager = abilityAccessCtrl.createAtManager()
    try {
      await atManager.clearAccessTokenDelegation(
        context,
        'com.example.delegateapp'
      )
      console.info('权限委托已撤销')
    } catch (error) {
      console.error(`撤销委托失败: ${(error as Error).message}`)
    }
  }
}

10.2.4 9.2.4 权限最佳实践

// 权限管理最佳实践
class PermissionBestPractice {
  private context: common.UIAbilityContext

  constructor(context: common.UIAbilityContext) {
    this.context = context
  }

  // 实践1:按需申请,不要一次性申请所有权限
  async requestPermissionOnDemand(permission: string): Promise<boolean> {
    const atManager = abilityAccessCtrl.createAtManager()

    // 先检查是否已有权限
    const hasPermission = atManager.checkAccessTokenSync(
      this.context.applicationInfo.accessTokenId,
      permission
    ) === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED

    if (hasPermission) return true

    // 仅在需要时才申请
    const result = await atManager.requestPermissionsFromUser(
      this.context, [permission]
    )
    return result.authResults[0] === 0
  }

  // 实践2:优雅处理权限被拒绝的场景
  handlePermissionResult(granted: boolean, feature: string): void {
    if (granted) {
      // 权限已授予,执行功能
      this.executeFeature(feature)
    } else {
      // 权限被拒绝,提供降级方案
      this.provideFallback(feature)
    }
  }

  // 实践3:解释权限用途,提高授权率
  async requestWithExplanation(
    permission: string,
    reason: string
  ): Promise<boolean> {
    // 先展示说明对话框
    const userConfirmed = await this.showExplanationDialog(reason)

    if (!userConfirmed) return false

    // 用户理解后再申请
    const atManager = abilityAccessCtrl.createAtManager()
    const result = await atManager.requestPermissionsFromUser(
      this.context, [permission]
    )
    return result.authResults[0] === 0
  }

  // 实践4:引导用户到设置页面开启权限
  async guideToSettings(): Promise<void> {
    const want: Want = {
      action: 'action.settings.application.details',
      parameters: {
        'settingsParamBundleName': this.context.abilityInfo.bundleName
      }
    }
    await this.context.startAbility(want)
  }

  private executeFeature(feature: string): void {}
  private provideFallback(feature: string): void {}
  private async showExplanationDialog(reason: string): Promise<boolean> { return true }
}

import { Want } from '@kit.AbilityKit'

10.2.5 9.2.5 常见权限错误处理

// 权限错误处理器
class PermissionErrorHandler {
  // 处理各种权限错误场景
  static handlePermissionError(
    error: Error,
    context: common.UIAbilityContext
  ): void {
    const errorMessage = error.message

    // 场景1:权限未声明
    if (errorMessage.includes('not declared')) {
      console.error('权限未在module.json5中声明,请检查配置')
      return
    }

    // 场景2:权限被用户拒绝
    if (errorMessage.includes('permission denied')) {
      console.warn('用户拒绝了权限申请')
      // 提供降级方案
      return
    }

    // 场景3:权限类型不匹配
    if (errorMessage.includes('system_grant')) {
      console.error('尝试动态申请系统权限,这是不允许的')
      return
    }

    // 场景4:应用不在前台
    if (errorMessage.includes('not foreground')) {
      console.error('后台无法申请权限,请在前台状态下申请')
      return
    }
  }
}

10.3 9.3 数据加密与安全存储

10.3.1 9.3.1 对称加密(AES)

AES(Advanced Encryption Standard)是最常用的对称加密算法,适用于大量数据的加密。

import { cryptoFramework } from '@kit.CryptoKit'
import { util } from '@kit.ArkTS'

// AES加密工具类
class AESCryptoUtil {
  private static readonly ALGORITHM = 'AES128|CBC|PKCS7'
  private static readonly KEY_LENGTH = 128  // 位
  private static readonly IV_LENGTH = 16    // 字节

  // 生成随机密钥
  static async generateKey(): Promise<cryptoFramework.AesKeyPairSpec> {
    const aesGenerator = cryptoFramework.createAesKeyPairGenerator()
    const keyParams: cryptoFramework.AesKeyPairSpec = {
      keySize: AESCryptoUtil.KEY_LENGTH
    }
    await aesGenerator.init(keyParams)
    const keyPair = await aesGenerator.generateKeyPair()
    return keyPair as cryptoFramework.AesKeyPairSpec
  }

  // AES加密
  static async encrypt(
    plainText: string,
    key: ArrayBuffer,
    iv: ArrayBuffer
  ): Promise<ArrayBuffer> {
    // 创建AES加密器
    const aesCipher = cryptoFramework.createCipher(AESCryptoUtil.ALGORITHM)

    // 导入密钥
    const secretKey = cryptoFramework.createSymKeyByKey(key, 'AES128')
    const paramsSpec = cryptoFramework.createIvParamsSpec(iv)

    await aesCipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, secretKey, paramsSpec)

    // 转换明文
    const textEncoder = new util.TextEncoder()
    const plainBuffer = textEncoder.encodeInto(plainText)
    const input = cryptoFramework.createDataObjSrc(plainBuffer)

    // 执行加密
    const output = await aesCipher.doFinal(input)
    return output.data
  }

  // AES解密
  static async decrypt(
    cipherData: ArrayBuffer,
    key: ArrayBuffer,
    iv: ArrayBuffer
  ): Promise<string> {
    const aesCipher = cryptoFramework.createCipher(AESCryptoUtil.ALGORITHM)

    const secretKey = cryptoFramework.createSymKeyByKey(key, 'AES128')
    const paramsSpec = cryptoFramework.createIvParamsSpec(iv)

    await aesCipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, secretKey, paramsSpec)

    const input = cryptoFramework.createDataObjSrc(new Uint8Array(cipherData))
    const output = await aesCipher.doFinal(input)

    // 转换回字符串
    const textDecoder = new util.TextDecoder('utf-8')
    return textDecoder.decodeWithStream(new Uint8Array(output.data))
  }

  // 生成随机IV
  static generateIV(): ArrayBuffer {
    const iv = new ArrayBuffer(AESCryptoUtil.IV_LENGTH)
    const view = new Uint8Array(iv)
    for (let i = 0; i < AESCryptoUtil.IV_LENGTH; i++) {
      view[i] = Math.floor(Math.random() * 256)
    }
    return iv
  }
}

// 使用示例
async function aesDemo(): Promise<void> {
  const plainText = '这是一条需要加密的敏感信息'

  // 生成密钥和IV
  const key = new ArrayBuffer(16)  // 128位密钥
  const iv = AESCryptoUtil.generateIV()

  // 加密
  const encrypted = await AESCryptoUtil.encrypt(plainText, key, iv)
  console.info(`加密后数据长度: ${encrypted.byteLength} 字节`)

  // 解密
  const decrypted = await AESCryptoUtil.decrypt(encrypted, key, iv)
  console.info(`解密结果: ${decrypted}`)
}

10.3.2 9.3.2 非对称加密(RSA/ECC)

非对称加密使用公钥/私钥对,适用于密钥交换和数字签名。

// RSA加密工具类
class RSACryptoUtil {
  private static readonly KEY_SIZE = 2048

  // 生成RSA密钥对
  static async generateKeyPair(): Promise<{
    publicKey: ArrayBuffer,
    privateKey: ArrayBuffer
  }> {
    const rsaKeyPairGenerator = cryptoFramework.createRsaKeyPairGenerator()
    await rsaKeyPairGenerator.init({
      keySize: RSACryptoUtil.KEY_SIZE
    })
    const keyPair = await rsaKeyPairGenerator.generateKeyPair()

    const publicKey = await keyPair.pubKey.convertToEncoding('PKCS8')
    const privateKey = await keyPair.priKey.convertToEncoding('PKCS8')

    return {
      publicKey: publicKey.data,
      privateKey: privateKey.data
    }
  }

  // RSA公钥加密
  static async encryptWithPublicKey(
    plainText: string,
    publicKeyData: ArrayBuffer
  ): Promise<ArrayBuffer> {
    const rsaCipher = cryptoFramework.createCipher('RSA2048|PKCS1_V1_5')

    // 导入公钥
    const pubKey = cryptoFramework.createPubKeyFromData(
      publicKeyData, 'RSA', 'PKCS8'
    )

    await rsaCipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey)

    const textEncoder = new util.TextEncoder()
    const plainBuffer = textEncoder.encodeInto(plainText)
    const input = cryptoFramework.createDataObjSrc(plainBuffer)

    const output = await rsaCipher.doFinal(input)
    return output.data
  }

  // RSA私钥解密
  static async decryptWithPrivateKey(
    cipherData: ArrayBuffer,
    privateKeyData: ArrayBuffer
  ): Promise<string> {
    const rsaCipher = cryptoFramework.createCipher('RSA2048|PKCS1_V1_5')

    const priKey = cryptoFramework.createPriKeyFromData(
      privateKeyData, 'RSA', 'PKCS8'
    )

    await rsaCipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, priKey)

    const input = cryptoFramework.createDataObjSrc(new Uint8Array(cipherData))
    const output = await rsaCipher.doFinal(input)

    const textDecoder = new util.TextDecoder('utf-8')
    return textDecoder.decodeWithStream(new Uint8Array(output.data))
  }

  // RSA数字签名
  static async sign(
    data: string,
    privateKeyData: ArrayBuffer
  ): Promise<ArrayBuffer> {
    const signer = cryptoFramework.createSign('SHA256withRSA')

    const priKey = cryptoFramework.createPriKeyFromData(
      privateKeyData, 'RSA', 'PKCS8'
    )

    await signer.init(priKey)

    const textEncoder = new util.TextEncoder()
    const dataBuffer = textEncoder.encodeInto(data)
    await signer.update(cryptoFramework.createDataObjSrc(dataBuffer))

    const signature = await signer.sign()
    return signature.data
  }

  // RSA签名验证
  static async verify(
    data: string,
    signatureData: ArrayBuffer,
    publicKeyData: ArrayBuffer
  ): Promise<boolean> {
    const verifier = cryptoFramework.createVerify('SHA256withRSA')

    const pubKey = cryptoFramework.createPubKeyFromData(
      publicKeyData, 'RSA', 'PKCS8'
    )

    await verifier.init(pubKey)

    const textEncoder = new util.TextEncoder()
    const dataBuffer = textEncoder.encodeInto(data)
    await verifier.update(cryptoFramework.createDataObjSrc(dataBuffer))

    const isValid = await verifier.verify(
      cryptoFramework.createDataObjSrc(new Uint8Array(signatureData))
    )

    return isValid
  }
}

10.3.3 9.3.3 哈希算法(SHA系列)

哈希算法用于数据完整性校验和密码存储。

// 哈希工具类
class HashUtil {
  // SHA-256哈希
  static async sha256(data: string): Promise<string> {
    const md = cryptoFramework.createMd('SHA256')

    const textEncoder = new util.TextEncoder()
    const dataBuffer = textEncoder.encodeInto(data)
    await md.update(cryptoFramework.createDataObjSrc(dataBuffer))

    const result = await md.digest()
    return HashUtil.arrayBufferToHex(result.data)
  }

  // SHA-512哈希
  static async sha512(data: string): Promise<string> {
    const md = cryptoFramework.createMd('SHA512')

    const textEncoder = new util.TextEncoder()
    const dataBuffer = textEncoder.encodeInto(data)
    await md.update(cryptoFramework.createDataObjSrc(dataBuffer))

    const result = await md.digest()
    return HashUtil.arrayBufferToHex(result.data)
  }

  // HMAC(带密钥的哈希)
  static async hmac(
    data: string,
    key: ArrayBuffer,
    algorithm: string = 'HMAC_SHA256'
  ): Promise<string> {
    const hmacObj = cryptoFramework.createHmac(algorithm)

    const hmacKey = cryptoFramework.createSymKeyByKey(key, 'HMAC')
    await hmacObj.init(hmacKey)

    const textEncoder = new util.TextEncoder()
    const dataBuffer = textEncoder.encodeInto(data)
    await hmacObj.update(cryptoFramework.createDataObjSrc(dataBuffer))

    const result = await hmacObj.doFinal()
    return HashUtil.arrayBufferToHex(result.data)
  }

  // 密码哈希(使用PBKDF2)
  static async hashPassword(
    password: string,
    salt: ArrayBuffer,
    iterations: number = 100000
  ): Promise<string> {
    const pbkdf2 = cryptoFramework.createPbkdf2('PBKDF2_WITH_HMAC_SHA256')

    const textEncoder = new util.TextEncoder()
    const passwordBuffer = textEncoder.encodeInto(password)
    const passwordKey = cryptoFramework.createSymKeyByKey(passwordBuffer, 'PBKDF2')

    const paramsSpec = cryptoFramework.createPbkdf2ParamsSpec(
      salt, iterations, 32  // 输出32字节
    )

    await pbkdf2.init(passwordKey, paramsSpec)
    const result = await pbkdf2.deriveKey()
    return HashUtil.arrayBufferToHex(result.data)
  }

  // ArrayBuffer转十六进制字符串
  private static arrayBufferToHex(buffer: ArrayBuffer): string {
    const uint8Array = new Uint8Array(buffer)
    return Array.from(uint8Array)
      .map(byte => byte.toString(16).padStart(2, '0'))
      .join('')
  }
}

// 使用示例
async function hashDemo(): Promise<void> {
  // 数据完整性校验
  const originalData = '重要数据内容'
  const hash = await HashUtil.sha256(originalData)
  console.info(`数据哈希: ${hash}`)

  // 密码存储(永远不要存储明文密码)
  const password = 'user_password_123'
  const salt = AESCryptoUtil.generateIV()  // 使用随机盐
  const hashedPassword = await HashUtil.hashPassword(password, salt)
  console.info(`密码哈希: ${hashedPassword}`)
}

10.3.4 9.3.4 KeyStore密钥管理

密钥的安全管理是加密体系的关键。HarmonyOS提供了KeyStore用于安全存储密钥。

import { keyStore } from '@kit.SecurityKit'

// 密钥管理器
class KeyStoreManager {
  private static instance: KeyStoreManager | null = null
  private storeAlias: string = 'app_key_store'

  static getInstance(): KeyStoreManager {
    if (!KeyStoreManager.instance) {
      KeyStoreManager.instance = new KeyStoreManager()
    }
    return KeyStoreManager.instance
  }

  // 生成并存储AES密钥
  async generateAndStoreAESKey(alias: string): Promise<void> {
    try {
      const keyStoreInstance = keyStore.getInstance(this.storeAlias)

      // 检查密钥是否已存在
      const hasKey = await keyStoreInstance.hasKey(alias)
      if (hasKey) {
        console.info(`密钥 ${alias} 已存在`)
        return
      }

      // 生成AES密钥并存储到KeyStore
      await keyStoreInstance.setKey(alias, {
        keyType: keyStore.KeyType.AES,
        keySize: 128,
        // 密钥存储在TEE中,受到硬件级保护
        isUserAuthenticationRequired: false
      })

      console.info(`密钥 ${alias} 已生成并存储`)
    } catch (error) {
      console.error(`密钥生成失败: ${(error as Error).message}`)
    }
  }

  // 从KeyStore获取密钥
  async getKey(alias: string): Promise<ArrayBuffer | null> {
    try {
      const keyStoreInstance = keyStore.getInstance(this.storeAlias)
      const key = await keyStoreInstance.getKey(alias)
      return key
    } catch (error) {
      console.error(`获取密钥失败: ${(error as Error).message}`)
      return null
    }
  }

  // 删除密钥
  async deleteKey(alias: string): Promise<void> {
    try {
      const keyStoreInstance = keyStore.getInstance(this.storeAlias)
      await keyStoreInstance.deleteKey(alias)
      console.info(`密钥 ${alias} 已删除`)
    } catch (error) {
      console.error(`删除密钥失败: ${(error as Error).message}`)
    }
  }

  // 检查密钥是否存在
  async hasKey(alias: string): Promise<boolean> {
    try {
      const keyStoreInstance = keyStore.getInstance(this.storeAlias)
      return await keyStoreInstance.hasKey(alias)
    } catch (error) {
      return false
    }
  }
}

10.3.5 9.3.5 安全存储方案

HarmonyOS提供了多种安全存储方案,适用于不同的安全需求。

import { preferences } from '@kit.ArkData'
import { relationalStore } from '@kit.ArkData'

// 安全存储管理器
class SecureStorageManager {
  private context: Context

  constructor(context: Context) {
    this.context = context
  }

  // 方案1:Preferences(轻量级数据)
  // 适用于存储Token、配置等小数据
  async saveToPreferences(key: string, value: string): Promise<void> {
    const pref = await preferences.getPreferences(this.context, 'secure_prefs')
    await pref.put(key, value)
    await pref.flush()
  }

  async getFromPreferences(key: string): Promise<string | null> {
    const pref = await preferences.getPreferences(this.context, 'secure_prefs')
    const value = await pref.get(key, '')
    return value ? value as string : null
  }

  // 方案2:关系型数据库(结构化数据)
  // 适用于存储用户数据、业务数据
  async initDatabase(): Promise<relationalStore.RdbStore> {
    const config: relationalStore.StoreConfig = {
      name: 'secure_data.db',
      securityLevel: relationalStore.SecurityLevel.S1  // 安全等级
    }
    return await relationalStore.getRdbStore(this.context, config)
  }

  // 方案3:使用加密的Preferences存储敏感数据
  async saveEncryptedPreference(key: string, sensitiveData: string): Promise<void> {
    // 先使用AES加密
    const keyStoreManager = KeyStoreManager.getInstance()
    const cryptoKey = await keyStoreManager.getKey('pref_encryption_key')

    if (!cryptoKey) {
      throw new Error('加密密钥不存在')
    }

    const iv = AESCryptoUtil.generateIV()
    const encrypted = await AESCryptoUtil.encrypt(sensitiveData, cryptoKey, iv)

    // 存储加密后的数据和IV
    const pref = await preferences.getPreferences(this.context, 'encrypted_prefs')
    await pref.put(`${key}_data`, Array.from(new Uint8Array(encrypted)).join(','))
    await pref.put(`${key}_iv`, Array.from(new Uint8Array(iv)).join(','))
    await pref.flush()
  }

  // 读取加密的偏好设置
  async getEncryptedPreference(key: string): Promise<string | null> {
    const pref = await preferences.getPreferences(this.context, 'encrypted_prefs')
    const dataStr = await pref.get(`${key}_data`, '') as string
    const ivStr = await pref.get(`${key}_iv`, '') as string

    if (!dataStr || !ivStr) return null

    // 还原加密数据
    const encrypted = new Uint8Array(dataStr.split(',').map(Number)).buffer
    const iv = new Uint8Array(ivStr.split(',').map(Number)).buffer

    const keyStoreManager = KeyStoreManager.getInstance()
    const cryptoKey = await keyStoreManager.getKey('pref_encryption_key')

    if (!cryptoKey) return null

    return await AESCryptoUtil.decrypt(encrypted, cryptoKey, iv)
  }
}

10.4 9.4 网络安全

10.4.1 9.4.1 HTTPS证书校验

HTTPS是保障网络通信安全的基础。在HarmonyOS中,需要正确配置证书校验以防止中间人攻击。

import { http } from '@kit.NetworkKit'
import { cryptoFramework } from '@kit.CryptoKit'

// HTTPS安全请求客户端
class SecureHttpClient {
  private baseUrl: string

  constructor(baseUrl: string) {
    this.baseUrl = baseUrl
  }

  // 标准HTTPS请求(使用系统默认证书校验)
  async secureGet<T>(path: string): Promise<T> {
    const httpRequest = http.createHttp()
    try {
      const response = await httpRequest.request(`${this.baseUrl}${path}`, {
        method: http.RequestMethod.GET,
        header: { 'Content-Type': 'application/json' },
        // 使用系统默认证书校验
        connectTimeout: 15000,
        readTimeout: 30000
      })

      if (response.responseCode === http.ResponseCode.OK) {
        return JSON.parse(response.result as string) as T
      }
      throw new Error(`请求失败: ${response.responseCode}`)
    } finally {
      httpRequest.destroy()
    }
  }

  // 自定义证书校验(适用于自签名证书或证书锁定)
  async requestWithCertPinning<T>(path: string): Promise<T> {
    const httpRequest = http.createHttp()
    try {
      const response = await httpRequest.request(`${this.baseUrl}${path}`, {
        method: http.RequestMethod.GET,
        header: { 'Content-Type': 'application/json' },
        // 证书锁定配置
        caPath: '',  // 自定义CA证书路径(如需要)
        clientCert: '',  // 客户端证书(双向认证时使用)
        connectTimeout: 15000,
        readTimeout: 30000
      })

      if (response.responseCode === http.ResponseCode.OK) {
        return JSON.parse(response.result as string) as T
      }
      throw new Error(`请求失败: ${response.responseCode}`)
    } finally {
      httpRequest.destroy()
    }
  }
}

// 证书锁定(Certificate Pinning)实现
class CertificatePinner {
  // 预期的证书SHA256指纹
  private pinnedHashes: string[] = [
    'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=',
    'sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB='
  ]

  // 校验服务器证书
  verifyCertificate(certChain: string[]): boolean {
    if (certChain.length === 0) return false

    // 计算证书链中每个证书的指纹
    for (const cert of certChain) {
      const certHash = this.computeCertHash(cert)
      if (this.pinnedHashes.includes(certHash)) {
        return true  // 找到匹配的锁定证书
      }
    }

    console.error('证书锁定校验失败:未找到匹配的证书指纹')
    return false
  }

  private computeCertHash(cert: string): string {
    // 计算证书的SHA256指纹
    return `sha256/${cert}`
  }
}

10.4.2 9.4.2 网络安全配置

// 网络安全配置
class NetworkSecurityConfig {
  // 安全连接配置
  static getSecureConfig(): object {
    return {
      // 强制使用HTTPS
      enforceHTTPS: true,

      // 最低TLS版本
      minTLSVersion: 'TLSv1.2',

      // 允许的密码套件(仅使用强密码套件)
      allowedCipherSuites: [
        'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
        'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
        'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'
      ],

      // 禁用不安全的协议
      disabledProtocols: ['SSLv3', 'TLSv1.0', 'TLSv1.1'],

      // 证书校验模式
      certValidation: 'strict'  // strict | pinning | custom
    }
  }

  // 网络安全检测
  static async checkNetworkSecurity(url: string): Promise<SecurityCheckResult> {
    const result: SecurityCheckResult = {
      isHTTPS: url.startsWith('https://'),
      tlsVersion: '',
      certValid: false,
      certExpiry: '',
      warnings: []
    }

    if (!result.isHTTPS) {
      result.warnings.push('连接未使用HTTPS,数据可能被窃听')
    }

    // 实际项目中需要检查TLS版本和证书状态
    return result
  }
}

interface SecurityCheckResult {
  isHTTPS: boolean
  tlsVersion: string
  certValid: boolean
  certExpiry: string
  warnings: string[]
}

10.4.3 9.4.3 数据传输加密

// 端到端加密通信
class EndToEndEncryption {
  private privateKey: ArrayBuffer | null = null
  private publicKey: ArrayBuffer | null = null
  private peerPublicKey: ArrayBuffer | null = null
  private sessionKey: ArrayBuffer | null = null

  // 初始化本地密钥对
  async initialize(): Promise<void> {
    const keyPair = await RSACryptoUtil.generateKeyPair()
    this.privateKey = keyPair.privateKey
    this.publicKey = keyPair.publicKey
  }

  // 获取公钥(用于发送给通信对方)
  getPublicKey(): ArrayBuffer | null {
    return this.publicKey
  }

  // 设置对方公钥
  setPeerPublicKey(publicKey: ArrayBuffer): void {
    this.peerPublicKey = publicKey
  }

  // 协商会话密钥(使用RSA加密AES密钥进行交换)
  async negotiateSessionKey(): Promise<void> {
    if (!this.peerPublicKey) {
      throw new Error('对方公钥未设置')
    }

    // 生成随机AES会话密钥
    const sessionKey = new ArrayBuffer(16)
    const view = new Uint8Array(sessionKey)
    for (let i = 0; i < 16; i++) {
      view[i] = Math.floor(Math.random() * 256)
    }

    // 使用对方公钥加密会话密钥
    const encryptedKey = await RSACryptoUtil.encryptWithPublicKey(
      'session_key_data',
      this.peerPublicKey
    )

    this.sessionKey = sessionKey
    // encryptedKey 需要发送给对方
  }

  // 加密消息
  async encryptMessage(message: string): Promise<ArrayBuffer> {
    if (!this.sessionKey) {
      throw new Error('会话密钥未协商')
    }

    const iv = AESCryptoUtil.generateIV()
    const encrypted = await AESCryptoUtil.encrypt(message, this.sessionKey, iv)

    // 将IV和密文拼接
    const result = new Uint8Array(iv.byteLength + encrypted.byteLength)
    result.set(new Uint8Array(iv), 0)
    result.set(new Uint8Array(encrypted), iv.byteLength)

    return result.buffer
  }

  // 解密消息
  async decryptMessage(data: ArrayBuffer): Promise<string> {
    if (!this.sessionKey) {
      throw new Error('会话密钥未协商')
    }

    // 分离IV和密文
    const ivLength = 16
    const iv = data.slice(0, ivLength)
    const cipherData = data.slice(ivLength)

    return await AESCryptoUtil.decrypt(cipherData, this.sessionKey, iv)
  }
}

10.4.4 9.4.4 防中间人攻击

// 中间人攻击防护
class MITMProtection {
  // 1. 证书锁定
  private certificatePinner: CertificatePinner

  constructor() {
    this.certificatePinner = new CertificatePinner()
  }

  // 2. 请求签名验证
  async signRequest(url: string, body: string, timestamp: number): Promise<{
    signature: string,
    nonce: string
  }> {
    const nonce = this.generateNonce()
    const signString = `${url}|${body}|${timestamp}|${nonce}`

    // 使用HMAC签名
    const key = new ArrayBuffer(32)  // 从安全存储获取
    const signature = await HashUtil.hmac(signString, key)

    return { signature, nonce }
  }

  // 3. 响应验证
  async verifyResponse(
    responseBody: string,
    signature: string,
    timestamp: number
  ): Promise<boolean> {
    // 检查时间戳防重放
    const now = Date.now()
    if (Math.abs(now - timestamp) > 60000) {  // 超过60秒
      console.error('响应时间戳异常,可能遭受重放攻击')
      return false
    }

    // 验证签名
    const key = new ArrayBuffer(32)
    const expectedSignature = await HashUtil.hmac(
      `${responseBody}|${timestamp}`,
      key
    )

    return signature === expectedSignature
  }

  // 4. 双向TLS认证
  setupMutualTLS(): object {
    return {
      // 客户端证书配置
      clientCert: {
        certPath: '/data/storage/client.crt',
        keyPath: '/data/storage/client.key'
      },
      // 服务端证书校验
      serverCertValidation: {
        verifyHostname: true,
        verifyCertificateChain: true,
        minTLSVersion: 'TLSv1.2'
      }
    }
  }

  private generateNonce(): string {
    return `${Date.now()}_${Math.random().toString(36).substr(2, 16)}`
  }
}

10.4.5 9.4.5 API安全(签名、鉴权)

// API安全网关客户端
class SecureApiGateway {
  private baseUrl: string
  private appId: string
  private appSecret: string

  constructor(baseUrl: string, appId: string, appSecret: string) {
    this.baseUrl = baseUrl
    this.appId = appId
    this.appSecret = appSecret
  }

  // 带签名的API请求
  async signedRequest<T>(
    method: string,
    path: string,
    body?: object
  ): Promise<T> {
    const timestamp = Date.now().toString()
    const nonce = this.generateNonce()
    const bodyStr = body ? JSON.stringify(body) : ''

    // 生成签名
    const signString = `${method}\n${path}\n${timestamp}\n${nonce}\n${bodyStr}`
    const signature = await this.generateSignature(signString)

    const httpRequest = http.createHttp()
    try {
      const response = await httpRequest.request(`${this.baseUrl}${path}`, {
        method: method as http.RequestMethod,
        header: {
          'Content-Type': 'application/json',
          'X-App-Id': this.appId,
          'X-Timestamp': timestamp,
          'X-Nonce': nonce,
          'X-Signature': signature,
          'Authorization': `Bearer ${await this.getAccessToken()}`
        },
        extraData: bodyStr || undefined,
        connectTimeout: 15000,
        readTimeout: 30000
      })

      if (response.responseCode === http.ResponseCode.OK) {
        return JSON.parse(response.result as string) as T
      }
      throw new Error(`API请求失败: ${response.responseCode}`)
    } finally {
      httpRequest.destroy()
    }
  }

  // 生成API签名
  private async generateSignature(signString: string): Promise<string> {
    const encoder = new util.TextEncoder()
    const keyBuffer = encoder.encodeInto(this.appSecret)
    const signature = await HashUtil.hmac(signString, keyBuffer.buffer)
    return signature
  }

  // OAuth2 Token获取
  private async getAccessToken(): Promise<string> {
    return ''
  }

  private generateNonce(): string {
    return `${Date.now()}_${Math.random().toString(36).substr(2, 16)}`
  }
}

10.5 9.5 应用安全加固

10.5.1 9.5.1 代码混淆

代码混淆可以增加逆向工程的难度,保护应用的核心逻辑。

// 代码混淆配置(build-profile.json5)
// 在release构建中启用代码混淆
/*
{
  "app": {
    "products": [{
      "name": "release",
      "buildOption": {
        "arkOptions": {
          "obfuscation": {
            "enabled": true,
            "files": ["obfuscation-rules.json5"],
            "rules": [
              {
                "type": "rename",
                "enabled": true,
                "options": {
                  "identifiers": true,
                  "properties": false
                }
              },
              {
                "type": "controlFlow",
                "enabled": true
              },
              {
                "type": "stringEncryption",
                "enabled": true
              }
            ]
          }
        }
      }
    }]
  }
}
*/

// 混淆规则文件 obfuscation-rules.json5
/*
{
  "rules": [
    {
      "action": "keep",
      "type": "class",
      "name": "com.example.entry.EntryAbility"
    },
    {
      "action": "keep",
      "type": "class",
      "name": "*Component"
    }
  ]
}
*/

// 安全编码实践:避免在代码中硬编码敏感信息
class SecurityCodingPractice {
  // 不推荐:硬编码密钥
  // private readonly API_KEY = 'sk_live_abc123...'

  // 推荐:从安全存储中获取密钥
  async getApiKey(): Promise<string> {
    const storage = new SecureStorageManager(getContext(this))
    const key = await storage.getEncryptedPreference('api_key')
    return key ?? ''
  }

  // 不推荐:明文存储敏感数据
  // await pref.put('password', 'user_password_123')

  // 推荐:加密后存储
  async storePasswordSecurely(password: string): Promise<void> {
    const hashedPassword = await HashUtil.hashPassword(
      password,
      AESCryptoUtil.generateIV()
    )
    const storage = new SecureStorageManager(getContext(this))
    await storage.saveEncryptedPreference('password_hash', hashedPassword)
  }

  // 不推荐:在日志中输出敏感信息
  // console.info(`用户密码: ${password}`)

  // 推荐:脱敏后输出
  logSensitiveInfo(tag: string, data: string): void {
    const masked = data.substring(0, 3) + '***' + data.substring(data.length - 3)
    console.info(`[${tag}] 数据: ${masked}`)
  }
}

10.5.2 9.5.2 应用签名机制

应用签名确保应用的完整性和来源可信。

// 应用签名验证工具
class AppSignatureVerifier {
  // 验证应用签名
  static async verifyAppSignature(context: Context): Promise<boolean> {
    try {
      const bundleInfo = context.bundleManager.getBundleInfoForSelfSync(
        context.abilityInfo.bundleName
      )

      // 获取应用签名信息
      const signatureInfo = bundleInfo.signatureInfo
      if (!signatureInfo) {
        console.error('无法获取应用签名信息')
        return false
      }

      // 验证签名指纹
      const expectedFingerprint = 'EXPECTED_SHA256_FINGERPRINT'
      const actualFingerprint = signatureInfo.sha256Fingerprint

      if (actualFingerprint !== expectedFingerprint) {
        console.error('应用签名不匹配,可能被篡改')
        return false
      }

      return true
    } catch (error) {
      console.error(`签名验证失败: ${(error as Error).message}`)
      return false
    }
  }

  // 验证应用安装来源
  static async verifyInstallSource(context: Context): Promise<boolean> {
    try {
      const installerInfo = await context.bundleManager.getBundleInstallerInfoForSelf()
      const trustedInstallers = ['com.huawei.appmarket', 'com.example.official']

      if (!trustedInstallers.includes(installerInfo.name)) {
        console.warn(`应用来自非官方渠道: ${installerInfo.name}`)
        return false
      }

      return true
    } catch (error) {
      return false
    }
  }
}

10.5.3 9.5.3 完整性校验

// 文件完整性校验
class IntegrityChecker {
  // 计算文件SHA256
  static async computeFileHash(filePath: string): Promise<string> {
    const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY)
    try {
      const md = cryptoFramework.createMd('SHA256')

      // 分块读取大文件
      const bufferSize = 8192
      const buffer = new ArrayBuffer(bufferSize)
      let bytesRead = 0

      do {
        const readResult = fileIo.readSync(file.fd, buffer)
        bytesRead = readResult.length

        if (bytesRead > 0) {
          const chunk = buffer.slice(0, bytesRead)
          await md.update(cryptoFramework.createDataObjSrc(new Uint8Array(chunk)))
        }
      } while (bytesRead === bufferSize)

      const result = await md.digest()
      return Array.from(new Uint8Array(result.data))
        .map(b => b.toString(16).padStart(2, '0'))
        .join('')
    } finally {
      fileIo.closeSync(file.fd)
    }
  }

  // 校验文件完整性
  static async verifyFileIntegrity(
    filePath: string,
    expectedHash: string
  ): Promise<boolean> {
    const actualHash = await IntegrityChecker.computeFileHash(filePath)
    return actualHash === expectedHash
  }

  // 数据完整性校验(带HMAC)
  static async verifyDataIntegrity(
    data: string,
    expectedHmac: string,
    key: ArrayBuffer
  ): Promise<boolean> {
    const actualHmac = await HashUtil.hmac(data, key)
    return actualHmac === expectedHmac
  }
}

10.5.4 9.5.4 反调试技术

// 反调试保护
class AntiDebugProtection {
  private static isDebugDetected: boolean = false

  // 检测调试器连接
  static checkDebugger(): boolean {
    // 检查是否被调试器附加
    // 在HarmonyOS中可以通过系统API检测
    try {
      const isDebuggable = false  // 通过系统API获取
      if (isDebuggable) {
        AntiDebugProtection.isDebugDetected = true
        AntiDebugProtection.onDebugDetected()
      }
      return isDebuggable
    } catch (error) {
      return false
    }
  }

  // 检测模拟器环境
  static checkEmulator(): boolean {
    // 通过设备特征判断是否为模拟器
    const deviceInfo = {
      brand: 'unknown',
      model: 'unknown',
      isEmulator: false
    }

    // 检测模拟器特征
    if (deviceInfo.isEmulator) {
      console.warn('检测到模拟器环境')
      return true
    }

    return false
  }

  // 检测到调试时的处理
  private static onDebugDetected(): void {
    // 策略1:退出应用
    // 策略2:清除敏感数据
    // 策略3:上报安全事件
    console.error('检测到调试行为,应用将终止运行')
  }

  // 定时检测
  static startPeriodicCheck(intervalMs: number = 5000): void {
    setInterval(() => {
      AntiDebugProtection.checkDebugger()
    }, intervalMs)
  }
}

10.5.5 9.5.5 安全编码规范

// 安全编码规范示例
class SecureCodingGuidelines {

  // 规范1:输入验证
  static validateInput(input: string, maxLength: number = 1000): boolean {
    // 长度检查
    if (input.length > maxLength) return false

    // 特殊字符过滤(防XSS)
    const sanitized = input
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#x27;')

    return sanitized.length > 0
  }

  // 规范2:SQL注入防护
  static async safeQuery(
    store: relationalStore.RdbStore,
    userId: string
  ): Promise<relationalStore.ValuesBucket[]> {
    // 使用参数化查询,防止SQL注入
    const predicates = new relationalStore.RdbPredicates('users')
    predicates.equalTo('user_id', userId)  // 参数化绑定

    const result = await store.query(predicates, ['name', 'email'])
    const results: relationalStore.ValuesBucket[] = []

    while (result.goToNextRow()) {
      const name = result.getString(result.getColumnIndex('name'))
      const email = result.getString(result.getColumnIndex('email'))
      results.push({ name, email })
    }

    result.close()
    return results
  }

  // 规范3:安全随机数生成
  static generateSecureToken(length: number = 32): string {
    // 使用密码学安全的随机数
    const bytes = new Uint8Array(length)
    // 在实际项目中应使用系统安全随机数API
    for (let i = 0; i < length; i++) {
      bytes[i] = Math.floor(Math.random() * 256)
    }
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('')
  }

  // 规范4:安全的字符串比较(防止时序攻击)
  static secureCompare(a: string, b: string): boolean {
    if (a.length !== b.length) return false

    let result = 0
    for (let i = 0; i < a.length; i++) {
      result |= a.charCodeAt(i) ^ b.charCodeAt(i)
    }
    return result === 0
  }

  // 规范5:敏感数据内存清理
  static clearSensitiveData(buffer: ArrayBuffer): void {
    const uint8 = new Uint8Array(buffer)
    // 用随机数据覆盖
    for (let i = 0; i < uint8.length; i++) {
      uint8[i] = Math.floor(Math.random() * 256)
    }
    // 再用零覆盖
    uint8.fill(0)
  }
}

10.6 9.6 安全审计与合规

10.6.1 9.6.1 安全漏洞扫描

// 安全审计检查器
class SecurityAuditor {
  private issues: SecurityIssue[] = []

  // 执行全面安全审计
  async runFullAudit(context: Context): Promise<AuditReport> {
    this.issues = []

    // 1. 检查权限配置
    await this.auditPermissions(context)

    // 2. 检查数据存储安全
    await this.auditDataStorage(context)

    // 3. 检查网络通信安全
    await this.auditNetworkSecurity()

    // 4. 检查代码安全
    await this.auditCodeSecurity()

    // 5. 检查日志安全
    await this.auditLogSecurity()

    return {
      timestamp: Date.now(),
      totalIssues: this.issues.length,
      criticalCount: this.issues.filter(i => i.severity === 'critical').length,
      highCount: this.issues.filter(i => i.severity === 'high').length,
      mediumCount: this.issues.filter(i => i.severity === 'medium').length,
      lowCount: this.issues.filter(i => i.severity === 'low').length,
      issues: this.issues,
      recommendations: this.generateRecommendations()
    }
  }

  // 权限审计
  private async auditPermissions(context: Context): Promise<void> {
    // 检查是否申请了不必要的权限
    // 检查权限使用场景声明是否完整
    // 检查动态权限申请是否有合理的触发时机
  }

  // 数据存储审计
  private async auditDataStorage(context: Context): Promise<void> {
    // 检查Preferences中是否存储了明文敏感数据
    // 检查数据库安全等级配置
    // 检查文件存储权限设置
  }

  // 网络安全审计
  private async auditNetworkSecurity(): Promise<void> {
    // 检查是否存在HTTP明文请求
    // 检查TLS版本配置
    // 检查证书校验是否完整
  }

  // 代码安全审计
  private async auditCodeSecurity(): Promise<void> {
    // 检查是否硬编码了密钥或凭证
    // 检查是否存在SQL注入风险
    // 检查是否存在XSS风险
  }

  // 日志安全审计
  private async auditLogSecurity(): Promise<void> {
    // 检查是否在日志中输出了敏感信息
    // 检查release版本是否关闭了调试日志
  }

  private generateRecommendations(): string[] {
    const recommendations: string[] = []

    for (const issue of this.issues) {
      switch (issue.type) {
        case 'PLAINTEXT_STORAGE':
          recommendations.push(`[${issue.file}] 敏感数据应加密存储`)
          break
        case 'HTTP_REQUEST':
          recommendations.push(`[${issue.file}] 应使用HTTPS替代HTTP`)
          break
        case 'HARDCODED_SECRET':
          recommendations.push(`[${issue.file}] 不应硬编码密钥,应从安全存储获取`)
          break
        case 'MISSING_CERT_VALIDATION':
          recommendations.push(`[${issue.file}] 应启用完整的证书校验`)
          break
      }
    }

    return recommendations
  }
}

interface SecurityIssue {
  type: string
  severity: 'critical' | 'high' | 'medium' | 'low'
  file: string
  line: number
  description: string
}

interface AuditReport {
  timestamp: number
  totalIssues: number
  criticalCount: number
  highCount: number
  mediumCount: number
  lowCount: number
  issues: SecurityIssue[]
  recommendations: string[]
}

10.6.2 9.6.2 日志安全

// 安全日志管理器
class SecureLogManager {
  private static isReleaseBuild: boolean = true
  private static sensitivePatterns: RegExp[] = [
    /password/i,
    /token/i,
    /secret/i,
    /key/i,
    /credential/i,
    /\d{16}/,  // 信用卡号
    /\b\d{3}-\d{2}-\d{4}\b/  // 身份证号模式
  ]

  // 安全日志输出
  static info(tag: string, message: string): void {
    if (SecureLogManager.isReleaseBuild) return  // Release版本不输出日志

    const sanitized = SecureLogManager.sanitize(message)
    console.info(`[${tag}] ${sanitized}`)
  }

  static error(tag: string, message: string, error?: Error): void {
    if (SecureLogManager.isReleaseBuild) {
      // Release版本仅上报到安全日志服务
      SecureLogManager.reportToSecurityService(tag, message, error)
      return
    }

    const sanitized = SecureLogManager.sanitize(message)
    console.error(`[${tag}] ${sanitized}`)
  }

  // 敏感信息脱敏
  static sanitize(message: string): string {
    let sanitized = message

    for (const pattern of SecureLogManager.sensitivePatterns) {
      sanitized = sanitized.replace(pattern, match => {
        if (match.length <= 4) return '***'
        return match.substring(0, 2) + '***' + match.substring(match.length - 2)
      })
    }

    // 手机号脱敏
    sanitized = sanitized.replace(
      /(\d{3})\d{4}(\d{4})/g,
      '$1****$2'
    )

    // 邮箱脱敏
    sanitized = sanitized.replace(
      /(\w{2})\w+(@\w+)/g,
      '$1***$2'
    )

    return sanitized
  }

  // 上报安全事件
  private static reportToSecurityService(
    tag: string,
    message: string,
    error?: Error
  ): void {
    // 上报到安全监控平台
    // 注意:上报内容也需要脱敏处理
  }
}

10.6.3 9.6.3 隐私合规要求

// 隐私合规管理器
class PrivacyComplianceManager {
  // 隐私政策同意状态管理
  private static readonly PRIVACY_KEY = 'privacy_policy_accepted'
  private static readonly PRIVACY_VERSION_KEY = 'privacy_policy_version'

  // 检查用户是否已同意隐私政策
  static async hasAcceptedPrivacyPolicy(context: Context): Promise<boolean> {
    const pref = await preferences.getPreferences(context, 'privacy')
    return await pref.get(PrivacyComplianceManager.PRIVACY_KEY, false) as boolean
  }

  // 记录用户同意
  static async recordConsent(
    context: Context,
    version: string,
    consentItems: ConsentItem[]
  ): Promise<void> {
    const pref = await preferences.getPreferences(context, 'privacy')

    await pref.put(PrivacyComplianceManager.PRIVACY_KEY, true)
    await pref.put(PrivacyComplianceManager.PRIVACY_VERSION_KEY, version)
    await pref.put('consent_timestamp', Date.now())

    // 记录每项授权
    for (const item of consentItems) {
      await pref.put(`consent_${item.type}`, item.granted)
    }

    await pref.flush()
  }

  // 隐私政策更新检测
  static async checkPolicyUpdate(
    context: Context,
    latestVersion: string
  ): Promise<boolean> {
    const pref = await preferences.getPreferences(context, 'privacy')
    const currentVersion = await pref.get(
      PrivacyComplianceManager.PRIVACY_VERSION_KEY,
      ''
    ) as string

    return currentVersion !== latestVersion
  }

  // 数据收集最小化检查
  static validateDataCollection(
    purpose: string,
    dataTypes: string[]
  ): ComplianceCheckResult {
    const requiredDataMap: Record<string, string[]> = {
      'user_registration': ['username', 'password'],
      'order_placement': ['address', 'phone'],
      'recommendation': ['browsing_history']
    }

    const required = requiredDataMap[purpose] ?? []
    const unnecessary = dataTypes.filter(dt => !required.includes(dt))

    return {
      compliant: unnecessary.length === 0,
      unnecessaryDataTypes: unnecessary,
      message: unnecessary.length > 0
        ? `收集了不必要的数据类型: ${unnecessary.join(', ')}`
        : '数据收集符合最小化原则'
    }
  }

  // 用户数据删除请求处理
  static async handleDataDeletionRequest(
    context: Context,
    userId: string
  ): Promise<void> {
    // 1. 删除本地数据
    await SecureStorageManager.prototype.saveToPreferences.call(
      new SecureStorageManager(context),
      `user_${userId}_deleted`,
      'true'
    )

    // 2. 通知服务端删除数据
    // 3. 记录删除日志
    // 4. 确认删除完成
  }
}

interface ConsentItem {
  type: string
  granted: boolean
  description: string
}

interface ComplianceCheckResult {
  compliant: boolean
  unnecessaryDataTypes: string[]
  message: string
}

10.6.4 9.6.4 安全测试方法

// 安全测试框架
class SecurityTestSuite {
  private results: TestResult[] = []

  // 运行所有安全测试
  async runAllTests(context: Context): Promise<void> {
    await this.testDataEncryption()
    await this.testSecureStorage()
    await this.testNetworkSecurity()
    await this.testPermissionHandling()
    await this.testInputValidation()
  }

  // 测试数据加密
  async testDataEncryption(): Promise<void> {
    try {
      const plainText = '测试敏感数据'
      const key = new ArrayBuffer(16)
      const iv = AESCryptoUtil.generateIV()

      const encrypted = await AESCryptoUtil.encrypt(plainText, key, iv)
      const decrypted = await AESCryptoUtil.decrypt(encrypted, key, iv)

      this.addResult('数据加密', plainText === decrypted,
        plainText === decrypted ? '加密解密正确' : '加密解密结果不匹配')
    } catch (error) {
      this.addResult('数据加密', false, `测试异常: ${(error as Error).message}`)
    }
  }

  // 测试安全存储
  async testSecureStorage(): Promise<void> {
    // 验证敏感数据不以明文形式存储
    // 验证加密密钥存储在KeyStore中
    this.addResult('安全存储', true, '测试通过')
  }

  // 测试网络安全
  async testNetworkSecurity(): Promise<void> {
    // 验证所有请求使用HTTPS
    // 验证证书校验已启用
    // 验证TLS版本 >= 1.2
    this.addResult('网络安全', true, '测试通过')
  }

  // 测试权限处理
  async testPermissionHandling(): Promise<void> {
    // 验证权限拒绝后的降级方案
    // 验证权限说明对话框
    this.addResult('权限处理', true, '测试通过')
  }

  // 测试输入验证
  async testInputValidation(): Promise<void> {
    // 测试SQL注入防护
    const maliciousInput = "'; DROP TABLE users; --"
    const isValid = SecureCodingGuidelines.validateInput(maliciousInput)

    this.addResult('输入验证', true, 'SQL注入防护有效')
  }

  private addResult(name: string, passed: boolean, message: string): void {
    this.results.push({ name, passed, message })
    console.info(`[安全测试] ${name}: ${passed ? 'PASS' : 'FAIL'} - ${message}`)
  }

  getReport(): string {
    const passed = this.results.filter(r => r.passed).length
    const total = this.results.length
    return `安全测试报告: ${passed}/${total} 通过`
  }
}

interface TestResult {
  name: string
  passed: boolean
  message: string
}

10.6.5 9.6.5 常见安全问题案例分析

// 常见安全问题及修复方案
class SecurityCaseStudies {

  // 案例1:明文存储Token
  // 问题代码:
  // await pref.put('auth_token', 'eyJhbGciOiJIUzI1NiIs...')
  //
  // 修复方案:
  static async fix_PlaintextTokenStorage(context: Context): Promise<void> {
    const storage = new SecureStorageManager(context)
    // 使用加密存储
    await storage.saveEncryptedPreference(
      'auth_token',
      'eyJhbGciOiJIUzI1NiIs...'
    )
  }

  // 案例2:不安全的WebView配置
  // 问题代码:
  // Web({ src: 'https://untrusted.com' })
  //   .javaScriptAccess(true)  // 允许JS执行
  //   .domStorageAccess(true)  // 允许DOM存储
  //
  // 修复方案:
  // 仅对可信域名启用必要的Web功能
  static webViewSecurityConfig(): object {
    return {
      javaScriptAccess: true,       // 如确实需要可保留
      domStorageAccess: false,      // 禁用不必要的DOM存储
      fileAccess: false,            // 禁止文件访问
      onlineAccess: true,           // 允许在线访问
      // 配置URL拦截,仅允许访问白名单域名
      urlFilter: [
        'https://trusted-domain.com/*',
        'https://api.example.com/*'
      ]
    }
  }

  // 案例3:不安全的IPC通信
  // 问题代码:通过公共Intent传递敏感数据
  //
  // 修复方案:使用权限保护的IPC通道
  static secureIPCConfig(): object {
    return {
      // 使用签名级权限保护IPC
      requiredPermission: 'ohos.permission.MY_APP_ACCESS',
      // 验证调用方身份
      verifyCallerIdentity: true,
      // 数据加密传输
      encryptPayload: true
    }
  }

  // 案例4:日志泄露敏感信息
  // 问题代码:
  // console.info(`用户登录: username=${username}, password=${password}`)
  //
  // 修复方案:
  static secureLogging(username: string, password: string): void {
    // Release版本不输出日志
    // 敏感信息脱敏
    const maskedPassword = '***'
    console.info(`用户登录: username=${username}, password=${maskedPassword}`)
  }

  // 案例5:不安全的临时文件
  // 问题代码:将敏感数据写入外部存储
  //
  // 修复方案:使用应用沙箱内部存储
  static secureTempFile(context: Context): void {
    // 使用cacheDir存储临时文件
    const tempPath = `${context.cacheDir}/temp_sensitive_data`
    // 使用后立即删除
    // fileIo.unlinkSync(tempPath)
  }
}

10.7 本章小结

本章全面讲解了HarmonyOS应用安全开发的完整知识体系:

  1. 安全架构:理解了HarmonyOS的分层安全模型,包括硬件安全层、内核安全层、系统服务层和应用框架层。掌握了应用沙箱机制和权限管理体系的工作原理。

  2. 权限管理:深入学习了动态权限申请的完整流程,理解了权限分级体系,掌握了代理授权机制和权限管理的最佳实践。

  3. 数据加密与安全存储:掌握了AES对称加密、RSA非对称加密、SHA哈希算法的使用方法,了解了KeyStore密钥管理和多种安全存储方案。

  4. 网络安全:学习了HTTPS证书校验、网络安全配置、端到端加密、防中间人攻击和API安全(签名、鉴权)的实现方法。

  5. 应用安全加固:掌握了代码混淆、应用签名验证、完整性校验、反调试技术和安全编码规范。

  6. 安全审计与合规:了解了安全漏洞扫描、日志安全、隐私合规要求、安全测试方法和常见安全问题的解决方案。


10.8 练习题

10.8.1 一、单选题

1. HarmonyOS中,哪种权限类型需要用户在运行时动态授权?

A. normal权限 B. system_basic权限 C. user_grant权限 D. system_core权限

答案:C

解析: HarmonyOS的权限分为多个级别。normal权限在安装时自动授予;system_basic和system_core权限仅系统应用可获取;user_grant权限需要应用在运行时向用户动态申请,用户可以选择授予或拒绝。


2. 以下哪种加密算法属于非对称加密?

A. AES B. DES C. RSA D. 3DES

答案:C

解析: RSA是典型的非对称加密算法,使用公钥/私钥对进行加密和解密。AES、DES和3DES都是对称加密算法,使用相同的密钥进行加密和解密。


3. 在HarmonyOS中,防止中间人攻击最有效的手段是什么?

A. 使用HTTP协议 B. 证书锁定(Certificate Pinning) C. 增大请求超时时间 D. 使用GET请求替代POST请求

答案:B

解析: 证书锁定(Certificate Pinning)将服务端证书或公钥绑定在客户端代码中,即使攻击者获取了合法的CA证书进行中间人攻击,由于证书指纹与锁定的不匹配,攻击也会失败。使用HTTP协议本身就不安全。


10.8.2 二、多选题

4. 以下哪些是HarmonyOS应用安全存储的方案?

A. Preferences(轻量级数据) B. 关系型数据库RDB(结构化数据) C. KeyStore(密钥管理) D. 外部SD卡存储 E. 加密的Preferences(敏感数据)

答案:A、B、C、E

解析: HarmonyOS提供了Preferences用于轻量级数据存储、RDB用于结构化数据存储、KeyStore用于安全密钥管理,以及通过加密实现的Preferences用于敏感数据存储。外部SD卡不属于安全存储方案,因为外部存储可被其他应用访问。


5. 关于安全编码规范,以下哪些做法是正确的?

A. 使用参数化查询防止SQL注入 B. 在日志中输出完整的用户密码以便调试 C. 对用户输入进行长度和内容验证 D. 使用安全随机数生成器生成Token E. 在代码中硬编码API密钥以简化配置

答案:A、C、D

解析: 参数化查询是防止SQL注入的标准做法;输入验证是基本的安全措施;安全随机数生成器确保Token不可预测。在日志中输出密码违反日志安全原则;硬编码API密钥是严重的安全隐患,应从安全存储中获取。


10.8.3 三、判断题

6. HarmonyOS的应用沙箱机制确保每个应用只能访问自己的数据目录,无法直接访问其他应用的数据。

答案:正确

解析: 应用沙箱是HarmonyOS安全架构的核心组成部分。每个应用被分配独立的沙箱目录,通过文件系统隔离和进程隔离确保应用间数据不可直接访问,必须通过授权的IPC机制才能进行跨应用数据交换。


7. AES加密和RSA加密可以结合使用,通常用RSA加密AES密钥,用AES加密实际数据。

答案:正确

解析: 这种混合加密方案结合了两者的优势:RSA非对称加密解决密钥交换问题(安全性高但速度慢),AES对称加密解决大数据量加密问题(速度快)。实际应用中(如TLS协议)广泛采用这种模式。


8. 在release版本的HarmonyOS应用中,应该保留所有console.info日志输出以便问题排查。

答案:错误

解析: Release版本应关闭或限制日志输出。过多的日志不仅影响性能,更重要的是可能泄露敏感信息(如用户数据、Token等)。正确的做法是使用条件编译或日志级别控制,确保release版本不输出敏感日志,同时通过安全日志服务上报关键事件。