RS256 vs HS256: What's the difference?
Asked Answered
M

5

370

I'm using Auth0 to handle authentication in my web app. I'm using ASP.NET Core v1.0.0 and Angular 2 rc5 and I don't know much about authentication/security in general.

In the Auth0 docs for ASP.NET Core Web Api, there are two choices for the JWT algorithm being RS256 and HS256. This may be a dumb question but:

What's the difference between RS256 and HS256? What are some use cases (if applicable)?

Malisamalison answered 31/8, 2016 at 0:37 Comment(1)
See also the answers on security stack exchange Recommended algorithms for JWTDispersal
O
617

Both choices refer to what algorithm the identity provider uses to sign the JWT. Signing is a cryptographic operation that generates a "signature" (part of the JWT) that the recipient of the token can validate to ensure that the token has not been tampered with.

  • RS256 (RSA Signature with SHA-256) is an asymmetric algorithm, and it uses a public/private key pair: the identity provider has a private (secret) key used to generate the signature, and the consumer of the JWT gets a public key to validate the signature. Since the public key, as opposed to the private key, doesn't need to be kept secured, most identity providers make it easily available for consumers to obtain and use (usually through a metadata URL).

  • HS256 (HMAC with SHA-256), on the other hand, involves a combination of a hashing function and one (secret) key that is shared between the two parties used to generate the hash that will serve as the signature. Since the same key is used both to generate the signature and to validate it, care must be taken to ensure that the key is not compromised.

If you will be developing the application consuming the JWTs, you can safely use HS256, because you will have control on who uses the secret keys. If, on the other hand, you don't have control over the client, or you have no way of securing a secret key, RS256 will be a better fit, since the consumer only needs to know the public (shared) key.

Since the public key is usually made available from metadata endpoints, clients can be programmed to retrieve the public key automatically. If this is the case (as it is with the .Net Core libraries), you will have less work to do on configuration (the libraries will fetch the public key from the server). Symmetric keys, on the other hand, need to be exchanged out of band (ensuring a secure communication channel), and manually updated if there is a signing key rollover.

Auth0 provides metadata endpoints for the OIDC, SAML and WS-Fed protocols, where the public keys can be retrieved. You can see those endpoints under the "Advanced Settings" of a client.

The OIDC metadata endpoint, for example, takes the form of https://{account domain}/.well-known/openid-configuration. If you browse to that URL, you will see a JSON object with a reference to https://{account domain}/.well-known/jwks.json, which contains the public key (or keys) of the account, represented as a JSON Web Key Set.

If you look at the RS256 samples, you will see that you don't need to configure the public key anywhere: it's retrieved automatically by the framework.

Outspeak answered 31/8, 2016 at 1:29 Comment(5)
NB when using rs256 - there is (or was) a security risk in many libraries which allowed the token to determine which algorithm to use. Essentially the attacker could use the public rs256 key with a hs256 encoding to pretend it is the secret key. So make sure your library doesn't have this behaviour!Stretcherbearer
One small correction, "HS256 (HMAC with SHA-256), on the other hand, is a symmetric algorithm" - HMAC does not utilize a symmetric-key algorithm (which would allow you to encrypt and decrypt the signature by its definition). It utilizes a cryptographic hash function and a secret cryptographic key underneath HMAC. Which implies calculation of the hash (one way function) over the message with an appended secret key.Gilberto
Example with Google : Go to accounts.google.com/.well-known/openid-configuration and look at jwks_uri ; it forwards you to googleapis.com/oauth2/v3/certs where you can find keys. Then you just have to retrieve the good key by its kid.Streetcar
Worth noting that as HS256 shares a key between two parties that makes this algorithm unable to support multiple audiences in the one access_token, whereas RS256 can support many audiences. This matters with Identity clients like Auth0 who only allow a request to the /userinfo endpoint using an access_token if the configuration is RS256, as they need this token to support your api domain as an aud, as well as their auth0 domain.Disprove
It is also worth noting that the RSA signature algorithm is vulnerable to quantum cryptography. So an attacker with a sufficiently large quantum computer could forge a signature.Clevelandclevenger
B
123

