Skip to content

Commit

Permalink
feat(jwt) add support for RS512 JWT tokens
Browse files Browse the repository at this point in the history
* add a new value to the enum for `config.algorithm`: `RS512`
* add support for RS512 signing and verification in the `jwt_parser.lua`
module
* appropriate unit and integration tests
* add a new RS512 key pair to the tests fixtures

From #2666
Implements #2473

Signed-off-by: Thibault Charbonnier <thibaultcha@me.com>
  • Loading branch information
sarraz1 authored and thibaultcha committed Jul 10, 2017
1 parent 786f171 commit 4e2e1d7
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 2 deletions.
8 changes: 7 additions & 1 deletion kong/plugins/jwt/daos.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ local SCHEMA = {
key = {type = "string", unique = true, default = utils.random_string},
secret = {type = "string", unique = true, default = utils.random_string},
rsa_public_key = {type = "string"},
algorithm = {type = "string", enum = {"HS256", "RS256", "ES256"}, default = 'HS256'}
algorithm = {type = "string", enum = {"HS256", "RS256", "RS512", "ES256"}, default = 'HS256'}
},
self_check = function(schema, plugin_t, dao, is_update)
if plugin_t.algorithm == "RS256" and plugin_t.rsa_public_key == nil then
Expand All @@ -21,6 +21,12 @@ local SCHEMA = {
if plugin_t.algorithm == "RS256" and crypto.pkey.from_pem(plugin_t.rsa_public_key) == nil then
return false, Errors.schema "'rsa_public_key' format is invalid"
end
if plugin_t.algorithm == "RS512" and plugin_t.rsa_public_key == nil then
return false, Errors.schema "no mandatory 'rsa_public_key'"
end
if plugin_t.algorithm == "RS512" and crypto.pkey.from_pem(plugin_t.rsa_public_key) == nil then
return false, Errors.schema "'rsa_public_key' format is invalid"
end
return true
end,
marshall_event = function(self, t)
Expand Down
5 changes: 5 additions & 0 deletions kong/plugins/jwt/jwt_parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ local alg_sign = {
--["HS384"] = function(data, key) return crypto.hmac.digest("sha384", data, key, true) end,
--["HS512"] = function(data, key) return crypto.hmac.digest("sha512", data, key, true) end
["RS256"] = function(data, key) return crypto.sign('sha256', data, crypto.pkey.from_pem(key, true)) end,
["RS512"] = function(data, key) return crypto.sign('sha512', data, crypto.pkey.from_pem(key, true)) end,
["ES256"] = function(data, key)
local pkeyPrivate = crypto.pkey.from_pem(key, true)
local signature = crypto.sign('sha256', data, pkeyPrivate)
Expand All @@ -49,6 +50,10 @@ local alg_verify = {
local pkey = assert(crypto.pkey.from_pem(key), "Consumer Public Key is Invalid")
return crypto.verify('sha256', data, signature, pkey)
end,
["RS512"] = function(data, signature, key)
local pkey = assert(crypto.pkey.from_pem(key), "Consumer Public Key is Invalid")
return crypto.verify('sha512', data, signature, pkey)
end,
["ES256"] = function(data, signature, key)
local pkey = assert(crypto.pkey.from_pem(key), "Consumer Public Key is Invalid")
assert(#signature == 64, "Signature must be 64 bytes.")
Expand Down
23 changes: 23 additions & 0 deletions spec/03-plugins/17-jwt/01-jwt_parser_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ describe("Plugin: jwt (parser)", function()
[[tcFKN-Pi7_rWzBtQwP2u4CrFD4ZJbn2sxobzSlFb9fn4nRh_-rPPjDSeHVKwrpsYp]] ..
[[FSLBJxwX-KhbeGUfalg2eu9tHLDPHC4gTCpoQKxxRIwfMjW5zlHOZhohKZV2ZtpcgA]] , token)
end)
it("should encode using RS512", function()
local token = jwt_parser.encode({
sub = "1234567890",
name = "John Doe",
admin = true,
}, fixtures.rs512_private_key, "RS512")

assert.equal([[eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1]] ..
[[ZSwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.]] ..
[[VhoFYud-lrxtkbkfMl0Wkr4fERsDNjGfvHc2hFEecjLqSJ65_cydJ]] ..
[[iU011QqAmlMM8oIRCnoGKvA63XeE7M6qPsNkJ_vHMoqO-Hg3ajx1R]] ..
[[aWmVaHyeTCkkyStNvh88phVSH5EB5wIYjukHErRXLCTL9UhE0Z60f]] ..
[[NzLeEZ5yJZS-rhOK3fa0QSVoTD2QKVITYBcX_xt6NzHzTTx_3kQ1K]] ..
[[lcuueNlOLmCYx_6tissUvMY91KjcZfs3z9tYREu5paFx0pSiPvgNB]] ..
[[vrWQfbm3irr-1YcBH7wJuIinPDrERVohK1v37t8fDnSqhi1tWUati]] ..
[[7Mynkb3JrpCeF3IyReSvkhQA]], token)
end)

it("should encode using ES256", function()
local token = jwt_parser.encode({
Expand Down Expand Up @@ -90,6 +107,12 @@ describe("Plugin: jwt (parser)", function()
assert.True(jwt:verify_signature(fixtures.rs256_public_key))
assert.False(jwt:verify_signature(fixtures.rs256_public_key:gsub('QAB', 'zzz')))
end)
it("using RS512", function()
local token = jwt_parser.encode({sub = "foo"}, fixtures.rs512_private_key, 'RS512')
local jwt = assert(jwt_parser:new(token))
assert.True(jwt:verify_signature(fixtures.rs512_public_key))
assert.False(jwt:verify_signature(fixtures.rs512_public_key:gsub('AE=', 'zzz')))
end)
it("using ES256", function()
for _ = 1, 500 do
local token = jwt_parser.encode({sub = "foo"}, fixtures.es256_private_key, 'ES256')
Expand Down
43 changes: 42 additions & 1 deletion spec/03-plugins/17-jwt/03-access_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ local PAYLOAD = {
}

describe("Plugin: jwt (access)", function()
local jwt_secret, base64_jwt_secret, rsa_jwt_secret_1, rsa_jwt_secret_2
local jwt_secret, base64_jwt_secret, rsa_jwt_secret_1, rsa_jwt_secret_2, rsa_jwt_secret_3
local proxy_client, admin_client

setup(function()
Expand All @@ -28,6 +28,7 @@ describe("Plugin: jwt (access)", function()
local consumer2 = assert(helpers.dao.consumers:insert {username = "jwt_tests_base64_consumer"})
local consumer3 = assert(helpers.dao.consumers:insert {username = "jwt_tests_rsa_consumer_1"})
local consumer4 = assert(helpers.dao.consumers:insert {username = "jwt_tests_rsa_consumer_2"})
local consumer5 = assert(helpers.dao.consumers:insert {username = "jwt_tests_rsa_consumer_5"})
local anonymous_user = assert(helpers.dao.consumers:insert {username = "no-body"})

assert(helpers.dao.plugins:insert {name = "jwt", config = {}, api_id = api1.id})
Expand All @@ -50,6 +51,11 @@ describe("Plugin: jwt (access)", function()
algorithm = "RS256",
rsa_public_key = fixtures.rs256_public_key
})
rsa_jwt_secret_3 = assert(helpers.dao.jwt_secrets:insert {
consumer_id = consumer5.id,
algorithm = "RS512",
rsa_public_key = fixtures.rs512_public_key
})

assert(helpers.start_kong())
proxy_client = helpers.proxy_client()
Expand Down Expand Up @@ -263,6 +269,41 @@ describe("Plugin: jwt (access)", function()
end)
end)

describe("RS512", function()
it("verifies JWT", function()
PAYLOAD.iss = rsa_jwt_secret_3.key
local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs512_private_key, "RS512")
local authorization = "Bearer " .. jwt
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
["Authorization"] = authorization,
["Host"] = "jwt.com",
}
})
local body = cjson.decode(assert.res_status(200, res))
assert.equal(authorization, body.headers.authorization)
assert.equal("jwt_tests_rsa_consumer_5", body.headers["x-consumer-username"])
end)
it("identifies Consumer", function()
PAYLOAD.iss = rsa_jwt_secret_3.key
local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs512_private_key, "RS512")
local authorization = "Bearer " .. jwt
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
["Authorization"] = authorization,
["Host"] = "jwt.com",
}
})
local body = cjson.decode(assert.res_status(200, res))
assert.equal(authorization, body.headers.authorization)
assert.equal("jwt_tests_rsa_consumer_5", body.headers["x-consumer-username"])
end)
end)

