Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upSupport x5c JWT header as specified in RFC-7515 (JWS) #308
Conversation
sourcelevel-bot
bot
commented
May 4, 2019
|
Hello, @itstehkman! This is your first Pull Request that will be reviewed by Ebert, an automatic Code Review service. It will leave comments on this diff with potential issues and style violations found in the code as you push new commits. You can also see all the issues found on this Pull Request on its review page. Please check our documentation for more information. |
| end | ||
|
|
||
| def valid? | ||
| return true if @cert_chain.nil? || @cert_chain.empty? |
This comment has been minimized.
This comment has been minimized.
sourcelevel-bot
bot
May 4, 2019
Use @cert_chain.blank? instead of @cert_chain.nil? || @cert_chain.empty?.
| # @option opts [String] 'x5t' (not yet supported) | ||
| def initialize(opts) | ||
| unless opts['x5c'].nil? | ||
| signing_der = ::Base64.decode64((opts['x5c'].first)) |
This comment has been minimized.
This comment has been minimized.
|
|
||
| raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty? | ||
| raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless options_includes_algo_in_header? | ||
|
|
||
| if @options[:validate_cert] == true && !!@header['x5c'] |
This comment has been minimized.
This comment has been minimized.
|
|
||
| raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty? | ||
| raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless options_includes_algo_in_header? | ||
|
|
||
| if @options[:validate_cert] == true && !!@header['x5c'] | ||
| return false unless ::JWT::X509::Validator.new({ 'x5c' => header['x5c'] }).valid? |
This comment has been minimized.
This comment has been minimized.
| @@ -35,10 +35,15 @@ def decode_segments | |||
| def verify_signature | |||
| @key = find_key(&@keyfinder) if @keyfinder | |||
| @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks] | |||
| @key = ::JWT::X509::KeyFinder.new({ 'x5c' => header['x5c'] }).public_key if header['x5c'] | |||
This comment has been minimized.
This comment has been minimized.
| @@ -0,0 +1,30 @@ | |||
| module JWT | |||
| module X509 | |||
This comment has been minimized.
This comment has been minimized.
| end | ||
|
|
||
| def valid? | ||
| return true if @cert_chain.nil? || @cert_chain.empty? |
This comment has been minimized.
This comment has been minimized.
| # @option opts [Array<String>] 'x5c' x509 cert chain, where first is the cert used to sign | ||
| # @option opts [String] 'x5t' (not yet supported) | ||
| def initialize(opts) | ||
| unless opts['x5c'].nil? |
This comment has been minimized.
This comment has been minimized.
| # @option opts [Array<String>] 'x5c' x509 cert chain, where first is the cert used to sign | ||
| # @option opts [String] 'x5t' (not yet supported) | ||
| def initialize(opts) | ||
| unless opts['x5c'].nil? |
This comment has been minimized.
This comment has been minimized.
| @@ -0,0 +1,17 @@ | |||
| module JWT | |||
| module X509 | |||
This comment has been minimized.
This comment has been minimized.
| # @option opts [Array<String>] 'x5c' x509 cert chain, where first is the cert used to sign | ||
| # @option opts [String] 'x5t' (not yet supported) | ||
| def initialize(opts) | ||
| unless opts['x5c'].nil? |
This comment has been minimized.
This comment has been minimized.
| @@ -35,10 +35,15 @@ def decode_segments | |||
| def verify_signature | |||
| @key = find_key(&@keyfinder) if @keyfinder | |||
| @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks] | |||
| @key = ::JWT::X509::KeyFinder.new({ 'x5c' => header['x5c'] }).public_key if header['x5c'] | |||
This comment has been minimized.
This comment has been minimized.
| def initialize(opts) | ||
| x5c = opts[:x5c] | ||
|
|
||
| unless x5c.nil? |
This comment has been minimized.
This comment has been minimized.
sourcelevel-bot
bot
commented
May 7, 2019
|
Ebert has finished reviewing this Pull Request and has found:
You can see more details about this review at https://ebertapp.io/github/jwt/ruby-jwt/pulls/308. |
| signing_der = ::Base64.decode64 x5c.first | ||
| @signing_cert = OpenSSL::X509::Certificate.new signing_der | ||
| len = x5c.length | ||
| @cert_chain = x5c[1...len].map do |b64der| |
This comment has been minimized.
This comment has been minimized.
bdewater
May 24, 2019
Remove len = x5c.length and use:
| @cert_chain = x5c[1...len].map do |b64der| | |
| @cert_chain = x5c[1..-1].map do |b64der| |
| @signing_cert = OpenSSL::X509::Certificate.new signing_der | ||
| len = x5c.length | ||
| @cert_chain = x5c[1...len].map do |b64der| | ||
| OpenSSL::X509::Certificate.new(::Base64.decode64(b64der)) |
This comment has been minimized.
This comment has been minimized.
bdewater
May 24, 2019
See comment above about RFC 4648.
| OpenSSL::X509::Certificate.new(::Base64.decode64(b64der)) | |
| OpenSSL::X509::Certificate.new(::Base64.strict_decode64(b64der)) |
| x5c = opts[:x5c] | ||
|
|
||
| unless x5c.nil? | ||
| signing_der = ::Base64.decode64 x5c.first |
This comment has been minimized.
This comment has been minimized.
bdewater
May 24, 2019
We should use the strict variant here, as documented this method complies with RFC 4648 mentioned by the JWT spec.
| signing_der = ::Base64.decode64 x5c.first | |
| signing_der = ::Base64.strict_decode64(x5c.first) |
| store.add_cert cert | ||
| end | ||
| store.verify @signing_cert | ||
| end |
This comment has been minimized.
This comment has been minimized.
bdewater
May 24, 2019
This block of code has security problems. From RFC 5280 section 3.2:
In general, a chain of multiple certificates may be needed, comprising a certificate of the public key owner (the end entity) signed by one CA, and zero or more additional certificates of CAs signed by other CAs. Such chains, called certification paths, are required because a public key user is only initialized with a limited number of assured CA public keys.
More concretely:
- Chain validation can not be skipped: this would allow an attacker to just strip the chain and inject their own certificate and use it to sign, bypassing any security check. Even a single certificate must chain correctly to a trust anchor.
- The trust anchor should be configurable by the consuming app. It may be tempting to default to
OpenSSL::X509::Store#set_defaults_pathbut instead of allowing all the certificate authorities to sign, the user should specify this themselves and preferably only limit to root certificate(s) that apply for their use case. E.g. when validating a JWT from Google, use only the cert(s) from https://pki.goog/ - RFC 5280 section 3.3 also mentions revocation checking, which is absent in this PR.
I've been implementing all of the above in the WebAuthn gem, you can find my code here: cedarcode/webauthn-ruby#208 - feel free to use it, I'd appreciate a mention as co-author if you do
This comment has been minimized.
This comment has been minimized.
itstehkman
Jul 4, 2019
•
Author
@bdewater sorry for the late response here! Thank you for all the feedback, definitely seems like I was missing some important parts. And that webauthn PR looks impressive.
Unfortunately I am no longer working on this PR, as my project's requirements changed and I don't need to use x5c for JWTs anymore. Please feel free to take my work and continue it if you'd like!
This comment has been minimized.
This comment has been minimized.
rabajaj0509
Oct 3, 2019
Contributor
@bdewater would you be interested in taking over this PR, because it looks like a important feature. Newer versions of the IDP's are supporting the x5c header and I think we should support it in this gem too.
Let me know if you would be able to work on this one, else I can pick it up and resolve :)
This comment has been minimized.
This comment has been minimized.
bdewater
Oct 6, 2019
•
I'm currently on vacation but was planning to pick this up again after I'm back next week :)
itstehkman commentedMay 4, 2019
•
edited by excpt
Taking a stab at supporting x5c (#59)
https://tools.ietf.org/html/rfc7515#section-4.1.6
I'll fix the ebert issues later, I just want the idea validated so I can move forward.