# 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 头加密的操作。 >![HTTPS client hello 加密方案设计1](../../../_static/HTTPS_client_hello_crypto_1.png) >修改内容如下图: >![image-20210302170800000](../../../_static/HTTPS_client_hello_crypto_2.png) ### 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); >```