JWT使用HS512算法生成全局服务token原理

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 u
		int 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 token
			int 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算法实现源码地址:

你可能感兴趣的:(项目总结,java,算法,c++,token,JWT)