博览会-安全环境
电源
一个 博览模块 利用设备的安全环境(HSM、SE等)锁定的生物鉴别认证支持加密操作.
需要博览会SDK 53或更新。 支持Android 11(API 30)和更新,以及iOS 16.4和更新.
支持更古老的Android版本的应用程序仍然可以包括库. 那边 isSecureEnvironmentSupported 财务报告和审定财务报表 isUserAuthenticationAvailable 返回时 false,其他函数则丢出一个 SecureEnvironmentNotSupportedError。 。 。 .
开始
首先使用您的软件包管理器安装模块 .
npm install @animo-id/expo-secure-environment
然后预建应用程序, 以使 Expo 模块包可以添加为本地依赖 :
npx expo prebuild
您已经安装并配置了安全环境模块 .
使用量
您现在可以导入 @animo-id/expo-secure-environment 在您的申请.
支持加密算法
密钥算法页:1
签名算法: ECDSA和SHA256
检查支持
密钥被存储在iOS上的"安全飞地"中,并存储在StrongBox,Android上的"信任执行环境"(TEE)或软件中. 检查设备在生成密钥前是否有一个 :
import { isSecureEnvironmentSupported } from "@animo-id/expo-secure-environment";
isSecureEnvironmentSupported(); // Secure Enclave on iOS, StrongBox on Android
isSecureEnvironmentSupported({ minimumKeyStorage: "trustedEnvironment" }); // TEE or StrongBox on Android
需要用户认证的密钥也需要注册生物鉴别技术或设备密码. 在创建此密钥的流程前选中此项, 例如, 请用户先设置 Face ID :
import { isUserAuthenticationAvailable } from "@animo-id/expo-secure-environment";
isUserAuthenticationAvailable(); // biometrics enrolled
isUserAuthenticationAvailable({ methods: ["biometric", "deviceCredential"] }); // biometrics or a device passcode
在iOS上,这也是 false 生物鉴别技术在尝试失败后被锁定.
创建密钥对
默认使用密钥需要每次生物鉴别认证:
import { generateKeypair } from "@animo-id/expo-secure-environment";
await generateKeypair("my-key");
用选项配置密钥 :
await generateKeypair("my-key", {
userAuthentication: {
methods: ["biometric", "deviceCredential"],
invalidateOnBiometricChange: true,
},
minimumKeyStorage: "trustedEnvironment",
});
| 选项 | 默认 | 说明 |
|---|---|---|
userAuthentication | 需要签名的用户认证,或 false 没有. | |
userAuthentication.methods | ['biometric'] | biometric (身份证、触摸身份证、Android 第3类生物鉴别技术)和/或 deviceCredential (device passcode,PIN,图案或密码). |
userAuthentication.invalidateOnBiometricChange | true | 当在设备上添加或去除生物鉴别技术时,密钥是否变得无法使用. 仅适用于下列情况: methods 实值 ['biometric']。 。 。 . |
userAuthentication.validitySeconds | 0 | 仅限Android. 该密钥在用户认证后可以使用多久 . 0 需要认证每个签名. |
minimumKeyStorage | 'strongBox' | 仅限Android. 将密钥存储在最安全的存储器中 Android 上的密钥存储iOS总是使用安全飞地. |
preferStrongBox | true | 仅限Android. 设备拥有时是否将密钥存储在 StrongBox 中, 以及何时 minimumKeyStorage 不需要这样. |
attestationChallenge | 仅限Android. 创建包含此挑战的密钥验证, 参见 密钥证明。 。 。 . |
Android 上的密钥存储
Android在索取时会将密钥存储于StrongBox. 所有其他密钥都去设备的默认密钥存储:TEE,或者没有TEE设备上的软件,如模拟器. Apps不能选择TEE和软件,所以 minimumKeyStorage 设置您接受的最低安全存储 :
minimumKeyStorage | 存储密钥的地方 | 失败的时间 |
|---|---|---|
strongBox | StrongBox, 一个单独的安全元素. | 该设备没有"强框". |
trustedEnvironment | 当设备拥有了它时强框,否则默认的密钥存储,必须是TEE. | 该装置没有TEE. |
software | 设备拥有后有强框,否则默认密钥存储:TEE,或软件. | 从来没有. |
当StrongBox无法存储密钥时,它会转到默认密钥存储处取而代之. StrongBox比TEE慢,可以同时运行较少的操作. 要总是使用默认密钥存储, 例如当您同时生成许多密钥时, 设置 preferStrongBox: false。 。 。 .
软件中的一键无法被你的应用程序所读取,但Android本身可以读取,因此它不能被保护免受被损坏(如被扎入)的设备.
getKeyStorage 返回键的结尾 :
import { getKeyStorage } from "@animo-id/expo-secure-environment";
await getKeyStorage("my-key"); // 'strongBox', 'trustedEnvironment', 'software' or 'secureHardware'
Android 11 不说安全硬件中的密钥是StrongBox还是TEE,所以它返回了 secureHardware 两者都有 核查人也可以从 密钥验证。 。 。 .
拿公用钥匙
返回 P-256 公钥的压缩形式(而不是 DER 编码的主题PublicKeyInfo ):
import { getPublicBytesForKeyId } from "@animo-id/expo-secure-environment";
const publicKey: Uint8Array = await getPublicBytesForKeyId("my-key");
签名数据
返回原始签名(而不是一个编码为 ECDSA-Sig-Value) 。 当密钥需要用户认证时,请用户先认证:
import { sign } from "@animo-id/expo-secure-environment";
const signature = await sign("my-key", message, {
prompt: { title: "Sign in to Example", cancel: "Cancel" },
});
等待一个显示提示的签名, 然后再开始下一个 。 iOS 显示一个提示, 所以第二个提示取消第一个提示, 然后丢出一个 UserAuthenticationCanceledErrorAndroid 显示一个接一个的提示.
多次签名
一次创建多个签名, 例如要提交多个证书, 使用 withUserAuthentication。在iOS上,用户在第一次签名时用需要的密钥进行认证一次,下一次签名则重新使用该认证。 不需要用户认证的密钥从不问. 在Android上 它的标志像 sign: 保存默认值 validitySeconds 联 合 国 0 并显示每个签名一个提示, 标题中写着用户的签名 。 安全硬件随后允许每个认证一个签名,所以应用程序中没有任何其他代码可以不经询问用户而签名.
import { withUserAuthentication } from "@animo-id/expo-secure-environment";
async function signCredentials(credentials: { keyId: string; name: string }[], message: Uint8Array) {
return withUserAuthentication(
async ({ sign }) => {
const signatures = [];
for (const [index, { keyId, name }] of credentials.entries()) {
// The prompt of each signature on Android
const prompt = { title: `Share ${name}`, subtitle: `${index + 1} of ${credentials.length}` };
signatures.push(await sign(keyId, message, { prompt }));
}
return signatures;
},
// The prompt of the one authentication on iOS
{ prompt: { title: `Share ${credentials.length} credentials` } }
);
}
只有 sign 联 合 国 withUserAuthentication 重用认证, 并且直到调用返回为止 。 后被掷出. 其它 sign 应用程序中的呼叫仍然询问用户, 也当回调运行时, 所以通过 sign 用于共享认证的代码。 它一次签一个消息,所以第二个提示不能取消第一个. 在iOS上,下一个密钥在接受用户认证的方法时重新使用认证:这个密钥只接受密码在生物鉴别后再次询问.
在Android上,键生成方式为 validitySeconds 上文 0 询问一次,然后在有效结束之前不急于签名。 有效性不那么可靠。 在它结束之前,应用程序中的任何代码都可以在不迅速的情况下签名,而不只是 sign 联 合 国 withUserAuthentication,并用其中一把钥匙解锁设备 methods 也开始吧 如果你使用它, 请保持足够长的时间 以便你马上创建的签名.
密钥证明 (Android)
在Android上,一个密钥可以用密钥验证来创建:来自设备安全硬件的证书链,证明密钥是在那里生成的,描述密钥是如何被保护的,包括挑战. 创建密钥时通过挑战,然后获得证书链(base64被编码为 DER 证书,叶先:
import {
generateKeypair,
getAttestationCertificateChain,
} from "@animo-id/expo-secure-environment";
await generateKeypair("my-key", { attestationChallenge: challenge });
const certificateChain: string[] = await getAttestationCertificateChain("my-key");
iOS没有安全飞地密钥的密钥证明. 挑战被忽视 getAttestationCertificateChain 掷出.
OpenID4VCI 中的密钥证明 key_storage 财务报告和审定财务报表 user_authentication 键的级别(攻击潜在阻力,ISO 18045)。 您的钱包从如何配置密钥来决定这些关卡 userAuthentication,从何处 getKeyStorage 说一个密钥被存储。 这个库没有指定关卡.
错误
所有错误延伸 SecureEnvironmentError,具有 keyId 和本地错误 code。有特定原因的错误有自己的类:
| 错误 | 掷出时 |
|---|---|
SecureEnvironmentNotSupportedError | 设备没有安全飞地, 或者没有请求 minimumKeyStorage,或运行Android 10或以上. |
KeyNotFoundError | 此 ID 不存在密钥 . |
KeyAlreadyExistsError | 此 ID 的密钥已经存在 . |
KeyInvalidatedError | 仅限Android. 该密钥被永久作废,因为生物鉴别技术改变或屏幕锁被去掉. |
UserAuthenticationCanceledError | 用户,应用程序或系统取消了认证提示. |
UserAuthenticationFailedError | 用户没有成功认证,例如指纹不被识别. |
UserAuthenticationLockedOutError | 生物测量在尝试失败太多后被锁定. 当用户进入设备密码时,它们会解锁,在Android上,第一次闭锁也于30秒后结束. |
UserAuthenticationUnavailableError | 用户无法使用所请求的方法进行认证,例如没有注册生物鉴别技术. |
从0.2.0移走
用0.2.0创建的密钥继续工作. sign 从密钥本身读取它是否需要用户认证.
目前最低iOS版本为16.4 (was 13.4),最低Expo SDK为53.
选项代替 biometricsBacked
generateKeypair 财务报告和审定财务报表 sign 不再需要一个 biometricsBacked 参数。 默认值与0.2.0相同:每个签名的生物鉴别认证,当生物鉴别仪改变时,密钥无效,而Android存储于StrongBox.
// 0.2.0
await generateKeypair("my-key", false);
await sign("my-key", message, false);
// now
await generateKeypair("my-key", { userAuthentication: false });
await sign("my-key", message);
第三个论点是: sign 现在设置认证提示的文本,参见 签名数据。 。 。 .
撤回安全的环境被移除
setFallbackSecureEnvironment, (中文(简体) ), shouldUseFallbackSecureEnvironment, (中文(简体) ), isLocalSecureEnvironmentSupported 编辑 SecureEnvironment 界面被删除。 现在的功能总是使用设备的安全环境,以及 generateKeypair 丢出一个 SecureEnvironmentNotSupportedError 当设备没有它.
如果您使用后退, 请检查支持 isSecureEnvironmentSupported 并呼唤你的后退你自己。 继续使用存储在那里的后退键 :
isSecureEnvironmentSupported不总是给出与0.2.0相同的结果. 在iOS上它现在返回false没有安全飞地,例如模拟器。 在Android上它会返回true用于每个带有 StrongBox 的设备,其中0.2.0 需要 KeyMint 1.0 或更新。 存储每个密钥创建位置, 而不是再次检查支持 .- 在Android设备上,没有强框,您可以在TEE中存储密钥
minimumKeyStorage: "trustedEnvironment"而不是用倒计时 装有"强Box"的设备仍然存放在那里.
错误
nativeError改为:code,本地错误代码(例如:.ERR_KEY_NOT_FOUND) (中文(简体) ). 原始错误在cause。 。 。 .- 构造器选择:
new SecureEnvironmentError(message, { keyId, code, cause })。 。 。 .SecureEnvironmentError.fromNative删除. - 认证提示的错误现在有自己的类 。 在Android上,0.2.0 扔这些作为一个平地
SecureEnvironmentError。 。 。 .
KeyNotFoundError 财务报告和审定财务报表 KeyAlreadyExistsError 不变。 其他错误类别被删除 :
| 0.2.0 (中文(简体) ) | 现在 |
|---|---|
BiometricAuthenticationFailed | UserAuthenticationCanceledError, (中文(简体) ), UserAuthenticationFailedError, (中文(简体) ), UserAuthenticationLockedOutError 或者说 UserAuthenticationUnavailableError |
NoHardwareKeyStore | SecureEnvironmentNotSupportedError |
KeychainQueryError, (中文(简体) ), CouldNotConvertIntoPublicKey, (中文(简体) ), CouldNotConvertIntoExternalRepresentation, (中文(简体) ), CouldNotCreateSignature, (中文(简体) ), CouldNotGenerateKeyPair, (中文(简体) ), NoSignatureOnCryptoObject, (中文(简体) ), NotExecutedFromMainThread | SecureEnvironmentError |
捐款
你想修补还是补充? 太好了,我们喜欢社区贡献! 要参与,请跟随我们 捐款准则。 。 。 .
测试在例应用中运行 反应原生机理。 。 。 . pnpm test:ios 财务报告和审定财务报表 pnpm test:android 用模拟器或仿真器运行它们, pnpm test:ios:device 财务报告和审定财务报表 pnpm test:android:device 电话连接到你的电脑上 只有电话测试安全硬件 见 一般事务人员(其他职等) 详细情况.
许可证
此寄存器根据 Apache 2.0 (英语) 执照.