Xamarin.iOS真机调用DCAppAttestService.AttestKeyAsync报error2
问题根因
两个报错分别来自代码逻辑错误和SDK版本问题:
com.apple.devicecheck.error error 2对应Apple官方定义的无效输入错误,原因是传入AttestKeyAsync的客户端哈希格式不符合要求:自定义Sha256()方法返回十六进制格式的哈希字符串,再通过NSData.FromString()转成的NSData是字符串的UTF8编码结果,并非Apple要求的32字节原始SHA256二进制哈希值。Specified argument was out of the range of valid values. (Parameter 'Platform name: 5.')是旧版本Xamarin.iOS对DCAppAttestService的平台枚举绑定bug,升级到Xamarin.iOS 15.0及以上稳定版本即可解决,传参正确时该异常不会复现。
DCDevice.CurrentDevice.GenerateTokenAsync()可正常执行,是因为该接口无强哈希格式校验,且不触发新版AppAttest相关的平台校验逻辑。
可正常运行的实现代码
注意计算哈希时直接返回原始二进制结果,不要转十六进制字符串:
using System; using System.Security.Cryptography; using System.Text; using DeviceCheck; using Foundation; public async Task<byte[]> GetAttestObject(string Challenge) { DCAppAttestService atserv = DCAppAttestService.SharedService; // AppAttest仅支持iOS14及以上版本的真实设备,模拟器永远返回Supported=false if (!atserv.Supported) { return null; } try { // 生成密钥对,生产环境需要将返回的keyId持久化存储,不要每次调用都重新生成 var keyId = await atserv.GenerateKeyAsync(); // 计算challenge的原始32字节SHA256哈希 byte[] challengeRawBytes = Encoding.UTF8.GetBytes(Challenge); byte[] sha256Hash; using (SHA256 sha256 = SHA256.Create()) { sha256Hash = sha256.ComputeHash(challengeRawBytes); } // 直接用原始哈希字节构造NSData,*禁止用NSData.FromString传入哈希字符串* NSData hashValue = NSData.FromArray(sha256Hash); // 执行密钥认证 NSData attestResult = await atserv.AttestKeyAsync(keyId, hashValue); // 转换为字节数组返回 byte[] resultBytes = new byte[attestResult.Length]; System.Runtime.InteropServices.Marshal.Copy(attestResult.Bytes, resultBytes, 0, (int)attestResult.Length); return resultBytes; } catch (Exception ex) { // 可按需添加错误日志逻辑 return null; } }
注意事项
- 测试必须使用真实iOS设备,系统版本不低于iOS14.0,模拟器不支持AppAttest服务
- 需要提前在苹果开发者后台为对应App ID开启App Attest能力,同时在项目的Entitlements.plist中配置对应权限,否则会触发权限错误
- 传入的Challenge值必须由服务端动态下发,禁止本地生成固定值,避免存在安全漏洞
- 首次生成的keyId需要本地持久化保存,后续认证、断言操作都需要复用该keyId,重复生成密钥会导致服务端校验失败
内容的提问来源于stack exchange,提问作者RADU
相关产品推荐
相关产品推荐