In cryptography there are two types of algorithms used:

Symmetric algorithms

A single key is used to encrypt data. When encrypted with the key, the data can be decrypted using the same key. If, for example, Mary encrypts a message using the key "my-secret" and sends it to John, he will be able to decrypt the message correctly with the same key "my-secret".

Asymmetric algorithms

Two keys are used to encrypt and decrypt messages. While one key(public) is used to encrypt the message, the other key(private) can only be used to decrypt it. So, John can generate both public and private keys, then send only the public key to Mary to encrypt her message. The message can only be decrypted using the private key.

HS256 and RS256 Scenario

These algorithms are NOT used to encrypt/decryt data. Rather they are used to verify the origin or the authenticity of the data. When Mary needs to send an open message to Jhon and he needs to verify that the message is surely from Mary, HS256 or RS256 can be used.

HS256 can create a signature for a given sample of data using a single key. When the message is transmitted along with the signature, the receiving party can use the same key to verify that the signature matches the message.

RS256 uses pair of keys to do the same. A signature can only be generated using the private key. And the public key has to be used to verify the signature. In this scenario, even if Jack finds the public key, he cannot create a spoof message with a signature to impersonate Mary.

Bearskin answered 23/1, 2017 at 8:0 Comment(2)
I think you made a mistake in describing asymmetric keys. The public key is for decryption and the private key is for encryption.Zulemazullo
The public key is accessible to anyone and is used to encrypt. But the private key is kept by the server to decrypt what is encrypted by the public key. The private key is never ent out.Bearskin
L
46

There is a difference in performance.

Simply put HS256 is about 1 order of magnitude faster than RS256 for verification but about 2 orders of magnitude faster than RS256 for issuing (signing).

 640,251  91,464.3 ops/s
  86,123  12,303.3 ops/s (RS256 verify)
   7,046   1,006.5 ops/s (RS256 sign)

Don't get hung up on the actual numbers, just think of them with respect of each other.

[Program.cs]

class Program
{
    static void Main(string[] args)
    {
        foreach (var duration in new[] { 1, 3, 5, 7 })
        {
            var t = TimeSpan.FromSeconds(duration);

            byte[] publicKey, privateKey;

            using (var rsa = new RSACryptoServiceProvider())
            {
                publicKey = rsa.ExportCspBlob(false);
                privateKey = rsa.ExportCspBlob(true);
            }

            byte[] key = new byte[64];

            using (var rng = new RNGCryptoServiceProvider())
            {
                rng.GetBytes(key);
            }

            var s1 = new Stopwatch();
            var n1 = 0;

            using (var hs256 = new HMACSHA256(key))
            {
                while (s1.Elapsed < t)
                {
                    s1.Start();
                    var hash = hs256.ComputeHash(privateKey);
                    s1.Stop();
                    n1++;
                }
            }

            byte[] sign;

            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportCspBlob(privateKey);

                sign = rsa.SignData(privateKey, "SHA256");
            }

            var s2 = new Stopwatch();
            var n2 = 0;

            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportCspBlob(publicKey);

                while (s2.Elapsed < t)
                {
                    s2.Start();
                    var success = rsa.VerifyData(privateKey, "SHA256", sign);
                    s2.Stop();
                    n2++;
                }
            }

            var s3 = new Stopwatch();
            var n3 = 0;

            using (var rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportCspBlob(privateKey);

                while (s3.Elapsed < t)
                {
                    s3.Start();
                    rsa.SignData(privateKey, "SHA256");
                    s3.Stop();
                    n3++;
                }
            }

            Console.WriteLine($"{s1.Elapsed.TotalSeconds:0} {n1,7:N0} {n1 / s1.Elapsed.TotalSeconds,9:N1} ops/s");
            Console.WriteLine($"{s2.Elapsed.TotalSeconds:0} {n2,7:N0} {n2 / s2.Elapsed.TotalSeconds,9:N1} ops/s");
            Console.WriteLine($"{s3.Elapsed.TotalSeconds:0} {n3,7:N0} {n3 / s3.Elapsed.TotalSeconds,9:N1} ops/s");

