1

I'm currently working on a project where I have to "convert" some code from Ruby(version 1.9.3p194) to Golang(version 1.7). There is this part where Ruby uses RSA public key encryption and I always get a consistent result every time it gets executed. This is the function used:

Edit: I overlooked that after the public key encryption, there is a base 64 encoding as well

public_key = OpenSSL::PKey::RSA.new(public_encryption_key)
public_encrypted_text = public_key.public_encrypt(text, OpenSSL::PKey::RSA::NO_PADDING)
base64_encrypted_text = Base64.encode64(public_encrypted_text).gsub("\n", "")
escaped_encrypted_text = URI.escape(encrypted_key, "/+=")

However in Golang, due to the rsa library I can't get a consistent result since the function to encrypt takes a random parameter to generate different result each time. I understand why it needs to be different every time, but i can't get anything remotely similar to what ruby generates. These are the functions used in Golang:

//keyBytes is the public key as []byte
block, _ := pem.Decode(keyBytes)
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
    return nil, err
}

pubKey, ok := key.(*rsa.PublicKey)
if !ok {
    return nil, errors.New("Cannot convert to rsa.PublicKey")
}

result, err := rsa.EncryptPKCS1v15(cryptorand.Reader, pubKey, text)
encryptedText := base64.URLEncoding.EncodeToString(result)
encryptedText = strings.TrimRight(encryptedText, "=")

One of the problems is that ruby can encrypt the text with no problem, and in golang I'm getting an error that the key is too short to encrypt everything.

If I encrypt something else, like "Hello". When decrypting I get from ruby the error "padding check failed". The decryption is being handle like follows:

private_key.private_decrypt(Base64.decode64(text))  

EDIT: Thanks to the answer of gusto2 I now know better what is going on since I didn't have much understanding of RSA.

Now in Golang I was able to encrypt the text using PKCS1 v1.5, and just to be sure I tried to decrypt that as well, also in Golang, with no problem.

However in Ruby I still wasn't able to decrypt using the private key. So I figured that the base64 encoding used in Golang was the issue. So I changed that part to this:

encryptedText := base64.StdEncoding.EncodeToString(result)

And also I removed the last line were the equal sign was being trimmed.

With that done it worked like a charm.

Fer VT
  • 500
  • 1
  • 8
  • 25
  • 1
    Why do you need the output to be identical? Have you checked whether the message encrypted by Go can be decrypted by Ruby as required? – Adrian Dec 26 '17 at 17:22
  • I'd be interested in the versions you are using for Ruby at least. And the goal or objective. What is the PKI used for concretely ? – Ely Dec 26 '17 at 17:24
  • 1
    You should never end up the identical cipher text, else someone would be able to break the encryption. – JimB Dec 26 '17 at 17:27
  • 3
    In other words, you did it wrong in Ruby because it gave you the rope to hang yourself, now your looking to do it wrong in Go. – President James K. Polk Dec 26 '17 at 17:45
  • Im using ruby 1.9.3, the encrypted part can be decrypted but ruby doesn't give the output that is needed. As a side note, I didn't code the ruby part that we're trying to change to golang, that is just the tip of a horrible coded rails iceberg. – Fer VT Dec 26 '17 at 17:51
  • Can you expand on that? If Ruby can decrypt it, you're halfway home. What's wrong with the output? It seems like that's the problem to fix. – Adrian Dec 26 '17 at 18:30
  • Let me edit the question, since i overlooked a couple of details. – Fer VT Dec 26 '17 at 18:38
  • As far as I can tell all public interfaces available from `crypto/rsa` enforce either PKCS15 or OAEP padding and will thus always reject your ciphertext during decryption. Either do it right in Ruby or you'll have to roll your own rsa decryptor in go. – President James K. Polk Dec 26 '17 at 18:53
  • 1
    To add to everyone else's concerns, a more existential one. RSA crypto has a very limited possible message length (somewhat less than the number of bits in the key). The more typical approach is to use the public key to encrypt a random symmetric key for e.g. AES-256-CBC or similar and use that to send the actual message. See https://stackoverflow.com/questions/5866129/rsa-encryption-problem-size-of-payload-data and https://crypto.stackexchange.com/questions/14/how-can-i-use-asymmetric-encryption-such-as-rsa-to-encrypt-an-arbitrary-length – BJ Black Dec 26 '17 at 23:26
  • what is the `cryptorand` variable? – gusto2 Dec 27 '17 at 07:09

1 Answers1

2

I am no knowledgeable about golang, however I may know something about RSA.

The difference seems to be in the padding.

For ruby - no padding is used
For golang - PKCS1v15 padding is used

In the rubyexample you use OpenSSL::PKey::RSA::NO_PADDING is used which is VERY VERY unsafe. It is called textbook RSA and is not inteded in real-life use as it has many weaknesses and dangerous traps. So the ruby example is very dangerously unsafe because of using the textbook RSA. As well it is limited to encrypting small messages (much smaller than the keyspace).

There are two padding types used with RSA:

  • PKCS1 v1 (commonly referred as PKCS1) - this is a deterministic padding (the output is always the same), many cryptographers consider this option obsolete as some weaknesses has been found when not used properly, but it is still in use and not considered broken.

  • PKCS1 v2 (commonly refered as OAEP or PSS) which is stochastic (randomized) padding. You can distinguish the last two as the output of OAEP is always different.

One of the problems is that ruby can encrypt the text with no problem, and in golang I'm getting an error that the key is too short to encrypt everything

You've provided only a small part of the golang example, so here I may only assume many things.

As you claim the golang example outputs randomized output and according to the parameters PKCS1 v1.5 is used, I'd assume the implementation is doing hybrid encryption which is good and much safer way to encrypt data with RSA (using symmetric encryption with random key and wrap/encrypt the key with RSA).

gusto2
  • 11,210
  • 2
  • 17
  • 36
  • Thank you so much. Now I have a better understanding of how this works, I modified a couple of things in the code and now it works. I edited the question with the changes. – Fer VT Dec 27 '17 at 20:35