Closed mattys101 closed 4 months ago
Thanks Matt, for sharing the details.
The EC class as is was unfinished, written for a specific use-case some time ago and the internals where very ... internal!
My "employer" actually uses ruby-jwt on JRuby but there was a patch due the missing dsa_verify_asn1
+ it's an older version the app is currently locked into.
Please do open new tickets if you find more issues... :pray:
No worries. Thanks for cleaning up the implementation and making it compatible. Will let you know in a new ticket if we run into any trouble when we switch over to the updated version.
I have been trying to get the ruby-jwt library working with jruby-openssl and have made some headway in identifying some issues. The main issues are around EC keys and PSS keys, the latter is completely missing #288, while I have somewhat of a workaround for the former.
The key problem with the EC implementation (pun intended š) is twofold:
This generally results in a NullPointerException as there is no curve name set in one or both of the places.
(A third issues is the inconsistent signing/verification interface with the standard OpenSSL library which is identified in #241 and #255)
Inconsistent initialization
The behaviour of
OpenSSL::PKey.read
andOpenSSL::Pkey::EC.new
is not the same:PKey.read instantiates PKeyEC directly via its constructor without using the initialize method. As a result the
unwrapPrivateKeyWithName
method is not called to set the curveName properly, unlike the initialize method which doesPKeyEC(Ruby runtime, RubyClass type, PrivateKey privKey, PublicKey pubKey)
constructor callunwrapPrivateKeyWithName
TODO set curveName ?!?!?!?!?!?!?!
in the initialize method but that will only address one situation if it is added there)PKey.read
does not read all alternatives of EC public key PEM files:PKeyEC.new
attempts to call bothPEMInputOutput.readECPubKey
andPEMInputOutput.readECPublicKey
,PKey.read
only triesreadECPubKey
(viaPEMInputOutput.readPubKey
);readECPublicKey
is never attemptedimpl.PKey
class only reads RSA and DSA keys from X509 data which is also a potential issuePKey.read
also attempts to readPEMInputOutput.readECPublicKey
Fixing the curve name information would partly address #189, #256, and #257, but private keys still do not produce the correct output from
to_pem
, although it does appear to correct public keys.Curve Name access
The curve name is stored in both
PKeyEC
andPKeyEC.Group
and there are several accessors, some as ruby methods, some as private java methods, some that do additional processing, and others that access the value raw. This is a recipe for disaster and leads to inconsistent usage of curve name, ultimately leading to unexpectedNullPointerException
s in various cases when things are not setup properly.Fix: refactor where and how the key information, including curve name, is captured and retrieved in the
PKeyEC
andPKeyEC.Group
classes. Ideally, there should be only one location where the information is stored, likely on the group (or possibly retrieved from the underlying ECNamedCurve object), and the objects should delegate as appropriate. Moreover, the ruby and plain Java methods should be consistent and used consistently.A similar treatment should be make across the PKeyEC class and its nested classes. As the Group and Point classes are nested classes, there is no reason they cannot leverage shared code. This would allow, for example, an easy avenue for implementing instantiation of PKey::EC::Group objects from PEM/DER strings consistently as for PKey::EC objects, and making the interfaces more compatible with the standard Ruby OpenSSL library.
Workaround
I have a temporary workaround at the ruby level for the moment. Note that it only works if you retrieve the curve_name from the group before using the key as is done in ruby-jwt to verify the algorithm. This will ensure the curve name is consistent in both the key and group objects, avoiding any unexpected NullPointerExceptions.