Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key: unsupported #129

Closed
thibaudgg opened this issue Jun 25, 2022 · 25 comments
Closed

OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key: unsupported #129

thibaudgg opened this issue Jun 25, 2022 · 25 comments

Comments

@thibaudgg
Copy link

I tried to upgrade to the latest Heroku-22 Stack, which ships with OpenSSL 3 and got the following error:

OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key: unsupported
  from vendor/ruby-3.1.2/lib/ruby/3.1.0/openssl/pkey.rb:348:in `initialize'
  from vendor/ruby-3.1.2/lib/ruby/3.1.0/openssl/pkey.rb:348:in `new'
  from vendor/ruby-3.1.2/lib/ruby/3.1.0/openssl/pkey.rb:348:in `new'
  from epics (1.8.1) lib/epics/key.rb:8:in `initialize'
  from epics (1.8.1) lib/epics/client.rb:260:in `new'
  from epics (1.8.1) lib/epics/client.rb:260:in `block in extract_keys'
  from epics (1.8.1) lib/epics/client.rb:259:in `each'
  from epics (1.8.1) lib/epics/client.rb:259:in `each_with_object'
  from epics (1.8.1) lib/epics/client.rb:259:in `extract_keys'
  from epics (1.8.1) lib/epics/client.rb:12:in `initialize'

Heroku mentions this:

In addition, OpenSSL 3 drops default support for a number of legacy/insecure cryptographic algorithms. If you see TLS/SSL related failures connecting from your application to external services, it is likely that those servers are running outdated/insecure software or configurations, that will need to be fixed by the maintainers of those services.

I wonder if this is an issue with the Bank servers as it is failing with the same error on all the different ones I connect to, which tends to point to an implementation problem in epics.

@tobischo
Copy link
Collaborator

Hi @thibaudgg,
at the point in time when this is called no connection to the bank server has been made yet.

It is happening during initialization and based on the error message it looks like the keys_content that was passed is not valid: https://github.com/railslove/epics/blob/v1.8.1/lib/epics/client.rb#L12

epics expects a json string here which it can decrypt with the passphrase passed in the second parameter
https://github.com/railslove/epics/blob/v1.8.1/lib/epics/client.rb#L258

So it might also be caused by the passphrase not matching the one defined during initial setup.

@thibaudgg
Copy link
Author

Hi @tobischo, thank you for looking into it and for your detailed answer.

My main concern is that everything is running well under the Heroku-20 Stack which ships with openssl 1.1.1f-1ubuntu2.16, all the rest is identical (same credentials and Ruby/Gems version) and that for many different banks credentials. Would it be that OpenSSL 3 is pickier about the keys_content values structure?

