HTTPS client hello 加密方案分析

需求概述

  1. encryption_algorithm_and_key(Map of String):指定 sub extension 是否加密、以及采用哪种加密算法、加密随机数。

  2. encrypted_fields_for_sni_white_list(Array of String):指定 SNI 类型业务需要加密的 sub extension 字段清单。

  3. encrypted_fields_for_dst_ip_white_list(Array of String):指定目的 IP 地址类型业务需要加密的 sub extension 字段清单。

实现设计

1 修改配置文件

在全局 HTTPS 头增强插入配置文件中,增加 encryption_key 字段存放密钥。

{
   "https_header_enrichment_config":{
       "state":"ACTIVE",
       "sni_white_map":{
           "www.baidu.com":"192.168.0.2",
           "www.99cloud.net":"192.168.0.3"
       },
       "open_sni_cheat_proof":true,
       "dst_ip_white_list":[
           "192.168.0.2/24",
           "192.168.0.0/24"
       ],
       "identify_dst_IP_white_list_only":false,
       "extension_type":17516,
       "encryption_algorithm_and_key":{
           "RC4":"justfortest"
       },
       "encrypted_fields_for_sni_white_list":[
           "x-up-calling-line-id",
           "x-up-bear-type",
           "x-forwarded-for"
       ],
       "encrypted_fields_for_dst_ip_white_list":[
           "SUPI",
           "PEI",
           "GPSI",
           "ULI",
           "DNN",
           "UPF-IP",
           "RAT"
       ]
   }
}
<?xml version="1.0" encoding="UTF-8" ?>
  <https_header_enrichment_config>
  	<state>ACTIVE</state>
  	<sni_white_map>
  		<www.baidu.com>192.168.0.2</www.baidu.com>
  		<www.99cloud.net>192.168.0.3</www.99cloud.net>
  	</sni_white_map>
  	<open_sni_cheat_proof>true</open_sni_cheat_proof>
  	<dst_ip_white_list>192.168.0.2/24</dst_ip_white_list>
  	<dst_ip_white_list>192.168.0.0/24</dst_ip_white_list>
  	<identify_dst_IP_white_list_only>false</identify_dst_IP_white_list_only>
  	<extension_type>17516</extension_type>
  	<encryption_algorithm_and_key>
  		<RC4>justfortest</RC4>
  	</encryption_algorithm_and_key>
  	<encrypted_fields_for_sni_white_list>x-up-calling-line-id</encrypted_fields_for_sni_white_list>
  	<encrypted_fields_for_sni_white_list>x-up-bear-type</encrypted_fields_for_sni_white_list>
  	<encrypted_fields_for_sni_white_list>x-forwarded-for</encrypted_fields_for_sni_white_list>
  	<encrypted_fields_for_dst_ip_white_list>SUPI</encrypted_fields_for_dst_ip_white_list>
  	<encrypted_fields_for_dst_ip_white_list>PEI</encrypted_fields_for_dst_ip_white_list>
  	<encrypted_fields_for_dst_ip_white_list>GPSI</encrypted_fields_for_dst_ip_white_list>
  	<encrypted_fields_for_dst_ip_white_list>ULI</encrypted_fields_for_dst_ip_white_list>
  	<encrypted_fields_for_dst_ip_white_list>DNN</encrypted_fields_for_dst_ip_white_list>
  	<encrypted_fields_for_dst_ip_white_list>UPF-IP</encrypted_fields_for_dst_ip_white_list>
  	<encrypted_fields_for_dst_ip_white_list>RAT</encrypted_fields_for_dst_ip_white_list>
  </https_header_enrichment_config>

注:encryption_algorithm_and_key 标签是一个 key - value 的映射关系, value 是加密随机数, key 有3种取值,分别是: RC4 、 DES 、 AES ,表示密钥所属的算法,不同算法对密钥的要求不一样,配置时需确认密钥是否正确。各算法对密钥长度的要求如下: RC4 – 密钥长度为 1-256 字节; DES – 密钥长度为 8 字节; AES – 密钥长度为 16/24/32 位。

2 修改 picohttpparser.h 文件

在 picohttpparser.h 文件中修改 http_header_enrichment_config_t 结构体,新增 char * encryption_key 成员,该成员用于存放密钥。

3 修改 upf_conf.c 文件

从配置文件中读取密钥,并存放到 upf_main.https_HE_config.encryption_key 中 。

4 修改 upf_pfcp.c 文件

在 upf_pfcp.c 文件中增加对 HTTPS 头加密的操作。

HTTPS client hello 加密方案设计1

修改内容如下图:

image-20210302170800000

5 新增头增强字段加密函数

该函数将根据指定的算法类型使用 Openssl 实现加密。

/* src_data  : 要加密的数据内容
* result    : 加密结果
* algorithm : 加密算法
* key       : 密钥
* 返回值    : 加密后的字段长度
*/
int encryped_header_field(char **src_data, char **result, const u8 algorithm, char **key);

Openssl 加密算法 API 使用

此章节简单叙述 openssl 的算法 API 使用,代码块部分为官方 API 参数介绍。

