本文旨在深入探讨华为鸿蒙HarmonyOS Next系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。
在当今蓬勃发展的游戏产业中,游戏公平性成为了玩家体验的核心要素之一。然而,外挂和作弊行为却如影随形,严重破坏了游戏的公平竞争环境。华为鸿蒙HarmonyOS Next凭借其强大的安全特性,特别是Device Certificate Kit,为游戏开发者提供了有力的武器来构建高效的防作弊系统。
一、场景描述
在多人在线游戏中,作弊者常常使用外挂来获取不正当优势,如自动瞄准、加速、透视等功能,这不仅损害了普通玩家的利益,也影响了游戏的可玩性和寿命。同时,模拟器和云手机等非真实设备的使用也为作弊提供了便利,因为它们可以更容易地运行作弊程序且逃避一些常规的检测机制。因此,构建一个能够准确识别外挂并阻止非真实设备运行游戏的防作弊系统至关重要。
二、架构设计
为了实现游戏防作弊的目标,我们以Device Certificate Kit的真实性证明为基础,与Universal Keystore Kit紧密结合,构建了一个全面的验证体系。
- Device Certificate Kit
- 负责生成和管理设备证书链,其中包含设备的公钥和相关身份信息。在游戏启动时,设备向服务器发送证书链,用于验证设备的真实性和身份唯一性。
- 通过证书链中的密钥证明证书,确保设备公钥的来源可靠,并且可以追溯到可信的根证书。
- Universal Keystore Kit
- 安全地存储设备的私钥,确保私钥不被非法获取。在需要进行签名等操作时,为应用提供安全的私钥访问环境。
- 与Device Certificate Kit协同工作,保证密钥对的完整性和安全性,防止密钥被篡改或替换。
三、实现步骤
- 实现应用ID和设备公钥的绑定,并通过证书链进行验证
- 游戏应用在安装时,为每个应用实例生成唯一的应用ID。同时,通过Device Certificate Kit生成设备公私钥对,并将设备公钥与应用ID进行绑定。示例代码如下:
import { huks } from '@kit.UniversalKeystoreKit';
import { BusinessError } from '@kit.BasicServicesKit';let appId = 'your_game_app_id';
let keyAlias = 'game_key'; function GetGenerateProperties() {let properties: Array<huks.HuksParam> = new Array();let index = 0;properties[index++] = {tag: huks.HuksTag.HUKS_TAG_ALGORITHM,value: huks.HuksKeyAlg.HUKS_ALG_ECC};properties[index++] = {tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256};properties[index++] = {tag: huks.HuksTag.HUKS_TAG_PURPOSE,value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_SIGN |huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_VERIFY};properties[index++] = {tag: huks.HuksTag.HUKS_TAG_DIGEST,value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256}return properties;
}async function GenerateKey(keyAlias: string) {let genProperties = GetGenerateProperties();let options: huks.HuksOptions = {properties: genProperties}await huks.generateKeyItem(keyAlias, options).then(() => {console.info(`promise: generate Key success.`);}).catch((err: BusinessError) => {console.error(`promise: generate Key failed, error: ` + err.message);})
}GenerateKey(keyAlias);// 假设这里有将应用ID和设备公钥绑定的逻辑(实际应用中可能需要与游戏服务器交互来记录绑定关系)
bindAppIdAndPublicKey(appId, getPublicKeyFromKeyAlias(keyAlias));
- 当游戏启动并连接服务器时,设备将证书链发送给服务器。服务器首先验证证书链的完整性和有效性,检查证书是否过期、签名是否正确等。然后,服务器根据证书中的信息获取设备公钥,并验证其与应用ID的绑定关系是否正确。
- 针对不同攻击场景设计多层验证机制,阻止模拟器、云手机等非真实设备
- 模拟器检测:通过Device Certificate Kit的硬件级证书链验证,检查设备的硬件特征是否与真实设备一致。模拟器通常无法完全模拟真实设备的硬件环境,因此在证书链验证过程中可能会暴露其非真实设备的特征。例如,真实设备的证书链可能包含特定的硬件标识信息,而模拟器无法提供或伪造这些信息。
- 云手机识别:对于云手机等虚拟设备,除了证书链验证外,还可以结合网络特征分析等技术。云手机通常具有与真实移动设备不同的网络延迟、IP地址分配模式等特征。通过在服务器端对设备的网络连接进行分析,判断其是否为云手机环境。如果检测到疑似云手机的网络特征,服务器可以进一步要求设备提供额外的证明或直接拒绝其连接。
- 运行环境检测:在游戏运行过程中,持续监测游戏运行环境的完整性。例如,检查游戏进程是否被注入了外挂程序、内存中是否存在可疑的代码修改等。可以通过定期检查游戏关键模块的哈希值、监控系统调用等方式来实现。如果发现运行环境被篡改,立即采取相应措施,如暂停游戏、封禁账号等。
- 在服务器端进行证书链和挑战值的验证
- 证书链验证:服务器收到设备发送的证书链后,使用Device Certificate Kit的相关API进行详细验证。包括检查证书链的长度是否符合预期、证书的颁发机构是否可信、证书的有效期是否有效等。示例代码如下:
import { cert } from '@kit.DeviceCertificateKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';// 假设这是设备证书链数据(实际应用中需从设备获取)
let deviceCertChainData = "-----BEGIN CERTIFICATE-----\n" +
"MIID6jCCAtKgAwIBAgIIIM2q/TmRoLcwDQYJKoZIhvcNAQELBQAwWjELMAkGA1\n" +
"UEBhMCRU4xEDAOBgNVBAgTB0VuZ2xhbmQxDzANBgNVBAcTBkxvbmRvbjEMMA\n" +
"oGA1UEChMDdHMyMQwwCgYDVQQLEwN0czIxDDAKBgNVBAMTA3RzMjAeFw0yMzEy\n" +
"MDUwNzM5MDBaFw0yNDEwMzEyMzU5MDBaMGExCzAJBgNVBAYTAkNOMRAwDgYDVQQI\n" +
"EwdKaWFuZ3N1MRAwDgYDVQQHEwdOYW5qaW5nMQwwCgYDVQQKEwN0czMxDDAKBg\n" +
"NVBAsTA3RzMzESMBAGA1UEAxMJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
"MIIBCgKCAQEAtt+2QxUevbolYLp51QGcUpageI4fwGLIqv4fj4aoVnHFOOBqVOVpfCLR\n" +
"p26LFV/F8ebwPyo8YEBKSwXzMD1573rMSbaH9BalscH5lZYAbetXoio6YRvzlcmc\n" +
"rVvLBNMeVnxY86xHpo0MTNyP7W024rZsxWO98xFQVdoiaBC+7+midlisx2Y+7u0\n" +
"zT9GjeUP6JLdLFUZJKUPSTK3jVzw9v1eZQZKYoNfU6vFMd6ndtwW6qEnwpzmmX\n" +
"/UT+p5ThAMH593zszlz330nTSXBjIsGkyvOz9gSB0Z0LAuJj06XUNhGL5xKJYKbdI3\n" +
"8MFQFJKvRHfgTAvVsvAvpBUM2DuBKwIDAQABo4GsMIGpMAkGA1UdEwQCMAA\n" +
"wHQYDVR0OBBYEFDfsHTMZwoA6eaDFlBUyDpka+sYtMAsGA1UdDwQEAwID+DAnBgN\n" +
"VHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMEMBQGA1UdEQQNM\n" +
"AuCCTEyNy4wLjAuMTARBglghkgBhvhCAQEEBAMCBkAwHgYJYIZIAYb4QgENBBEWD3hj\n" +
"YSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEAp5vTvXrt8ZpgRJVtzv9ss0lJ\n" +
"izp1fJf+ft5cDXrs7TSD5oHrSW2vk/ZieIMhexU4LFwhs4OE7jK6pgI48Dseqxx7\n" +
"B/KktxhVMJUmVXd9Ayjp6f+BtZlIk0cArPuoXToXjsV8caTGBXHRdzxpAk/w9syc\n" +
"GYrbH9TrdNMuTizOb+k268oKXUageZNxHmd7YvOXkcNgrd29jzwXKDYYiUa1DI\n" +
"SzDnYaJOgPt0B/5izhoWNK7GhJDy9KEuLURcTSWFysbbnljwO9INPT9MmlS83PdAg\n" +
"NiS8VXF4pce1W9U5jH7d7k0JDVSXybebe1iPFphsZpYM/NE+jap+mPy1nTCbf9g==\n" +
"-----END CERTIFICATE-----\n" +
"-----BEGIN CERTIFICATE-----\n" +
"MIIC0zCCAoWgAwIBAgIIXpLoPpQVWnkwBQYDK2VwMFoxCzAJBgNVBAYTAkV\n" +
"OMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYDVQQHEwZMb25kb24xDDAKBgNVBAoT\n" +
"A3RzMTEMMAoGA1UECxMDdHMxMQwwCgYDVQQDEwN0czEwHhcNMjMxMjA1MDczNzA\n" +
"wWhcNMjQwOTAxMjM1OTAwWjBaMQswCQYDVQQGEwJFTjEQMA4GA1UECBMHRW5nbGFu\n" +
"ZDEPMA0GA1UEBxMGTG9uZG9uMQwwCgYDVQQKEwN0czIxDDAKBgNVBAsTA3RzMjEMM\n" +
"AoGA1UEAxMDdHMyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtt+2QxUev\n" +
"bolYLp51QGcUpageI4fwGLIqv4fj4aoVnHFOOBqVOVpfCLRp26LFV/F8ebwPyo8YEBK\n" +
"SwXzMD1573rMSbaH9BalscH5lZYAbetXoio6YRvzlcmcrVvLBNMeVnxY86xHpo0\n" +
"MNTyP7W024rZsxWO98xFQVdoiaBC+7+midlisx2Y+7u0jzT9GjeUP6JLdLFUZJKUP\n" +
"STK3jVzw9v1eZQZKYoNfU6vFMd6ndtwW6qEnwpzmmX/UT+p5ThAMH593zszlz\n" +
"330nTSXBjIsGkyvOz9gSB0Z0LAuJj06XUNhGL5xKJYKbdI38MFQFJKvRHfgTAvVsvAv\n" +
"pBUM2DuBKwIDAQABo28wbTAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQ3\n" +
"7B0zGcKAOnmgxZQVMg6ZGvrGLTALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAg\n" +
"AHMB4GCGCGSAGG+EIBDQQRFg94Y2EgY2VydGlmaWNhdGUwBQYDK2VwA0EAuasLBe\n" +
"55YgvFb4wmHeohylc9r8cFGS1LNQ5UcSn3cGqMYf6ehnef16NLuCW6upHCs8Sui4iAMvs\n" +
"uKPWR9dKBA==\n" +
"-----END CERTIFICATE-----\n";let textEncoder = new util.TextEncoder();
let encodingBlob: cert.EncodingBlob = {data: textEncoder.encodeInto(deviceCertChainData),encodingFormat: cert.EncodingFormat.FORMAT_PEM
};let x509CertChain: cert.X509CertChain = {} as cert.X509CertChain;try {x509CertChain = await cert.createX509CertChain(encodingBlob);
} catch (err) {let e: BusinessError = err as BusinessError;console.error(`createX509CertChain failed, errCode: ${e.code}, errMsg: ${e.message}`);
}// 证书链校验数据(假设,实际需根据真实情况配置)
const param: cert.CertChainValidationParameters = {date: '20231212080000Z',trustAnchors: [{CAPubKey: new Uint8Array([0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,0x03, 0x21, 0x00, 0xbb, 0x16, 0x9d, 0x8f, 0x5c, 0x30, 0xd0, 0xba, 0x8f, 0x37, 0x6e,0x33, 0xaf, 0x6f, 0x23, 0x71, 0x23, 0xa5, 0x49, 0x60, 0x1e, 0xd1, 0x07, 0x4b, 0xc9,0x11, 0x7e, 0x66, 0x01, 0xba, 0x92, 0x52]),CASubject: new Uint8Array([0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,0x04, 0x06, 0x13, 0x02, 0x45, 0x4e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04,0x08, 0x13, 0x07, 0x45, 0x6e, 0x67, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d,0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x06, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x31,0x6c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x03, 0x74, 0x73, 0x31, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x03, 0x74, 0x73, 0x31, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x03, 0x74, 0x73, 0x31])}]
};try {const validationRes = await x509CertChain.validate(param);console.log('X509CertChain validate success');
} catch (err) {console.error('X509CertChain validate failed');
}
- 挑战值验证:服务器向设备发送一个一次性的挑战值(challenge),设备使用私钥对挑战值进行签名,并将签名后的结果返回给服务器。服务器使用设备公钥验证签名是否正确。如果签名验证失败,说明设备可能存在作弊行为或者设备身份不真实。示例代码如下:
import { huks } from '@kit.UniversalKeystoreKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';// 假设这是从服务器获取的挑战值
let challenge = 'random_challenge_string'; let keyAlias = 'game_key'; async function getPrivateKey() {let options: huks.HuksOptions = {properties: []}try {let key = await huks.getKeyItem(keyAlias, options);return key;} catch (err) {let e: BusinessError = err as BusinessError;console.error(`getPrivateKey failed, error: ` + err.message);}
}let privateKey = await getPrivateKey();async function signChallenge(challenge: string) {let dataToSign = new Uint8Array(textEncoder.encodeInto(challenge).data);let signature = await cryptoFramework.sign({key: privateKey,data: dataToSign});console.log('Signature:', signature.data);return signature;
}let signature = await signChallenge(challenge);// 将签名发送回服务器(这里只是示例,实际通信方式需根据具体情况实现)
sendSignatureToServer(signature);
- 服务器端验证签名:
import { cert } from '@kit.DeviceCertificateKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';// 接收到设备发送的签名后进行校验
async function verifyChallengeSignature(signature: Uint8Array, challenge: string, deviceCert: cert.X509Cert) {try {// 获取证书中的公钥let pubKey = deviceCert.getPublicKey();let dataToVerify = new Uint8Array(textEncoder.encodeInto(challenge).data);let result = await cryptoFramework.verify({key: pubKey,data: dataToVerify,signature: signature});if (result) {console.log('Challenge signature verification succeeded.');return true;} else {console.log('Challenge signature verification failed.');return false;}} catch (error) {let e: BusinessError = error as BusinessError;console.error(`verifyChallengeSignature failed, errCode: ${e.code}, errMsg:${e.message}`);return false;}
}
四、技术亮点
- 利用硬件级证书链验证确保设备身份唯一性
- Device Certificate Kit基于硬件的证书链验证机制,使得每个设备的证书链都与设备的硬件特征紧密绑定。例如,证书链中的某些字段可能包含设备的硬件序列号、芯片标识等信息。在验证过程中,服务器可以通过检查这些硬件相关信息来确保设备身份的唯一性。即使作弊者试图克隆设备或在模拟器中伪造证书链,由于无法获取真实的硬件信息,其伪造的证书链在验证时将无法通过。这种硬件级别的绑定大大提高了设备身份验证的准确性和安全性,有效防止了设备伪造和身份冒用。
- 防止数据包抓取、伪造及重放的多重安全机制设计
- 数据加密传输:利用Device Certificate Kit和Crypto Architecture Kit对游戏数据进行加密传输。在设备与服务器之间的通信中,使用证书中的公钥对数据进行加密,确保数据在传输过程中不被第三方抓取和窃取。只有拥有相应私钥的服务器才能解密并读取数据,这样即使数据包被拦截,攻击者也无法获取其中的内容。
- 签名验证防止伪造:对于游戏中的关键操作(如角色移动、技能释放、道具使用等),设备使用私钥对操作数据进行签名,然后将签名和数据一起发送给服务器。服务器使用设备公钥验证签名的有效性,如果签名不正确,则认为数据被伪造,拒绝该操作。这种签名机制确保了数据的完整性和真实性,防止作弊者伪造游戏操作指令。
- 挑战值和时间戳防止重放攻击:通过服务器发送挑战值和记录操作时间戳的方式来防止重放攻击。每次设备向服务器发送操作请求时,服务器会附带一个一次性的挑战值,设备必须对挑战值进行签名并返回。同时,服务器记录每个操作的时间戳,对于在短时间内重复收到的相同签名和操作数据(在合理时间范围外),视为重放攻击并拒绝处理。这样可以有效防止作弊者利用之前截获的合法数据包进行重放攻击,保证游戏操作的时效性和安全性。
五、示例代码
- 设备真实性验证核心代码(补充完整流程)
- 设备端获取证书链并发送给服务器:
import { cert } from '@kit.DeviceCertificateKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';// 假设这是设备证书链数据(实际应用中需从设备获取完整证书链)
let deviceCertChainData = "-----BEGIN CERTIFICATE-----\n" +
"MIID6jCCAtKgAwIBAgIIIM2q/TmRoLcwDQYJKoZIhvcNAQELBQAwWjELMAkGA1\n" +
"UEBhMCRU4xEDAOBgNVBAgTB0VuZ2xhbmQxDzANBgNVBAcTBkxvbmRvbjEMMA\n" +
"oGA1UEChMDdHMyMQwwCgYDVQQLEwN0czIxDDAKBgNVBAMTA3RzMjAeFw0yMzEy\n" +
"MDUwNzM5MDBaFw0yNDEwMzEyMzU5MDBaMGExCzAJBgNVBAYTAkNOMRAwDgYDVQQI\n" +
"EwdKaWFuZ3N1MRAwDgYDVQQHEwdOYW5qaW5nMQwwCgYDVQQKEwN0czMxDDAKBg\n" +
"NVBAsTA3RzMzESMBAGA1UEAxMJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
"MIIBCgKCAQEAtt+2QxUevbolYLp51QGcUpageI4fwGLIqv4fj4aoVnHFOOBqVOVpfCLR\n" +
"p26LFV/F8ebwPyo8YEBKSwXzMD1573rMSbaH9BalscH5lZYAbetXoio6YRvzlcmc\n" +
"rVvLBNMeVnxY86xHpo0MTNyP7W024rZsxWO98xFQVdoiaBC+7+midlisx2Y+7u0\n" +
"zT9GjeUP6JLdLFUZJKUPSTK3jVzw9v1eZQZKYoNfU6vFMd6ndtwW6qEnwpzmmX\n" +
"/UT+p5ThAMH593zszlz330nTSXBjIsGkyvOz9gSB0Z0LAuJj06XUNhGL5xKJYKbdI3\n" +
"8MFQFJKvRHfgTAvVsvAvpBUM2DuBKwIDAQABo4GsMIGpMAkGA1UdEwQCMAA\n" +
"wHQYDVR0OBBYEFDfsHTMZwoA6eaDFlBUyDpka+sYtMAsGA1UdDwQEAwID+DAnBgN\n" +
"VHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMEMBQGA1UdEQQNM\n" +
"AuCCTEyNy4wLjAuMTARBglghkgBhvhCAQEEBAMCBkAwHgYJYIZIAYb4QgENBBEWD3hj\n" +
"YSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEAp5vTvXrt8ZpgRJVtzv9ss0lJ\n" +
"izp1fJf+ft5cDXrs7TSD5oHrSW2vk/ZieIMhexU4LFwhs4OE7jK6pgI48Dseqxx7\n" +
"B/KktxhVMJUmVXd9Ayjp6f+BtZlIk0cArPuoXToXjsV8caTGBXHRdzxpAk/w9syc\n" +
"GYrbH9TrdNMuTizOb+k268oKXUageZNxHmd7YvOXkcNgrd29jzwXKDYYiUa1DI\n" +
"SzDnYaJOgPt0B/5izhoWNK7GhJDy9KEuLURcTSWFysbbnljwO9INPT9MmlS83PdAg\n" +
"NiS8VXF4pce1W9U5jH7d7k0JDVSXybebe1iPFphsZpYM/NE+jap+mPy1nTCbf9g==\n" +
"-----END CERTIFICATE-----\n" +
"-----BEGIN CERTIFICATE-----\n" +
"MIIC0zCCAoWgAwIBAgIIXpLoPpQVWnkwBQYDK2VwMFoxCzAJBgNVBAYTAkV\n" +
"OMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYDVQQHEwZMb25kb24xDDAKBgNVBAoT\n" +
"A3RzMTEMMAoGA1UECxMDdHMxMQwwCgYDVQQDEwN0czEwHhcNMjMxMjA1MDczNzA\n" +
"wWhcNMjQwOTAxMjM1OTAwWjBaMQswCQYDVQQGEwJFTjEQMA4GA1UECBMHRW5nbGFu\n" +
"ZDEPMA0GA1UEBxMGTG9uZG9uMQwwCgYDVQQKEwN0czIxDDAKBgNVBAsTA3RzMjEMM\n" +
"AoGA1UEAxMDdHMyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtt+2QxUev\n" +
"bolYLp51QGcUpageI4fwGLIqv4fj4aoVnHFOOBqVOVpfCLRp26LFV/F8ebwPyo8YEBK\n" +
"SwXzMD1573rMSbaH9BalscH5lZYAbetXoio6YRvzlcmcrVvLBNMeVnxY86xHpo0\n" +
"MNTyP7W024rZsxWO98xFQVdoiaBC+7+midlisx2Y+7u0jzT9GjeUP6JLdLFUZJKUP\n" +
"STK3jVzw9v1eZQZKYoNfU6vFMd6ndtwW6qEnwpzmmX/UT+p5ThAMH593zszlz\n" +
"330nTSXBjIsGkyvOz9gSB0Z0LAuJj06XUNhGL5xKJYKbdI38MFQFJKvRHfgTAvVsvAv\n" +
"pBUM2DuBKwIDAQABo28wbTAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQ3\n" +
"7B0zGcKAOnmgxZQVMg6ZGvrGLTALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAg\n" +
"AHMB4GCGCGSAGG+EIBDQQRFg94Y2EgY2VydGlmaWNhdGUwBQYDK2VwA0EAuasLBe\n" +
"55YgvFb4wmHeohylc9r8cFGS1LNQ5UcSn3cGqMYf6ehnef16NLuCW6upHCs8Sui4iAMvs\n" +
"uKPWR9dKBA==\n" +
"-----END CERTIFICATE-----\n";let textEncoder = new util.TextEncoder();
let encodingBlob: cert.EncodingBlob = {data: textEncoder.encodeInto(deviceCertChainData),encodingFormat: cert.EncodingFormat.FORMAT_PEM
};let x509CertChain: cert.X509CertChain = {} as cert.X509CertChain;try {x509CertChain = await cert.createX509CertChain(encodingBlob);
} catch (err) {let e: BusinessError = err as BusinessError;console.error(`createX509CertChain failed, errCode: ${e.code}, errMsg: ${e.message}`);
}// 发送证书链到服务器(这里只是示例,实际通信方式需根据具体情况实现)
sendCertChainToServer(x509CertChain);
- 服务器端验证证书链(上述已部分展示,补充完整错误处理等):
import { cert } from '@kit.DeviceCertificateKit';
import { BusinessError } from '@kit.BasicServicesKit';// 接收到设备发送的证书链后进行验证
async function verifyDeviceCertChain(certChain: cert.X509CertChain) {// 证书链校验数据(假设,实际需根据真实情况配置)const param: cert.CertChainValidationParameters = {date: '20231212080000Z',trustAnchors: [{CAPubKey: new Uint8Array([0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,0x03, 0x21, 0x00, 0xbb, 0x16, 0x9d, 0x8f, 0x5c, 0x30, 0xd0, 0xba, 0x8f, 0x37, 0x6e,0x33, 0xaf, 0x6f, 0x23, 0x71, 0x23, 0xa5, 0x49, 0x60, 0x1e, 0xd1, 0x07, 0x4b, 0xc9,0x11, 0x7e, 0x66, 0x01, 0xba, 0x92, 0x52]),CASubject: new Uint8Array([0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,0x04, 0x06, 0x13, 0x02, 0x45, 0x4e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04,0x08, 0x13, 0x07, 0x45, 0x6e, 0x67, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d,0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x06, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x03, 0x74, 0x73, 0x31, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x03, 0x74, 0x73, 0x31, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x03, 0x74, 0x73, 0x31])}]};try {const validationRes = await certChain.validate(param);if (validationRes) {console.log('X509CertChain validate success');return true;} else {console.log('X509CertChain validate failed');return false;}} catch (err) {console.error('X509CertChain validate failed with error:', err);return false;}
}
- 密钥签名核心代码(完整流程)
- 设备端使用私钥对数据进行签名(补充完整错误处理和数据准备):
import { huks } from '@kit.UniversalKeystoreKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { util } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';let keyAlias = 'game_key'; async function getPrivateKey() {let options: huks.HuksOptions = {properties: []}try {let key = await huks.getKeyItem(keyAlias, options);return key;} catch (err) {let e: BusinessError = err as BusinessError;console.error(`getPrivateKey failed, error: ` + err.message);}
}let privateKey = await getPrivateKey();// 假设这是要签名的数据,例如游戏操作数据
let gameOperationData = '{"operation":"move","parameters":{"x":10,"y":20}}'; async function signGameData(data: string) {let dataToSign = new Uint8Array(textEncoder.encodeInto(data).data);try {let signature = await cryptoFramework.sign({key: privateKey,data: dataToSign});console.log('Signature:', signature.data);return signature;} catch (error) {let e: BusinessError = error as BusinessError;console.error(`signGameData failed, errCode: ${e.code}, errMsg:${e.message}`);}
}let signature = await signGameData(gameOperationData);// 将签名和数据一起发送到服务器(这里只是示例,实际通信方式需根据具体情况实现)
sendSignatureAndDataToServer(signature, gameOperationData);
- 服务器端验证签名(上述已部分展示,补充完整数据获取和处理):
import { cert } from '@kit.DeviceCertificateKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';// 接收到设备发送的签名和数据后进行校验
async function verifySignature(signature: Uint8Array, data: string, deviceCert: cert.X509Cert) {try {// 获取证书中的公钥let pubKey = deviceCert.getPublicKey();let dataToVerify = new Uint8Array(textEncoder.encodeInto(data).data);let result = await cryptoFramework.verify({key: pubKey,data: dataToVerify,signature: signature});if (result) {console.log('Signature verification succeeded.');return true;} else {console.log('Signature verification failed.');return false;}} catch (error) {let e: BusinessError = error as BusinessError;console.error(`verifySignature failed, errCode: ${e.code}, errMsg:${e.message}`);return false;}
}
- 证书链校验核心代码(完整流程及错误处理优化)
- 设备端获取并发送证书链(与上述设备真实性验证中获取证书链代码类似,可复用):
import { cert } from '@kit.DeviceCertificateKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';// 假设这是设备证书链数据(实际应用中需从设备获取完整证书链)
let deviceCertChainData = "-----BEGIN CERTIFICATE-----\n" +
"MIID6jCCAtKgAwIBAgIIIM2q/TmRoLcwDQYJKoZIhvcNAQELBQAwWjELMAkGA1\n" +
"UEBhMCRU4xEDAOBgNVBAgTB0VuZ2xhbmQxDzANBgNVBAcTBkxvbmRvbjEMMA\n" +
"oGA1UEChMDdHMyMQwwCgYDVQQLEwN0czIxDDAKBgNVBAMTA3RzMjAeFw0yMzEy\n" +
"MDUwNzM5MDBaFw0yNDEwMzEyMzU5MDBaMGExCzAJBgNVBAYTAkNOMRAwDgYDVQQI\n" +
"EwdKaWFuZ3N1MRAwDgYDVQQHEwdOYW5qaW5nMQwwCgYDVQQKEwN0czMxDDAKBg\n" +
"NVBAsTA3RzMzESMBAGA1UEAxMJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
"MIIBCgKCAQEAtt+2QxUevbolYLp51QGcUpageI4fwGLIqv4fj4aoVnHFOOBqVOVpfCLR\n" +
"p26LFV/F8ebwPyo8YEBKSwXzMD1573rMSbaH9BalscH5lZYAbetXoio6YRvzlcmc\n" +
"rVvLBNMeVnxY86xHpo0MTNyP7W024rZsxWO98xFQVdoiaBC+7+midlisx2Y+7u0\n" +
"zT9GjeUP6JLdLFUZJKUPSTK3jVzw9v1eZQZKYoNfU6vFMd6ndtwW6qEnwpzmmX\n" +
"/UT+p5ThAMH593zszlz330nTSXBjIsGkyvOz9gSB0Z0LAuJj06XUNhGL5xKJYKbdI3\n" +
"8MFQFJKvRHfgTAvVsvAvpBUM2DuBKwIDAQABo4GsMIGpMAkGA1UdEwQCMAA\n" +
"wHQYDVR0OBBYEFDfsHTMZwoA6eaDFlBUyDpka+sYtMAsGA1UdDwQEAwID+DAnBgN\n" +
"VHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMEMBQGA1UdEQQNM\n" +
"AuCCTEyNy4wLjAuMTARBglghkgBhvhCAQEEBAMCBkAwHgYJYIZIAYb4QgENBBEWD3hj\n" +
"YSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEAp5vTvXrt8ZpgRJVtzv9ss0lJ\n" +
"izp1fJf+ft5cDXrs7TSD5oHrSW2vk/ZieIMhexU4LFwhs4OE7jK6pgI48Dseqxx7\n" +
"B/KktxhVMJUmVXd9Ayjp6f+BtZlIk0cArPuoXToXjsV8caTGBXHRdzxpAk/w9syc\n" +
"GYrbH9TrdNMuTizOb+k268oKXUageZNxHmd7YvOXkcNgrd29jzwXKDYYiUa1DI\n" +
"SzDnYaJOgPt0B/5izhoWNK7GhJDy9KEuLURcTSWFysbbnljwO9INPT9MmlS83PdAg\n" +
"NiS8VXF4pce1W9U5jH7d7k0JDVSXybebe1iPFphsZpYM/NE+jap+mPy1nTCbf9g==\n" +
"-----END CERTIFICATE-----\n" +
"-----BEGIN CERTIFICATE-----\n" +
"MIIC0zCCAoWgAwIBAgIIXpLoPpQVWnkwBQYDK2VwMFoxCzAJBgNVBAYTAkV\n" +
"OMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYDVQQHEwZMb25kb24xDDAKBgNVBAoT\n" +
"A3RzMTEMMAoGA1UECxMDdHMxMQwwCgYDVQQDEwN0czEwHhcNMjMxMjA1MDczNzA\n" +
"wWhcNMjQwOTAxMjM1OTAwWjBaMQswCQYDVQQGEwJFTjEQMA4GA1UECBMHRW5nbGFu\n" +
"ZDEPMA0GA1UEBxMGTG9uZG9uMQwwCgYDVQQKEwN0czIxDDAKBgNVBAsTA3RzMjEMM\n" +
"AoGA1UEAxMDdHMyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +
"MIIBCgKCAQEAtt+2QxUevbolYLp51QGcUpageI4fwGLIqv4fj4aoVnHFOOBqVOVpfCLRp26LFV/F8ebwPyo8YEBK\n" +
"SwXzMD1573rMSbaH9BalscH5lZYAbetXoio6YRvzlcmcrVvLBNMeVnxY86xHpo0\n" +
"MNTyP7W024rZsxWO98xFQVdoiaBC+7+midlisx2Y+7u0jzT9GjeUP6JLdLFUZJKUP\n" +
"STK3jVzw9v1eZQZKYoNfU6vFMd6ndtwW6qEnwpzmmX/UT+p5ThAMH593zszlz\n" +
"330nTSXBjIsGkyvOz9gSB0Z0LAuJj06XUNhGL5xKJYKbdI38MFQFJKvRHfgTAvVsvAv\n" +
"pBUM2DuBKwIDAQABo28wbTAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQ3\n" +
"7B0zGcKAOnmgxZQVMg6ZGvrGLTALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAg\n" +
"AHMB4GCGCGSAGG+EIBDQQRFg94Y2EgY2VydGlmaWNhdGUwBQYDK2VwA0EAuasLBe\n" +
"55YgvFb4wmHeohylc9r8cFGS1LNQ5UcSn3cGqMYf6ehnef16NLuCW6upHCs8Sui4iAMvs\n" +
"uKPWR9dKBA==\n" +
"-----END CERTIFICATE-----\n";let textEncoder = new util.TextEncoder();
let encodingBlob: cert.EncodingBlob = {data: textEncoder.encodeInto(deviceCertChainData),encodingFormat: cert.EncodingFormat.FORMAT_PEM
};let x509CertChain: cert.X509CertChain = {} as cert.X509CertChain;try {x509CertChain = await cert.createX509CertChain(encodingBlob);
} catch (err) {let e: BusinessError = err as BusinessError;console.error(`createX509CertChain failed, errCode: ${e.code}, errMsg: ${e.message}`);
}// 发送证书链到服务器(这里只是示例,实际通信方式需根据具体情况实现)
sendCertChainToServer(x509CertChain);
- 服务器端验证证书链(优化错误处理和结果返回):
import { cert } from '@kit.DeviceCertificateKit';
import { BusinessError } from '@kit.BasicServicesKit';// 接收到设备发送的证书链后进行验证
async function verifyDeviceCertChain(certChain: cert.X509CertChain) {// 证书链校验数据(假设,实际需根据真实情况配置)const param: cert.CertChainValidationParameters = {date: '20231212080000Z',trustAnchors: [{CAPubKey: new Uint8Array([0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,0x03, 0x21, 0x00, 0xbb, 0x16, 0x9d, 0x8f, 0x5c, 0x30, 0xd0, 0xba, 0x8f, 0x37, 0x6e,0x33, 0xaf, 0x6f, 0x23, 0x71, 0x23, 0xa5, 0x49, 0x60, 0x1e, 0xd1, 0x07, 0x4b, 0xc9,0x11, 0x7e, 0x66, 0x01, 0xba, 0x92, 0x52]),CASubject: new Uint8Array([0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,0x04, 0x06, 0x13, 0x02, 0x45, 0x4e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04,0x08, 0x13, 0x07, 0x45, 0x6e, 0x67, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d,0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x06, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x03, 0x74, 0x73, 0x31, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x03, 0x74, 0x73, 0x31, 0x31,0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x03, 0x74, 0x73, 0x31])}]};try {const validationRes = await certChain.validate(param);if (validationRes) {console.log('X509CertChain validate success');return true;} else {console.log('X509CertChain validate failed');return false;}} catch (err) {console.error('X509CertChain validate failed with error:', err);return false;}
}
通过以上基于HarmonyOS Next和Device Certificate Kit的游戏防作弊系统设计,我们能够有效地识别和防止外挂及非真实设备的攻击,为游戏玩家提供一个公平、安全的游戏环境。在实际开发中,我们可以根据游戏的具体特点和需求,进一步优化和扩展该防作弊系统,确保游戏的长期健康发展。希望这篇文章能为游戏开发领域的小伙伴们提供一些有价值的参考和思路,让我们一起共同打造一个纯净的游戏世界!如果在实现过程中遇到问题,不要灰心,仔细分析,参考相关文档和示例代码,相信你一定能够攻克难题。加油!