简要说明
本设计为湖南大学密码学的一次课程作业设计。非作业目的可随意引用。
由于本人初次接触密码学,本设计可能存在问题以及漏洞。若发现望指出。
GitHub :
中文utf-8
简单偏移替换密码
初次尝试
中文utf-8的读取
utf-8
的格式
UTF-8编码规则:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。
获取单个utf-8
编码的长度,注意当最高位为0
情况。
int get_utf_8_len(char s){ int i = 0x80,len = 0; while(s&i) {i=i>>1;len++;} return len==0?1:len;}
从byte
数组中获取单个utf-8
字符
word_length = get_utf_8_len(word_byte[i]);strncpy(word_utf_8,word_byte+i,word_length);
从3字节utf-8
字符中获取utf-8
编号
int get_utf_8_code(char *s){ return (*s & 0x0F)<<12 | (*(s+1)&0x3F)<<6 |(*(s+2)&0x3F);}
获取中文字符
常用中文显示范围
U+4e00 - U+9fa5
故利用utf_8_is_cn
来判断
#define max_cn_utf_8 0x9fa5#define min_cn_utf_8 0x4e00#define cn_utf_8_size (max_cn_utf_8-min_cn_utf_8)#define utf_8_is_cn(code) (code>=min_cn_utf_8 && code
偏移加密测试
/** * Caesar_cipher_encrpt * 简单凯撒加密测试,bias为偏移量 * 常用中文大小为cn_utf_8_size */void Caesar_cipher_encrpt(int_32U *plain_code,int_32U *cipher_code,int_32U bias){ *cipher_code = ((*plain_code-min_cn_utf_8)+bias)%cn_utf_8_size + min_cn_utf_8;}/** * Caesar_cipher_decrpt * 简单凯撒解密测试,bias为偏移量 * 常用中文大小为cn_utf_8_size */void Caesar_cipher_decrpt(int_32U *cipher_code,int_32U *plain_code,int_32U bias){ *plain_code = ((*cipher_code+cn_utf_8_size-min_cn_utf_8)-bias)%cn_utf_8_size + min_cn_utf_8;}
主函数中测试:
if(utf_8_is_cn(utf_8_code)){ Caesar_cipher_encrpt(&utf_8_code,&cipher_code,100); get_utf_8_word(cipher_code, word_utf_8); printf("Encrpted: %s ",word_utf_8); Caesar_cipher_decrpt(&cipher_code,&utf_8_code,100); get_utf_8_word(utf_8_code, word_utf_8); printf("Decrpted: %s\n",word_utf_8);}
测试结果
Encrpted: 云 Decrpted: 中Encrpted: 旫 Decrpted: 文Encrpted: 匄 Decrpted: 加Encrpted: 尪 Decrpted: 密Encrpted: 涯 Decrpted: 测Encrpted: 谹 Decrpted: 试
注意 第一部分代码中有部分错误
在进行下面改进的过程中发现,没有用unsigned
的话,会导致%
为有符号数。
故将原来的int
重新定义如下
typedef unsigned int int_32U;typedef unsigned long long int_64U;
加密解密程序框架设计
~ ./encryption test outEncrption end!~ ./decryption out test1Decryption end!
初版简单偏移替换加密解密见Caesar_cipher
中源代码。
替换加密加强
由于中文文字过多,使用完整的密码转换本不再合理。故分析针对每一个明文,可用的因素有以下几点:
- 类似秘钥的
seed
- 明文所在位置:明文字符所在位置作为一个因素引入,可以防止相同的字替换到相同的字符,尽量避免统计概率的暴露。
搭建起支持变换seed
的框架如下:
#define find_bias(seed1,seed2) generate_bias_simple(seed1,seed2)typedef unsigned int int_32U;typedef unsigned long long int_64U;int generate_bias_simple(int_32U seed1,int_64U seed2);
在传入替换加密解密时,利用生成的bias
bias = find_bias(count,(int_64U)seed_high<<32|seed_low);Caesar_cipher_decrpt(&utf_8_code,&plain_code,bias);
而最简单的,利用上述两个因素的bias
计算如下:
int generate_bias_simple(int_32U seed1,int_64U seed2){ return (seed1*seed2)%cn_utf_8_size;}
该算法加密主要部分代码
while(!feof(fp_in)){ if(fgets(word_byte,max_len,fp_in)==NULL) continue; for(i = 0; word_byte[i]!='\0';i = i + word_length){ count ++; //获取utf-8编码该字长度 word_length = get_utf_8_len(word_byte[i]); //获取utf-8字,放入word_utf_8中 strncpy(word_utf_8,word_byte+i,word_length); word_utf_8[word_length] = '\0'; // printf("%s",word_utf_8); if(word_length == 3) { //获取当前utf-8字符的utf-8码 utf_8_code = get_utf_8_code(word_byte+i); //若utf-8为中文 进行加密 if(utf_8_is_cn(utf_8_code)){ bias = find_bias(count,(int_64U)seed_high<<32|seed_low); //偏移替换加密 Caesar_cipher_encrpt(&utf_8_code,&cipher_code,bias); //获取utf_8的字 get_utf_8_word(cipher_code, word_utf_8); fprintf(fp_out,"%s",word_utf_8); } else{ fprintf(fp_out,"%s",word_utf_8); } } else fprintf(fp_out,"%s",word_utf_8); }}
该算法解密主要部分代码
while(!feof(fp_in)){ if(fgets(word_byte,max_len,fp_in)==NULL) continue; for(i = 0; word_byte[i]!='\0' ;i = i + word_length){ count ++; //获取utf-8编码该字长度 word_length = get_utf_8_len(word_byte[i]); //获取utf-8字,放入word_utf_8中 strncpy(word_utf_8,word_byte+i,word_length); word_utf_8[word_length] = '\0'; if(word_length == 3) { //获取当前utf-8字符的utf-8码 utf_8_code = get_utf_8_code(word_byte+i); //若utf-8为中文 进行解密 if(utf_8_is_cn(utf_8_code)){ //计算偏移量 bias = find_bias(count,(int_64U)seed_high<<32|seed_low); //凯撒密码解密部分 Caesar_cipher_decrpt(&utf_8_code,&plain_code,bias); get_utf_8_word(plain_code, word_utf_8); fprintf(fp_out,"%s",word_utf_8); } else{ fprintf(fp_out,"%s",word_utf_8); } } else fprintf(fp_out,"%s",word_utf_8); }}
加密解密测试
test_txt
内容
句子测试:在密码学中,恺撒密码(英语:Caesar cipher),或称恺撒加密、恺撒变换、变换加密,是一种最简单且最广为人知的加密技术。相同字符测试:密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密。
输入命令加密
~ ./encryption test_txt out ab12idhsEncrption end!
out
内容
偮呢酊媸:鮯禕鉙片迻,魚確汷懺(旼駥:Caesar cipher),嚐饗籪紋阆鮵、瘺盛鄎鴡、捨潻覦轕,澏叩簿鑟參嚅齲捂薮燲滻煮沖玟祎粑媈。蹐柭騥擘历鳽:郭赶暾捇忐誽蝆悎崗妠蒍脖綟囧印繝竦睯傷黥砭璶焿鰬颵锾溆欏闼銅輎桖擟慨豕裞。
输入命令解密
~ ./decryption out test_out ab12idhsDecryption end!
test_out
内容
句子测试:在密码学中,恺撒密码(英语:Caesar cipher),或称恺撒加密、恺撒变换、变换加密,是一种最简单且最广为人知的加密技术。相同字符测试:密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密。
若输入秘钥错误 (只错了一位)
~ ./decryption out test_out ab12idhrDecryption end!
test_out
内容
另孒济诙:圯寎砊孰丸,悇撠寕砑(范谀:Caesar cipher),戺稕悠撹勈寯、悥撾吅掐、合掓勒对,晤丶稄朸箹厏乏朼庼乸仹砥盅勢尉拄杴。着呕审筱涗谢:尖尗尘尙尚尛尜尝尞尟尠尡尢尣尤尥尦尧尨尩尪尫尬尭尮尯尰就尲尳尴尵尶尷尸尹。
很明显,在秘钥在只错一位的情况下,已经在正确的明文周围了,下面就来想办法结局一下这个相关性
打乱秘钥与偏移的连续性
由于之前已经写好了整体的框架,这里只需要更改generate_bias_simple
函数中的内容。
这次增强的主要原因是,当密钥只有少数几位差距时,乘法所具有的连续性不能够很好的被打乱,所以导致上面密文与原文过于接近。
所以希望能找到一种映射,从连续的秘钥,映射到非连续的秘钥。
经过思考,觉得希望每次所乘秘钥均不同。故设计以下改变seed2
的办法。(可能有数学缺陷,先这么设计着,还不知道怎么证明)
- 右边32-bit等于与左32-bit异或
- 交换左右部分
- 循环左移
seed1&0x07
位
测试函数如下:(见key_test.c
文件)
int main(int argc, const char * argv[]) { int_64u s_key = 0xf18283a18c4d5fb1; int count; printf("%llx\n",s_key); for(count = 0; count <100;count ++){ s_key = s_key>>32 | ((int)s_key^(s_key>>32))<<32; s_key = s_key>>(64-(count&0x7))|s_key << (count&0x7); printf("%llx\n",s_key); } return 0;}
截取部分如下
11ac4ddcb0ddcedb42e3060e23589bb986ee76dd0b8c18396b1377243773b6ecc60c1c86b1377245e76dd878c18390ce
由于是移动是0 1 2 … 7
则每次是移动28 bit
,且每次均有异或。其循环的周期还是比较大的。
修改产生bias
函数如下:
int generate_bias_advance(int_32U seed1,int_64U seed2){ seed2 = seed2>>32 | ((int)seed2^(seed2>>32))<<32; seed2 = seed2>>(64-(seed1&0x7))|seed2 << (seed1&0x7); return (seed2*seed1)%cn_utf_8_size;}
修改后利用秘钥ab12idhr
解密ab12idhs
加密的上面同一段文字,结果为
匧塘摣琕:儼壎煓鸳缪,楤度爓爑(根厅:Caesar cipher),腅嫉少緥镭軽、萜呁磻门、鎭邩娍枨,緷旐秭券儲仢鏛髹棬鳹欓急韫鏲觍曹摁。局澃郉酭鏩箌:辻煍蕘婍勤过婣笥賃歝祸鐲甉艬酒非觋敭涘籲霮甑皜懲蛓彽憸撲枮架實縫菛妍嗘麗。
这次第二排没有明显与位置相关的痕迹了。
性能测试
测试环境
利用2k字的文件进行测试,并在代码中加入统计部分。
~ ./encryption test_2k out ab12idhrEncrption finish! Count : 1843 Time : 0.000746secondsSpeed : 2469 k per socend~ ./decryption out test_out ab12idhrDecryption finish! Count : 1843 Time : 0.000720secondsSpeed : 2558 k per socend
存在的问题 & 改进方向
- 针对每个字均更改秘钥可能导致效率不够高,可以更改成每一轮更改秘钥。
- 没有验证数学上的严谨性,可能存在周期性问题,当获取大量明文密文对是可能会受到攻击。
- 可以在输入秘钥上提供更友好的16进制输入方式。
- 可以添加加密接口供其他程序使用。
英文 utf-8
替换密码
简要说明
英文utf-8
替换密码的设计继承了前文中文的加密,用法与文件目录相同,不再展示说明。
设计内容
设计思路
由于已经完成了utf-8
的中文加密的内容,所有希望能够有足够的兼容性,能够同时加密中文以及英文。故继承了中文加密的算法,只做出了少数改变。
添加英文utf-8
相关宏
#define min_en_utf_8 0x61#define max_en_utf_8 0x7a#define en_utf_8_size (max_en_utf_8-min_en_utf_8)#define utf_8_is_en(code) (code>=min_en_utf_8 && code < max_en_utf_8)
修改偏移量宏,使其能够兼容英文
#define find_bias_cn(seed1,seed2) generate_bias_advance(seed1,seed2,cn_utf_8_size)#define find_bias_en(seed1,seed2) generate_bias_advance(seed1,seed2,en_utf_8_size)
增加英文加密解密替换模块
void Caesar_cipher_encrpt_en(int_8U *plain_code,int_8U *cipher_code,int_8U bias){ *cipher_code = ((*plain_code-min_en_utf_8)+bias)%en_utf_8_size + min_en_utf_8;}void Caesar_cipher_decrpt_en(int_8U *cipher_code,int_8U *plain_code,int_8U bias){ *plain_code = ((*cipher_code+en_utf_8_size-min_en_utf_8)-bias)%en_utf_8_size + min_en_utf_8;}
增加英文处理模块
else if(word_length == 1){ utf_8_en_code = word_utf_8[0]; if(utf_8_is_en(utf_8_en_code)){ count_work++; count_en++; //这里是英语处理 bias = find_bias_en(count_en,(int_64U)seed_high<<32|seed_low); Caesar_cipher_encrpt_en(&utf_8_en_code,&cipher_en_code,bias); *word_utf_8 = cipher_en_code; *(word_utf_8 + 1) = 0x00; fprintf(fp_out,"%s",word_utf_8); } else{ fprintf(fp_out,"%s",word_utf_8); }}
测试功能
同中文相同的处理内容,处理结果如下(秘钥ab12idhs
):
瓍赋忲躗:締跁妕軂锻,牴譊崙詒(昉萘:Ceuqvm pfgssl),竨孵短餀藼釁、惫苨鈒郵、枊衭賡廹,巔醳捱嬈愷軷吉遞愴器撞窧鳰辣摽酢瓴。娐賉馻蚟倌包:烙莓鮋樁閰羫咯膄儯闤鳬泃鬴誻櫏尟忩蓴鹍澅伓闋胯衟釤靅三片咗伶霏拺爺處佪甉。
性能测试
同样找了一个大约为2k字的测试文档,保存在test_2k
,测试环境同中文加密测试,测试结果如下:
~ ./encryption test_2k test_out ab12idhsStartEncrption finish! Count : 8448 Time : 0.003290secondsSpeed : 2559 k per socend~ ./decryption test_out test_2k ab12idhsDecryption finish! Count : 8448 Time : 0.003720secondsSpeed : 2264 k per socend
这个结果可以说明,计算秘钥的函数严重影响了效率
存在的问题
- 由于英语的字母较少,很容易替换到相同的字母,大大减少了可替换的空间。
- 为了兼容中文加密,英语也采用中文的比较复杂的算法,导致其速度没有改善。
- 英文用替换加密实际证明不太靠谱,还是需要结合扩散形成更复杂的分组加密。