2

I am required to send a query to the bank which contains a verification code $vk_mac in a specified string format. The code has to be a SHA1 hash and RSA encrypted with my public key and presented in base64 format. Unfortunately, so far, I have been unsuccessful - the bank gives me "Wrong signature" and that all the info I'm getting.

What I have is this:

$rsa = new Crypt_RSA();
$rsa->loadKey(file_get_contents("private_key.pem"));
$rsa->loadKey($rsa->getPublicKey());
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$encrypted = $rsa->encrypt(sha1($vk_mac));
$vk_mac = base64_encode($encrypted);

private_key.pem here is my private key in plain text. I tried setting the encryption mode to CRYPT_RSA_ENCRYPTION_OAEP without luck. I am 99.9% sure, that the starting $vk_mac string is formatted correctly and contains all the required details.

Does anybody have any idea what can I be doing wrong? Thank you.


Edit:

I've changed the code to this (where vk_mac is the starting formatted string that needs to be signed and private_key.pem is my the decoded private key):

$rsa = new Crypt_RSA();
$rsa->loadKey(file_get_contents("private_key.pem"));
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$hashed = $rsa->hash->hash($vk_mac);
$encrypted = $rsa->sign($hashed);
$signature = base64_encode($encrypted);

I can tell the generated signature is correct, since when I do this:

$rsa->loadKey($rsa->getPublicKey());
$verified = $rsa->verify($hashed, base64_decode($signature));

$verified returns TRUE.

The bank though, responds "Incorrect signature". Any more ideas?

Edit:

Specification

VK_MAC control code calculation

VK_MAC, for electronic signature, using in the request, for checking and confirming used version of the algorithm, indicated in the parameter VK_VERSION. In this time version 008 is used. VK_MAC is presented as a request parameter in BASE64 coding.

Version 008

The value of the MAC008 function is calculated using the public key algorithm RSA. Values of empty fields are taken into account as well – “000”.

MAC008(x1,x2,…,xn) := RSA(SHA-1(p(x1)|| x1|| p(x2 )|| x2 || … ||p(xn)||xn),d,n)

Where: || is an operation of adding the string x1, x2, …, xn are the query parameters p is a function of the parameter length. The length is a number in the form of a three-digit string d is the RSA secret exponent n is the RSA modulus The signature is calculated in accordance with the PKCS1 standard (RFC 2437).

neubert
  • 15,947
  • 24
  • 120
  • 212
donk
  • 1,540
  • 4
  • 23
  • 46
  • Crypt_RSA is a dead project. You should switch over to `mcrypt` or `openssl` instead. – Marc B May 20 '11 at 19:43
  • I'll look into those. But still - the required result should be reached, shouldn't it? The encryption standard is the same. – donk May 20 '11 at 20:12
  • 6
    Marc B is confusing PEAR's Crypt_RSA for phpseclib's Crypt_RSA. phpseclib's Crypt_RSA is very much alive. A new release was made just two weeks ago, in fact, on May 9. mcrypt doesn't do RSA and the openssl extension is just dumb. I'd stick with phpseclib. –  May 21 '11 at 10:19
  • @donkapone: Your description of the requirements are very imprecise. You seem to mix signature and encryption. What you really should do is ask for detailed specifications together with test vectors. – Accipitridae May 22 '11 at 16:06
  • 2
    Now you sign a hash. RSA signatures also use a hash. Hence you effectively hash twice, which looks very odd to me. Seriously, I can't understand why don't you ask for the specs. With this try and error approach you have absolutely no guarantee to get a secure implementation. – Accipitridae May 23 '11 at 07:18
  • I do have the specs - but they are very vague. I've added them in. – donk May 23 '11 at 11:14

3 Answers3

3

What if you try $rsa->sign()? PKCS#1 doesn't do signing by simply encrypting the hash and if your bank is using an interoperable RSA solution they're probably not doing that either.

  • Have you tried doing $rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1) ? By default, Crypt_RSA does PSS signatures. They provide better security but aren't as widely used as PKCS1 signatures. –  May 21 '11 at 12:08
  • $rsa->encrypt(sha1($vk_mac, true)). Try that. sha1() by default returns the hex-encoded version of the hash. –  May 21 '11 at 16:05
  • Thank you for all your responses and help. This did not work though. As of now, I've tried encrypting the string using my private key or the bank's public key. Neither options worked though. – donk May 21 '11 at 16:57
  • I don't really understand how could I implement the shown solution into my code.. – donk May 22 '11 at 11:07
  • You would have needed to modify Crypt/RSA.php as per that post. Find the line it asks you to find and replace it with the one it asks you to replace it with. I guess it's a moot point since the issue has been resolved but that was the idea behind my posting the link. –  May 23 '11 at 17:29
2

The code was almost correct - I did not need to hash it again though (thanks @Accipitridae).

The solution was that the merchant's ID had to be uppercase, and not lowercase as provided. It does not say anywhere in the spec that it has to be uppercase as well. Nice.

donk
  • 1,540
  • 4
  • 23
  • 46
0

As mentioned above you can do this easily with openssl. Below is how I would do so.

$hashed = sha1($vk_mac);
openssl_public_encrypt($vk_mac, $encrypted, ($pubkey));
$vk_mac = base6$_encode($encrypted);

Read the documentation on openssl_public_encrypt for more.

Steven W
  • 33
  • 4
  • OK, so you would first encrypt vk_mac (which involves randomization) and then compute a hash of the ciphertext. Now can you tell us what the receiver does with that? – Accipitridae May 22 '11 at 16:00
  • @accipitridae I see the error I made and edited the code to reflect. I should have double checked thank you for catching my error. It seems as though the authors issue is querying the server not encrypting and encoding the verification code. – Steven W May 23 '11 at 01:20
  • Still doesn't make much sense to me. You should either encrypt with the receivers public key or sign with the senders private key. But you seem to encrypt with the private key. Of course, as long as the OP doesn't know what he has to implement, it is kind of hard to help him. – Accipitridae May 23 '11 at 07:25
  • oh... well that's embarrassing. I used the author's code and chopped it up. As a result I didn't pay attention to private or public. You are right. The author seems to think that it is his "public key" that is to be used. Lesson learned, when helping people proofread. – Steven W May 23 '11 at 08:51