基于C语言的UTF-8中英文替换密码设计

简要说明

本设计为湖南大学密码学的一次课程作业设计。非作业目的可随意引用。

由于本人初次接触密码学,本设计可能存在问题以及漏洞。若发现望指出。

GitHub : https://github.com/He11oLiu/SubstitutionCipher

中文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 <max_cn_utf_8)

偏移加密测试

/**
 *  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 out
Encrption end!
~ ./decryption out test1
Decryption 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 ab12idhs
Encrption end!

out内容

偮呢酊媸:
𫚗祎鉙片迻,鱼确汷忏(旼駥:Caesar cipher),尝飨簖纹阆𫚛、瘘盛鄎鴡、舍潻觎轕,澏叩簿鑟参嚅龋捂薮燲浐煮冲玟祎粑𫝨。
蹐柭騥擘历𫛚:
郭赶暾捇忐誽蝆悎岗妠𫇭脖𫄫囧印繝竦睯伤黥砭璶焿鰬颵锾溆椤闼铜輎桖𪭧慨豕裞。

输入命令解密

~ ./decryption out test_out ab12idhs
Decryption end!

test_out内容

句子测试:
在密码学中,恺撒密码(英语:Caesar cipher),或称恺撒加密、恺撒变换、变换加密,是一种最简单且最广为人知的加密技术。
相同字符测试:
密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密密。

若输入秘钥错误 (只错了一位)

~ ./decryption out test_out ab12idhr
Decryption 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;
}

截取部分如下

11ac4ddcb0ddcedb
42e3060e23589bb9
86ee76dd0b8c1839
6b1377243773b6ec
c60c1c86b1377245
e76dd878c18390ce

由于是移动是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 ab12idhr
Encrption finish! Count : 1843 Time : 0.000746seconds
Speed : 2469 k per socend
~ ./decryption out test_out ab12idhr
Decryption finish! Count : 1843 Time : 0.000720seconds
Speed : 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 ab12idhs
StartEncrption finish! Count : 8448 Time : 0.003290seconds
Speed : 2559 k per socend
~ ./decryption test_out test_2k ab12idhs
Decryption finish! Count : 8448 Time : 0.003720seconds
Speed : 2264 k per socend

这个结果可以说明,计算秘钥的函数严重影响了效率

存在的问题

  • 由于英语的字母较少,很容易替换到相同的字母,大大减少了可替换的空间。
  • 为了兼容中文加密,英语也采用中文的比较复杂的算法,导致其速度没有改善。
  • 英文用替换加密实际证明不太靠谱,还是需要结合扩散形成更复杂的分组加密。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章