Skip to content

Commit

Permalink
Merge pull request #10 from sporkmonger/master
Browse files Browse the repository at this point in the history
Fix issue with signature verification in JRuby
  • Loading branch information
progrium committed Jul 20, 2012
2 parents 321a20c + 961edeb commit 6915be7
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 28 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language: ruby
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- jruby
- ree
script: "bundle exec rake test"
14 changes: 10 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
source "https://rubygems.org"
source :rubygems

gem "multi_json", "~> 1.0"
gem 'json', '>= 1.2.4'
gem 'multi_json', '~> 1.0'
gem 'jruby-openssl', :platforms => :jruby

group :development do
gem "echoe", ">=4.6.3"
gem "rspec"
gem 'echoe', '>= 4.6.3'
end

group :test, :development do
gem 'rake'
gem 'rspec'
end
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ The tests are written with rspec. Given you have rake and rspec, you can run tes
* Ilya Zhitomirskiy <[email protected]>
* Daniel Grippi <[email protected]>
* Jeff Lindsay <[email protected]>
* Bob Aman <[email protected]>

## License

Expand Down
6 changes: 3 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ require 'rubygems'
require 'rake'
require 'echoe'

Echoe.new('jwt', '0.1.4') do |p|
Echoe.new('jwt', '0.1.5') do |p|
p.description = "JSON Web Token implementation in Ruby"
p.url = "http://github.com/progrium/ruby-jwt"
p.author = "Jeff Lindsay"
p.email = "[email protected]"
p.ignore_pattern = ["tmp/*"]
p.runtime_dependencies = ["multi_json ~> 1.0"]
p.runtime_dependencies = ["multi_json ~>1.0"]
p.development_dependencies = ["echoe >=4.6.3"]
end

task :test do
sh "rspec spec/jwt.rb"
sh "rspec spec/jwt_spec.rb"
end
32 changes: 18 additions & 14 deletions lib/jwt.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
#
# JSON Web Token implementation
#
#
# Should be up to date with the latest spec:
# http://self-issued.info/docs/draft-jones-json-web-token-06.html

Expand All @@ -10,7 +10,7 @@

module JWT
class DecodeError < Exception; end

def self.sign(algorithm, msg, key)
if ["HS256", "HS384", "HS512"].include?(algorithm)
sign_hmac(algorithm, msg, key)
Expand All @@ -32,16 +32,16 @@ def self.verify_rsa(algorithm, public_key, signing_input, signature)
def self.sign_hmac(algorithm, msg, key)
OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
end

def self.base64url_decode(str)
str += '=' * (4 - str.length.modulo(4))
Base64.decode64(str.gsub("-", "+").gsub("_", "/"))
end

def self.base64url_encode(str)
Base64.encode64(str).gsub("+", "-").gsub("/", "_").gsub("\n", "").gsub('=', '')
end
end

def self.encode(payload, key, algorithm='HS256', header_fields={})
algorithm ||= "none"
segments = []
Expand All @@ -57,7 +57,7 @@ def self.encode(payload, key, algorithm='HS256', header_fields={})
end
segments.join('.')
end

def self.decode(jwt, key=nil, verify=true, &keyfinder)
segments = jwt.split('.')
raise JWT::DecodeError.new("Not enough or too many segments") unless [2,3].include? segments.length
Expand All @@ -77,12 +77,16 @@ def self.decode(jwt, key=nil, verify=true, &keyfinder)
key = keyfinder.call(header)
end

if ["HS256", "HS384", "HS512"].include?(algo)
raise JWT::DecodeError.new("Signature verification failed") unless signature == sign_hmac(algo, signing_input, key)
elsif ["RS256", "RS384", "RS512"].include?(algo)
raise JWT::DecodeError.new("Signature verification failed") unless verify_rsa(algo, key, signing_input, signature)
else
raise JWT::DecodeError.new("Algorithm not supported")
begin
if ["HS256", "HS384", "HS512"].include?(algo)
raise JWT::DecodeError.new("Signature verification failed") unless signature == sign_hmac(algo, signing_input, key)
elsif ["RS256", "RS384", "RS512"].include?(algo)
raise JWT::DecodeError.new("Signature verification failed") unless verify_rsa(algo, key, signing_input, signature)
else
raise JWT::DecodeError.new("Algorithm not supported")
end
rescue OpenSSL::PKey::PKeyError
raise JWT::DecodeError.new("Signature verification failed")
end
end
payload
Expand Down
39 changes: 32 additions & 7 deletions spec/jwt_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@
end

