JWE: Proteção de Dados com um Toque de Magia

Hugo Guilherme Gomes
TOTVS Developers
Published in
4 min readSep 11, 2024

O Que é JWE?

Imagine que você tem um segredo muito importante. Você não quer apenas sussurrá-lo para o seu amigo; você quer garantir que ninguém mais no caminho possa escutar. É aí que entra o JWE, ou JSON Web Encryption. Pense nele como uma capa de invisibilidade para os seus dados, mantendo-os seguros e protegidos de olhares curiosos.

Quando Utilizar o JWE?

Você deve usar JWE sempre que precisar garantir que os dados que está enviando ou armazenando estejam seguros e inacessíveis para qualquer pessoa não autorizada. Por exemplo, podemos utilizar a classe tJWT (JSON Web Token) para gerar a informação em formato JSON (JavaScript Object Notation) e adicionar criptografia usando o JWE para garantir a confidencialidade das informações, tornando-o seguro contra acessos não autorizados.

Exemplos incluem:

  • Aplicações Web: Quando você está transmitindo informações sensíveis entre o navegador e o servidor.
  • APIs: Proteger dados que estão sendo trocados entre diferentes serviços.
  • Autenticação: Guardar tokens de autenticação de forma segura.

Finalidade e Vantagens do JWE

A principal finalidade do JWE é garantir que os dados fiquem indecifráveis para qualquer um que não tenha a chave correta. Vamos explorar algumas vantagens.

  1. Segurança Robusta: Com JWE, seus dados estão sempre protegidos com criptografia de ponta.
  2. Integridade dos Dados: Além de criptografar, ele garante que os dados não foram alterados durante a transmissão.
  3. Flexibilidade: Pode ser usado em uma variedade de cenários, desde web até dispositivos móveis e IoT (Internet of Things).
  4. Compatibilidade: Por ser um padrão aberto, é suportado por diversas linguagens e bibliotecas.

Entendendo a Estrutura do JWE

O JWE é construído com cinco componentes, cada um separado por um ponto “.”, que formam a sua estrutura compacta:

Exemplo em TLPP

Agora, vamos ao que interessa: como criar um JWE em TLPP.

User Function encryptJWE()
Local tokenJWE := ""
Local plainTextDecrypted := ""
Local oTokenJWE := Nil
Local cError := ""

// Variáveis para armazenar a chave JWK e o texto a ser criptografado
Local jwk
Local plainText := "Texto a ser criptografado"

// Definindo a chave JWK RSA em formato JSON
jwk := '{"kty": "RSA","n":"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw","e": "AQAB","d":"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ","p": "1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0","q":"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc","dp":"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE","dq":"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis","qi":"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"}'

// INICIO teste usando RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256

// Criando uma instância da classe JWE
oTokenJWE:= tJWE():New()

// Importando a chave JWK RSA para o objeto JWE
oTokenJWE:importKeyJWK(jwk, LEN(jwk))

// Configurando um novo cabeçalho JWE
oTokenJWE:newHeader()

// Definindo algoritmos de criptografia no cabeçalho JWE
oTokenJWE:setHeader("alg","RSA1_5")
oTokenJWE:setHeader("enc","A128CBC-HS256")

// Criptografando o texto
oTokenJWE:encrypt(plainText)

// Obtendo o token JWE criptografado
tokenJWE := oTokenJWE:getToken()
conout("Token gerado: " + tokenJWE)

// Deserializando token JWE
oTokenJWE:deserializeJWE()

// Descriptografando o token JWE
plainTextDecrypted := oTokenJWE:decrypt()

conout("Token Descriptografado: " + plainTextDecrypted)

// Liberando o objeto JWE
oTokenJWE:release()
FreeObj(oTokenJWE)

return 0

Caso de uso

  1. O cliente (aplicação) prepara o dado sensível (neste caso, o número de CPF).
  2. O dado é criptografado usando JWE.
  3. O token JWE criptografado é transmitido via uma chamada HTTP para a API.
  4. A API recebe o JWE e, com a chave correta, descriptografa o token e processa o dado sensível.
User Function sendDataToAPI()

