鸿蒙静态资源HAR开发日志
前言
此日志仅记录开卡鸿蒙SDK即鸿蒙静态资源包HAR时遇到的一些问题,仅供可能同样遇到问题同样不知所措的你参考。先说一下我的开发环境
xml
API版本:API12
"targetSdkVersion": "5.0.0(12)",
"compatibleSdkVersion": "5.0.0(12)",
DevEco Studio版本:6.0.2
Build Version: 6.0.2.642, built on March 5, 2026
DevEco Studio版本也是一边做一边升级,从6.0.0升级到6.0.2了已经。
1.HAR的网络请求配置
HAR的新建无需多言,不会的可以直接翻阅官方文档构建HAR或者可以谷歌一下。HAR的网络配置请求因为跟随主App包所以静态资源包无需额外配置网络权限
json
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
但是记得检查一下主App是否有相关配置,位置:App根目录->entry->src->main->module.json5在该文件下module下添加上面的代码。
2.RSASHA256私钥签名
这么复杂的代码当然不能自己写啦!千问...没问出满意的答案,最终还是chatgpt给出了看上去比较合适的,经过几轮与它关于API版本、兼容性还有一些比较低级类型问题激烈的讨论与修改以后。经过验证的最终版本如下:
typescript
// RsaSignUtil.ts
import cryptoFramework from '@ohos.security.cryptoFramework';
import util from '@ohos.util';
export class RsaSignUtil {
/**
* Base64 -> Uint8Array
*/
private static base64ToUint8Array(base64: string): Uint8Array {
const helper = new util.Base64Helper();
return helper.decodeSync(base64);
}
/**
* Uint8Array -> Base64
*/
private static uint8ArrayToBase64(bytes: Uint8Array): string {
const helper = new util.Base64Helper();
return helper.encodeToStringSync(bytes);
}
/**
* Uint8Array -> Hex
*/
private static uint8ArrayToHex(bytes: Uint8Array): string {
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
private static hexToUint8Array(hex: string): Uint8Array {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return bytes;
}
/**
* string -> Uint8Array
*/
private static strToUint8Array(data: string): Uint8Array {
const encoder = new util.TextEncoder();
return encoder.encodeInto(data);
}
/**
* 加载私钥(PKCS8)
*/
private static async loadPrivateKey(privateKeyBase64: string) {
try {
let asyKeyGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024');
const keyPair = await asyKeyGenerator.convertPemKey(null, privateKeyBase64)
console.log('keyPair', keyPair.priKey.getEncoded().data)
return keyPair.priKey
} catch (error) {
console.log(error)
return null;
}
}
/**
* 加载公钥(X509)
*/
private static async loadPublicKey(publicKeyBase64: string) {
try {
const keyGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024");
const keyPair = await keyGenerator.convertPemKey(publicKeyBase64, null);
console.log('keyPair', keyPair.pubKey.getEncoded().data)
return keyPair.pubKey;
} catch (error) {
return null
}
}
/**
* 签名
* @param data 原文
* @param privateKeyBase64 PKCS8私钥
* @param outputType 输出格式 base64 | hex
*/
static async sign(
data: string,
privateKeyBase64: string,
outputType: 'base64' | 'hex' = 'base64'
): Promise<string> {
try {
const input = RsaSignUtil.strToUint8Array(data);
const privateKey = await RsaSignUtil.loadPrivateKey(privateKeyBase64);
// 参数格式: 'RSA1024|PKCS1|SHA256' 或 'RSA2048|PKCS1|SHA256'
// 根据你的私钥长度选择 RSA1024 或 RSA2048
// PKCS1 是常用的填充模式,如果是 PSS 模式则改为 'PSS'
let signAlg = 'RSA1024|PKCS1|SHA256';
const signer = cryptoFramework.createSign(signAlg);
await signer.init(privateKey);
// ✅ 关键:直接传入 data
const signature = await signer.sign({
data: input
});
const signBytes = new Uint8Array(signature.data);
console.log('signBytes:', signBytes)
if (outputType === 'hex') {
return RsaSignUtil.uint8ArrayToHex(signBytes);
}
return RsaSignUtil.uint8ArrayToBase64(signBytes);
} catch (e) {
console.error("RSA签名失败:", e);
throw new Error("RSA签名失败");
}
}
/**
* 验签
* @param data 原文
* @param signatureBase64 签名(Base64)
* @param publicKeyBase64 公钥(X509)
*/
// ===== 核心:验签 =====
static async verify(
data: string,
signature: string,
publicKeyBase64: string,
signType: 'base64' | 'hex' = 'base64'
): Promise<boolean> {
try {
// 1️⃣ 原文
const input = RsaSignUtil.strToUint8Array(data);
// 2️⃣ 签名(Base64 → Uint8Array)
let signatureBytes: Uint8Array;
if (signType === 'hex') {
signatureBytes = RsaSignUtil.hexToUint8Array(signature);
} else {
signatureBytes = RsaSignUtil.base64ToUint8Array(signature);
}
// 3️⃣ 公钥
const publicKey = await RsaSignUtil.loadPublicKey(publicKeyBase64);
// 4️⃣ 创建 verifier
let verifyAlg = 'RSA1024|PKCS1|SHA256';
const verifier = cryptoFramework.createVerify(verifyAlg);
await verifier.init(publicKey);
// 5️⃣ 验签(⚠️ 必须传 DataBlob)
const result = await verifier.verify(
{ data: input },
{ data: signatureBytes }
);
return result;
} catch (e) {
console.error("RSA验签失败:", e);
return false;
}
}
}
将我们生成的私钥数据尝试跟安卓iOS一样,使用PEM 转 DER掐头去尾的中间数据进行私钥解析签名。但每次运行必报错:
bash
Failed to parse params!
create c generator fail.
再次陷入跟千问与chatgpt的水深火热的探讨中...
1.他们认为我的密钥是pkcs1格式,但鸿蒙仅支持pkcs8(其实都支持)
2.密钥加载格式DER还是PEM的格式化问题
...
AI给的createSign和createVerify参数是不对的要注意,这不得不再次提到官方文档的重要性验签(全局搜createVerify就能看到),该参数需要根据你的密钥签名算法格式来敲定,比如我的密钥长度是1024的pkcs1,切记!!!确定一下你使用的密钥格式,如果你不确定就找谁给的密钥问一下,不然真的会shit...
之前使用convertKey转换一直受挫,直到看到这里convertpemkey,走头无路之下只能转换一下思路。后来发现签名确实可以了。
签名验签调用如下
typescript
const data = '101300000131011773990659305'
// appCode + userId + timeStamp;
console.log("签名源数据:", data);
const privateKey = '-----BEGIN RSA PRIVATE KEY-----\n' +
'MIICXQIBAAKBgQDACLTwa3NJFQ+GjaCyuMkhDSGtXdgb905qCq0INfa2jyxWtBPa\n' +
'kcj1TtCWmjx193FmIl7XlCbll7DRucKdxuczicreUeCtrApD+27Sh45MlAiHEVlW\n' +
'45tpVvkpcDkGvh8z16Z+2hteeYKS3LvKqoXI2mpu9iozA4+DRb0/6mBlLQIDAQAB\n' +
'AoGBAKAZ4jibbOdeTq5gf7zhgJY0q4ItvlI6kj6onfA9hW5Y2Z+DzRWp+8BBMHiP\n' +
'KJ81aG69cxMKqo86M6n21IHUZcZJgdrm95kncOdrTFLJgmMtIpftfNwPilQe5Vp8\n' +
'G337e3iFup1bIPZnOR1UYp+76Qhp6m59t9P48Wy2KAGR2MghAkEA3m/3Rh9XU6XY\n' +
'bYKpx+8LngbQhrMDwn8PjVU8iBsbnupg01bfz1T4XBDkpDfuDddtp/EITb6OgrWk\n' +
'osezun2QdQJBAN0CW5uLb+J48A29Fxvx78pYcTtKhx2TgkIaEDyDZGfYV1aNKYk4\n' +
'Z1xFgBKq3vnBcBzRyrwzR6flftovpFOO6tkCQQCWmFNfRETJOxUmPzpXRD4nRRHU\n' +
'wEohWgjbdQPAWin/E0tuifiI1Ew5eK5zh/JBqMJy2zr197dgQz4tfurtrakpAkAE\n' +
'nP/8hzSWrZ+VKdVJqAsxVhdG/Y9EdsfJSXH9UWDPKhzomZm8W4kMzAaYXSi6XedR\n' +
'Mq/grdFZF9lGBKiRdfJBAkBSRpSF8NiuK8Ak4gHOfEVbXdHxnV3kXit24AB44Oh5\n' +
'8juYyePBPIojFy7gEPxg0IA1mZ83+b3fvGGMe/t+3PPv\n' +
'-----END RSA PRIVATE KEY-----'
const sign = await RsaSignUtil.sign(data, privateKey, 'hex');
console.log("签名结果:", sign);
const pub = '-----BEGIN PUBLIC KEY-----\n' +
'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDACLTwa3NJFQ+GjaCyuMkhDSGt\n' +
'Xdgb905qCq0INfa2jyxWtBPakcj1TtCWmjx193FmIl7XlCbll7DRucKdxuczicre\n' +
'UeCtrApD+27Sh45MlAiHEVlW45tpVvkpcDkGvh8z16Z+2hteeYKS3LvKqoXI2mpu\n' +
'9iozA4+DRb0/6mBlLQIDAQAB\n' +
'-----END PUBLIC KEY-----'
const result = await RsaSignUtil.verify(data, sign, pub, 'hex')
console.log(String(result))
3.参考
HAR包开发
使用ArkTS语言完成JWT鉴权令牌
非对称密钥生成和转换规格
@ohos.security.cryptoFramework (加解密算法库框架)之convertPemKey
使用RSA密钥对(PKCS1模式)签名验签(ArkTS)
鸿蒙HarmonyOS-http四种请求的统一封装或HarmonyOS-工具类封装-http请求