简体   繁体   English

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

[英]Verifying a Digital Signature produced by C# on an Android Device

I'm writing an Android application that would like to verify that a string produced by a C# program is authentic (ie produced by another application that I've written). 我正在编写一个Android应用程序,该程序要验证C#程序生成的字符串是真实的(即,由我编写的另一个应用程序生成的字符串)。 To do that, I'm signing the string and transmitting the string and the hash of the string to the Android device. 为此,我要对字符串进行签名,然后将字符串和字符串的哈希值传输到Android设备。 I can include the public key used to create the hashed string in the Android application so I don't have to transmit it. 我可以在Android应用程序中包含用于创建哈希字符串的公钥,因此不必传输它。 The C# code that produces all of that is akin to what follows: 产生所有这些的C#代码类似于以下内容:

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

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

keyParameters = cryptoProvider.ExportParameters(false);

To transmit the data, I encode the signature as base64 using: 为了传输数据,我使用以下代码将签名编码为base64:

signatureString = System.Convert.ToBase64String(signature); signatureString = System.Convert.ToBase64String(signature);

I manually extracted the modulus and the public exponent from the key Parameters. 我从关键参数中手动提取了模数和公共指数。 They appear to both be Base64 encoded already. 它们似乎都已经被Base64编码。 In the android application I'm attempting to verify the signature as follows: 在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));

When I compare decodedBytes to the SHA1 hash of the original string, they're different. 当我将decodedBytes与原始字符串的SHA1哈希值进行比较时,它们是不同的。 Trying to figure out why, I stepped through the process with oodles of logging statements. 为了弄清楚原因,我逐步完成了许多日志记录语句。 It appears that the BigInteger for the modulus is different from the modulus used by the C# code. 看来,模数的BigInteger与C#代码使用的模数不同。 The same happens with the exponent. 指数也是如此。 My suspicion is that this is because Java's byte type is unsigned? 我怀疑这是因为Java的字节类型是无符号的吗?

So, two questions: 因此,有两个问题:

1) What am I doing (or not doing) wrong? 1)我在做什么(或不做什么)错了? Is there a way to manually move the modulus and exponent from one device to another to verify a signature? 有没有办法手动将模量和指数从一个设备移动到另一个设备以验证签名?

2) Is there a way to accomplish what I'm aiming for that works at a more appropriate level of abstraction? 2)有没有办法在更合适的抽象水平上实现我要达到的目标?

Question #1: 问题1:

You want to verify a signature therefore you should not use the Cipher class but the Signature class. 您要验证签名,因此不应使用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));

Note: SHA1withRSA algorithm uses RSA with SHA-1 as described in the PKCS#1 standard. 注意:如PKCS#1标准中所述, SHA1withRSA算法使用具有SHA-1的RSA。 I am not a .net expert and I don't know if the RSACryptoServiceProvider.SignData(...) uses this standard. 我不是.net专家,所以我不知道RSACryptoServiceProvider.SignData(...)使用此标准。

Most of the time signature keys are transfered with certificates which binds a public key (modulus + exponent in the RSA case) to an entity (the owner of the key, can be a person, a computer, a company...) 大多数时间签名密钥都是通过证书传输的,该证书将公钥(在RSA情况下为模+指数)绑定到实体(密钥的所有者,可以是个人,计算机,公司...)

Question #2: 问题2:

Definitely it depends on your use case: why do you need signature (authentication, integrity)? 绝对取决于您的用例:为什么需要签名(身份验证,完整性)? what are you signing? 你在签名什么


UPDATE: 更新:

Using a simple RSA signature, as far as used with a proper scheme/protocol (eg PKCS#1 , implemented everywhere, at least the 1.5 version), is ok. 可以使用简单的RSA签名,只要与正确的方案/协议(例如PKCS#1 ,至少在1.5版本上都已实现)一起使用即可。 Another solution is using a Message Authentication Code (like HMAC ) which provides authentication and integrity service and is using symmetric keys. 另一种解决方案是使用消息身份验证代码 (如HMAC ),该代码提供身份验证和完整性服务,并使用对称密钥。

To carry the public key you can either hardcode the key (or use a self-signed certificate) in your verifying application. 要携带公共密钥,您可以在验证应用程序中对密钥进行硬编码(或使用自签名证书)。 You must be aware that if the private key is compromised, lost or destroyed you will have to update all clients. 您必须知道,如果私钥被泄露,丢失或破坏,则必须更新所有客户端。 A second solution is using a PKI issuing a signing certificate; 第二种解决方案是使用PKI颁发签名证书。 this solution supports key revocation and renewal but has several drawbacks: not so easy to setup and maintain, may be an overkill depending on your needs and environment. 此解决方案支持密钥吊销和续订,但有几个缺点:设置和维护不那么容易,根据您的需要和环境,可能会显得过分杀伤力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM