views:

216

answers:

3

I'm working on implementing Bing Cashback. In order to verify an incoming request from Bing as valid they provide a signature. The signature is a 160-bit SHA-1 hash of the url encrypted using RSA.

Microsoft provides the RSA "public key", modulus and exponent, with which I'm supposed to decrypt the hash.

Is there a way to create the Java key objects needed to decrypt the hash as Microsoft says?

Everything I can find creates RSA key pairs automatically since that's how RSA is supposed to work. I'd really like to use the Java objects if at all possible since that's obviously more reliable than a hand coded solution.

The example code they've provided is in .NET and uses a .NET library function to verify the hash. Specifically RSACryptoServiceProvider.VerifyHash()

A: 

Something like this should do the trick:

  private PublicKey convertPublicKey(String publicKey) throws Exception{
    PublicKey pub = null;

    byte[] pubKey = Hex.decodeHex(publicKey.toCharArray());
    X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKey);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    pub = (RSAPublicKey) keyFactory.generatePublic(pubSpec);

    return pub;
  }

This assumes the Public key is given as a hex string, and you'll need the Apache Commons Codec library

If you have the key in a different format, try the KeyFactory for more information.

Jason Nichols
I'll have to check and see if we have that library or if we can get it. Additionally will the Cipher object take a public key in DECRYPT_MODE? This seems very backwards to me.
meleager
It will if you're using the Sun JRE, but not the IBM version. Coincidentally, the an IBM JRE utilizing the Bouncy Castle crypto provider will also work.
Jason Nichols
Thanks, I'll give it a shot.
meleager
It may seem backwards,but what you're doing is essentially decrypting the 'signed' hash. Once decrypted, the hash you computed and the hash given to you by Microsoft should match.
Jason Nichols
...but this is the hard way of doing what erickson has done the right way.
GregS
My code above is perfectly correct (and works). If the original poster has the key in a hex String (instead of modulus/exponent format), it's the only answer that will actually help him. Ironic that I'm the one who got no points for it.
Jason Nichols
I understand what's going on now. It's an interesting verification process, really all I'm doing is verifying that the hash was calculated and encrypted correctly. Since essentially anyone can do this with no ill effect decrypting with the public key is fine.Not to be contrary but Erickson's code was actually quite useful. While I appreciate your taking the hex encoding into consideration Erickson's code is a little cleaner. Thanks again for the help.
meleager
No worries, and good luck.
Jason Nichols
+1  A: 

Use java.security.spec.RSAPublicKeySpec. It can construct a key from exponent and modulus. Then use java.security.KeyFactory.generatePublic() with key spec as a parameter.

Seva Alekseyev
+5  A: 
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey pub = factory.generatePublic(spec);
Signature verifier = Signature.getInstance("SHA1withRSA");
verifier.initVerify(pub);
verifier.update(url.getBytes("UTF-8")); // Or whatever interface specifies.
boolean okay = verifier.verify(signature);
erickson
Will Cipher take a public key in DECRYPT_MODE?
meleager
Yes, the RSA cipher implementation of most providers will accept a public key for decryption. They will even check for the correct padding. However, it is better to use a `Signature` instance. I'll update my answer to demonstrate.
erickson
This worked perfectly, thank you very much.
meleager
Thanks again. I was actually implementing this in CF but this bit of Java was crucial.
meleager