# 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` 字段存放密钥。
>
>```bash
>{
> "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
>
>
> ACTIVE
>
> 192.168.0.2
> 192.168.0.3
>
> true
> 192.168.0.2/24
> 192.168.0.0/24
> false
> 17516
>
> justfortest
>
> x-up-calling-line-id
> x-up-bear-type
> x-forwarded-for
> SUPI
> PEI
> GPSI
> ULI
> DNN
> UPF-IP
> RAT
>
>```
>
>注:`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 头加密的操作。
>
>修改内容如下图:
>
### 5 新增头增强字段加密函数
>该函数将根据指定的算法类型使用 Openssl 实现加密。
>
>```C
>/* 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
>
>2) RC4_KEY key;
>
>3) RC4_set_key(&key, strlen(key_data), key_data); // 设置密钥
>
>```C
>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 ) 中
>
>```C
>void RC4(RC4_KEY *key, unsigned long len, const unsigned char *indata,
> unsigned char *outdata);
>```
2.openssl des
>加密/解密:
>1) #include
>
>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); // 初始化密钥但不检查校验
>
>```C
>/*函数作用 : 这两个函数的主要区别在于是否检测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 模式加密
>
>```C
>/*函数作用 : 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
>
>2) AES_KEY aes_key;
>
>3) unsinged char ivec[AES_BLOCK_SIZE]; // 初始化向量 IV (用于 CBC 模式)
>
>4) AES_set_encrypt_key( key, 128, &aes_key); // 初始化密钥
>
>```C
>/*函数作用 : 设定加密用的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);
>
>```C
>/*函数作用 : 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); // 初始化密钥
>
>```C
>/*函数作用 : 设定解密用的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);
>
>```C
>/*函数作用 : 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);
>```