【FAQ】HarmonyOS SDK 闭源开放能力 — Device Security Kit

1.问题描述:

请问有没有C接口(NDK)直接读取CPU型号、主板UUID、硬盘序列号、网卡MAC等信息(比如udev)?或者有没有可靠的设备唯一ID接口可供调用?

解决方案:

常见设备的标识有OAID、ODID、AAID、UDID等,定义和用途如下:

OAID(开放匿名设备标识符)一种非永 久性设备标识符,基于OAID,可在保护用户个人数据隐私安全的前提下,媒体App、广告平台、三方监测平台等开发者,可获取设备上的OAID,进行个性化广告推荐或广告转化归因分析。

ODID(开发者匿名设备标识符):用于识别同一设备上运行的同一个开发者的应用,标识应用身份。帮助开发者更好地理解用户在不同应用间的行为,从而提供更个性化的服务和推荐。

AAID(应用匿名标识符):标识应用的身份,主要用于应用的消息推送。

UDID(设备唯一标识符):标识设备的属性,可作为设备唯一识别码。

只有UDID才能作为设备的唯一标识符,不会随设备重置或应用卸载而发生变化,但UDID只允许系统应用及企业定制应用申请特殊权限才能获取。当前设备重置时还无法保证标识符不发生改变,但有方案可以实现应用卸载时标识符不发生改变。

为了保证及时在应用卸载后仍能有效的确保获取的设备标识符不发生变化,间接达到“唯一标识符”的目的,华为提供了关键资产存储服务,开发者可以将设备标识符放在asset里,设置IS_PERSISTENT()为true,实现在应用卸载时保留关键资产,达到标识符不清除的效果。如获取ODID后配合使用Asset Store Kit能力,保持ODID不变的效果,示例代码如下:

import { asset } from '@kit.AssetStoreKit';
import util from '@ohos.util';
import { deviceInfo } from '@kit.BasicServicesKit';
function stringToArray(str: string): Uint8Array {
  let textEncoder = new util.TextEncoder();
  return textEncoder.encodeInto(str);
}
function setAttr(id: string) {
  let attr: asset.AssetMap = new Map();
  attr.set(asset.Tag.SECRET, stringToArray(id));
  attr.set(asset.Tag.ALIAS, stringToArray('demo_device_id'));
  attr.set(asset.Tag.IS_PERSISTENT, true);
  try {
    asset.add(attr).then(() => {
      console.info(`Asset added successfully.`);
    }).catch(() => {
      console.error(`Failed to add Asset.`);
    })
  } catch (error) {
    console.error(`Failed to add Asset.`);
  }
}
function arrayToString(arr: Uint8Array): string {
  let textDecoder = util.TextDecoder.create("utf-8", { ignoreBOM: true });
  let str = textDecoder.decodeWithStream(arr, { stream: false })
  return str;
}
async function getAttr(): Promise {
  let query: asset.AssetMap = new Map();
  query.set(asset.Tag.ALIAS, stringToArray('demo_device_id')); // 指定了关键资产别名,最多查询到一条满足条件的关键资产
  query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL); // 此处表示需要返回关键资产的所有信息,即属性+明文
  try {
    const res: Array = await asset.query(query)
    // 解析密钥
    let secret: Uint8Array = res[0].get(asset.Tag.SECRET) as Uint8Array;
    // 将uint8array解析为字符串
    let secretStr: string = arrayToString(secret);
    return secretStr;
  } catch (error) {
    console.error(`Failed to query Asset.`);
    return '';
  }
}
@Entry
@Component
struct AttrTest {
  build() {
    Column() {
      Button('获取设备ID').onClick(async (event: ClickEvent) => {
        let deviceId: string = await getAttr();
        if (deviceId === undefined || deviceId === null || deviceId.length === 0) {
          deviceId = deviceInfo.ODID;
          setAttr(deviceId);
        }
        console.log('设备ID为:' + deviceId)
      })
        .height(100)
        .width('100%')
    }
  }
}

2.问题描述:

如何使用DSA算法实现签名验签的功能?

解决方案:

  1. 配置DSA1024公钥和私钥中包含的公共参数dsaCommonSpec。

  2. 设置DSA1024密钥对中包含的全参数。

  3. 调用createAsyKeyGeneratorBySpec方法生成DSA算法的非对称密钥生成器。

  4. 通过密钥生成器生成DSA非对称密钥对。

  5. 使用DSA私钥对数据进行签名。

  6. 使用DSA公钥对签名数据进行验签。

完整示例代码如下:

