在 Android 设备上验证 C# 生成的数字签名

Posted

技术标签:

【中文标题】在 Android 设备上验证 C# 生成的数字签名【英文标题】:Verifying a Digital Signature produced by C# on an Android Device 【发布时间】:2011-04-18 16:34:22 【问题描述】:

我正在编写一个 android 应用程序,它想验证由 C# 程序生成的字符串是否真实(即由我编写的另一个应用程序生成)。为此,我对字符串进行签名并将字符串和字符串的哈希传输到 Android 设备。我可以在 Android 应用程序中包含用于创建散列字符串的公钥,这样我就不必传输它了。产生所有这些的 C# 代码类似于以下内容:

bytes = ASCIIEncoding.ASCII.GetBytes(someString);
provider = new RSACryptoServiceProvider();

signature = provider.SignData(bytes, new SHA1CryptoServiceProvider());    

keyParameters = cryptoProvider.ExportParameters(false);

为了传输数据,我将签名编码为 base64,使用:

signatureString = System.Convert.ToBase64String(signature);

我从关键参数中手动提取模数和公共指数。它们似乎都已经是 Base64 编码的。在 android 应用程序中,我尝试按如下方式验证签名:

String modulus = "<modulus string here...>";
String exponent = "<exponent string here...>";

BigInteger BIModulus = new BigInteger(1, Base64.decode(modulus));
BigInteger BIExponent = new BigInteger(1, Base64.decode(exponent));

RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(BIModulus, BIExponent);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);

byte[] decodedBytes = cipher.doFinal(Base64.decode(signature));

当我将decodedBytes 与原始字符串的 SHA1 哈希值进行比较时,它们是不同的。试图找出原因,我用大量的日志语句逐步完成了这个过程。模数的 BigInteger 似乎与 C# 代码使用的模数不同。指数也是如此。我怀疑这是因为Java的字节类型是无符号的?

那么,两个问题:

1) 我做错了什么(或没做错)?有没有办法手动将模数和指数从一个设备移动到另一个设备以验证签名?

2) 有没有一种方法可以在更合适的抽象级别上完成我的目标?

【问题讨论】:

【参考方案1】:

问题 #1:

您要验证签名,因此不应使用 Cipher 类,而应使用 Signature 类。

PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initVerify(publicKey);
sign.update(someString.getBytes("ASCII"));
boolean ok = sign.verify(Base64.decode(signature));

注意:SHA1withRSA 算法使用带有 SHA-1 的 RSA,如 PKCS#1 标准中所述。我不是.net 专家,不知道RSACryptoServiceProvider.SignData(...) 是否使用这个标准。

大多数时间签名密钥都使用certificates 传输,它将公钥(RSA 情况下的模数+指数)绑定到实体(密钥的所有者,可以是个人、计算机、公司。 ..)

问题 #2:

这绝对取决于您的用例:为什么需要签名(身份验证、完整性)?你在签什么?


更新:

使用简单的 RSA 签名,只要与适当的方案/协议一起使用(例如PKCS#1,在任何地方都实现,至少在 1.5 版本中),就可以了。另一种解决方案是使用消息验证码(如HMAC),它提供身份验证和完整性服务并使用对称密钥。

要携带公钥,您可以在验证应用程序中对密钥进行硬编码(或使用自签名证书)。您必须注意,如果私钥被泄露、丢失或破坏,您将不得不更新所有客户端。第二种解决方案是使用 PKI 颁发签名证书;此解决方案支持密钥撤销和更新,但有几个缺点:设置和维护不那么容易,根据您的需要和环境,可能有点过分了。

【讨论】:

这是身份验证和完整性。我们要确保 C# 应用程序已经生成了一个字符串(可能是 200 字节的数据)并且此后没有被修改。

以上是关于在 Android 设备上验证 C# 生成的数字签名的主要内容,如果未能解决你的问题,请参考以下文章

wince 在设备 c# 的简历上获取通知

如何在 C# 桌面应用程序和移动(android)设备之间通过本地网络进行通信? [关闭]

C# 生成四位数字字母混合验证码

PDF数字证书签名怎么弄?

C#生成随机验证码

RSA公私钥和签名、验签过程