describe("JWT private claims checks", function()
it("requires the checked fields to be in the claims", function()
local payload = {
Expand Down
40 changes: 40 additions & 0 deletions spec/03-plugins/17-jwt/fixtures.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,46 @@ FLiGOm5uTMEk8S4txs2efueg1XyymilCKzzuXlJvrvPA4u6HI7qNvuvkvUjQmwBH
gwIDAQAB
-----END PUBLIC KEY-----
]],
rs512_private_key = [[
-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQBoyqH30IH7YnHk2YLLygDwD0LUvrXRcWwVCsN1/2+DB+FLV8f+
/VoLegUowlcCea6vJCu9q9vnJqz2UhK7eN/kDYNhnx4WIdc3KjL+SXnp3KZozgn/
uCUeEYnMNRXLlx7GefG+C1yUgcFAaVJoyxx7dQellqWYrTW3nW9fMhioxSvuJUU8
u5v31GPzNeF69bfeKdI1NzhVLkztJhogEXdIgYitEcqepJQe1FpSBbVjdT5xbuMN
80pSnJHR11Qw2dPp6lDlao/hnvkYW77CZOVgK02oB0UEqjaasxPcaHiWerSP0yb7
nCdjR0kTgKA8um/gk0/F+FO3aOkrsZsgpK2vAgMBAAECggEAZVybjrmBAUgYIuTC
P50Fiy831dEizZSIl1Hx/xE1K+lTYy1lpqApmTBODT7uKtbIwWCbbrvt2YjvhNOe
ivhAmLb5flQLJh1Vr2aCLLWl1zA3RukFgvT78jnEsGIo0uU6P4F08/7JblyUMVmu
/O56fnCVFPbC9wuUCieestYiRBw3Z7TwcRmUx5JWJUuj4gzuFfRSyuzYeJoYUSJF
OLu5XtXaW4k0nj/LILC89qxQT/8HIIYa/7+S8TdbBfws6kQt5yiwUUOirzjOeuY9
RIvbmgapAVZhI2oxofu1r2XNLBBPHFDHlLeJasqRAa7vk3yVYtrcg20c5q3MZ1tx
Q+7NAQKBgQCqUxzBxK0h6d0GKe0rt30NSfNhFiKPR2AkQ8hXsjrCfJ36PbQqRyEt
zw13hgaHoS4yfj+19aQem1ZoTZVsUTvkD8CBBHZ/1PYjPoeOrjt6mDfp402z+f3E
FZTQq0dNFGsSGHn1yRUqXebM3SbbyDIDUqnHXMC1Dzsm2vSu6EJSIwKBgQCdgMAM
bQge8cQSevcARctOjqVpwirfsfqLebP+SBmzyNrs9Z5u8l/0EooojMRaGVClNzvb
yYCW6DWzac0jCKRuD9Svd41gGC3R5PztGGOyvLLkk33ad9NChwCz9np66THmQn6n
B+K/XrjDwUVHnItcRiARyXP3vn3uryv1hvRRBQKBgB7MtreHZDNswc4aiMvN+2wK
wlr9ELTOGGGWbEUHcr62oC6fN9QpVqOc/HdvogCmsd7pm4XA7LOoLWDhHrMeoXDl
NE9gSjllfjjzVroDYbgSjJHby7JO84egy29MebFDjvUPvgYnHY+yuUi0eRFnSzv0
l8T4TdSv82dcUsDKOSv3AoGAQmlxkUvAKtwiovA6imDjkyJO2UNINL6lOH5+yO+5
9rbwqQ4AWiPVFeNjYinI+XzHJoMduFVE5VzQl/A60VTpkIcYVUyBzk0jtOdrRsYL
8+fhPsR6Qs5XxCuMvlVl28HMipzrLp8Cm1LjcZdjEQkPMj9XcmiRf5tRGn2+eW8I
QckCgYAYYzr4nHmarWduk/1Fgm2qmFE96U/TjIRmk9vspwk5y47oM7LrnzU2Iyio
vaL3rwMZ0AcBcEOUvANkMCDAxgJZljeDr4IzUMQs95+m7Wb6BQTs4vKSLPGWYdjd
y1FoR04hSreMjG+K+mtQLGJC4USI1AJx1wKihgoxGrI1/7YiwQ==
-----END RSA PRIVATE KEY-----
]],
rs512_public_key = [[
-----BEGIN PUBLIC KEY-----
MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBoyqH30IH7YnHk2YLLygDw
D0LUvrXRcWwVCsN1/2+DB+FLV8f+/VoLegUowlcCea6vJCu9q9vnJqz2UhK7eN/k
DYNhnx4WIdc3KjL+SXnp3KZozgn/uCUeEYnMNRXLlx7GefG+C1yUgcFAaVJoyxx7
dQellqWYrTW3nW9fMhioxSvuJUU8u5v31GPzNeF69bfeKdI1NzhVLkztJhogEXdI
gYitEcqepJQe1FpSBbVjdT5xbuMN80pSnJHR11Qw2dPp6lDlao/hnvkYW77CZOVg
K02oB0UEqjaasxPcaHiWerSP0yb7nCdjR0kTgKA8um/gk0/F+FO3aOkrsZsgpK2v
AgMBAAE=
-----END PUBLIC KEY-----
]],
es256_private_key = [[
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgD8enltAi05AIoF2A
Expand Down

0 comments on commit 4e2e1d7

Please sign in to comment.