【问题标题】:C# Verify Json string via signature and RSA public keyC# 通过签名和 RSA 公钥验证 Json 字符串
【发布时间】:2018-08-18 21:57:31
【问题描述】:

我正在为本地 Android 市场创建一个 IAP 插件,我认为它的 API 与 google play 相似。

我构建了一个 Android 端,它将来自市场的所有响应返回到统一 c# side
所有部分工作正常,但我不知道如何验证响应的签名。

我是密码学的新手,每天都在搜索这方面的内容。

如果我错了,请纠正我。

  1. 他们使用hash 算法对数据进行签名并使用private key 加密hash
    我必须用public key 解密签名并比较哈希值。

这是我的public key (Base64):

MIHNMA0GCSqGSIb3DQEBAQUAA4G7ADCBtwKBrwDltnT/AaF3oMt+F3lza5JEvce0GLS1L9v9Z53lQ3LNluGk0eI2ukgWm7tAiIOLQgn11Sb9mW2VWkYTWGnZ1AZtY0GwdUQJUr7u3CWNznE6XH4UCVOVhGDCLnFrG8BcfDelhcfReGZQ3izOefhc4Oq6vZf5PfLwximK+FH27fR6XL8vg3yyK4LSwT764Dfd6H3IGes6EdTx/C3C690jdyMvhi2Q3qBiqfepHzW/jV8CAwEAAQ==

此密钥为ASN.1 DER 格式。

我把它分解并找到这个数据:

SEQUENCE
  SEQUENCE
    OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption(PKCS #1)
    NULL
  BIT STRING
    SEQUENCE
      INTEGER 969837669069837851043825067021609343597253227631794160042862620526559…
      INTEGER 65537
  1. 正如我在网上看到的第一个INTEGERModules 和第二个INTEGERExponent

所以在c#中写这样的代码=>

var parameter = new RSAParameters
{
    Modulus = HexToByteArray(/* "first_INTEGET" */),
    Exponent = BitConverter.GetBytes(/* "second_INTEGER" */)
};

市场向我发送Json,如下所示:

{"orderId": "0j8oJgE0Bett-neB", "purchaseToken": "0j8oJgE0Bett-neB", "developerPayload": "payload", "packageName": "com.some.market", "purchaseState": 0, "purchaseTime": 1520676644872, "productId": "card-1"}

签名是这样的:

hTFeQd25PZJ2DhGmXd0eO+C+oBeWsg983I4e5ztXtKAUrOIaNBaqAxHU3vW8acBs1I9fE5cxx/DI/sQGY4QSvpDnSm9aYz3do3joHPOXIVvXjSJfejxwzp9DKMUPd6LrgtxkaGevG+94NuKHFxpCdZlovEPXRJZyEznbASuYLqeW0KjP3jnvvw2O5iNlQRdh98h4Q18bSsaxq9zaRKExFLHkhNf/yO5m84kRB1G8

我正在寻找一种方法来执行此操作,但我不知道哪种方法对我来说是正确的。 我的验证码是这样的:

using (var rsa = new RSACryptoServiceProvider())
{
    rsa.ImportParameters(parameter);
    var hash = new SHA1Managed();
    bool dataOK = 
    rsa.VerifyData(hash.ComputeHash(Encoding.UTF8.GetBytes(json)), CryptoConfig.MapNameToOID("SHA1"), Encoding.UTF8.GetBytes(signature));
}
  1. 如何真正将signature 转换为byte[] 进行验证? (用编码还是什么???)

我搜索了很多,但搜索越多越混乱。

我是走错了路还是使用了错误的方法或......?

  1. 工作流程为什么要复杂?

有人可以帮帮我吗?

谢谢。

【问题讨论】:

  • 模数和公共指数只是十进制整数,而不是十六进制。
  • 非常感谢,如何将这个长十进制转换为字节数组?
  • 使用它创建一个 BigInteger,然后调用 ToByteArray() 方法,然后使用您必须编写的简单方法反转字节顺序。 BigInteger.ToByteArray() 返回一个 little-endian 数组,但 RSAParameters 字段是 big-endian。
  • 签名也是base-64,你为什么要用Encoding.UTF8.GetBytes呢?
  • 至于“工作流程为什么要复杂?” - 这是因为无论出于何种原因,ASN.1 DER 都不是 .NET 加密的“本机”格式。通常使用其他格式(例如原始公钥\私钥或 xml 格式的密钥)。所以这很复杂,因为没有内置方法可以将 ASN.1 DER 编码密钥转换为 .NET 预期的格式(但您可以在互联网上找到执行此操作的代码)。此外,您可能对 BouncyCastle(.NET 的自定义密码库)有更好的运气。

标签: c# unity3d cryptography signature public-key


【解决方案1】:

好的,我会按顺序回答:

  1. 他们使用哈希算法对数据进行签名并使用私钥加密该哈希。我必须用公钥解密签名并比较哈希。

    不,这是不正确的。您应该使用签名验证方法,就像您目前正在做的那样。将签名视为哈希加密是不正确的;甚至最新的 RSA 标准也竭尽全力解释这一点。对于 RSA,内部填充方法是不同的。对于 ECDSA,无法使用相同的方案直接加密/解密。

  2. 正如我在网上看到的,第一个 INTEGER 是 Modules,第二个 INTEGER 是 Exponent

    是的,尽管它是拼写为模数,而不是模块。它是公共指数,私钥也有一个私人指数。也没有帽子。

  3. 如何真正将签名转换为字节[]进行验证? (用编码还是什么???)

    标准 Base 64 已在评论部分提及。请注意,密钥和签名大小并不常见(但这本身就可以)。

  4. 为什么工作流程要复杂?

    好吧,最终必须有人对其进行编码,而加密很难。但为了方便您:整个 ASN.1 结构称为 SubjectPublicKeyInfo 结构;如果您在 Internet 上查找,您会发现可以从这种结构中导入的预制代码。

最终验证结构:确保您使用正确的签名格式(RSA-PKCS#1 v1.5 或 RSA-PSS),并且您确切知道哪些二进制数据被馈送到签名生成功能。例如,JSON 上的签名可以是 ASCII 或 UTF-8,也可以是 UTF-16 LE 或 BE。

最好询问签名的创建者。

【讨论】:

  • 不客气。请注意,签名本身字节数组。它的编码方式取决于协议。
  • 你是我的新密码学英雄。该类型的第一个英雄:)
猜你喜欢
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 2012-03-06
  • 2016-04-17
  • 2013-05-28
相关资源
最近更新 更多