You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/08/09 01:43:17 UTC
[apisix] branch master updated: feat(jwt-auth): support ES256 digital algorithm (#7627)
This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 535812d81 feat(jwt-auth): support ES256 digital algorithm (#7627)
535812d81 is described below
commit 535812d81f973f08d8c51843835fc1637e5262cd
Author: HaiYan <ha...@hotmail.com>
AuthorDate: Tue Aug 9 09:43:11 2022 +0800
feat(jwt-auth): support ES256 digital algorithm (#7627)
Co-authored-by: qihaiyan <qi...@hisense.com>
---
apisix/plugins/jwt-auth.lua | 24 +++++-----
docs/en/latest/plugins/jwt-auth.md | 8 ++--
docs/zh/latest/plugins/jwt-auth.md | 8 ++--
t/plugin/jwt-auth.t | 95 +++++++++++++++++++++++++++++++++++++-
4 files changed, 114 insertions(+), 21 deletions(-)
diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua
index 3cd601778..36006975f 100644
--- a/apisix/plugins/jwt-auth.lua
+++ b/apisix/plugins/jwt-auth.lua
@@ -60,7 +60,7 @@ local consumer_schema = {
secret = {type = "string"},
algorithm = {
type = "string",
- enum = {"HS256", "HS512", "RS256"},
+ enum = {"HS256", "HS512", "RS256", "ES256"},
default = "HS256"
},
exp = {type = "integer", minimum = 1, default = 86400},
@@ -94,7 +94,7 @@ local consumer_schema = {
public_key = {type = "string"},
private_key= {type = "string"},
algorithm = {
- enum = {"RS256"},
+ enum = {"RS256", "ES256"},
},
},
required = {"public_key", "private_key"},
@@ -106,7 +106,7 @@ local consumer_schema = {
properties = {}
},
algorithm = {
- enum = {"RS256"},
+ enum = {"RS256", "ES256"},
},
},
required = {"vault"},
@@ -166,7 +166,7 @@ function _M.check_schema(conf, schema_type)
return true
end
- if conf.algorithm ~= "RS256" and not conf.secret then
+ if conf.algorithm ~= "RS256" and conf.algorithm ~= "ES256" and not conf.secret then
conf.secret = ngx_encode_base64(resty_random.bytes(32, true))
elseif conf.base64_secret then
if ngx_decode_base64(conf.secret) == nil then
@@ -174,7 +174,7 @@ function _M.check_schema(conf, schema_type)
end
end
- if conf.algorithm == "RS256" then
+ if conf.algorithm == "RS256" or conf.algorithm == "ES256" then
-- Possible options are a) both are in vault, b) both in schema
-- c) one in schema, another in vault.
if not conf.public_key then
@@ -240,7 +240,7 @@ local function get_secret(conf, consumer_name)
end
-local function get_rsa_keypair(conf, consumer_name)
+local function get_rsa_or_ecdsa_keypair(conf, consumer_name)
local public_key = conf.public_key
local private_key = conf.private_key
-- if keys are present in conf, no need to query vault (fallback)
@@ -309,8 +309,10 @@ local function sign_jwt_with_HS(key, consumer, payload)
end
-local function sign_jwt_with_RS256(key, consumer, payload)
- local public_key, private_key, err = get_rsa_keypair(consumer.auth_conf, consumer.username)
+local function sign_jwt_with_RS256_ES256(key, consumer, payload)
+ local public_key, private_key, err = get_rsa_or_ecdsa_keypair(
+ consumer.auth_conf, consumer.username
+ )
if not public_key then
core.log.error("failed to sign jwt, err: ", err)
core.response.exit(503, "failed to sign jwt")
@@ -345,12 +347,12 @@ local function algorithm_handler(consumer, method_only)
end
return get_secret(consumer.auth_conf, consumer.username)
- elseif consumer.auth_conf.algorithm == "RS256" then
+ elseif consumer.auth_conf.algorithm == "RS256" or consumer.auth_conf.algorithm == "ES256" then
if method_only then
- return sign_jwt_with_RS256
+ return sign_jwt_with_RS256_ES256
end
- local public_key, _, err = get_rsa_keypair(consumer.auth_conf, consumer.username)
+ local public_key, _, err = get_rsa_or_ecdsa_keypair(consumer.auth_conf, consumer.username)
return public_key, err
end
end
diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md
index 2019d1a6b..353ddd3a2 100644
--- a/docs/en/latest/plugins/jwt-auth.md
+++ b/docs/en/latest/plugins/jwt-auth.md
@@ -43,12 +43,12 @@ For Consumer:
|---------------|---------|-------------------------------------------------------|---------|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| key | string | True | | | Unique key for a Consumer. |
| secret | string | False | | | The encryption key. If unspecified, auto generated in the background. |
-| public_key | string | True if `RS256` is set for the `algorithm` attribute. | | | RSA public key. |
-| private_key | string | True if `RS256` is set for the `algorithm` attribute. | | | RSA private key. |
-| algorithm | string | False | "HS256" | ["HS256", "HS512", "RS256"] | Encryption algorithm. |
+| public_key | string | True if `RS256` or `ES256` is set for the `algorithm` attribute. | | | RSA or ECDSA public key. |
+| private_key | string | True if `RS256` or `ES256` is set for the `algorithm` attribute. | | | RSA or ECDSA private key. |
+| algorithm | string | False | "HS256" | ["HS256", "HS512", "RS256", "ES256"] | Encryption algorithm. |
| exp | integer | False | 86400 | [1,...] | Expiry time of the token in seconds. |
| base64_secret | boolean | False | false | | Set to true if the secret is base64 encoded. |
-| vault | object | False | | | Set to true to use Vault for storing and retrieving secret (secret for HS256/HS512 or public_key and private_key for RS256). By default, the Vault path is `kv/apisix/consumer/<consumer_name>/jwt-auth`. |
+| vault | object | False | | | Set to true to use Vault for storing and retrieving secret (secret for HS256/HS512 or public_key and private_key for RS256/ES256). By default, the Vault path is `kv/apisix/consumer/<consumer_name>/jwt-auth`. |
| lifetime_grace_period | integer | False | 0 | [0,...] | Define the leeway in seconds to account for clock skew between the server that generated the jwt and the server validating it. Value should be zero (0) or a positive integer. |
:::info IMPORTANT
diff --git a/docs/zh/latest/plugins/jwt-auth.md b/docs/zh/latest/plugins/jwt-auth.md
index b8828ed90..a6ea26a82 100644
--- a/docs/zh/latest/plugins/jwt-auth.md
+++ b/docs/zh/latest/plugins/jwt-auth.md
@@ -43,12 +43,12 @@ Consumer 端:
| ------------- | ------- | ----- | ------- | --------------------------- | ------------------------------------------------------------------------------------------------------------ |
| key | string | 是 | | | Consumer 的 `access_key` 必须是唯一的。如果不同 Consumer 使用了相同的 `access_key` ,将会出现请求匹配异常。 |
| secret | string | 否 | | | 加密秘钥。如果未指定,后台将会自动生成。 |
-| public_key | string | 否 | | | RSA 公钥, `algorithm` 属性选择 `RS256` 算法时必选。 |
-| private_key | string | 否 | | | RSA 私钥, `algorithm` 属性选择 `RS256` 算法时必选。 |
-| algorithm | string | 否 | "HS256" | ["HS256", "HS512", "RS256"] | 加密算法。 |
+| public_key | string | 否 | | | RSA 或 ECDSA 公钥, `algorithm` 属性选择 `RS256` 或 `ES256` 算法时必选。 |
+| private_key | string | 否 | | | RSA 或 ECDSA 私钥, `algorithm` 属性选择 `RS256` 或 `ES256` 算法时必选。 |
+| algorithm | string | 否 | "HS256" | ["HS256", "HS512", "RS256", "ES256"] | 加密算法。 |
| exp | integer | 否 | 86400 | [1,...] | token 的超时时间。 |
| base64_secret | boolean | 否 | false | | 当设置为 `true` 时,密钥为 base64 编码。 |
-| vault | object | 否 | | | 是否使用 Vault 作为存储和检索密钥(HS256/HS512 的密钥或 RS256 的公钥和私钥)的方式。该插件默认使用 `kv/apisix/consumer/<consumer name>/jwt-auth` 路径进行密钥检索。 |
+| vault | object | 否 | | | 是否使用 Vault 作为存储和检索密钥(HS256/HS512 的密钥或 RS256/ES256 的公钥和私钥)的方式。该插件默认使用 `kv/apisix/consumer/<consumer name>/jwt-auth` 路径进行密钥检索。 |
| lifetime_grace_period | integer | 否 | 0 | [0,...] | 定义生成 JWT 的服务器和验证 JWT 的服务器之间的时钟偏移。该值应该是零(0)或一个正整数。 |
:::info IMPORTANT
diff --git a/t/plugin/jwt-auth.t b/t/plugin/jwt-auth.t
index bfee5cf0d..cfea01313 100644
--- a/t/plugin/jwt-auth.t
+++ b/t/plugin/jwt-auth.t
@@ -498,7 +498,7 @@ property "key" is required
local code, body, raw = t('/apisix/admin/schema/plugins/jwt-auth?schema_type=consumer',
ngx.HTTP_GET,
[[
-{"dependencies":{"algorithm":{"oneOf":[{"properties":{"algorithm":{"default":"HS256","enum":["HS256","HS512"]}}},{"required":["public_key","private_key"],"properties":{"algorithm":{"enum":["RS256"]},"public_key":{"type":"string"},"private_key":{"type":"string"}}}]}},"required":["key"],"type":"object","properties":{"base64_secret":{"default":false,"type":"boolean"},"secret":{"type":"string"},"algorithm":{"enum":["HS256","HS512","RS256"],"default":"HS256","type":"string"},"exp":{"minimum": [...]
+{"dependencies":{"algorithm":{"oneOf":[{"properties":{"algorithm":{"default":"HS256","enum":["HS256","HS512"]}}},{"required":["public_key","private_key"],"properties":{"algorithm":{"enum":["RS256","ES256"]},"public_key":{"type":"string"},"private_key":{"type":"string"}}}]}},"required":["key"],"type":"object","properties":{"base64_secret":{"default":false,"type":"boolean"},"secret":{"type":"string"},"algorithm":{"enum":["HS256","HS512","RS256","ES256"],"default":"HS256","type":"string"}," [...]
]]
)
@@ -1083,7 +1083,7 @@ hello world
content_by_lua_block {
local plugin = require("apisix.plugins.jwt-auth")
local core = require("apisix.core")
- local conf = {key = "123", algorithm = "ES256"}
+ local conf = {key = "123", algorithm = "ES512"}
local ok, err = plugin.check_schema(conf, core.schema.TYPE_CONSUMER)
if not ok then
@@ -1235,3 +1235,94 @@ qr/failed to validate dependent schema for \\"algorithm\\"/
--- error_code: 400
--- response_body_like eval
qr/failed to validate dependent schema for \\"algorithm\\"/
+
+
+
+=== TEST 52: add consumer with username and plugins with public_key, private_key(ES256)
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/consumers',
+ ngx.HTTP_PUT,
+ [[{
+ "username": "kerouac",
+ "plugins": {
+ "jwt-auth": {
+ "key": "user-key-es256",
+ "algorithm": "ES256",
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9\nq9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2\nOF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r\n1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G\n-----END PRIVATE KEY-----"
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 53: JWT sign and verify use ES256 algorithm(private_key numbits = 512)
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {}
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 54: sign/verify use ES256 algorithm(private_key numbits = 512)
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, err, sign = t('/apisix/plugin/jwt/sign?key=user-key-es256',
+ ngx.HTTP_GET
+ )
+
+ if code > 200 then
+ ngx.status = code
+ ngx.say(err)
+ return
+ end
+
+ local code, _, res = t('/hello?jwt=' .. sign,
+ ngx.HTTP_GET
+ )
+
+ ngx.status = code
+ ngx.print(res)
+ }
+ }
+--- response_body
+hello world