【问题标题】:Use BouncyCastle to verify Android KeyStore signed payload使用 BouncyCastle 验证 Android KeyStore 签名的有效负载
【发布时间】:2021-07-17 21:18:49
【问题描述】:

我正在尝试使用 Android 的 KeyStore API 对数据进行签名,并使用 BouncyCastle 和 C# 验证服务器中的数据。

我的验证过程是这样的:

  1. 在 android 上生成密钥对
  2. 将公钥发送到服务器
  3. 在服务器上生成质询
  4. 使用私钥在 android 上签署挑战
  5. 使用公钥验证服务器上的签名质询

我无法让验证正常工作。如何让验证成功?是否存在重大逻辑错误?

这是我现在使用的代码:

使用KeyPairGenerator 生成密钥对:

val generator: KeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")

val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder("KEY_ALIAS",
    KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
)
    .setDigests(KeyProperties.DIGEST_SHA256)
    .setKeySize(2048)
    .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
    .build()

generator.initialize(parameterSpec)
generator.generateKeyPair()

获取发送到服务器的公钥:

val publicKeyBytes = keyStore.getCertificate("KEY_ALIAS").publicKey.encoded
val publicKey = Base64.encodeToString(publicKeyBytes, Base64.DEFAULT)

然后,服务器会生成一个质询有效负载,该有效负载必须由 android 应用程序进行验证。签名是这样完成的:

val challengeBytes = Base64.decode(challenge, Base64.DEFAULT)
val entry = keyStore.getEntry("KEY_ALIAS", null) as KeyStore.PrivateKeyEntry

val signature = Signature.getInstance("SHA256withRSA/PSS")

signature.initSign(entry.privateKey)
signature.update(challengeBytes)
val signedBytes = signature.sign()

val signedChallenge = Base64.encodeToString(signedBytes, Base64.NO_WRAP)

这个signedChallenge 然后被发送到服务器,服务器将使用 C# 中的 BouncyCastle 对其进行验证:

byte[] challengeBytes = // Original challenge
byte[] signedBytes = Convert.FromBase64String(signedChallenge);

ISigner signer = SignerUtilities
    .GetSigner(
        SignerUtilities
            .GetObjectIdentifier("SHA256withRSA/PSS"));

var pKeyParams = (RsaKeyParameters) PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
signer.Init(false, pKeyParams);
signer.BlockUpdate(challengeBytes, 0, challengeBytes.Length);

isVerified = signer.VerifySignature(signedBytes);

设置

Android:SDK 23+ 服务器是使用 NuGet 包 Portable.BouncyCastle 版本 1.8.10 用 C# 编写的

【问题讨论】:

  • 您的 C# 签名者实例在我的机器上不起作用,但 ISigner signer = SignerUtilities.GetSigner("SHA256withRSA/PSS"); 可以。这样,验证成功(但在 API 28 P 下)。试试这个。
  • 当我使用您的方法创建实例时,我得到相同的 ISigner 并且验证不起作用。但是,我会调查它并尝试获取更多信息
  • 您应该在 C# 端调试 signer.contentDigest1signer.contentDigest2signer.sLensigner.trailer 参数的值。与 Android 代码兼容的必要值为Sha256DigestSha256Digest0x200xbc。在我的机器上,当通过 OID 指定签名者时(根据 RFC8017 中的默认值),它们具有值 Sha1DigestSha1Digest0x140xbc,当直接指定签名者时它们具有正确的值通过算法。
  • 我使用的BC版本和你的一样(Portable.BouncyCastle,1.8.10)。但是,标准 BouncyCastle v1.8.9 的行为相同。 .NET 是 .NET Core 3.1/5.0 以及 .NET Framework 4.8。您正在运行哪个 .NET 版本?
  • 我用GetObjectIdentifier("SHA256withRSA/PSS") 对其进行了测试,它工作。事实上你必须使用ISigner signer = SignerUtilities.GetSigner("SHA256withRSA/PSS");

标签: c# android bouncycastle keystore


【解决方案1】:

问题在于 C# 代码中 ISigner 的实例化。

为了让它工作,我必须像这样得到签名者:

ISigner signer = SignerUtilities.GetSigner("SHA256withRSA/PSS"); 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 2019-04-23
    • 2021-09-05
    • 2012-09-09
    • 2013-05-15
    • 1970-01-01
    • 2017-10-15
    相关资源
    最近更新 更多