Reputation: 43
I am having to implement a payment gateway in Rails that I've not worked with or seen before (Westpac's Payway in Australia if anyone is interested).
Their documentation isn't bad and the system is fairly logical, so much so that it's been quite painless so far (a miracle for payment integration).
Where there is an issue is that after the payment is POSTed directly to Westpac and the payment processed they redirect back to our site with a large encrypted parameter. This is then meant to be decrypted by us to get access to the actual parameters.
Here is Westpac's guidance:
The parameters are encrypted using AES with Cipher Block Chaining, using PCKS-5 Padding. The decryption algorithm should be initialised with a 16 byte, zero-filled initialization vector, and should use your encryption key (which can be found on the Security page of PayWay Net Shopping Cart setup).
Before decryption, the parameters passed with the redirect will appear as follows:
EncryptedParameters=QzFtdn0%2B66KJV5L8ihbr6ofdmrkEQwqMXI3ayF7UpVlRheR7r5fA6 IqBszeKFoGSyR7c7J4YsXgaOergu5SWD%2FvL%2FzPSrZER9BS7mZGckriBrhYt%2FKMAbTSS8F XR72gWJZsul9aGyGbFripp7XxE9NQHVMWCko0NlpWe7oZ0RBIgNpIZ3JojAfX7b1j%2F5ACJ79S VeOIK80layBwCmIPOpB%2B%2BNI6krE0wekvkkLKF7CXilj5qITvmv%2FpMqwVDchv%2FUNMfCi 4uUA4igHGhaZDQcV8U%2BcYRO8dv%2FnqVbAjkNwBqxqN3UPNFz0Tt76%2BP7H48PDpU23c61eM 7mx%2FZh%2Few5Pd0WkiCwZVkSZoov97BWdnMIw5tOAiqHvAR3%2BnfmGsx
Westpac has no Rails demos but they do have PHP. Here is the PHP demo:
function decrypt_parameters( $base64Key, $encryptedParametersText, $signatureText )
{
$key = base64_decode( $base64Key );
$iv = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
$td = mcrypt_module_open('rijndael-128', '', 'cbc', '');
// Decrypt the parameter text
mcrypt_generic_init($td, $key, $iv);
$parametersText = mdecrypt_generic($td, base64_decode( $encryptedParametersText ) );
$parametersText = pkcs5_unpad( $parametersText );
mcrypt_generic_deinit($td);
}
Here is what I've tried in Rails:
def Crypto.decrypt(encrypted_data, key, iv, cipher_type)
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(encrypted_data) + aes.final
end
iv = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
key = Base64.decode64("mysecretkey")
data = Base64.decode64("QzFtdn0%2B66KJV5L8ihbr6ofdmrkEQwqMXI3ayF7UpVlRheR7r5fA6
IqBszeKFoGSyR7c7J4YsXgaOergu5SWD%2FvL%2FzPSrZER9BS7mZGckriBrhYt%2FKMAbTSS8F
XR72gWJZsul9aGyGbFripp7XxE9NQHVMWCko0NlpWe7oZ0RBIgNpIZ3JojAfX7b1j%2F5ACJ79S
VeOIK80layBwCmIPOpB%2B%2BNI6krE0wekvkkLKF7CXilj5qITvmv%2FpMqwVDchv%2FUNMfCi
4uUA4igHGhaZDQcV8U%2BcYRO8dv%2FnqVbAjkNwBqxqN3UPNFz0Tt76%2BP7H48PDpU23c61eM
7mx%2FZh%2Few5Pd0WkiCwZVkSZoov97BWdnMIw5tOAiqHvAR3%2BnfmGsx")
cleartext = Crypto.decrypt(data, key, iv, "AES-128-CBC")
And I simply pass in the same initialization vector as noted in the PHP, though I'm not sure this is correct for Rails.
In any event, the key is provided and easy to Base64 decode, as are the Encrypted Parameters. At the end of the day, I'm getting this error:
cipher.rb:21:in `final': wrong final block length (OpenSSL::Cipher::CipherError)
from cipher.rb:21:in `decrypt'
from cipher.rb:29:in `<main>'
Admittedly, I'm out of my depth on this Crypto stuff but am up against a wall and do not have the time (despite the interest) to learn more.
Upvotes: 4
Views: 2847
Reputation: 12537
The problem was, that the input data was additionally "URI-escaped" and ruby's base64-decoder did not "care" about the invalid base64-input (%
is no base64-digit), so no error was raised.
The solution was to "unescape" the URI-encoding with URI.unescape
:
require 'uri'
data = Base64.decode64(
URI.unescape("QzFtdn0%2B66 ... Iw5tOAiqHvAR3%2BnfmGsx"))
Of course, if the input data is received from a GET
/POST
parameter, the input data is most probably already "unescaped" by your web-stack - just as a note of caution (double unescape
may cause problems if a percent-sign %
appears in the input data).
Upvotes: 8