1.openssl rc4

加密/解密:

1) #include <openssl/rc4.h>

2) RC4_KEY key;

3) RC4_set_key(&key, strlen(key_data), key_data); // 设置密钥

void RC4_set_key(RC4_KEY *key, int len, const unsigned char *data);

4) RC4(&key, strlen(src_data), src_data, encry_data); // 加密明文 ( src_data ) 到结果 ( encry_data ) 中

void RC4(RC4_KEY *key, unsigned long len, const unsigned char *indata,
  	unsigned char *outdata);

2.openssl des

加密/解密:

1) #include <openssl/des.h>

2) DES_cblock key;

3) DES_key_schedule schedule; // 密码表

4) DES_cblock ivec; // 初始化向量 IV (用于 CBC 模式)

5) DES_string_to_key( key_string, &key); // 设置密钥。注1。

6) DES_set_key_checked(&key, &schedule); // 初始化密钥并对奇偶校验位进行检查

或 DES_set_key_unchecked(&key, &schedule); // 初始化密钥但不检查校验

/*函数作用 : 这两个函数的主要区别在于是否检测key的奇偶校检位
*           checked会对奇偶校检位进行检查,如果校检位错误,返回-1,如果key强度比较弱,返回-2;
*           unchecked则不会检查
*/
int DES_set_key_checked(const_DES_cblock *key, DES_key_schedule *schedule);
void DES_set_key_unchecked(const_DES_cblock *key, DES_key_schedule *schedule);

7) DES_ecb_encrypt(&src_data, &encry_data, &schedule, 1); // ECB模式加密[^2][^3]。

或 DES_ncbc_encrypt(&encry_data, &decry_data, sizeof(encry_data), &schedule, &ivec, 1)

// CBC 模式加密

/*函数作用 : DES ECB计算
*input    : 输入数据;(8字节长度)
*output   : 输出数据;(8字节长度)
*ks       : 密钥
*enc      : 加密:DES_ENCRYPT , 解密:DES_DECRYPT
*/
void DES_ecb_encrypt(const_DES_cblock *input,DES_cblock *output, \
  	DES_key_schedule *ks,int enc);

/*input   : 输入数据;(8字节长度)
*output  : 输出数据;(8字节长度)
*length  : 数据长度;(这里数据长度不包含初始化向量长度)
*schedule: 密钥;
*ivec    : 初始化向量;(一般为8个字节0)
*enc     : 加密:DES_ENCRYPT , 解密:DES_DECRYPT
*/
void DES_ncbc_encrypt(const unsigned char *input,unsigned char *output, \
  	long length,DES_key_schedule *schedule,DES_cblock *ivec,int enc);

备注:

[^1] : 自己指定密钥时,切记勿使用函数DES_string_to_key,该函数是根据输入的string随机计算key,并不是将输入的string当作key。

[^2] : ECB模式下,设置key必须使用DES_set_key_unchecked函数,而不能使用DES_set_key_check函数,否则计算出来的数据会不正确。

[^3] : 最后一个参数mode,值为1则加密,0则解密。

[^4] : openssl在进行DES运算时,仅按8字节块加密,所以必须自己进行数据拆分。

3.openssl aes

加密:

1) #include <openssl/aes.h>

2) AES_KEY aes_key;

3) unsinged char ivec[AES_BLOCK_SIZE]; // 初始化向量 IV (用于 CBC 模式)

4) AES_set_encrypt_key( key, 128, &aes_key); // 初始化密钥

/*函数作用 : 设定加密用的Key
*userKey  : 密钥数值
*bits     : 密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128
*key      : AES_KEY对象指针
*返回值   : 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256
*/
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,AES_KEY *key);

5) AES_cbc_encrypt( &src_data, &encry_data, sizeof(src_data), &aes_key, ivec, 1);

/*函数作用 : AES加密/解密单个数据块,CBC模式
*in       : 需要加密/解密的数据
*out      : 计算后输出的数据
*length   : 数据长度
*key      : 密钥
*ivec     : 初始向量
*enc      : AES_ENCRYPT 代表加密, AES_DECRYPT代表解密
*/
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, \
  	size_t length, const AES_KEY *key,    unsigned char *ivec, const int enc);

解密:

1) AES_set_decrypt_key( key, 128, &aes_key); // 初始化密钥

/*函数作用 : 设定解密用的Key
*userKey  : 密钥数值
*bits     : 密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128
*key      : AES_KEY对象指针
*返回值   : 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256
*/
int AES_set_decrypt_key(const unsigned char *userKey, const int bits,  AES_KEY *key);

2)AES_cbc_encrypt( &src_data, &encry_data, sizeof(src_data), &aes_key, ivec, 0);

/*函数作用 : AES加密/解密单个数据块,CBC模式
*in       : 需要加密/解密的数据
*out      : 计算后输出的数据
*length   : 数据长度
*key      : 密钥
*ivec     : 初始向量
*enc      : AES_ENCRYPT 代表加密, AES_DECRYPT代表解密
*/
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,   size_t length, \
  	const AES_KEY *key,    unsigned char *ivec, const int enc);