it "encodes and decodes JWTs for RSA signatures" do
private_key = OpenSSL::PKey::RSA.generate(512)
private_key = OpenSSL::PKey::RSA.generate(512)
jwt = JWT.encode(@payload, private_key, "RS256")
decoded_payload = JWT.decode(jwt, private_key.public_key)
decoded_payload.should == @payload
end

it "encodes and decodes JWTs with custom header fields" do
private_key = OpenSSL::PKey::RSA.generate(512)
private_key = OpenSSL::PKey::RSA.generate(512)
jwt = JWT.encode(@payload, private_key, "RS256", {"kid" => 'default'})
decoded_payload = JWT.decode(jwt) do |header|
header["kid"].should == 'default'
private_key.public_key
end
decoded_payload.should == @payload
end

it "decodes valid JWTs" do
example_payload = {"hello" => "world"}
example_secret = 'secret'
Expand All @@ -50,23 +50,48 @@
jwt = JWT.encode(@payload, right_private_key, "RS256")
lambda { JWT.decode(jwt, bad_private_key.public_key) }.should raise_error(JWT::DecodeError)
end

it "allows decoding without key" do
right_secret = 'foo'
bad_secret = 'bar'
jwt = JWT.encode(@payload, right_secret)
decoded_payload = JWT.decode(jwt, bad_secret, false)
decoded_payload.should == @payload
end

it "raises exception on unsupported crypto algorithm" do
lambda { JWT.encode(@payload, "secret", 'HS1024') }.should raise_error(NotImplementedError)
end

it "encodes and decodes plaintext JWTs" do
jwt = JWT.encode(@payload, nil, nil)
jwt.split('.').length.should == 2
decoded_payload = JWT.decode(jwt, nil, nil)
decoded_payload.should == @payload
end

it "raise exception on invalid signature" do
pubkey = OpenSSL::PKey::RSA.new(<<-PUBKEY)
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCaY7425h964bjaoLeUm
SlZ8sK7VtVk9zHbGmZh2ygGYwfuUf2bmMye2Ofv99yDE/rd4loVIAcu7RVvDRgHq
3/CZTnIrSvHsiJQsHBNa3d+F1ihPfzURzf1M5k7CFReBj2SBXhDXd57oRfBQj12w
CVhhwP6kGTAWuoppbIIIBfNF2lE/Nvm7lVVYQqL9xOrP/AQ4xRbpQlB8Ll9sO9Or
SvbWhCDa/LMOWxHdmrcJi6XoSg1vnOyCoKbyAoauTt/XqdkHbkDdQ6HFbJieu9il
LDZZNliPhfENuKeC2MCGVXTEu8Cqhy1w6e4axavLlXoYf4laJIZ/e7au8SqDbY0B
xwIDAQAB
-----END PUBLIC KEY-----
PUBKEY
jwt = (
'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY' +
'XVkIjoiMTA2MDM1Nzg5MTY4OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSI' +
'sImNpZCI6IjEwNjAzNTc4OTE2ODguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb' +
'20iLCJpZCI6IjExNjQ1MjgyNDMwOTg1Njc4MjE2MyIsInRva2VuX2hhc2giOiJ' +
'0Z2hEOUo4bjhWME4ydmN3NmVNaWpnIiwiaWF0IjoxMzIwNjcwOTc4LCJleHAiO' +
'jEzMjA2NzQ4Nzh9.D8x_wirkxDElqKdJBcsIws3Ogesk38okz6MN7zqC7nEAA7' +
'wcy1PxsROY1fmBvXSer0IQesAqOW-rPOCNReSn-eY8d53ph1x2HAF-AzEi3GOl' +
'6hFycH8wj7Su6JqqyEbIVLxE7q7DkAZGaMPkxbTHs1EhSd5_oaKQ6O4xO3ZnnT4'
)
lambda { JWT.decode(jwt, pubkey, true) }.should raise_error(JWT::DecodeError)
end
end

0 comments on commit 6915be7

Please sign in to comment.