-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
feat(hmac-auth): add support for RSA signatures #11133
base: master
Are you sure you want to change the base?
Changes from all commits
d5bedea
b5db139
49ce30a
d309fff
79f8e15
ea5c7bd
54edb06
f52df68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
message: "hmac-auth: Added support for RSA signatures" | ||
type: feature | ||
scope: Plugin | ||
prs: | ||
- 11133 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,9 @@ return { | |
[3005000000] = { | ||
cors = { | ||
"private_network", | ||
}, | ||
hmac_auth = { | ||
"public_key", | ||
} | ||
} | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,6 +1,7 @@ | ||||||
local constants = require "kong.constants" | ||||||
local sha256 = require "resty.sha256" | ||||||
local openssl_hmac = require "resty.openssl.hmac" | ||||||
local openssl_pkey = require "resty.openssl.pkey" | ||||||
local utils = require "kong.tools.utils" | ||||||
|
||||||
|
||||||
|
@@ -31,18 +32,45 @@ local SIGNATURE_NOT_VALID = "HMAC signature cannot be verified" | |||||
local SIGNATURE_NOT_SAME = "HMAC signature does not match" | ||||||
|
||||||
|
||||||
local function verify_rsa(public_key, signature, signing_string, md_alg) | ||||||
local pub, err = openssl_pkey.new(public_key) | ||||||
if not pub then | ||||||
kong.log.err("failed to create public key : ", err) | ||||||
return false | ||||||
end | ||||||
|
||||||
local verified, err = pub:verify(signature, signing_string, md_alg) | ||||||
if not err then | ||||||
return verified | ||||||
else | ||||||
kong.log.err("failed to verify signature : ", err) | ||||||
return false | ||||||
end | ||||||
end | ||||||
|
||||||
|
||||||
local rsa = { | ||||||
["rsa-sha256"] = function(public_key, signature, signing_string) | ||||||
return verify_rsa(public_key, signature, signing_string, "sha256") | ||||||
end, | ||||||
["rsa-sha512"] = function(public_key, signature, signing_string) | ||||||
return verify_rsa(public_key, signature, signing_string, "sha512") | ||||||
end, | ||||||
} | ||||||
|
||||||
|
||||||
local hmac = { | ||||||
["hmac-sha1"] = function(secret, data) | ||||||
return hmac_sha1(secret, data) | ||||||
["hmac-sha1"] = function(secret, signature, signing_string) | ||||||
return signature == hmac_sha1(secret, signing_string) | ||||||
end, | ||||||
["hmac-sha256"] = function(secret, data) | ||||||
return openssl_hmac.new(secret, "sha256"):final(data) | ||||||
["hmac-sha256"] = function(secret, signature, signing_string) | ||||||
return signature == openssl_hmac.new(secret, "sha256"):final(signing_string) | ||||||
end, | ||||||
["hmac-sha384"] = function(secret, data) | ||||||
return openssl_hmac.new(secret, "sha384"):final(data) | ||||||
["hmac-sha384"] = function(secret, signature, signing_string) | ||||||
return signature == openssl_hmac.new(secret, "sha384"):final(signing_string) | ||||||
end, | ||||||
["hmac-sha512"] = function(secret, data) | ||||||
return openssl_hmac.new(secret, "sha512"):final(data) | ||||||
["hmac-sha512"] = function(secret, signature, signing_string) | ||||||
return signature == openssl_hmac.new(secret, "sha512"):final(signing_string) | ||||||
end, | ||||||
} | ||||||
|
||||||
|
@@ -97,7 +125,7 @@ local function retrieve_hmac_fields(authorization_header) | |||||
-- parse the header to retrieve hamc parameters | ||||||
if authorization_header then | ||||||
local iterator, iter_err = re_gmatch(authorization_header, | ||||||
"\\s*[Hh]mac\\s*username=\"(.+)\"," .. | ||||||
"(\\s*[Hh]mac)?\\s*username=\"(.+)\"," .. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to specify |
||||||
"\\s*algorithm=\"(.+)\",\\s*header" .. | ||||||
"s=\"(.+)\",\\s*signature=\"(.+)\"", | ||||||
"jo") | ||||||
|
@@ -113,10 +141,10 @@ local function retrieve_hmac_fields(authorization_header) | |||||
end | ||||||
|
||||||
if m and #m >= 4 then | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we increment the check in the if as well?
Suggested change
|
||||||
hmac_params.username = m[1] | ||||||
hmac_params.algorithm = m[2] | ||||||
hmac_params.hmac_headers = utils.split(m[3], " ") | ||||||
hmac_params.signature = m[4] | ||||||
hmac_params.username = m[2] | ||||||
hmac_params.algorithm = m[3] | ||||||
hmac_params.hmac_headers = utils.split(m[4], " ") | ||||||
hmac_params.signature = m[5] | ||||||
end | ||||||
end | ||||||
|
||||||
|
@@ -126,7 +154,7 @@ end | |||||
|
||||||
-- plugin assumes the request parameters being used for creating | ||||||
-- signature by client are not changed by core or any other plugin | ||||||
local function create_hash(request_uri, hmac_params) | ||||||
local function generate_signing_string(request_uri, hmac_params) | ||||||
local signing_string = "" | ||||||
local hmac_headers = hmac_params.hmac_headers | ||||||
|
||||||
|
@@ -161,14 +189,18 @@ local function create_hash(request_uri, hmac_params) | |||||
end | ||||||
end | ||||||
|
||||||
return hmac[hmac_params.algorithm](hmac_params.secret, signing_string) | ||||||
return signing_string | ||||||
end | ||||||
|
||||||
|
||||||
local function validate_signature(hmac_params) | ||||||
local signature_1 = create_hash(kong_request.get_path_with_query(), hmac_params) | ||||||
local signature_2 = decode_base64(hmac_params.signature) | ||||||
return signature_1 == signature_2 | ||||||
local signature = decode_base64(hmac_params.signature) | ||||||
local signing_string = generate_signing_string(kong_request.get_path_with_query(), hmac_params) | ||||||
if hmac_params.algorithm:sub(1, 4) == "rsa-" then | ||||||
return rsa[hmac_params.algorithm](hmac_params.public_key, signature, signing_string) | ||||||
else | ||||||
return hmac[hmac_params.algorithm](hmac_params.secret, signature, signing_string) | ||||||
end | ||||||
end | ||||||
|
||||||
|
||||||
|
@@ -324,6 +356,7 @@ local function do_authentication(conf) | |||||
end | ||||||
|
||||||
hmac_params.secret = credential.secret | ||||||
hmac_params.public_key = credential.public_key | ||||||
|
||||||
if not validate_signature(hmac_params) then | ||||||
return false, { status = 401, message = SIGNATURE_NOT_SAME } | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -17,6 +17,7 @@ return { | |||||||||||||||
{ consumer = { type = "foreign", reference = "consumers", required = true, on_delete = "cascade", }, }, | ||||||||||||||||
{ username = { type = "string", required = true, unique = true }, }, | ||||||||||||||||
{ secret = { type = "string", auto = true }, }, | ||||||||||||||||
{ public_key = { type = "string" }, }, | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about
Suggested change
An additional validator to validate it's a RSA key (not EC or ECX keys) would be good. |
||||||||||||||||
{ tags = typedefs.tags }, | ||||||||||||||||
}, | ||||||||||||||||
}, | ||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
return { | ||
postgres = { | ||
up = [[ | ||
DO $$ | ||
BEGIN | ||
ALTER TABLE IF EXISTS ONLY hmacauth_credentials ADD public_key TEXT; | ||
EXCEPTION WHEN DUPLICATE_COLUMN THEN | ||
-- Do nothing, accept existing state | ||
END$$; | ||
|
||
]], | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ return { | |
"000_base_hmac_auth", | ||
"002_130_to_140", | ||
"003_200_to_210", | ||
"004_330_to_340", | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,8 @@ local ALGORITHMS = { | |
"hmac-sha256", | ||
"hmac-sha384", | ||
"hmac-sha512", | ||
"rsa-sha256", | ||
"rsa-sha512", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add these new options in the description of the |
||
} | ||
|
||
|
||
|
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.
error message needs to be changed accordingly
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.
The specific string format for the "Authorization" header that includes the method (e.g., "Hmac" or "Rsa") and the various components (e.g., "username," "algorithm," "headers," and "signature") may not be standardized in the HTTP Signatures draft (draft-cavage-http-signatures). The draft defines the concept of HTTP signatures and how they can be generated and verified, but it doesn't prescribe a specific format for the "Authorization" header.
The format you are using, which includes "Hmac" or "Rsa" as the method prefix, is a common convention used in some implementations for clarity and to distinguish between different authentication methods (HMAC and RSA, in this case). However, this specific format might not be explicitly detailed in the draft itself.
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.
agree. need to output the right error message