import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { buffer } from '@kit.ArkTS';
let input: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from("This is Sign test plan", 'utf-8').buffer) };
// 配置DSA1024公钥和私钥中包含的公共参数
function genDsa1024CommonSpecBigE() {
  let dsaCommonSpec: cryptoFramework.DSACommonParamsSpec = {
    algName: "DSA",
    specType: cryptoFramework.AsyKeySpecType.COMMON_PARAMS_SPEC,
    p: BigInt("0xed1501551b8ab3547f6355ffdc2913856ddeca198833dbd04f020e5f25e47c50e0b3894f7690a0d2ea5ed3a7be25c54292a698e1f086eb3a97deb4dbf04fcad2dafd94a9f35c3ae338ab35477e16981ded6a5b13d5ff20bf55f1b262303ad3a80af71aa6aa2354d20e9c82647664bdb6b333b7bea0a5f49d55ca40bc312a1729"),
    q: BigInt("0xd23304044019d5d382cfeabf351636c7ab219694ac845051f60b047b"),
    g: BigInt("0x2cc266d8bd33c3009bd67f285a257ba74f0c3a7e12b722864632a0ac3f2c17c91c2f3f67eb2d57071ef47aaa8f8e17a21ad2c1072ee1ce281362aad01dcbcd3876455cd17e1dd55d4ed36fa011db40f0bbb8cba01d066f392b5eaa9404bfcb775f2196a6bc20eeec3db32d54e94d87ecdb7a0310a5a017c5cdb8ac78597778bd"),
  }
  return dsaCommonSpec;
}
// 设置DSA1024密钥对中包含的全参数
function genDsa1024KeyPairSpecBigE() {
  let dsaCommonSpec = genDsa1024CommonSpecBigE();
  let dsaKeyPairSpec: cryptoFramework.DSAKeyPairSpec = {
    algName: "DSA",
    specType: cryptoFramework.AsyKeySpecType.KEY_PAIR_SPEC,
    params: dsaCommonSpec,
    sk: BigInt("0xa2dd2adb2d11392c2541930f61f1165c370aabd2d78d00342e0a2fd9"),
    pk: BigInt("0xae6b5d5042e758f3fc9a02d009d896df115811a75b5f7b382d8526270dbb3c029403fafb8573ba4ef0314ea86f09d01e82a14d1ebb67b0c331f41049bd6b1842658b0592e706a5e4d20c14b67977e17df7bdd464cce14b5f13bae6607760fcdf394e0b73ac70aaf141fa4dafd736bd0364b1d6e6c0d7683a5de6b9221e7f2d6b"),
  }
  return dsaKeyPairSpec;
}
async function signMessagePromise(priKey: cryptoFramework.PriKey) {
  let signAlg = "DSA1024|SHA256";
  let signer = cryptoFramework.createSign(signAlg);
  await signer.init(priKey);
  let signData = await signer.sign(input);
  return signData;
}
async function verifyMessagePromise(signMessageBlob: cryptoFramework.DataBlob, pubKey: cryptoFramework.PubKey) {
  let verifyAlg = "DSA1024|SHA256";
  let verifier = cryptoFramework.createVerify(verifyAlg);
  await verifier.init(pubKey);
  let res = await verifier.verify(input, signMessageBlob);
  console.info('DSA verify result is ' + res);
  return res;
}
function main() {
  let asyKeyPairSpec = genDsa1024KeyPairSpecBigE();
  let asyKeyGeneratorBySpec = cryptoFramework.createAsyKeyGeneratorBySpec(asyKeyPairSpec);
  // 异步获取非对称密钥生成器生成的密钥
  asyKeyGeneratorBySpec.generateKeyPair(async (err, keyPair) => {
    if (err) {
      console.error('generateKeyPair: error.');
      return;
    }
    console.info('generateKeyPair: success.');
    // 签名
    let signData = await signMessagePromise(keyPair.priKey)
    // 验签
    let verifyResult = await verifyMessagePromise(signData, keyPair.pubKey);
    if (verifyResult === true) {
      console.info('verify success');
    } else {
      console.error('verify failed');
    }
  })
}

3.问题描述:

从应用设置页跳转至系统设置显示没有权限。

解决方案:

应用在权限管理界面的操作,未先进行相关权限申请,则根据系统设计,无法在系统隐私设置权限页面设置,可以参考以下步骤:

  1. 通过 getSelfPermissionStatus接口查询应用权限状态,参考代码:
getPermissionStatus(permission: string) {
  try {
    let data: abilityAccessCtrl.PermissionStatus = this.atManager.getSelfPermissionStatus(permission);
    console.info(`data->${data}`);
  } catch (err) {
    console.error(`catch err->${err}`);
  }
}
  1. 当结果为NOT_DETERMINED时,表示未操作。应用声明用户授权权限,暂未调用requestPermissionsFromUser接口请求用户授权,此时可以调用请求用户授权接口进行授权,参考代码:
reqPermissionFromUser(permissionList: Array) {
  let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  atManager.requestPermissionsFromUser(context, permissionList,
    (err: BusinessError, data: PermissionRequestResult) => {
      if (err) {
        console.error(`requestPermissionsFromUser fail, err->${err}`);
      } else {
        console.info('data permissions:' + data.permissions);
        console.info('data authResults:' + data.authResults);
      }
    });
}
  1. 当前结果为已授权或未授权时,可以跳转到系统权限设置页面调整,或者使用 requestPermissionOnSetting拉起权限设置弹框。参考代码:
applyPermissions(permissionList: Array) {
  if (!this.atManager || !this.context) {
    return
  }
  this.atManager.requestPermissionOnSetting(this.context, permissionList)
    .then((data: Array) => {
      console.info(`data: ${data}`);
    })
    .catch((err: BusinessError) => {
      console.error(`data: ${err}`);
    });
}


请使用浏览器的分享功能分享到微信等