我正在开发一个应用程序来测试 Android 设备的签名功能。我的应用程序非常简单: 它生成一个密钥 -> 对用户输入的文本进行签名 -> 显示签名
总体来说没有任何问题。有一天,我试图找出 Android Keystore 支持哪些哈希函数,特别是 StrongBox。
这就是我尝试过的:
1. 给定 SHA 函数列表(SHA-1、SHA-224、SHA-256、SHA-384、SHA-512、NONE),只有 SHA-256 在模拟器上工作,该模拟器根据本部分工作Android 文档:https://source.android.com/docs/security/features/keystore/features
这是有道理的:即使 StrongBox 在模拟器上未激活,它仍然遵循文档。
2. 然后我用 StrongBox 检查了实际手机上的 ECDSA 哈希。 (三星 Galaxy S21 5G) 支持的哈希算法有 SHA1、SHA256、SHA385、SHA512,甚至没有,由于某种原因,唯一不支持的算法(MD5 除外):SHA224
那时我就有疑问了。为什么这样?我在 AOSP 中也没有找到任何与此相关的原因或文章。
我承认我的代码可能很糟糕,(我对密码学还很陌生),这就是为什么下面你会找到我使用的函数:
对于 ECDSA 密钥生成:
@RequiresApi(Build.VERSION_CODES.R)
private fun generateKeyPair() {
try {
val keyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"
)
val parameterSpec = KeyGenParameterSpec.Builder(
"biometricKeyAlias",
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
)
.setDigests(KeyProperties.DIGEST_SHA256)
.setUserAuthenticationRequired(true)
.setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG)
.setAttestationChallenge("attestation_challenge".toByteArray())
.setIsStrongBoxBacked(true)
.build()
keyPairGenerator.initialize(parameterSpec)
val keyPair = keyPairGenerator.generateKeyPair()
publicKey = keyPair.public
Log.d("MainActivity", "Key pair generated successfully")
} catch (e: java.lang.Exception) {
Log.e("MainActivity", "Key pair generation failed", e)
}
}
与 ECDSA 签约:
@RequiresApi(Build.VERSION_CODES.S)
private fun handleButtonClick() {
generateKeyPair()
try {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val privateKey = keyStore.getKey("biometricKeyAlias", null) as? PrivateKey
val signature = Signature.getInstance("SHA256withECDSA")
signature.initSign(privateKey)
val cryptoObject = BiometricPrompt.CryptoObject(signature)
currentAuthContext = AuthContext.SIGN_DATA
biometricPrompt.authenticate(
promptInfo,
cryptoObject
) // Pass the CryptoObject here
} catch (e: Exception) {
Log.e("MainActivity", "Error setting up signature: ${e.localizedMessage}")
}
}
对于 RSA 我做了同样的事情,只是添加了一个填充
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
再次,对于每个摘要函数,签名均已成功生成,但 SHA224 除外:RSA 和 ECDSA 的签名操作期间出现错误
Keystore operation failed
。
如果有人想检查我的代码修订版,请随意探索https://github.com/VKrivkov/BSP--Offline-Payments-/blob/main/BiometricRSA/app/src/main/java/com /示例/生物识别/MainActivity.kt
PS:为了简单起见,每个操作都在主活动中完成。
我相应地改变了这里的值:
而不是
.setDigests(KeyProperties.DIGEST_SHA256)
我写
.setDigests(KeyProperties.DIGEST_SHA1)
.setDigests(KeyProperties.DIGEST_SHA512)
等
而不是
val signature = Signature.getInstance("SHA256withECDSA")
我写
val signature = Signature.getInstance("SHA1withECDSA")
val signature = Signature.getInstance("SHA512withECDSA")
等
我问的原因 - 为什么只有 SHA224?对我来说完全是无稽之谈。我不需要 SHA224,只是想告诉自己。文档对此保持沉默(或者我只是盲目的),所以我想知道,这是一个取决于手机制造商的东西,还是其他东西?
SHA-224之所以不被实现支持,可能是因为它没有太多用处。 SHA-224 与 SHA-256 相同;唯一的区别是它以不同的初始值开始,并将输出大小削减为 224 位。因此,除了将输出值与 SHA-256 生成的初始(最左边)值分开之外,它没有比 SHA-256 提供任何优势。
它的主要用途是对具有 224 个密钥大小的 ECDSA 执行哈希,因为它与字段大小完全匹配。然而,大小低于 256 位的曲线不提供 128 位的安全性。同样,哈希算法也不提供 128 位的“抗冲突性”。对于数字签名来说,抗冲突性是最重要的。因此,P-224(或不同名称的 secp224r1 曲线)和 SHA-224 仅为签名生成提供 112 位安全性。
说实话,我也没有看到 SHA-384 有多大用处。对我来说,192 位的安全性没有多大意义,并且只有当您将哈希值重新用于另一个签名或应用程序时,域分离问题才会成为问题。在这种情况下,您也可能只使用单个哈希两次并遇到完全相同的问题。特别是使用 SHA-384 与 RSA PKCS#1、OAEP 或 PSS 填充没有任何
意义:签名或密文大小保持不变,并且您需要执行相同的甚至更多操作(对于 MGF1)在 OAEP 或 PSS 中)以获得较低的安全性。我对此进行了测试,SHA-512 的性能明显更好,没有任何缺点。 请注意,SHA-512/224 和 SHA-512/256 也是由 NIST 定义的,但它们可能不受支持。定义它们是因为它们在 64 位机器上提供比 SHA-256 稍好的性能。不过,现在许多智能手机和台式机 CPU 都具有 SHA-256 加速功能,因此不再需要加速。因此,SHA-224
不是。 有关 SHA-224 有用性(或缺乏)的更多信息,请访问 密码学网站
。