Quando decidimos utilizar JWT em nossas API's e frontend SPA, precisamos utilizar um algoritmo para emitir um token.

Há diversas opções para assinar o JWT. Ele deve ser simétrico ou assimétrico? Probabilístico ou determinístico?

No fim, escolher o algoritmo para assinar JWT é fácil, tão fácil quanto gerenciar sessão no ASP Clássico (alerta de ironia). Afinal, nada melhor que decidir se você vai arriscar sua segurança usando aquela chave que até a tia do café conhece ou se vai fazer algo sério com criptografia assimétrica e chaves públicas.

Se liga no código, já comentado pra você não dizer que não entendeu:

// gerador de JWT (JWS ou JWE)
var tokenHandler = new JsonWebTokenHandler();

// Gerando uma chave assimétrica RSA com 2048 bits (afinal, quem não gosta de números gigantes?)
var key = new RsaSecurityKey(RSA.Create(2048));

// Criando o JWT com descrição dos detalhes ocultada pra não facilitar sua vida
var jwt = new SecurityTokenDescriptor
{
    Issuer = \"www.mysite.com\",
    Audience = \"your-spa\",
    // Details escondidos porque você não merece tudo mastigado
    SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSsaPssSha256) // Olha aí o RSA com algoritmo probabilístico
};

// Finalmente assinando essa belezura criptográfica
string jws = tokenHandler.CreateToken(jwt);
Console.WriteLine(jws);

Não é só RSA não, dá pra ir na simplicidade perigosa das chaves simétricas ou até no hype do ECDSA, que é a nova moda da criptografia.

Algoritmos

Um algoritmo criptográfico é só uma receita matemática gourmetizada pra deixar o texto ilegível pros curiosos sem a chave. Tipo aquele comentário que você deixa no código e que ninguém entende.

crypto

JWT com chave simétrica

Essa aqui é a versão preguiçosa da criptografia: usa uma chave só pra tudo. Se vazar, meu amigo, abraça o problema com carinho.

symmetric-encryption-1

HMAC

É só um jeito bonitinho de dizer que sua segurança depende da qualidade do hash que você escolheu. SHA256 tá bom ou quer algo mais exótico? É também o único jeito que Dev Influencer sabe fazer.

Afinal, se vai começar a dançar no TikTok e ganhar like falando de programação, começa com o tutorial top de NodeJS e JWT. Não vou falar de ninguém do mundo .NET porque eles já sabem que eu odeio eles.

Quando utilizar uma chave simétrica?

Quando você é corajoso (ou preguiçoso) o suficiente pra deixar UMA chave rodando solta na API. Um time pequeno talvez aguente o tranco, mas imagina o estrago se cair nas mãos erradas... Compartilhar chaves é coisa de quem gosta de correr perigo. Uma vez com a chave na mão, alterar o JWT é ridiculamente simples.

Um zé mané vira admin do seu sistema em 30 segundos se a chave vazar.

Gerando uma chave simétrica HMAC

Se você insiste nisso, ao menos faça direito e use tamanhos de chave decentes. Tem documentação séria falando disso (links no final). Vamos ver um exemplo prático:

static void Main(string[] args)
{
    var tokenHandler = new JsonWebTokenHandler();
    SecurityKey key = AutoGeneratedHmac(64); // 64 bytes é aceitável pelo NIST, viu?

    Jwt.SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    Console.WriteLine($\"{tokenHandler.CreateToken(Jwt)}{Environment.NewLine}\");
}

// Geração de chave segura usando RandomNumberGenerator
private static SecurityKey AutoGeneratedHmac(int bytes)
{
    return new SymmetricSecurityKey(GenerateHmacKey(bytes));
}

private static byte[] GenerateHmacKey(int bytes)
{
    byte[] data = new byte[bytes];
    RandomNumberGenerator.Create().GetBytes(data);
    return data;
}

Lembre-se que guardar essa chave é uma burocracia à parte, senão seu JWT expira tão rápido quanto o hype do último framework JavaScript.

JWT com chave assimétrica

Aqui já é coisa fina: duas chaves, uma pra assinar e outra pra validar. Se alguém pegar só a pública não faz mal, já que ela só serve pra ler e validar o token.

asymmetric-encryption-1

RSA

O clássico das criptografias assimétricas. Tipo o Java: antigo, mas ainda muito popular.

ECDsa

Essa é a nova queridinha, mais rápida e elegante que RSA, dizem os hipsters da segurança. A RFC recomenda, então quem sou eu pra discordar?

Quando utilizar chave assimétrica?

Sempre! Ou você gosta de correr riscos?

Gerando RSASSA-PSS using SHA-256 and MGF1 with SHA-256

Siga o padrãozinho recomendado pela RFC:

public static void Run()
{
    var tokenHandler = new JsonWebTokenHandler();
    var key = new RsaSecurityKey(RSA.Create(2048)) // 2048 bits, mínimo aceitável
    {
        KeyId = Guid.NewGuid().ToString()
    };

    Jwt.SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSsaPssSha256);
    Console.WriteLine($\"{tokenHandler.CreateToken(Jwt)}{Environment.NewLine}\");
}

Bem fácil, igual gerenciar dependências no Node.js.

Gerando ECDSA using P-256 and SHA-256

Tá aqui, direto ao ponto:

public static void Run()
{
    var tokenHandler = new JsonWebTokenHandler();
    var key = new ECDsaSecurityKey(ECDsa.Create(ECCurve.NamedCurves.nistP256)) // essa curva aí mesmo, não inventa moda
    {
        KeyId = Guid.NewGuid().ToString()
    };

    Jwt.SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256);
    var lastJws = tokenHandler.CreateToken(Jwt);
    Console.WriteLine($\"{lastJws}{Environment.NewLine}\");
}

Fácil, né? Só falta explicar isso no daily pra galera do front.

Componente Security.Jwt

Mas se você acha que tudo isso é burocrático demais, usa o NetDevPack Security.Jwt, que faz tudo por você (até expirar as chaves antes do seu certificado SSL).

Download

<img src="https://github.com/ashleymcnamara/artwork/blob/master/clippy_octocat.png?raw=true" width="250" class="center" /> Pega o código pronto no GitHub e pare de reclamar.

Conclusão

Criptografia não precisa ser esse bicho de sete cabeças que inventaram por aí, ainda mais com tanto componente pronto no mercado. Espero que você durma tranquilo com sua API, sem deixar chaves soltas em repositórios abertos.

Referências