Closed thbar closed 7 years ago
@thbar I'm going to ask @potatosalad to jump in. I personally use HS512 which is just a simple sha signing. This means that the JWT can be read but not tampered with. I'm hoping @potatosalad can provide more information on the more exotic key types.
A cryptographically secure randomly generated 16 byte (or 128 bit) symmetric oct
key for use with signature algorithm HS256
should be sufficient for many HTTP bearer authentication use cases.
## Generate a 128-bit oct JWK
JOSE.JWK.generate_key({:oct, 16}) |> JOSE.JWK.to_map |> elem(1)
# %{"k" => "5Fn8i7r5cRWZW_yyr9Flkg", "kty" => "oct"}
## Configure Guardian to use HS256 and our generated JWK
config :guardian, Guardian,
allowed_algos: ["HS256"],
secret_key: %{"k" => "5Fn8i7r5cRWZW_yyr9Flkg", "kty" => "oct"}
Anything larger than that (like using HS512
that @hassox mentioned with a 64 byte key), is also perfectly acceptable and could potentially change the time required for a brute force attack from decades to centuries.
If you want public-key cryptography, I provide my personal opinion at the very bottom of this comment, but it's much more difficult to recommend one as the "sane default".
There are really only two major types of keys to choose from for the signature operations: symmetric and asymmetric.
In simpler terms:
The main difference in behavior between the two is:
Relating the above to SSL/TLS, certificates are signed using an asymmetric secret key and distributed to clients with the embedded public key. This means (for example) your browser can verify that a certificate has been signed by Google. A symmetric key would be useless in this case because anyone who had the key would be able to sign a certificate as if they were Google.
The following algorithms use a symmetric key (or an oct
key type), which is really just a sequence of bytes.
HS256
- HMAC using SHA-256HS384
- HMAC using SHA-384HS512
- HMAC using SHA-512The number at the end of the signature algorithm specifies the output length of the signature in bits. The signature is always the same for the same given secret key and message.
The recommended key length is mentioned in RFC 2104:
The key for HMAC can be of any length (keys longer than B bytes are first hashed using H). However, less than L bytes is strongly discouraged as it would decrease the security strength of the function. Keys longer than L bytes are acceptable but the extra length would not significantly increase the function strength. (A longer key may be advisable if the randomness of the key is considered weak.)
So for HS256
the B
is 64 bytes and the L
is 32 bytes, which means any key between 32 and 64 bytes would be recommended. For HS384
, any key between 48 and 128 bytes. For HS512
, any key between 64 and 128 bytes.
That said, any key 16 bytes (or 128 bits) or greater should be sufficient for most general purpose use. A 128-bit key would currently take a very long time to brute force (many years or decades on current technology). The HMAC group is the only group of signature algorithms in jose that are likely quantum computer resistant.
There are three major types of asymmetric key types for signature algorithm use.
Note: EdDSA in jose is still in draft form and the exact specification may change.
For ECDSA, there are three algorithms which are tied to a specific curve for the EC
key type:
ES256
- ECDSA using P-256 and SHA-256ES384
- ECDSA using P-384 and SHA-384ES512
- ECDSA using P-521 and SHA-512For EdDSA, there are two algorithms which are tied to a specific curve for the OKP
key type:
Ed25519
- EdDSA using Ed25519Ed448
- EdDSA using Ed448For RSA, there are six algorithms which use different padding schemes (PKCS#1.5 versus PSS) for the RSA
key type:
RS256
- RSASSA-PKCS1-v1_5 using SHA-256RS384
- RSASSA-PKCS1-v1_5 using SHA-384RS512
- RSASSA-PKCS1-v1_5 using SHA-512PS256
- RSASSA-PSS using SHA-256 and MGF1 with SHA-256PS384
- RSASSA-PSS using SHA-384 and MGF1 with SHA-384PS512
- RSASSA-PSS using SHA-512 and MGF1 with SHA-512As of today (2016-05-19), my recommendation for asymmetric cryptography would be ES512
.
P-256
and P-384
elliptic curves, but both curves were later found to have potential weaknesses and are no longer considered safe. NIST operates very closely with the NSA, which has come under increased scrutiny lately and the safety of their recommendations have come into question. The P-521
elliptic curve was developed independently and is still considered to be safe.My near future recommendations are Ed25519
and Ed448
. They are still in draft algorithms, but in my opinion, they are near perfect asymmetric key types for HTTP bearer authentication.
Some of the cryptography community is working on public-key cryptography algorithms which are more quantum computer resistant, such as XMSS and Hash-based signatures which may finally provide a "sane default" for asymmetric keys.
If you want to use RSA, I would recommend the PS256
, PS384
, and PS512
algorithms over the other RSA algorithms. The PKCS#1.5 padding scheme (RS256
, RS384
, and RS512
) does not provide a high enough level of security generally should be replaced by the PSS padding scheme.
#############
### ES512 ###
#############
## Generate a JWK for use with ES512
JOSE.JWS.generate_key(%{"alg" => "ES512"}) |> JOSE.JWK.to_map |> elem(1)
# %{"alg" => "ES512", "crv" => "P-521", "d" => "AVDuOBokicU-yXj4zNwrK-29vmUvmOBNxHB-7_PgRO6e3VKTl-wuUmPEQnHtG_GYoC0cUHAJtpGlaeGF1mIRpeSk", "kty" => "EC", "use" => "sig", "x" => "ANT4yNLVHCeYtQOpjbhuXnoB69C4VoWLESxxbnEKt8W8BTL_7kdqUcCMBxxQvPhrf3fmliosAxb1BcspPtV4aofP", "y" => "AAPlYHP2qGpubv_qzYbNvMOxJUOuypaVeEYV6NnEtWpW2jHupr5xMaINDXgpN1CDwddZxQ-WpE4jQEl8onXD9_su"}
## Configure Guardian to use ES512 and our generated JWK
config :guardian, Guardian,
allowed_algos: ["ES512"],
secret_key: %{
"alg" => "ES512",
"crv" => "P-521",
"d" => "AVDuOBokicU-yXj4zNwrK-29vmUvmOBNxHB-7_PgRO6e3VKTl-wuUmPEQnHtG_GYoC0cUHAJtpGlaeGF1mIRpeSk",
"kty" => "EC",
"use" => "sig",
"x" => "ANT4yNLVHCeYtQOpjbhuXnoB69C4VoWLESxxbnEKt8W8BTL_7kdqUcCMBxxQvPhrf3fmliosAxb1BcspPtV4aofP",
"y" => "AAPlYHP2qGpubv_qzYbNvMOxJUOuypaVeEYV6NnEtWpW2jHupr5xMaINDXgpN1CDwddZxQ-WpE4jQEl8onXD9_su"
}
#############
### Ed448 ###
#############
## Generate a JWK for use with Ed448
## Note: you will need to run JOSE.crypto_fallback(true) or have erlang-libdecaf installed
JOSE.JWS.generate_key(%{"alg" => "Ed448"}) |> JOSE.JWK.to_map |> elem(1)
# %{"alg" => "Ed448", "crv" => "Ed448", "d" => "ArlWJD8C8B4yyBhDDSSpjjJgC9ASFSiPACajGpJFd1wLdfWNQdwvUdHUN-ZnRBBGUfebZgvb-ZTe", "kty" => "OKP", "use" => "sig", "x" => "OabW8I7n2qidiuShu_LlW7ntrG99_Q7d4Vps4tnxBF-ROnRZ4IQn2diPdMrX8vi0xCtDhGeUq-6A"}
## Configure Guardian to use Ed448 and our generated JWK
config :guardian, Guardian,
allowed_algos: ["Ed448"],
secret_key: %{
"alg" => "Ed448",
"crv" => "Ed448",
"d" => "ArlWJD8C8B4yyBhDDSSpjjJgC9ASFSiPACajGpJFd1wLdfWNQdwvUdHUN-ZnRBBGUfebZgvb-ZTe",
"kty" => "OKP",
"use" => "sig",
"x" => "OabW8I7n2qidiuShu_LlW7ntrG99_Q7d4Vps4tnxBF-ROnRZ4IQn2diPdMrX8vi0xCtDhGeUq-6A"
}
#############
### PS256 ###
#############
## Generate a JWK for use with PS256
## Note: you will need to run JOSE.crypto_fallback(true) since RSA PSS is not natively supported as of OTP 18
JOSE.JWS.generate_key(%{"alg" => "PS256"}) |> JOSE.JWK.to_map |> elem(1)
# %{"alg" => "PS256", "d" => "jDoVruE_9ilASyMyBqk-9EXTjnoaJwL6ruK0iOaftJw0i7AQC841jFuAbNpRIIfKfNqq7GKYWC9KDriRS0hbicUUwUoN__PmiSxMD6pthCGyjSL-_Sa5BkuhHkUnP5jtcsK86UJrVkLWf96m-R0Ks-Bm0xCEVww5kZH_uPw-JiTJpxVuwF9dSbjFEbNz_kxn0efD1toGGJ-GJXVlkt-n84buUzUmEhvQwq9j34a9ZEG_mNQ3NZoq2jZ3OUF_z7BQeYawKuuSWxlJ3sRVuVU75uQ-QGyUR__K-EmIFSGFhp3n9iFe2u7pGXpihlR27BdVVj8HJCb8QqbfbvUYfcDDxQ", "dp" => "l9as4VlICMtCImS0oBqNKjpDI1-34OFqsI0UWfmGSE0e9FTz-sVpNt6Yd97Y2SKCbJZu7DPqnh3C3LrdoTqYTrUao5s5MxzasMT1kT6xpcYbQEq5FeXl_eXClec_19SToBsRUa5Cp35T3k4jpX3io8SQ0kLdNrPvCDw4X_kjuEU", "dq" => "SY_FtN75qEl1MMbfdzTBnoKokk6lc1gkGt9K3BhSrB83sFuujswVc4GaKdSmzHrncC0PbRpcnl8DhLDl9xxkECC0V1gkWyXOHnUqyNpqyFIZNHTOE-WPwrLJ1o26JoVA3fLY_YEirqOcIGP5OvjdCRE_vE9Wtf2OZ9DmYowPasU", "e" => "AQAB", "kty" => "RSA", "n" => "zzsAdivyQfcixV06tn4YfxJ9JDLHqNbkInWQBQ1POayRlfW2Kr4CT_UVKKk-HOc5TxMilBm6J8WAVP0YsFRdyIdBoagIO1qX1q8jUpqQXaaqk0T3do0nbWbBc-M843d-h3Brq-xUiejm99OoyQzX1ViweoPbB4Zoh7N8MV4YVvu6DDlofd0xrDpPlmmqAocDLvRRyv7wshqT08FKJoJj9MQNnzu-RHN9bHbDFRECvYEL_sxcO_dOHEfVW9U2TmaiAUa9B1p_QkzvWtQO8ttzLz_o7ujZMxfbnJzZi2aOg6_4Zftc_1HhxEqKI6GmsGFvUI4yuFN8NfQ6laUMAn2qJQ", "p" => "-7VhtutTjz69dMVC7CQW4FTWS7q1PQu-YLCT_xsHt7RPbJeuxTL2LA7CfIFbpzsDqMS4fUd-gR9phuUDQFQWSLCA1wXEszpAdD6r5fht9LssN4Uuy6ivOVuazLalA52zBj8vAOqyroGo-WUlYwT2BoxJqDDmLFNJDpc-ioGphe8", "q" => "0sN9Piqlfsrmh7v090AjUB0WmIMqICkXBYWgXvEOcTyif9jzeHmieiiT2-eQdA4ZxoZXXVY31SdkyiCqNET6B4eJjA8VR1xBrRVj4Bg3ASbDTVuCezHYHZMh-ITgVINt061wE_1n1aGeDMJ8ZUlOV2kk7YX0STJvN2-mrwWBhSs", "qi" => "LownZt59Rjp8vpH9rzkXLLvJchc46mBeGVSKmfcortkh4PTkpv0pFU83RsVuLjjWAIwTVncMqXebLraxPI4m-Eo4ysQGFq6fLHYKHJGdUOZc15O4iZHz7A11PixHoD_DPQvjD3_Nu71dTY18-FNKUwcYErgnPessP2JcE1SgEzA", "use" => "sig"}
## Configure Guardian to use PS256 and our generated JWK
config :guardian, Guardian,
allowed_algos: ["PS256"],
secret_key: %{
"alg" => "PS256",
"d" => "jDoVruE_9ilASyMyBqk-9EXTjnoaJwL6ruK0iOaftJw0i7AQC841jFuAbNpRIIfKfNqq7GKYWC9KDriRS0hbicUUwUoN__PmiSxMD6pthCGyjSL-_Sa5BkuhHkUnP5jtcsK86UJrVkLWf96m-R0Ks-Bm0xCEVww5kZH_uPw-JiTJpxVuwF9dSbjFEbNz_kxn0efD1toGGJ-GJXVlkt-n84buUzUmEhvQwq9j34a9ZEG_mNQ3NZoq2jZ3OUF_z7BQeYawKuuSWxlJ3sRVuVU75uQ-QGyUR__K-EmIFSGFhp3n9iFe2u7pGXpihlR27BdVVj8HJCb8QqbfbvUYfcDDxQ",
"dp" => "l9as4VlICMtCImS0oBqNKjpDI1-34OFqsI0UWfmGSE0e9FTz-sVpNt6Yd97Y2SKCbJZu7DPqnh3C3LrdoTqYTrUao5s5MxzasMT1kT6xpcYbQEq5FeXl_eXClec_19SToBsRUa5Cp35T3k4jpX3io8SQ0kLdNrPvCDw4X_kjuEU",
"dq" => "SY_FtN75qEl1MMbfdzTBnoKokk6lc1gkGt9K3BhSrB83sFuujswVc4GaKdSmzHrncC0PbRpcnl8DhLDl9xxkECC0V1gkWyXOHnUqyNpqyFIZNHTOE-WPwrLJ1o26JoVA3fLY_YEirqOcIGP5OvjdCRE_vE9Wtf2OZ9DmYowPasU",
"e" => "AQAB",
"kty" => "RSA",
"n" => "zzsAdivyQfcixV06tn4YfxJ9JDLHqNbkInWQBQ1POayRlfW2Kr4CT_UVKKk-HOc5TxMilBm6J8WAVP0YsFRdyIdBoagIO1qX1q8jUpqQXaaqk0T3do0nbWbBc-M843d-h3Brq-xUiejm99OoyQzX1ViweoPbB4Zoh7N8MV4YVvu6DDlofd0xrDpPlmmqAocDLvRRyv7wshqT08FKJoJj9MQNnzu-RHN9bHbDFRECvYEL_sxcO_dOHEfVW9U2TmaiAUa9B1p_QkzvWtQO8ttzLz_o7ujZMxfbnJzZi2aOg6_4Zftc_1HhxEqKI6GmsGFvUI4yuFN8NfQ6laUMAn2qJQ",
"p" => "-7VhtutTjz69dMVC7CQW4FTWS7q1PQu-YLCT_xsHt7RPbJeuxTL2LA7CfIFbpzsDqMS4fUd-gR9phuUDQFQWSLCA1wXEszpAdD6r5fht9LssN4Uuy6ivOVuazLalA52zBj8vAOqyroGo-WUlYwT2BoxJqDDmLFNJDpc-ioGphe8",
"q" => "0sN9Piqlfsrmh7v090AjUB0WmIMqICkXBYWgXvEOcTyif9jzeHmieiiT2-eQdA4ZxoZXXVY31SdkyiCqNET6B4eJjA8VR1xBrRVj4Bg3ASbDTVuCezHYHZMh-ITgVINt061wE_1n1aGeDMJ8ZUlOV2kk7YX0STJvN2-mrwWBhSs",
"qi" => "LownZt59Rjp8vpH9rzkXLLvJchc46mBeGVSKmfcortkh4PTkpv0pFU83RsVuLjjWAIwTVncMqXebLraxPI4m-Eo4ysQGFq6fLHYKHJGdUOZc15O4iZHz7A11PixHoD_DPQvjD3_Nu71dTY18-FNKUwcYErgnPessP2JcE1SgEzA",
"use" => "sig"
}
Thank you so much for the detailed answer!
Thanks for such a detailed response. Is there a sample app somewhere? I tried to set up an app using ES512 like in the example and am getting
** (FunctionClauseError) no function clause matching in :base64url.encode/1
src/base64url.erl:22: :base64url.encode(%{"alg" => "ES512", "crv" => "P-521", "d" => "mhEZkkY6eUJKEVl4Qnjfnik3GZmvPsiQEJC3-9f-jiM2OSHPTTYkOzPfjWbOqhb5JARUaJzXL9YyqHJBDxM_VB8", "kty" => "EC", "use" => "sig", "x" => "AALDyto5SKJ73a7uqLjKLwoCjUvn6Ldpcade5FCOt1zsl9hJGIHVGlXN9ApEsAMo5QmmOlQ5s4C4Fhi2PvvHb9bc", "y" => "AUZGWn4yuFxXKuqYEds0cm31A0LxY5v7Bmur3LoJaykQrXxd-GdcX4ZH6_hot6VnVWBlsDcHjCBXjxLg0_uJLNM6"})
I'm guessing it's trying to pass that whole hash to base encode it.
Rest of config for reference
config :guardian, Guardian,
allowed_algos: ["ES512"],
issuer: "BlogBackend",
ttl: { 30, :days },
secret_key: %{
"alg" => "ES512",
"crv" => "P-521",
"d" => "mhEZkkY6eUJKEVl4Qnjfnik3GZmvPsiQEJC3-9f-jiM2OSHPTTYkOzPfjWbOqhb5JARUaJzXL9YyqHJBDxM_VB8",
"kty" => "EC", "use" => "sig",
"x" => "AALDyto5SKJ73a7uqLjKLwoCjUvn6Ldpcade5FCOt1zsl9hJGIHVGlXN9ApEsAMo5QmmOlQ5s4C4Fhi2PvvHb9bc",
"y" => "AUZGWn4yuFxXKuqYEds0cm31A0LxY5v7Bmur3LoJaykQrXxd-GdcX4ZH6_hot6VnVWBlsDcHjCBXjxLg0_uJLNM6"
},
serializer: BlogBackend.GuardianSerializer
@StevenNunez Which version of guardian are you using? Support for key types other than oct
was added in version 0.11.1.
I put together an example project potatosalad/guardian-example-app using the same config you provided with the dependency version set to guardian 0.11.1.
Here is a sign and verify example from the project while running iex -S mix
:
## Sign
{:ok, signed_binary, jwt_map} = Guardian.encode_and_sign("test")
# {:ok,
# "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0IiwiZXhwIjoxNDY2Njg3Mzg0LCJpYXQiOjE0NjQwOTUzODQsImlzcyI6IkJsb2dCYWNrZW5kIiwianRpIjoiMWU4OTg2M2YtZGI2My00MmJmLWE5YjYtOTY1MTU1NDk2ODA1IiwicGVtIjp7fSwic3ViIjoidGVzdCIsInR5cCI6InRva2VuIn0.AYMPeB1lgx90_79xBoKRJbKXGcIo5w8sS4ArJ3A0gZFzHf1cNlME1JHqbhzShcHsb6qH94n6gvqswgQgnLGKd2stAY8IIokRVcD8DTFwcyf6gf7Y-CO3LTOxWEsE6p2DbMaenTBdJsTe8Qhx41wRta8DOunXs33mAtApdwBIZzS9yxiJ",
# %{"aud" => "test", "exp" => 1466687384, "iat" => 1464095384,
# "iss" => "BlogBackend", "jti" => "1e89863f-db63-42bf-a9b6-965155496805",
# "pem" => %{}, "sub" => "test", "typ" => "token"}}
## Verify
{:ok, ^jwt_map} = Guardian.decode_and_verify(signed_binary)
# {:ok,
# %{"aud" => "test", "exp" => 1466687384, "iat" => 1464095384,
# "iss" => "BlogBackend", "jti" => "1e89863f-db63-42bf-a9b6-965155496805",
# "pem" => %{}, "sub" => "test", "typ" => "token"}}
Note: With ES512
, the signature value will change each time a message is signed. So even with the the exact same message and key, your signed_binary
may not match the one provided in the example above.
Is there any reason to not just use the result of
mix phoenix.gen.secret
? I got it working that way.
That's probably a POLA violation (or bug) that you have uncovered in jose. You are no longer using ES512
, but actually HS512
due to the key type being oct
. Since both key types use SHA-512, the sign and verify functions related to the ECDSA algorithms are allowing the oct
key type to pass through.
I will post an update here once the jose library has been fixed.
@potatosalad Thank you!
@potatosalad Hello, i have been using ES512 as you recommended, and my guardian set up was working until i updated jose from 1.7.9 to 1.8.0 my key is like this:
%{"alg" => "ES512", "crv" => "P-521",
"d" => "AZ-WCoPA2Kt-Jio-DFuOChf0SBinAYgklUFIdVLmlZwjHmRUntVVatkBGnGyjyUWC0aAqwPdwf5s1ubXtRWT5st3",
"kty" => "EC", "use" => "sig",
"x" => "AUgiiCT_ycsyZXpHXoYA4s-VpObDQZ99Sqi3vXXDH3lOoSNWg9QiusZ_p-z3OqQ-6wLFZJWORu6COWOF7ROCAEtQ",
"y" => "APmuazEPeBWB_C8xrY0xZSoawsYcL8CycvaOHwiYVh_lHUCVAltLzofi-jM-TxsFzmjUofnwI-gfQCYiZQz1b9lB"}
And the error from the console when i try to log in:
2016-08-31T23:57:17.281619+00:00 app[web.1]: Request: POST /sessions
2016-08-31T23:57:17.281620+00:00 app[web.1]: ** (exit) an exception was raised:
2016-08-31T23:57:17.281620+00:00 app[web.1]: ** (ErlangError) erlang error: {:not_supported, ["P-521", :HS512]}
2016-08-31T23:57:17.281628+00:00 app[web.1]: (jose) src/jose_jwk_kty_ec.erl:355: :jose_jwk_kty_ec.jws_alg_to_digest_type/2
2016-08-31T23:57:17.281629+00:00 app[web.1]: (jose) src/jose_jwk_kty_ec.erl:186: :jose_jwk_kty_ec.sign/3
2016-08-31T23:57:17.281630+00:00 app[web.1]: (jose) src/jose_jws.erl:311: :jose_jws.sign/4
2016-08-31T23:57:17.281631+00:00 app[web.1]: (jose) src/jose_jwt.erl:171: :jose_jwt.sign/3
2016-08-31T23:57:17.281631+00:00 app[web.1]: lib/guardian.ex:303: Guardian.encode_claims/1
2016-08-31T23:57:17.281632+00:00 app[web.1]: lib/guardian.ex:94: Guardian.encode_from_hooked/1
2016-08-31T23:57:17.281632+00:00 app[web.1]: lib/guardian/plug.ex:118: Guardian.Plug.sign_in/4
Hey @RomarioLopezC,
This is due to the release of jose 1.8.0
which is more strict about which algorithms are allowed to be used with specific keys as mentioned in potatosalad/erlang-jose#24.
It should be resolved if you add the following line to your guardian configuration:
allowed_algos: ["ES512"]
Let me know if you have any other issues. Also, it may go without saying, but you may need to change your secret key if that was your actual secret key you posted and not just an example.
Hello there! Coming from a Rails & "Devise" background, I'm rewriting an app and start using Guardian.
The first "newbie" question that comes up is : wow, that's a lot of different keys types (backed by jose), but how do I choose?
I'm going to do my homework, document myself & make due diligence, and will come up with conclusions, but I wondered if some basic orientation could be provided in the readme in order to help a newcomer choose that.
Thoughts?