            Console.WriteLine($"RS256 is {(n1 / s1.Elapsed.TotalSeconds) / (n2 / s2.Elapsed.TotalSeconds),9:N1}x slower (verify)");
            Console.WriteLine($"RS256 is {(n1 / s1.Elapsed.TotalSeconds) / (n3 / s3.Elapsed.TotalSeconds),9:N1}x slower (issue)");

            // RS256 is about 7.5x slower, but it can still do over 10K ops per sec.
        }
    }
}
Lillian answered 2/3, 2018 at 12:7 Comment(4)
These are important numbers. thank you. I tend to think of encryption as being more or less transparent wrt throughput, but your research implies that using R256 to sign intermachine communications adds 1 ms per hop.Cathleencathlene
@MatthewMarkMiller Keep in mind though that they are not equal in use. They have different characteristics. RS256 is asymmetric and therefore in a client/server style communication where you only share the public key, it is a better option. HS256 requires sharing the key that can both sign AND verify - only useful if you trust the two parties or don't need one of the parties to decrypt anything.Eudosia
@RobEvans yes, don't get hung up on the performance numbers here. Pick the right solution to your problem. This is just an observation, not a recommendation to favour HS256 over RS256 you must make that decision based on your context.Lillian
When protocol choice can have the same impact on latency as an extra kilometer of cable, that's worth knowing, especially in these days of long call chains and tight TTLs.Cathleencathlene
U
7

short answer, specific to OAuth2,

  • HS256 user client secret to generate the token signature and same secret is required to validate the token in back-end. So you should have a copy of that secret in your back-end server to verify the signature.
  • RS256 use public key encryption to sign the token.Signature(hash) will create using private key and it can verify using public key. So, no need of private key or client secret to store in back-end server, but back-end server will fetch the public key from openid configuration url in your tenant (https://[tenant]/.well-known/openid-configuration) to verify the token. KID parameter inside the access_toekn will use to detect the correct key(public) from openid-configuration.
Untraveled answered 11/5, 2020 at 7:45 Comment(6)
You still have to store the private key, to generate new tokensNimitz
@Nimitz private key is used by the authentication server (its his own private key). No one will store others private keyUntraveled
Yes, that's what I mean, the server generating tokens still needs to store the private key. But you have said that you don't have to store the private key on the backend server, which is wrong.Nimitz
@Nimitz "you don't have to store the private key in your back-end server" yes this is correct. If you read the question carefully, its related to Auth0 and you don't have to store Auth0's private key in your back-end resource server. but in your point of view , who ever generate tokens need his private key (in this case its Auth0)Untraveled
@Untraveled Does the public key always have to be provided externally? Could it be provided with/inside the JWT as well? And if so, how would another server validate that the encrypted payload wasn't re-encrypted just using a different public/private key set?Pharaoh
@Pharaoh public key shouldn't be there inside the token. Because the token should validate against a known public key, which should be known by the validator. Otherwise, someone in the middle will alter the token and attach his public key to the token. You can use different public/private key set for the same token, then you need to replace the token signature for the new keys.Untraveled
H
1

For JWT use cases

  • When you use JWT as a session token where the same application both produce and consume the JWT, HS256 is good. For example when a HTTP backend issues JWT as a cookie value. There is no use for a public key. It is also faster to produce and consume than RS256.
  • For best compatibility between implementations, HS256 is the only required algorithm (with none, which seldomly can be used).
  • The producer and consumer are different applications. With RS256 there is no need to think how to securely share the secret. The producer can keep the secret private and only give public key to the consumer.
  • Having multiple, even unknown, consumers is easy with RS256 since the public key can be exposed on a public endpoint for all consumers to see.
  • Key rotation can be implemented with RS256 by requiring the consumer fetch current set of keys for tokens still valid, if consumer has no recent cached set of keys (rate limit) and an unknown kid is received.
Hinton answered 21/11, 2023 at 16:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.