I just used the keys that Epics gave me when I set up each Banks connection (Epics::Client#save_keys), and so far it worked seamlessly, so I'm not really sure what I'm doing wrong here :).

@tobischo
Copy link
Collaborator

Hi @thibaudgg

just to rule out all the options before also investing a lot of time here on trying to recreate the issue:

So the keys and password for the keys is provided in exactly the same way and also reaches the code in exactly the same way between the different versions on heroku?

Did you by any chance manage to recreate the issue outside of heroku, e.g. inside of a docker container?
If we can also recreate it, we can work against the 'failing test case' to better investigate.

@tobischo
Copy link
Collaborator

tobischo commented Jul 29, 2022

Note for later:

This might be helpful

https://hub.docker.com/r/heroku/heroku/tags

@thibaudgg
Copy link
Author

So the keys and password for the keys is provided in exactly the same way and also reaches the code in exactly the same way between the different versions on heroku?

Correct, identical keys and code, only the Heroku stacks changed from 20 to 22. I just re-tested it now, so I'm confident this is the only changing piece.

Did you by any chance manage to recreate the issue outside of heroku, e.g. inside of a docker container?

Sadly not, I'm not using docker and haven't much experience running it either.

@tobischo
Copy link
Collaborator

tobischo commented Jul 31, 2022

Ok, so I managed to recreate it by using ubuntu:22.04 as a base image, installing openssl3 and ruby3, as well as epics

I am getting the same error there, however that does not yet provide a solution.
I will look into it further

Edit: It works fine with ruby3 and openssl 1.x.x, so indeed something that changed towards openssl 3

@tobischo
Copy link
Collaborator

This also looks related: ruby/openssl#369

@tobischo
Copy link
Collaborator

Ok, so from what I could notice so far:
The primary issue appears to be that epics is re-using the cipher for multiple keys once initialized

See:

@cipher ||= OpenSSL::Cipher.new("aes-256-cbc")

While that worked nicely for openssl 1.1.1, it does not appear to behave as intendend for openssl 3.
Some of the state appears to stick around. Running cipher.reset is also not working sufficiently.

When I adapt the code to run with individually initialized ciphers per key to be decrypted, it works just fine

@tobischo
Copy link
Collaborator

I created a PR in #132 which solved the key loading issue for me

@thibaudgg
Copy link
Author

Hey @tobischo, thanks for looking into it. I just gave a try to your PR #132 on the heroku-22 stack and all but one bank I support are working (I'm able to retrieve payments).

The only one that fails is raising:

/app/vendor/bundle/ruby/3.1.0/bundler/gems/epics-33d419b51584/lib/epics/middleware/parse_ebics.rb:12:in
`block in call': EPICS_UNKNOWN - unknown (Epics::Error::TechnicalError)

I checked again the same bank with the latest version of epics and heroku-20 and it is working fine so it isn't a bank server downtime/maintenance issue. Initializing the Epics::Client is working fine, but retrieving payments is failing with the error above.

@tobischo
Copy link
Collaborator

tobischo commented Aug 6, 2022

Is there anything obviously different between the banks? E.g. different key sizes?

If it is working for all of them but one, it will be rather difficult to debug this.
The thing that comes to mind here would be that there is an edge case for the specific keys that is not covered correctly.

Considering that it has been working with heroku-20 you are right, it shouldn't be a general issue with the bank server.
So another thing that it might be is that this one bank is not entirely up to date on their tls connection algorithms though and something is not supported (just guessing here)
I suppose one way to test this would be to make requests to the EBICS endpoint (just a standard HTTP(S) request) to check what algorithms are supported. If that is the issue it should already fail from the heroku-22 instance.

@thibaudgg
Copy link
Author

@tobischo I just gave another try to your PR on the heroku-22 stack and now all the banks I support are working without any issue.

As you mentioned, I also suspect that something wasn't up-to-date on the bank server side, as everything was quite similar (key sizes) between the one failing and the others. Maybe my previous test triggered some alarms and they already made the required update (even if that seems like a fast resolution for a bank 😅).

So from my side, I would give a GO for your PR. 👍🏻 Do you have an idea about when it will be merged and a new gem version will be released?

Thanks a lot for your work! 💪🏻

@tobischo
Copy link
Collaborator

tobischo commented Aug 12, 2022

Unfortunately not 😬
Right now I am waiting on the review from @bumi, who was the most responsive other person here recently to take a look at my changes.
As soon as he had some time to review it, I will push again for getting a release done

I already have a PR waiting for that and we could probably include it into the v2.0.0 of this gem then - no idea of the timeline for this though

@thibaudgg
Copy link
Author

@tobischo ok, no worries, I can use your PR branch in the meantime.

@tobischo
Copy link
Collaborator

tobischo commented Aug 29, 2022

@thibaudgg It is merged now. Will try to get it released.

See #131 on that. Closing this here for now

@thibaudgg
Copy link
Author

Hey @tobischo, I encountered this issue again with two different Swiss banks this week.

It could be my fault because I generated the keys with the 1.8.1 gem version and when I initiate the Epics client with the latest master version (2.1.1) I'm getting again this issue:

../ruby/3.2.2/lib/ruby/3.2.0/openssl/pkey.rb:356:in `initialize': Neither PUB key nor PRIV key: unsupported (OpenSSL::PKey::RSAError)
        from ../ruby/3.2.2/lib/ruby/3.2.0/openssl/pkey.rb:356:in `new'
        from ../ruby/3.2.2/lib/ruby/3.2.0/openssl/pkey.rb:356:in `new'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/epics-5f5c275122bb/lib/epics/key.rb:9:in `initialize'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/epics-5f5c275122bb/lib/epics/client.rb:262:in `new'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/epics-5f5c275122bb/lib/epics/client.rb:262:in `block in extract_keys'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/epics-5f5c275122bb/lib/epics/client.rb:261:in `each'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/epics-5f5c275122bb/lib/epics/client.rb:261:in `each_with_object'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/epics-5f5c275122bb/lib/epics/client.rb:261:in `extract_keys'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/epics-5f5c275122bb/lib/epics/client.rb:12:in `initialize'
        from (irb):11:in `new'
        from (irb):11:in `<main>'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in `<top (required)>'
        from ../ruby/3.2.2/bin/irb:25:in `load'
        from ../ruby/3.2.2/bin/irb:25:in `<top (required)>'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.12/lib/bundler/cli/exec.rb:58:in `load'
        from ../ruby/3.2.2/lib/ruby/gems/3.2.0/gems/bundler-2.4.12/lib/bundler/cli/exec.rb:58:in `kernel_load'

While trying to debug the issue, I found this one interesting tidbit when printing the encoded_keys:

class Epics::Key
  attr_accessor :key

  def initialize(encoded_key, passphrase = nil)
    print encoded_key
    if encoded_key.kind_of?(OpenSSL::PKey::RSA)
      self.key = encoded_key
    else
      self.key = OpenSSL::PKey::RSA.new(encoded_key)
    end
  end
...
end

The encoding of the RSA PRIVATE KEY isn't in plain text:

xF6\xEE\xE9z\x0E\x9C\xC1\r\xA1\xA1\f\x90\x00\xBF\xA7\x8DRIVATE KEY-----
MIIEowIBAAKCAQEAwebrPbZPLn0n/IwXvbFsQhq0nOXH18ZSdREdSYe9qri4T1hG
....

Do you think that restarting the INI key process with the latest master version (2.1.1) instead of 1.8.1 would solve this issue, or something is still wrong here?

Thanks for your support!

@thibaudgg
Copy link
Author

Could it be linked to that change? 131c1bd

All existing keys (that I also generated with 1.8.1) are still working fine on the latest master version (2.1.1) so I wonder if that's those new banks having issues...

@tobischo
Copy link
Collaborator

Both versions are executed with the same ruby and openssl version?

@thibaudgg
Copy link
Author

Yes, both are on Ruby 3.2.2 and the same openssl version (LibreSSL 3.3.6).

@tobischo
Copy link
Collaborator

Then it is likely related
However right now I am not sure how I would start debugging this without a failing test scenario

@tobischo
Copy link
Collaborator

Were those keys also generated on openssl 3?

So right now I am wondering about how to best recreate the issue to find a potential solution.

So right now the things to try would be:
Generate a new pair of key with epics 1.8.1 in your current ruby + openssl version.
Try to load it with latest. If that already 'works'/fails, that would be a good start.
Same the other way around: generate with 2.1.1 and load with 1.8.1

If both of these work it might actually additionally be related to the ruby/openssl version used to originally generate the keys

@thibaudgg
Copy link
Author

Were those keys also generated on openssl 3?

Yes, but using the 1.8.1 version, Ruby 3.2.2 and openssl version (LibreSSL 3.3.6).

So right now the things to try would be:
Generate a new pair of key with epics 1.8.1 in your current ruby + openssl version.
Try to load it with latest. If that already 'works'/fails, that would be a good start.
Same the other way around: generate with 2.1.1 and load with 1.8.1

From my experience/testing, generating and saving the final key (HPB command) works with 1.8.1 (didn't check with 2.1.1 as you can only do it one with the bank) and doing the HAA command was ok, but reinitializing the client with the final key was then failing with the error above on both versions. Is it helpful? 😬

@tobischo
Copy link
Collaborator

tobischo commented Jun 20, 2023

Alright, so it works nicely against openssl 1.1.1, but it fails against openssl 3.0.9

Considering that, my immediate recommendation would be to go with openssl 1 and not openssl 3. As far as I can tell, ruby does still not properly support openssl3...

Interestingly... with what you described now and with what I have observed now, going back on the change in 131c1bd is also not an option, since it fails with:

/usr/local/bundle/gems/epics-1.8.1/lib/epics/client.rb:110:in `set_key': rsa#set_key= is incompatible with OpenSSL 3.0 (OpenSSL::PKey::PKeyError)

@tobischo
Copy link
Collaborator

tobischo commented Jun 20, 2023

So you can generate the keys relatively easily without needing anything further

gem 'epics', '= 1.8.1'

require 'epics'

client = Epics::Client.setup(
           "<example>",
           "<example>",
           "<example>",
           "<example>",
           "<example>",
         )

client.save_keys("epics.key")

and then you can load it using:

gem 'epics', '= 2.1.1'

require 'epics'

keys = File.read('epics.key')

client = Epics::Client.new(
           keys,
           "<example>",
           "<example>",
           "<example>",
           "<example>",
           "<example>",
         )

Additionally for simplicity it can be loaded using:

passphrase = '<example>'

gem 'epics', '= 1.8.1'

require 'epics'

keys = File.read('epics.key')

json_keys = JSON.load(keys)

# A006
# X002
# E002

data = Base64.strict_decode64(json_keys['E002'])
salt = data[0..7]
data = data[8..-1]

cipher = OpenSSL::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(passphrase, salt, 1, cipher.key_len)
result = cipher.update(data) + cipher.final

The odd part here is, that if I generated the key with openssl 3 on epics 1.8.1 I get:

\xD3\xE8\xC9\n\xB8\xF2\xB1@2\xCC -c;BaRIVATE KEY-----\nMIIEpAI

As far as I can tell the first line appeared to be broken, when decrypted, but otherwise I could read the key. I am not sure if that would then solve the entire issue, however loading the keys would technically be possible if you could get them fixed that way.

Based on what I tried, in some openssl versions it works opening the key generated with epics 1.8.1 also with epics 2.1.1. Specifically this is the case for openssl 1.1.1t.
Using those keys in openssl 3 then also worked without an issue both on epics 1.8.1 and epics 2.1.1.

Generating keys with epics 2.1.1 and opening them with epics 2.1.1 on openssl3 worked without an issue.
Generating keys with epics 1.8.1 and opening them with epics 1.8.1 on openssl3 worked without an issue.
Generating keys with epics 2.1.1 and opening them with epics 1.8.1 on openssl3 is not working.
Generating keys with epics 1.8.1 and opening them with epics 2.1.1 on openssl3 is not working.

That means, the "best" way for you to get them safely transformed might be the following:

Execute the following for epics 1.8.1, since that is where you generated the keys:

gem 'epics', '= 1.8.1'

require 'epics'

keys = File.read('epics.key')

client = Epics::Client.new(
           keys,
           "<example>",
           "<example>",
           "<example>",
           "<example>",
           "<example>",
         )

client.keys.each do |key_type, key|
  File.open("#{key_type}.pem", 'w+').tap {|f| f.write(key.key.to_pem)}.close
end

This will extract the keys in PEM format.

Then as a next step you should be able to do the following (note that the setup is just there to generate some initial keys without requiring to load the original ones)

gem 'epics', '= 2.1.1'

require 'epics'

client = Epics::Client.setup(
           "<example>",
           "<example>",
           "<example>",
           "<example>",
           "<example>",
         )

['A006', 'E002', 'X002'].each do |key_type|
  base_key = File.read("#{key_type}.pem")

  client.keys[key_type] = Epics::Key.new(base_key)
end

client.save_keys('new_epics.key')

That key file should then be openable with a ruby compiled against openssl3 with epics 2.1.1

@thibaudgg
Copy link
Author

@tobischo thanks a lot for your detailed answer, I was able to fix both keys with the pem files. Everything works great now! 🥇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants