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
Support exporting RSA private keys #373
Conversation
end | ||
|
||
key | ||
end | ||
def rsa_pkey(jwk_n, jwk_e, jwk_d, jwk_p, jwk_q, jwk_dp, jwk_dq, jwk_qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perceived complexity for rsa_pkey is too high. [16/7]
end | ||
|
||
key | ||
end | ||
def rsa_pkey(jwk_n, jwk_e, jwk_d, jwk_p, jwk_q, jwk_dp, jwk_dq, jwk_qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid parameter lists longer than 5 parameters. [8/5]
end | ||
|
||
key | ||
end | ||
def rsa_pkey(jwk_n, jwk_e, jwk_d, jwk_p, jwk_q, jwk_dp, jwk_dq, jwk_qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method has too many lines. [17/15]
You might want to extract some of this method lines into new methods inside its current class or isolate it on a class of its own and make it a collaborator instead.
@@ -28,45 +28,68 @@ def kid | |||
OpenSSL::Digest::SHA256.hexdigest(sequence.to_der) | |||
end | |||
|
|||
def export | |||
{ | |||
def export(options = {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assignment Branch Condition size for export is too high. [28.04/20]
end | ||
|
||
key | ||
end | ||
def rsa_pkey(jwk_n, jwk_e, jwk_d, jwk_p, jwk_q, jwk_dp, jwk_dq, jwk_qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cyclomatic complexity for rsa_pkey is too high. [15/6]
end | ||
|
||
key | ||
end | ||
def rsa_pkey(jwk_n, jwk_e, jwk_d, jwk_p, jwk_q, jwk_dp, jwk_dq, jwk_qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JWT::JWK::RSA#rsa_pkey has approx 14 statements
def self.decode_open_ssl_bn(jwk_data) | ||
OpenSSL::BN.new(::Base64.urlsafe_decode64(jwk_data), BINARY) | ||
def decode_open_ssl_bn(jwk_data) | ||
return nil if jwk_data.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JWT::JWK::RSA#decode_open_ssl_bn performs a nil-check
|
||
key = OpenSSL::PKey::RSA.new | ||
|
||
if key.respond_to?(:set_key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JWT::JWK::RSA#rsa_pkey manually dispatches method call
SourceLevel has finished reviewing this Pull Request and has found:
|
Will look at those robot comments later. The amount of parameters is a little problematic for these complexity metrics |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I also apply this changes (option include_private) to HMAC JWK?
kty: KTY, | ||
n: encode_open_ssl_bn(public_key.n), | ||
e: encode_open_ssl_bn(public_key.e), | ||
kid: kid | ||
} | ||
|
||
return ret if options[:include_private] != true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also check if the keypair has a private key.
return ret unless private? && options[:include_private] == true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Will get back to this when I have the time.
If we can agree on the public export method API to be like this it could be already implemented for the new JWKs. I went with the options hash to be able to maybe add/override something on the export easier in the future, dunno if that was necessary. |
Options with a hash for export is the right way! It is better to handle and change. Method |
dp: encode_open_ssl_bn(keypair.dmp1), | ||
dq: encode_open_ssl_bn(keypair.dmq1), | ||
qi: encode_open_ssl_bn(keypair.iqmp) | ||
) | ||
end | ||
|
||
def encode_open_ssl_bn(key_part) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this method private. Also method kid
. So, we can expose the same set of methods in all other JWKs.
I propose to expose only a defined set of methods for all JWKs. All other methods should be private. An abstract class could help. module JWT
module JWK
class [NAME]
attr_reader :keypair
attr_reader :kid
def initialize(keypair, kid = nil)
# ...
end
def private?
# ...
end
def public_key
# ...
end
def export(options = {})
# ...
end
class << self
def import(jwk_data)
end
end
end
end
end |
@anakinj and @richardlarocque please review my code on PR #372. I have added a factory class that can be used by RSA and EC JWKs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense to me.
I don't pretend to understand the OpenSSL calls, but they seem fine.
I think there's an API question about what you want to do if someone passes export_private
on a public key JWK. You can raise an error or silently return the public key. My preference is to raise an error, but you can argue it either way.
@@ -34,7 +34,7 @@ | |||
it 'returns a hash with the public parts of the key' do | |||
expect(subject).to be_a Hash | |||
expect(subject).to include(:kty, :n, :e, :kid) | |||
expect(subject).not_to include(:d) | |||
expect(subject).not_to include(:d, :p, :dp, :dq,:qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: whitespace between :dq
and :qi
.
@@ -43,7 +43,7 @@ | |||
it 'returns a hash with the public parts of the key' do | |||
expect(subject).to be_a Hash | |||
expect(subject).to include(:kty, :n, :e, :kid) | |||
expect(subject).not_to include(:d) | |||
expect(subject).not_to include(:d, :p, :dp, :dq,:qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: whitespace again.
let(:keypair) { rsa_key } | ||
it 'returns a hash with the public AND private parts of the key' do | ||
expect(subject).to be_a Hash | ||
expect(subject).to include(:kty, :n, :e, :kid, :d, :p, :q, :dp, :dq,:qi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: also whitespace.
Thanks for the comments @richardlarocque. I hope to get back to this in the end of this week. |
@anakinj @richardlarocque @excpt I suggest to first merge PR #372 that includes the abstract class. Then JWK RSA and EC can merge and adapt own PR to the abstract structure. |
Replaced by #375 |
@phlegx @richardlarocque Got inspired by your work so made this thing that would allow the inclusion of the private key in the JWK.