JWT使用HS512算法生成全局服务token原理
JWT使用HS512算法生成全局服务token原理
- JWT使用HS512算法生成全局服务token原理
- 前言
- 一、jwt生成token原理
- 1、 jwt的头部承载两部分信息
- 2、Payload数据
- 3、signature
- 4、 签名的目的
- 二、 JWT的HS512算法生成token的C++语言实现
- 总结
前言
最近需要使用C++服务中根据用户名生成全局服务的token,然后在java服务中印证token正确性。因为java服务中使用jwt库生成全局的token, 我就对应找C++中jwt的库, github中有开源库叫jwt-cpp然后我就下载了, 我使用没有找到java中生成token好使用的接口、没有办法我只有看一下jwt生成token的步骤实现一遍
一、jwt生成token原理
HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),
your-512-bit-secret
) secret base64 encoded
jwt由三部分构成
- 头部(Header)
- 负载(Payload)即数据
- 签证(Signature)
1、 jwt的头部承载两部分信息
- 声明类型, 即jwt
- 声明加密的算法 (HMAC、SHA256, HS512)
头部json格式
{"typ“: "JWT", "alg":"HS512"}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
2、Payload数据
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明 :
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 :
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个payload:
{ "sub": "1234567890", "name": "John Doe", "admin": true}
然后将其进行base64加密,得到Jwt的第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
3、signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串(头部在前),然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,所以需要保护好。
4、 签名的目的
最后一步签名的过程,实际上是对头部以及载荷内容进行签名。一般而言,加密算法对于不同的输入产生的输出总是不一样的。对于两个不同的输入,产生同样的输出的概率极其地小(有可能比我成世界首富的概率还小)。所以,我们就把“不一样的输入产生不一样的输出”当做必然事件来看待吧。
所以,如果有人对头部以及载荷的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥的话,得出来的签名也一定会是不一样的。
服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名。那么服务器应用是怎么知道我们用的是哪一种算法呢?别忘了,我们在JWT的头部中已经用alg字段指明了我们的加密算法了。
如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应。
二、 JWT的HS512算法生成token的C++语言实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/hmac.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <iostream>
namespace chen {namespace jwt {//java 从最后 删除'='个数 url-unfriendly base64 char uint remove_padding( char* data, size_t len ){int paddingCount = 0;//int encoded_payload_len = strlen(encoded_payload);for (int i = len - 1; i > 0; --i){if (data[i] == '='){++paddingCount;}else{break;}}//replace URL-unfriendly Base64 chars to url-friendly ones:for (int i = 0; i < len; ++i){if (data[i] == '+'){data[i] = '-';}else if (data[i] == '/'){data[i] = '_';}}return len - paddingCount;}std::string create_token( const std::string& secret_key, const std::string& payload){// 构建头部static const char* header = "{\"alg\":\"HS512\"}";char buffer[10240] = {0};// 计算 Base64 编码后头部的长度int header_len = EVP_ENCODE_LENGTH(strlen(header));// 计算 Base64 编码后负载的长度int payload_len = payload.length();// = EVP_ENCODE_LENGTH(strlen(payload));// 分配内存存储 Base64 编码后的头部和负载char encoded_header[4096] = {0};char encoded_payload[4096] = {0};// Base64 编码头部和负载EVP_EncodeBlock((unsigned char*)encoded_header , (const unsigned char*)header, strlen(header));EVP_EncodeBlock((unsigned char*)encoded_payload , (const unsigned char*)payload.c_str(), payload.length());// 构建待签名字符串int unsigned_data_len = remove_padding( encoded_header , strlen(encoded_header) ) + remove_padding(encoded_payload, strlen(encoded_payload) ) + 2; // 加上两个点号snprintf(buffer, unsigned_data_len, "%s.%s", std::string(encoded_header, remove_padding((char*)encoded_header , strlen(encoded_header) )).c_str(), std::string(encoded_payload, remove_padding((char*)encoded_payload , strlen(encoded_payload))).c_str());// 使用 HMAC SHA-512 对待签名字符串进行签名signed char hmac_result[EVP_MAX_MD_SIZE];// = { 0 };unsigned int hmac_len;// = 0;HMAC(EVP_sha512(), secret_key.c_str(), secret_key.length(), (const unsigned char*)buffer, strlen(buffer), (unsigned char*)hmac_result, &hmac_len);// Base64 编码签名结果int signature_len = EVP_ENCODE_LENGTH(hmac_len);char encoded_signature[4096 * 2] = {0}; EVP_EncodeBlock((unsigned char*)encoded_signature , (unsigned char*)hmac_result, hmac_len);// 构建 JWT tokenint token_len = unsigned_data_len + remove_padding((char *)encoded_signature , strlen(encoded_signature) ) + 1; // 加上两个点号和结尾的 null 字符snprintf(buffer, token_len, "%s.%s.%s", std::string(encoded_header, remove_padding((char*)encoded_header, strlen(encoded_header))).c_str(), std::string(encoded_payload, remove_padding((char*)encoded_payload, strlen(encoded_payload))).c_str(), std::string(encoded_signature, remove_padding((char*)encoded_signature, strlen(encoded_signature))).c_str());return buffer;}void test_create_token(){std::string key = "chensong";std::string payload = "{\"chensong\":\"{\\\"userId\\\":18}\",\"sub\":\"chensong\"}";std::string token = create_token(key, payload);std::cout << "jwt token = " << token << std::endl;}}
}
总结
JWT的HS512算法实现源码地址: