Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reproducing Ruby OpenSSL private_encrypt output in C#

I have a simple Ruby script that I am using to do a private_encrypt on some HTTP headers to sign a web request that is to be sent to a ruby REST API, the API tests the Base64 encoded string against a Base64 encoded string it generates rather than decoding the Base64 and decrypting the data then testing the original string.

The script I am using is

require "openssl"
require "base64"

path_to_cert = ARGV[0].dup

plain_text = Base64.decode64(ARGV[1].dup)

private_key = OpenSSL::PKey::RSA.new(File.read(path_to_cert))

puts Base64.encode64(private_key.private_encrypt(plain_text))

The fact that the input is Base64 encoded is purely due to linebreaks and spaces in the input argument.

To use this I am having to shell out to ruby using System.Diagnostics.Process and capture the StdOut, while this isn't a major problem and works I'd like to remove the dependency on ruby but I am unable to reproduce the output using the C# RsaCryptoServiceProvider.

If I Base64 Encode the private_encrypt result of "SimpleString" using ruby i consistently get

auReJzoPSW3AhzsfT3EH4rD7lc4y2CJ026xIOiV6kjl2OKIj8GnzrPosoJDg\nSHrvLVKrSxYlegYgJRMx+vaAHSAm7RXrZh5An2SnVuO3qITa2TJ78hTc3bAw\nCDm4i9/4qictjxEFfnPRe6 EYCa4b3dnM5moa1eo9zbQPBa1eS6ItRCX4C0G0\n1tJpQsEvuums363eAhTUAYa6yEWuINLPmE0USW6jfFNnsxw8Nv9SnC+ziomb\n/mwlt9dS5/mzKM8yFMH6hdQYLoqc0QpjT+xaZ1ZyJ6dG5MVG h3JtjIVRTOSd\n+pUU/bo+obEHbrftG8u2uJImLSA+/1e8aapHaa3WNg==

When using the .Net

RsaCryptoServiceProvider.Encrypt("SimpleString", false) 

The result is always a different output due to the fact it is encrypting with the public key.

I have also tried

RsaCryptoServiceProvider.SignData

and while this always yields the same result, it is different to the result from ruby.

Can I use some CryptoAPI directly from .Net that will allow me to achieve the same result as Ruby?

like image 530
OneSHOT Avatar asked Apr 07 '11 10:04

OneSHOT


Video Answer


1 Answers

What you require is a method of generating "raw signatures". Raw signatures generally consist of modular exponentiation of PKCS#1 v1.5 compatible signature formats, although other padding methods may be used as well. The difference with a normal signing operation is that it does not perform any hashing, and - in the case of PKCS#1 v1.5 compatible signature formats - does not create the ASN.1 structure around the hash value, which is used to identify the hashing method used.

This raw signature formats are not a standardized method of generating signatures and should be avoided. private_encrypt mainly is used to support deprecated versions of SSL, that uses the method to create a signature for authentication using a concatenation of the raw output of MD5 and SHA1 hash values.

Finally, as Siva suggested, you may use the Bouncy Castle C# libraries to create raw signature formats. The raw signature format is not supported out of the box by most higher level API's, for the reasons given in the second paragraph.

[EDIT2] To accomplish this you need to use the raw "RSA" functionality.

if (mechanism.Equals("RSA"))
{
    return (new RsaDigestSigner(new NullDigest()));
}

After that the RsaDigestSigner class will use the EncodeBlock method in DigestInfo to generate the PKCS#1 padding. The NullDigest does not do nothing, it just returns the data given to it as signature of itself.

like image 177
Maarten Bodewes Avatar answered Sep 20 '22 15:09

Maarten Bodewes