Local oTokenJWE := Nil
Local oConfPrd := Nil
Local tokenJWE := ""
Local plainText := "123.456.789-10" // CPF a ser criptografado
Local jwkRsa := '{"kty": "RSA","e": "AQAB","n": "wsqJbopx18NQFYLYOq4ZeMSE89yGiEankUpf25yV8QqroKUGrASj_OeqTWUjwPGKTN1vGFFuHYxiJeAUQH2qQPmg9Oqk6-ATBEKn9COKYniQ5459UxCwmZA2RL6ufhrNyq0JF3GfXkjLDBfhU9zJJEOhknsA0L_c-X4AI3d_NbFdMqxNe1V_UWAlLcbKdwO6iC9fAvwUmDQxgy6R0DC1CMouQpenMRcALaSHar1cm4K-syoNobv3HEuqgZ3s6-hOOSqauqAO0GUozPpaIA7OeruyRl5sTWT0r-iz39bchID2bIKtcqLiFcSYPLBcxmsaQCqRlGhmv6stjTCLV1yT9w","kid": "ff3c5c96-392e-46ef-a839-6ff16027af78","d": "b9hXfQ8lOtw8mX1dpqPcoElGhbczz_-xq2znCXQpbBPSZBUddZvchRSH5pSSKPEHlgb3CSGIdpLqsBCv0C_XmCM9ViN8uqsYgDO9uCLIDK5plWttbkqA_EufvW03R9UgIKWmOL3W4g4t-C2mBb8aByaGGVNjLnlb6i186uBsPGkvaeLHbQcRQKAvhOUTeNiyiiCbUGJwCm4avMiZrsz1r81Y1Z5izo0ERxdZymxM3FRZ9vjTB-6DtitvTXXnaAm1JTu6TIpj38u2mnNLkGMbflOpgelMNKBZVxSmfobIbFN8CHVc1UqLK2ElsZ9RCQANgkMHlMkOMj-XT0wHa3VBUQ","p": "8mgriveKJAp1S7SHqirQAfZafxVuAK_A2QBYPsAUhikfBOvN0HtZjgurPXSJSdgR8KbWV7ZjdJM_eOivIb_XiuAaUdIOXbLRet7t9a_NJtmX9iybhoa9VOJFMBq_rbnbbte2kq0-FnXmv3cukbC2LaEw3aEcDgyURLCgWFqt7M0","q": "zbbTv5421GowOfKVEuVoA35CEWgl8mdasnEZac2LWxMwKExikKU5LLacLQlcOt7A6n1ZGUC2wyH8mstO5tV34Eug3fnNrbnxFUEE_ZB_njs_rtZnwz57AoUXOXVnd194seIZF9PjdzZcuwXwXbrZ2RSVW8if_ZH5OVYEM1EsA9M","dp": "1BaIYmIKn1X3InGlcSFcNRtSOnaJdFhRpotCqkRssKUx2qBlxs7ln_5dqLtZkx5VM_UE_GE7yzc6BZOwBxtOftdsr8HVh-14ksSR9rAGEsO2zVBiEuW4qZf_aQM-ScWfU--wcczZ0dT-Ou8P87Bk9K9fjcn0PeaLoz3WTPepzNE","dq": "kYw2u4_UmWvcXVOeV_VKJ5aQZkJ6_sxTpodRBMPyQmkMHKcW4eKU1mcJju_deqWadw5jGPPpm5yTXm5UkAwfOeookoWpGa7CvVf4kPNI6Aphn3GBjunJHNpPuU6w-wvomGsxd-NqQDGNYKHuFFMcyXO_zWXglQdP_1o1tJ1M-BM","qi": "j94Ens784M8zsfwWoJhYq9prcSZOGgNbtFWQZO8HP8pcNM9ls7YA4snTtAS_B4peWWFAFZ0LSKPCxAvJnrq69ocmEKEk7ss1Jo062f9pLTQ6cnhMjev3IqLocIFt5Vbsg_PWYpFSR7re6FRbF9EYOM7F2-HRv1idxKCWoyQfBqk"}'
Local cError := ""
Local cUrl := "[https://api.example.com](https://api.example.com/)"
Local aHeader := {}

// Inicializando objeto JWE
oTokenJWE := tJWE():New()

// Importando chave pública JWK (RSA)
oTokenJWE:importKeyJWK(jwkRsa, LEN(jwkRsa))

// Criando o cabeçalho JWE e definindo algoritmos
oTokenJWE:newHeader()
oTokenJWE:setHeader("alg", "RSA-OAEP") // Gerenciamento de chave RSA-OAEP
oTokenJWE:setHeader("enc", "A256GCM") // Criptografia de conteúdo A256GCM

// Criptografar o CPF
oTokenJWE:encrypt(plainText)

// Gerar o token JWE criptografado
tokenJWE := oTokenJWE:getToken()

// Verificação de erro na geração do token
If EMPTY(tokenJWE)
cError := "Erro ao gerar o token JWE."
conout(cError)
Return
EndIf

conout(tokenJWE)

// Inicializando FWRest com URL base da API
oConfPrd := FWRest():New(cUrl)

// Definir o caminho do endpoint da API
oConfPrd:SetPath("/process")

// Configurar cabeçalhos
aAdd(aHeader, "Accept: application/json")
Aadd(aHeader, "DPoP: " + tokenJWE) // Passar o JWE como parte do cabeçalho

// Realizar requisição GET com o cabeçalho contendo o JWE
If !oConfPrd:GET(aHeader)
MsgStop(Alltrim(oConfPrd:GetLastError()) + " - " + Alltrim(oConfPrd:cResult))
Return
EndIf

// Processar resposta JSON da API
FWJsonDeserialize(oConfPrd:cResult, @oResSel)

// Exibir resposta deserializada
conout("Dados recebidos: " + Str(oResSel))

// Liberar recursos
oTokenJWE:release()

FreeObj(oTokenJWE)

Return

Resumo do Fluxo:

  1. Criptografia dos Dados: Utilizando JWE, o CPF é criptografado com segurança.
  2. Configuração do FWRest: O token JWE é transmitido via cabeçalho HTTP para a API.
  3. Requisição HTTP: A requisição é enviada, e a resposta JSON é desserializada e processada.
  4. Segurança e Integridade: O uso de RSA-OAEP e A256GCM garante que os dados sejam criptografados e protegidos contra alterações.

Conclusão

O JWE é uma ferramenta incrível para garantir que seus dados estejam sempre protegidos. Com sua segurança robusta e flexibilidade, é ideal para uma ampla gama de aplicações. E com bibliotecas disponíveis para várias linguagens, como vimos no exemplo em TLPP, implementar JWE pode ser tão simples quanto apertar um botão!

Então, da próxima vez que precisar garantir a segurança dos seus dados, lembre-se do JWE e coloque sua capa de invisibilidade em ação!

https://tdninterno.totvs.com/display/tec/tJWE
https://tdninterno.totvs.com/display/tec/Classe+tJWT
https://datatracker.ietf.org/doc/html/rfc7516

Abraços Hugo Guilherme Gomes.

--

--

Hugo Guilherme Gomes
TOTVS Developers

Dev na TOTVS - Departamento de Tecnologia. "Não confie, verifique..."