开发者

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 (i.e. produced by another application that I've written). To do that, I'm signing the string and transmitting the string and the hash of the string to the Android device. I can include the public key used to create the hashed string in the Android application so I don't have to transmit it. The C# code that produces all of that is akin to what follows:

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:

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. In the android application I'm attempting to verify the signature as follows:

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);

开发者_Python百科byte[] decodedBytes = cipher.doFinal(Base64.decode(signature));

When I compare decodedBytes to the SHA1 hash of the original string, they're different. 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. The same happens with the exponent. My suspicion is that this is because Java's byte type is unsigned?

So, two questions:

1) What am I doing (or not doing) wrong? 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?


Question #1:

You want to verify a signature therefore you should not use the Cipher class but the Signature class.

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. I am not a .net expert and I don't know if the RSACryptoServiceProvider.SignData(...) uses this standard.

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...)

Question #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 (e.g. PKCS#1, implemented everywhere, at least the 1.5 version), is ok. Another solution is using a Message Authentication Code (like HMAC) which provides authentication and integrity service and is using symmetric keys.

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; 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