基於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

這個結果可以說明,計算祕鑰的函數嚴重影響了效率

存在的問題

  • 由於英語的字母較少,很容易替換到相同的字母,大大減少了可替換的空間。
  • 爲了兼容中文加密,英語也採用中文的比較複雜的算法,導致其速度沒有改善。
  • 英文用替換加密實際證明不太靠譜,還是需要結合擴散形成更復雜的分組加密。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章