密碼學(二月最佳)

                                        《主幹目錄

第一代加密

 隱藏


第二代加密

          移位

 替代

 同音替代


第三代加密

          維吉尼亞加密


第四代加密

  Enigma(恩尼格瑪機)


第五代加密

           DES


第六代加密

  RSA


第七代加密

  量子加密


        密碼學數千年的發展史,加密與解密不斷博弈。

        加密解密算法層出不窮,因此抓住密碼學發展史來突破複雜的表面,看懂密碼學最基礎的原理。

        您,願意聽我講述嗎?

         . .. . ..


密碼學演化進程(系統學習)

【古典加密】

  • 手工加密
  1.    隱藏法
  2.    移位法和替代法
  3.    維尼吉亞加密
  • 機器加密 【 圖靈極限 】
  1.    恩尼格瑪機

 【現代加密】

  • 計算機加密【   工程技術最大推動力   】
  1.    DES魔王加密系統
  2.    RSA加密系統
  3.    量子加密
  4.    WIFI 破解 

 上面的內容全部更新完了,不過我還想補充一些上去。

 【程序代碼】 

  簡單加密解密寫代碼, 默認C語言

  •   行列互換
  •   凱撒加密解密
  •   凱撒加密暴力破解
  •   ASCII碼全部字符加密解密與破解(凱撒演化)
  •   字符串安全輸入選擇
  •   摩爾斯電碼加密解密(Python)
  •   單套密碼頻率分析
  •   維吉尼亞加密解密
  •   維吉尼亞鑰匙長度破解[重合互指數法、Kasiski測試法](更新ing...)
  •   多套密碼頻率分析(更新ing...)
  •   循環移位加密
  •   打印二進制 ASCII 表
  •   僞隨機數算法解析
  •   C標準庫 rand( ) 函數僞隨機數破解
  •   打造僞隨機字符池實現真隨機數
  •   Marsaglia 首創 Knuth 的真隨機數算法
  •   異或加密、改進
  •   異或+循環位移加密圖片實戰
  •   大數計算(算法專欄的算法司南)

  複雜加密解密以寫好的框架解析,根據加密術原理寫代碼

【基礎數學知識】

  •  我已經寫好了初學數論,不過還有一些證明沒加,持續更新。

     加密的原則 :假設敵方已經擁有和自己一樣的解密水準,接着在這個基礎上制定加密手段,此加密方法要好用,不能一條信息幾分鐘還沒發出去,要求加密解密時間短,成本低。  

    數學的建議 :   加密的過程可以看成一個函數的運算,解密的過程可以看成是反函數的運算。原文是自變量,密文是函數值。

好的(加密鑰匙)函數不能通過幾個(原文)自變量和(密文)函數值就可推出函數。

這篇博客就是以上面的內容展開的,其中加密術的理論是零基礎的。代碼編寫能力在實現計算機加密算法要求頗高,不過我已經爲您提供了最典型的代碼片段減少編寫難度~

另外,精心配備如下資料,可降低計算機加密難度:


第一代加密

  隱藏法

     歷史上,第一個加密法由希羅多德古希臘歷史學家記載。那時,強盛的波斯帝國計劃入侵希臘,斯巴達的老國王得證實後,在一塊木板上些許這個消息並塗上一層蠟。這塊木板躲過沿路士兵的檢查,抵達斯巴達。收板人颳了蠟,就發現了下面的密報,於是馬上備戰。在公元前480年,擊毀了波斯200艘戰艦,5年的準備毀於一旦。

希羅多德的記錄,

  • 保密信息寫在送信人已剃光頭髮的頭皮上, 等頭髮長出來後,讓其去送信只是時長要月算,時效性不好;
  • 消息寫在綢緞上,用蠟裹成一個小球,讓送信人吞下去,收信時想辦法取出來;
  • 熟雞蛋殼上用 明礬和醋 組合的藥水 寫密文這樣蛋殼上不會有任何痕跡,剝了皮就能看見蛋白有字;

中華加密傳說,

  • 相傳安徽省鳳陽縣的農民朱元璋等人於中秋節前夕把“殺韃子”的紙條包在月餅裏,號召各家各戶殺掉統治漢人的蒙族韃子,後來朱元璋果真推翻元朝統治建立了明朝,自稱明太祖。
  • 又一傳說雲,北宋年間,遼國奸細王欽若打入宋朝內部“臥底”,官至“樞密使”,遼國爲了送密信給王時逃避路上盤查,竟把傳書人的大腿切開,把密件臘丸塞入大腿的肌肉裏,等腿傷痊癒後,再去宋朝,見到王欽若,此人把腿切開,把密件交王執行!
  • 《唐伯虎點秋香》使用一種行列互換,只能說有文采讓人行列都讀的像是一篇文字
  • 《龍游天下》丁五味借國主宰縣令,用烏賊?肚裏的墨汁寫借條,日久漸消;

檸檬加密:

    加密:用檸檬汁寫在白紙上的字,幹了以後就看不見了。

    解密:用電吹風一加熱,字跡就能顯現出來,這因爲檸檬的酸性腐蝕了紙張。

解密: 只需要受過專業訓練的士兵檢查嚴格(留意 蠟板、頭髮顏色,熟雞蛋直接剝殼),那麼類似的隱藏術是毫無用處的。

《唐伯虎點秋香》行列加密,

  • 唐寅今年十八屬姑蘇人氏身家清白素無遇犯只
  • 家況清貧留身華相府中充當書僮身價銀五十兩自
  • 節起暫爲僧房僕三年後支取從此次承值書房每日焚
  • 掃地洗硯磨墨等等聽貧使喚從頭做起立此爲據爲憑

因爲中文加密需要考慮編碼的問題,因此以英文加密舉例:

  • 加密 hello, world !

假設每行 5 個(任選)字母,共 3 行:

h e l l o
,   w o r
l d   !  

加密後:h,le dlw lo!or 

用 C4droid(android) 模擬出來:

第一行,設置每行爲 5 個字母;第二行,是加密;第三行,是解密。 

#include <stdio.h>
#include <string.h>

char *encode(char *buf, int line)
{
	int len = strlen(buf); 
	int nlen;
	if (len % line != 0)
		nlen = len + (line - len % line);
	else
		nlen = len;
	char *tmp = (char *)malloc(nlen + 1);
	char *secret = (char *)malloc(nlen + 1);
	char *psecret = secret;
	strcpy(tmp, buf);
	for (int i = strlen(tmp); i < nlen; i++)
		tmp[i] = ' ';    // 補空格
	tmp[nlen] = '\0';
	int row = nlen / line;
	char (*ptmp)[line] = tmp;
	for (int i = 0; i < line; i++)
	{
		for (int j = 0; j < row; j++)
		{
			*psecret++ = ptmp[j][i];
		}
	}
	*psecret = '\0';
	free(tmp);

	return secret;
}

char *decode(char *buf, int line)
{
	int len = strlen(buf);
	int nline = len / line;
	int row = line;
	char *desecret = (char *)malloc(len + 1);
	char *pd = desecret;
	char (*p)[nline] = buf;
	for (int i = 0; i < nline; i++)
	{
		for (int j = 0; j < row; j++)
		{
			*pd++ = p[j][i];
		}
	}
	*pd = '\0';
	while (*(--pd) == 32 )
		*pd = '\0';

	return desecret;
}

int main(int argc, const char* argv[])
{
     char buf[] = "hello, world !";
     printf("enter a number:>  ");
     int n;
     scanf("%d", &n);
	char *secret = encode(buf, n);
	printf("%s\n", secret);
	char *desecret = decode(secret, n);
	printf("%s\n", desecret);
	free(secret);
	free(desecret);

	return 0;
}

    純 C 實現,行列加密;好的代碼一看就懂,壓根不需要註釋;當然下面的代碼都會解釋思路和一些標準庫函數。

第二代加密

  移位法

    古羅馬時期, 凱撒加密術。對於信件中的每一個字母,會用 ta 後面的第 n 個字母代替。例如,n = 4 時,"China"加密的規則是用原來字母后面第 4 個字母代替原來的字母,即 "A" => "E", 如此,China => Glmre。

解密: 排列組合[非常麻煩,26^{n}種可能, n爲代替符號數量] 或 9世紀, 阿拉伯人發明 頻率分析法破解【概率論的出現】。

凱撒加密術代碼實現 :     解決了,加標點符號和截斷字符串問題,大小寫都是正常輸出的 ~

#include <stdio.h>
#include <string.h>
#include <ctype.h>

void EnCesar(char *str, int n);
void DeCesar(char *str, int n);
const int N = 2048;   // 某些場合不建議申請 2冪 的空間,容易被猜出來攻擊這個程序

int main(void)
{
	char str[N] = "";
	int n;
	printf("%s","明文 :> ");
	scanf("%2047[^\n]",str);   // 修改scnaf函數遇到空格就停止 ~, 並限制讀取長度爲2047
	while( printf("%s","移位 :> "),scanf("%d",&n), n>25); // 如果n大於25,重新輸入,26位剛好不變
	EnCesar(str,n);
	putchar(10);
	
	printf("%s -> %s\n","密文",str);
	DeCesar(str,n);
	printf("%s -> %s\n","明文",str);
	
	return 0;
}

void EnCesar(char *str, int n)
{
      if( !str  )  return;
	  for( ; *str; str++ )
	  {
	    if( isspace(*str) || ispunct(*str) || isdigit(*str) )  
	    {
	    	++str;
	         continue;
		// 跳過空格、標點符號、數字
	    }
		if( isupper(*str) ){   // 直接用'A' ~ 'Z'形式就破壞了C的可移植性,使用在EBCDIC字符集的機器上將會失敗!
			 *str -= 'A';  // 變成 0 ~ 25
			 *str = (*str+n)%26;
			 // 密文 = (字母 + n) mod 26
			 *str += 'A';
		}
		else if( islower(*str) ){   // 'a' ~ 'z'
			 *str -= 'a';  // 變成 0 ~ 25
			 *str = (*str+n)%26;
			 // 密文 = (字母 + n) mod 26
			 *str += 'a';
		}
	  }
}

void DeCesar(char *str, int n)
{
	 if( !str  )  return;
	  for( ; *str; str++ )
	  {
		if( isspace(*str) || ispunct(*str) || isdigit(*str) )  
		{
		      	++str;
		          continue;
		// 跳過空格、標點符號、數字
		}

		if( isupper(*str) ){  
			 *str -= 'A';  // 變成 0 ~ 25
			 *str += 26;                        // 避免減n爲負
			 *str = (*str-n)%26;
			 // 密文 = (字母 + n) mod 26,考慮到可讀性,我並沒有組合,您可以試一下
			 *str += 'A';
		}
		else if( islower(*str) ){
			 *str -= 'a';  // 變成 0 ~ 25
			 *str += 26;                      // 避免減n爲負
			 *str = (*str-n)%26;
			 // 密文 = (字母 + n) mod 26
			 *str += 'a';
		}
	  }
}

 字符串安全輸入選擇 : 字符串輸入的一些函數,可以跳過~(與密碼學沒什麼關係)

// p.s. C99輸入函數fgets(),第一種 指定stdin作爲輸入流來使 fgets() 模擬 gets() 的行爲,要記得fgets()會保留換行符會刷新緩衝區。在最後一個字符讀入數組中後,會再寫一個空字符到緩衝區結尾處, 所以最多讀取比指定輸入數量少1個的字符到數組中。考慮性能不推薦使用
char buf[BUFSIZE];
if(fgets(buf), sizeof(buf), stdin);

// 第二種,C11使用gets_s()會更安全、兼容gets(),TA只從stdin指向的流中讀取,不會保留換行符,同樣有一個參數來指定輸入最大字符數。如果這個參數爲0或者大於RSIZE_MAX、目標字符數組指針爲NULL,會退出函數,目標字符數組不會更改。否則最多讀取指定數量-1,-1是因爲在讀入的數組後要補一個空字符。
char buf[BUFSIZE];
if( gets_s(buf, sizeof(buf) == NULL){
      // 錯誤處理
}


// p.s. 第三種使用 getchar() , getchar() 返回stdin指向的輸入流中的下一個字符。一次讀取一個字符在控制上很靈活,也無額外性能開銷。
char buf[BUFSIZE];
while( ( ( ch = getchar() ) != '\n') && ch != EOF )
{
    if(index < BUFSIZE-1)
        buf[index++] = (unsigned char)ch;
    讀入數量++
}
buf[讀入的最後一個] = '\0'
//添加錯誤處理:
    if(feof(stdin)) { // 處理EOF }
    if(ferrors(stdin)) { // 處理錯誤 }
    if(讀入數量 > index) { // 處理截斷 }



// 如果讀取量級數據可使用 fread 優化, 設置頭指針 p1 和尾指針 p2,fread 一次讀入 1<<21 個字符存在 buf裏,然後用 p1 來調用;當 p1 撞到 p2 時再次用 fread 讀入 1<<21 個字符······

// fread優化宏定義實現 :
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;

////////////////////////////////////////////////////////////////////////////////////

// fread優化函數實現 :
char buf[1<<21],*p1=buf,*p2=buf;
inline int getc(){
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}
inline int redi() {
    int ret = 0,f = 0;char ch = getc();
    while (!isdigit (ch)) {
        if (ch == '-') f = 1;
        ch = getc();
    }
    while (isdigit (ch)) {
        ret = ret * 10 + ch - 48;
        ch = getc();
    }
    return f?-ret:ret;
}

// 第四種 TR 24731-2標準的getline(),如果輸入行過長,那麼TA不會截斷輸入而是使用realloc()調整緩衝區的大小
char *buf = malloc(buf_size)
if( (size = getline(&buffer, &buf_size, stdin) ) == -1 ){
      // 處理錯誤
}

free(buf)
buf = NULL;  // 防止野指針

古典密碼最小操作單位都是單個字符或者符號,如凱撒加密術。而現代密碼學,把研究對象用數來描述,再對數進行運算。不但突破了,字母作文最小單位對限制,還可以使用更高等的數學工具做運算,因此解密越來越難,但有趣是,解密方所需要的時間越來越少,從4000年的跨度變成1000年現在的頻率是幾十年甚至更少。

破解:倆種方法 [ 枚舉、頻率分析 ]

  • 暴力枚舉,26個字母加密一共25種可能,A是0,移位是不變的。在不知道鑰匙的情況下,枚舉出來很容易。我們不怕賊,就怕賊有耐心~
    for( int i = 1; i < 26; i ++ )   // 第 0 次不用,移位爲0不會變化
	{
        // 我們截獲的肯定是加密後的密文,所以使用解密函數枚舉 移位長度

	    DeCesar(str,i); printf("%d : %s\n",i,str);
	    EnCesar(str,i);  // 解密一次後要復原,下一次還要解密
	}
  • 頻率分析(下文細說)

p.s. 移位法分析工具:https://cryptii.com/pipes/caesar-cipher

再補充一種移位法(感謝紅領巾),整個ASCII碼字符加密解密與破解算法,原理同凱撒加密。

不過現在不是 26個子母,而是 127個 字符的加密解密與破解~ (p.s. 如果不瞭解ASCII碼錶,計算機加密有解釋)

#include <stdio.h>
#include <string.h>

int main(void)
{
	// ASCII 碼全部字符加密解密與破解
	char key[32] = "[ passWord + 666 ! ]";
	    //         //         //   加密
	for( int i = 0; i < strlen(key); i ++ )
	    key[i] += 10086;   // 移動 10086 位
	    puts(key);
	   
	    //        //         //    破解
	  for( int j=0; j<128; j ++ )
	 {
	 	printf("%-4d : > ",j);
	    for( int i = 0; i < strlen(key); i ++ )
     	    {
	    key[i] -= j;
	    printf("%c%c", key[i], i+1==strlen(key)?'\n':0);  // 方便閱讀可以把 0 改成空格
	    key[i] +=  j;
   	    }
   	}

       #if 0    // 運行這段代碼把 0 改成 1 就好,使用解密代碼,破解部分註釋掉即可得到key
       //        //         //    解密
	    for( int i = 0; i < strlen(key); i ++ )
	    key[i] -= 10086;   
	    puts(key);
	#endif
   
	return 0;
}

答案在 102 次枚舉,這個程序的破解(代碼在上面)枚舉 127 次 可以得到明文key。但這裏有一個值得思考的地方,當 10086(移位數)換成多少值,這個程序破解不了?枚舉,多枚舉幾次。

如果讓這個破解程序失效,值可以是 n(127+1)~n(127*2-1),n爲任意整數,0除外。

那麼怎麼改進呢,只需要改動破解代碼的一個地方,就可以解決這個問題~加油!

移位法,用這個改進版的話無論你加多少或者減多少位都可以在 枚舉 255 次以內破解得準確的密鑰key。

p.s. 把 j = 0 改成 j = -127

打印出來有許多的亂碼,實際上亂碼肯定不是原文,所以使用程序排除。程序再次改進,亂碼在ASCII碼亂碼即不是英文標點符號、阿拉伯數字、26個字母大小寫。代碼如下,也只需要添加倆個語句一個判斷、一個換行~

	    //        //         //    破解
	  for( int j=-127; j<=127; j ++ ,putchar(10) )
	 {
	 	printf("%-4d : > ",j);
	    for( int i = 0; i < strlen(key); i ++ ) 
   	    {
	    key[i] += j;
	    if( isprint(key[i]) ){
	    	// 如果 key[i] 是英文標點符號、0-9、A-Z和a-z、空格 , 就輸出。這樣就去亂碼了。
	     printf("%c%c", key[i], i+1==strlen(key)?'\n':' ');
	    }
	      key[i] -=  j;
   		}
   	}

這樣只需要找到最長的字符串對比 12 次,剛好他們一定是連續的~ 

更重要的是,如果TA的密文裏面有亂碼(非ASCII裏面的字符可能是一個表情或愛心?),那一定要跳,不然會導致破解只能破解數字部分。不過,我們理想環境接受的密文都是 ASCII 的...

暴力破解法總結,爲什麼暴力總結的這麼早。還不到這篇博客的 一半的一半,因爲暴力破解遇到後面的加密術會被虐成渣。得知道越是晚的加密術都有防範前一代加密術的解密法。

使用暴力破解的經驗:

  •  試想暴力破解法是否可行
  •  限定範圍再排列組合破解

舉個例子,試想是否可行,理想環境爲別人家中。

       >>判斷可行,假如您要偷一本書,首先您得確定TA在不在這戶人家。如果不在就找不到也就不找,所以不用暴力破解。

       >>限定範圍,如果在家裏是在A房間還是B房間,在某個房間裏我們應該帶着高科技工具按順序掃描書櫃。      

 替代法

     從16世紀的蘇格蘭女王瑪麗一世說起。瑪麗一世 27 歲時,被自己的姑姑英格蘭女王伊麗莎白一世關押了起來,這一關就是 18 年。到 ta 44 歲時,監獄裏的她和外界反叛軍密謀要殺害姑姑,一旦謀殺成功,ta 自己就能登上王位。當時傳信件是由侍女在遞紅酒時,藏在瓶塞中帶進去的。

瑪麗很聰明,包含暗殺計劃的並不是普通的信,而是加密過的。用的加密法便是替代法。所有英文字母被類似符文的東西替換,一些常用詞也用符號代替。代替方式,如下圖。

如果沒對照圖,誰也不能看懂,是嗎 ~  瑪麗此後就用這個加密方式與反叛軍(我覺得誰永遠的輸了,誰就是叛軍) ,一段時間後,瑪麗熟練掌握了,也不用一一對比來寫。這體現對加密的要求,不復雜實用。  可惜,這個傳信的人是雙面間諜,這些情況伊麗莎白全部知曉了。在位的女王正愁拿瑪麗沒辦法,現在終於可以言正名順的處死瑪麗了。

不過現在還急不得,必須抓到足夠硬的證據,而且最好把整個陰謀背後所有參與者一起除掉。此後,瑪麗和外界的通信,每一封都先經過雙面間諜送到密碼學校,花一定時間謄寫好,接着密封好遞出王宮。

那麼,密碼學校成功了嗎?? 這麼難的密碼呀 ?先看看著名的 摩爾斯電碼 ,再回來討論

摩爾斯電碼

  -- 與此類似的,還有著名的摩爾斯電碼

詳細介紹 : https://en.wikipedia.org/wiki/Morse_code

藉助 Python 的字典 實現摩爾斯電碼的加密解密,

# 莫爾斯電碼表,保存在字典中可以根據數組下標提取值一樣方便, A是 鍵 相當於數組下標,'._'是值相當與數組值
MORSE_CODE = { 'A':'.-', 'B':'-...',
		'C':'-.-.', 'D':'-..', 'E':'.',
		'F':'..-.', 'G':'--.', 'H':'....',
		'I':'..', 'J':'.---', 'K':'-.-',
		'L':'.-..', 'M':'--', 'N':'-.',
		'O':'---', 'P':'.--.', 'Q':'--.-',
		'R':'.-.', 'S':'...', 'T':'-',
		'U':'..-', 'V':'...-', 'W':'.--',
		'X':'-..-', 'Y':'-.--', 'Z':'--..',
		'1':'.----', '2':'..---', '3':'...--',
		'4':'....-', '5':'.....', '6':'-....',
		'7':'--...', '8':'---..', '9':'----.',
		'0':'-----', ', ':'--..--', '.':'.-.-.-',
		'?':'..--..', '/':'-..-.', '-':'-....-',
		'(':'-.--.', ')':'-.--.-' }

def encrypt(message):
	cipher = ''
	for letter in message:
		if letter != ' ':
			cipher += MORSE_CODE[letter] + ' '
		else:
			# 每隔一個字母空1格,每隔一個單詞空2格
			cipher += ' '
	return cipher

def decrypt(message):
	message += ' '
        # 要在解碼的摩爾斯碼末尾加一個空格 用作檢查提取字符並開始解碼
	decipher = ''
	citext = ''
	for letter in message:
		
		if (letter != ' '):
		# 檢查空格,i 統計空格的數量
			i = 0
			citext += letter
		else:
			i += 1

			if i == 2 :
                        # 倆個空格是新單詞
				decipher += ' '
			else:
				decipher += list( MORSE_CODE.keys() ) [list( MORSE_CODE.values()).index(citext) ] # 使用鍵值訪問鍵(加密相反)
				citext = ''
	return decipher


def main():
	message = "Earphones- Primetime Sexcrime"
	result = encrypt(message.upper())
	# 明文加密編成莫爾斯電碼
	print(result)

	message = ". .- .-. .--. .... --- -. . ... -....- .--. .-. .. -- . - .. -- .  ... . -..- -.-. .-. .. -- ."
	result = decrypt(message)
	# 密文解密翻譯爲英文原文
	print(result)

if __name__ == '__main__':
	main()

加密

         一次從一個單詞中提取一個字符,如果不是空格就與相應莫爾斯碼匹配,是空格就再加一個空格因爲新單詞要間隔

         將莫爾斯碼存儲在一個字符串的變量中,以後就在這個字符串變量中循環累加新的明文或密文

p.s. Python的字典在這裏非常好用,在C++裏面叫map。解密原理一樣

好了,繼續討論 瑪麗女王 ~

          愷撒密碼可以通過暴力破解來破譯,但替換密碼很難通過暴力破解來破譯,這是因爲替換密碼中可以使用的密鑰數量,比撒密碼多得多。

          爲了確認這一點,我們來計算一下簡單替換密碼中可以使用的密鑰總數吧。

p.s.一種密碼能夠使用的“所有密鑰的集合”稱爲密鑰空間( keyspace).所有可用密的總數就是密鑰空間的大小。密鑰空間越大,暴力破解就越困難。

替代密碼中,明文字母表中的a可以對應A、B、C、D .... Z (這26箇中的任意一個26種)b可以對應除a所對應的字母以外的剩餘 25 個字母中的任意 25 種,以此類推我們可以計算出替代密碼的密鑰總數爲:


26 × 25 × 24 × 23 × ... × 1= 4032914611266056355840000 = 4*10^{26}=2^{88}


這個數字相當於 4兆的約1000兆倍,密鑰的數量如此巨大,用暴力破解進行舉就會非常難。

因爲即便每秒能夠遍歷 10億 個密鑰,要遍歷完所有的密鑰也需要花費超過 120億年 的如果密碼破譯者的運氣足夠好,也許在第一次嘗試時就能夠找到正確的密銷,但反來說如果運氣特別差,也許嘗試到最後一次才能找到正確的密鑰因此平均來說,找到正確的鑰匙也需要花費約 60億年 的時間。

解密 :ta的破解方法是 頻率分析,由9世紀的阿拉伯產出,直至16世紀才被歐洲數學家注意到。

頻率分析

           原理,英文中字母出現的頻率,是不一樣的。比如,字母 e 是出現頻率最高的,佔 12.7%;  其次是 t,9.1%; 接着是 a, o, i, n 等,最少的是 z,只佔 0.1% ,見下圖,英語字母頻率統計。

英文字母頻率特徵
26個英文字母 頻率
e 12%
t---a---o---i---n---s---h---r 6%--9%
d---l 4%
c---u---m---w---f---g---y---p---b 1.5%--2.8%
v---k---j---x---q---z < 1%

 

另外,其他語言也有詳細統計。e.g. 法語、德語、西班牙語、葡萄牙語、世界語 ... 

    瑪麗和外界用密文往來很多,字符總量足夠多,收集到一起,統計哪個符號出現的比例最高,那個字符大概就是字母 e。 也會有,字母出現的頻率極爲接近,如 h、r、s,分別是 6.09%、5.98%、6.32%。但只需要留意字母前後關聯,便可區分開了。e.g. 字母 t 幾乎不可能出現在 字母 b、d、g、j、k、m、q 左右,而 字母 h 和 字母 e 經常連在一起, ee 一起出現的頻率遠大於 aa 一起出現的頻率,如此等等。

    頻率分析法的實質,便是大幅度減低字母排列組合的可能性,上面的條件如同中學的排列組合題。把 26^{n} 種可能減少爲線性,有的減爲只有 1、2 、3 可能,均攤下了依然是線性。這樣一來,即使第一步的統計各種符號出現的頻率時並不完全確定,但只要第二步,以拼寫規律篩選一下,代替符號對應但真實字母就可以找出來。

在審訊過程裏,儘管瑪麗始終不承認謀反,但證人和密碼學專家一起向公衆展示密文和原文,並告之解密規則,最後故事完了。想知道瑪麗但結局,還不如和我討論一下這個程序怎麼實現吶 ~ 

上代碼,單套加密頻率分析法:

#include <stdio.h>
#include <ctype.h>
int main(void)
{
	double number[32] = {0.0};   // 實際只要開闢 26 個對應字母表 26個字母
        size_t cnt[32] = {0};        // 大寫字母計數數組,也可以不用
	FILE *fp = NULL;
	size_t total=0;
	
	printf("%s","input test file name:> ");  // 如果覺得輸入麻煩,可以省略這一段代碼。
	char file_name[128] = "";
	scanf("%127[^\n]",file_name);            // 限制讀取長度爲 127
	
	fp=fopen( file_name,"r" );               // 可以使用 while循環+逗號表達式重寫打開文件失敗的代碼段,不熟悉可以看看上面的凱撒加密術寫法,add一個清空緩衝區
	if( fp != NULL )
		while( !feof(fp) ){
			char c=fgetc(fp);
			if( isupper(c) ){                // 'A' ~ 'Z'
				number[c-'A']++;
				total++, cnt[c-'A']++;
			}
			else if( islower(c) ){           // 'a' ~ 'z'
				number[c-'a']++;
				total++;
			}
		}
	else
		printf("fail to open %s\n",file_name);
	fclose(fp);
   
        double rate;
	for( size_t i = 0; i < 26; i ++ ){
		if( number[i] != 0 ){  // 找出出現的字母
		    rate = ( number[i] / total )* 100;
	    	printf("%c,%c  :  %0.3f%%\t, 其中 %c 出現 %u 次\n",'A'+i,'a'+i,rate, 'A'+i, cnt[i]);
    	}
	}
	return 0;
}
// 排序,希望交流希望交流

測試結果如下: [可以改寫,概率取整、排序,選的文本是一段官方文檔,數據越多越準確] 

    分析的是明文(沒有加密),文章所以看到使用頻率最高的是 e ,概率也是十分接近上面的圖示。而我們測試的一般是密文,已經加密過我們不知道鑰匙,就可以用頻率分析。假如其中有倆個字母 x、y 是這段文本測試出來出現頻率最高的,而且 x = y,那麼先用e代替x字母,而後其他字母也按頻率順序代替;重新開始下一次排列組合這次用字母e代替y,而後其他字母也按頻率順序代替。代替做完了,一個單詞出來可能是這樣 : thlee ,解密方分析沒有這個單詞,那麼換成three等繼續組合,最後解出來只是時間問題 !

p.s. 頻率分析方法總結:

  •  除了高頻字母以外,低頻字母也能夠成爲線索;
  •  搞清開頭和結尾能夠成爲線索,搞清單詞之間的分隔也能夠成爲線索;
  •  密文越長越容易破譯;
  •  同一個字母連續出現能夠成爲線索 因爲在簡單替換密碼中,某個字母在替換表中所對應的另一個字母是固定的;
  •  破譯速度會越來越快
  •  ... ...(密碼學家獨門方法,拿出來就是砸飯碗然我不會。)
  • 英語中出現頻率最高的12個字母可以簡記爲“ETAOIN SHRDLU”。除了出現當個字母有概率,倆個字母一起也沒有概率呢?比如ST、NG、TH,以及QU等雙字母組合出現的頻率非常高,NZ、QJ組合則極少;
  • 在所有的三字母組合裏,組合 the 出現的頻率最高;
  • 大部分情況,句中出現的單字母的單詞且該字母大寫的是 I;
  • 大部分情況,句中出現的單字母的單詞且該字母小寫的是 a;

     這回解密贏了加密,不過很快又有了新的加密法來針對頻率分析,ta 叫同音替代法

p.s. 字母出現頻率分析工具:https://www.dcode.fr/frequency-analysis

 

同音替代法

        史上最有名的採用同音替代法的密碼,是法國國王路易十三、十四時期的 "Grand Chiffre"。ta使用40多年,隨着拿破崙的倒臺突然失傳。直至1890年,才被完整的破解,破解方法就是從單詞拼讀規律入手的。這套加密法用了 587 種數字,來表示不同的發音。其中包含大量陷阱,如,一些數字只代表字母,不代表發音;很多數字只爲干擾字符,ta們沒有意義;還有一些數字即不是發音又不是字符,而是代表刪除前一個字符。 666

解密:爲了更好的說明,我舉一個具體的例子哈。字母 a 可以用 11、23、41 仨個數字代替,這三個數字翻譯過來都是 a ,越常常用的字母,如上文說的 字母e ,就用越多的符號代替它。這種想法的目標,就是讓每個數字出現的頻率大致相同,頻率特徵沒有了,密碼就不容易被破解。具體,見下圖。


怎麼破解 ? 

        思考 3 min...


   怎麼破解 ?

      我們聊一聊數學,您看懂了就發現解密很大程度不就是數學的概率論嘛。當年,概率論的誕生,正是一個賭徒數學家 Girolomo Gardano 研究概率出現的,著有《論賭徒的遊戲》,ta也是三元方程一般解法的發現者,更是導致瑪麗悲劇結果的發動機。

    數學的發展,有倆個高峯。

  • 一個是公元前 500 年到 公元前 300 年,之後一直下劃到公元 500 年跌到谷底。
  • 1000年後,瑪麗女王的時代,才超越古希臘巔峯時期的水平,到現在也還沒出現最高值。

另外,瑪麗TA並不是同時代的另一個瑪麗("血腥瑪麗"),瑪麗是蘇格蘭女王,血腥瑪麗是英格蘭女王,雖然同時代但不是同一個人。

命運可謂,從英格蘭國王要把一個侍女扶正爲女王遭到羅馬教廷的反對開始,就註定了瑪麗一生的不幸,那時瑪麗才一歲。

     怎麼破解呢 ?

        其中的一種方法是,通過字母的前後順序關係枚舉瞎猜。

            e.g. 字母q 後面出現最大可能是 字母u,而字母q又是一個不常用的字母,有很大概率猜中。其 ta 字母猜出來的難度大一些,但只要肯花時間,總能破解出來。

      數學理論: 數理統計中的大數定律。當樣本的容量趨於正無窮時,就可以歸納爲頻率收斂於概率。我們發現字母Q後面容易跟U,自然而然就排除了很多沒有必要的情況。


概率論出現之後,採用移位法和替代法的加密一方,輸了一局。但,加密一方並不情願更不會罷休。維吉尼亞加密法 大概您早有耳聞,

ta 的出現唯一目的便是爲打破 頻率分析[讓字母的頻率特徵消失]。做出這套加密法的是法國外交官 布萊斯· 德 · 維吉尼亞

考慮到當時歐洲外交官不斷的通信需求、數學也是法國國學 [法國數學家:笛卡兒、韋達、嘉當、韋伊、格羅滕迪克、達朗貝爾、拉格朗日、拉普拉斯、柯西、伽羅華、阿貝爾、梅森、費馬,帕斯卡,勒讓德,彭賽列、彭加勒、蒙日,傅立葉,柯西,伽羅瓦,埃爾米特,龐加萊......],普遍認同是法國維吉尼亞,而不是德國鍊金術士和意大利詩人,這裏只爲指出一個規律 --- 如果一知識產權,有人幾乎同時發現或發明,那麼 ta 的出現是必然,這也代表或許這以後會是一個行業,如電話的發明之爭 貝爾與梅烏奇,因爲這個領域已經形成了成熟的市場和產業。


       18 世紀時,歐洲各國都有致力加密解密的部門,叫"Dark room hall",ta 們與郵政系統配合運行。每天有許多信件本來是從各地寄到郵局,再從郵局分發出去。而 "Dark room hall" 的工作便是,解密所有寄給當地大使館的信件,如最著名的維也納 "Dark room hall" ,信被偷偷取給專業的速記員,讓其抄錄好內容,再小心的把信封好,三小時內送回郵局,郵局再按正常流程遞送給大使館。那時候,各國都在重要信件上使用了加密法,不過移位法和替代法的混合,依然沒有太多的作用,破譯只需要一天甚至幾個小時。破譯原理便是頻率分析法。一些國家除了自己看信,還暗自把消息賣給其他國家的情報部門。沒過幾年,很多國家覺察到自己的加密可能是失效了,這也催生了下一代加密法。


        爲了更好的理解新的加密法,我們先回顧一下第二代加密法[移位和代替法] 存在的漏洞。破解第二代加密法的原理是因爲每個字母實際使用的頻率是有固定值的。所以,不論那些字母被什麼符號代替或者移位了,都可以從頻率上找出 ta 的真身。

瑪麗女王就生活在加密輸於解密的年代。其實在瑪麗被 砍頭 的 40 年前[沒錯,結果便是如此] ,據說當時劊子手第一刀砍下的時候,脖子並沒有被砍斷,瑪麗當時對着劊子手呵斥:“把你該做的事做好!”這句話,嚇得劊子手直哆嗦。砍了三刀之後,劊子手拎起瑪麗的頭顱示衆,才發現那一頭烏黑的長髮原來是假髮,假髮套下已經滿是花白的頭髮了,瑪麗的嘴脣這時還在微微嘟囔着。
當另一個劊子手挪動瑪麗的屍身的時候,突然,一隻生前時常陪伴她的小狗,從長袍下跑了出來。這個劊子手趕了幾次,都沒趕走,每次小狗都又跑回瑪麗的身邊不肯離去


這就是瑪麗的一生的結局,是蘇格蘭國王的女兒註定了TA一生的不幸,幸福的時光只在16歲前。解密不成功,瑪麗也是一樣的結局,只是一場沒有充足證據的死刑判決。再退一步說,假如她因爲膽小壓根就拒絕了暗殺伊麗莎白一世的計劃呢?其實得到的結果就是,生活條件逐漸惡劣的一生的囚禁。

 

四百多年後的今天我們知道,其實英國始終就沒能再回到天主教。退一萬步說,瑪麗當初就算暗殺成功了,成爲了英格蘭的女王,大概率說,她還會在後續的奪權中被反殺。
想到這裏,相比所有的可能性,寧願是由密碼學的研究突破導致證據足夠確鑿,瑪麗才被判死刑這個結局。


新的加密法已經出現,ta 是替代法的改進版,叫 "多套符號加密法"。

爲了掩蓋字母使用中暴露的頻率特徵,解決辦法就是用多套符號代替原來的文字,如 原文字母A,從前只把 ta 替換成 F,現在把 ta 替換成 F 或者 G 這倆個。那什麼時候用 F 什麼用 G 呢 ?自定義即可,比如說,字母在奇數位時用 F 代替,字母在偶數位時用 G 代替。從前單套字符替代的時候,凡是文字中頻率爲 7.63% 的符號,差不多就代表 A 了。但現在 A 由 F 和 G 混合在一起, 7.63% 的頻率不可能再出現了,哪個符號是 A 也就沒人知道了,於是頻率分析法就暫時失效,這裏只是想說的形象,實際頻率分析依然可行。

第三代加密

   維吉尼亞加密法

        是TA, 是TA,就是TA。第三代加密法,不似第二代加密法,只使用 2 - 3套符號加密,維吉尼亞使用了整整 26 套字符加密。TA 是一個表格,第一行代表 原文字母,下面每一橫行代表原文分別 由哪些字母代替,每一豎列代表我們要用第幾套符號來替換原文。一共 26 個字母,一共 26 套代替法,所以,這個表是一個 26 * 26 的表,如圖。

 TA 具體是怎麼加密呢 ??

假設我要加密 "Hello, world !",現在的思路不同於單套加密方法。單套加密時,我們可以指定上面表格任意一行,比如指定第 16 行,原文的字母后移 16 位,只使用這一行的規則,"Hello, world !" 就變成了 "Xubbe, mehbt !"

但我們是多套加密,於是加密規則是這樣: 

  •       第一個字母用 第 2 套加密
  •       第二個字母用 第 4 套加密
  •       第三個字母用 第 8 套加密
  •       第四個字母用 第 16 套加密
  •       ...  ...
  •       最後一個字母用 第 2 套加密

事實,這只是一個程序的風格,採用 2^{^n} 循環同餘加密。大部分人更多的是僞隨機。隨機,因爲沒有規律,每個字母移動多少位都需要單獨指定,如果只是 "Hello, world !", 我還能接受,可是我們是寫信人呢 ? 一封信至少是 100 個單詞,500個字母吧。這樣不但加密麻煩,解密也費勁,只能拿表一一對照,還原每一個字母,耗時耗力也易錯。 在入門密碼學開始,我便強調,如果一個信息幾分鐘都沒發送出去,那麼這種加密方法是不會被採用的。事實上發明開始直至200年後,維吉尼亞加密法,雖然加密強悍[破解難度指數級增加],可幾乎沒人用呀,也是這個原因。以瑪麗女王舉例子,當年的瑪麗用了一段時間的代替法,便可不看暗文熟練讀寫。如果 ta 用維吉尼亞加密法,雖然,解密方破解不了,可這麼複雜的表格,恐怕只有記憶大師才能在一段時間瞭然於胸。在這個 加密法爲 26 * 26 的表格,而且還有長短不一的移位數,每加密一個字母,都要在表格裏找了又找。就算 瑪麗 熟練操作加密一個字母也得花 3秒 時間,寫一封 100單詞,500字母的信,要 25分鐘 的高強度加密,不僅超過了加密原則也易錯[我試了一下,Hello,World , 全軍覆沒]。

直到這個加密問題解決了,維吉尼亞才被 1861-1865年的美國南北戰爭廣泛使用。使用維吉尼亞的主要原因就是科學技術的發展,加密解密的工作已經交給機器完成。1860年時,不但有蒸汽機,還有電動機,機器運算快且不會出錯。第三代的維吉尼亞加密法,在真實使用時,人們事先規定每一個字母用了哪套移位法時,並不是隨機的指定,而是約定了一個規則,這個規則叫 "鑰匙?"。

鑰匙最初只是一個單詞,如,鑰匙是 "i s k e y",

字母 i 是字母表第 9 個字母也是鑰匙第 1 個字母,就代表加密使用第 9 套符號加密,即原文第一個字母后移 9 位,

字母 s 是字母表第 19 個也是鑰匙第 2 個字母,就代表加密使用第 19 套符號加密,即原文第二個字母后移 19 位,

後面字母 k、e、y 的同理。那麼鑰匙現在比完了,那後面的怎麼辦呢,好辦,就按照剛剛的規則循環一下就好,我想看完這個過程,您心理已經對代碼實現有一個大致思路了吧。

爲了分別演示,只採用第一套字母表。

第一套字母表順序: 


1:A    2:B    3:C    4:D    5:E    6:F    7:G    8:H    9:I    10:J    11:K     12:L    13:M    14:N    15:O    16:P    17:Q    18:R    19:S    20:T    21:U    22:V    23:W    24:X    25:Y    26:Z

原文: Hello, World !

鑰匙: iskey [下面的程序只採用大寫因爲大寫小寫順號相同]

明文      [密鑰-移動數]    密文
H    =    [i-9]    =>     R
e    =    [s-19]   =>     m
l    =    [k-11]   =>     y
l    =    [e-5]    =>     r
o    =    [y-25]   =>     y
,    跳過
W    =    [i-9]    =>     E
o    =    [s-19]   =>     b
r    =    [k-11]   =>     x
l    =    [e-5]    =>     v
d    =    [y-25]   =>     l
!     跳過

 Hello, World !  = 》 密文 : Rmyry, Ebxvl! 


所以,維吉尼亞加密法的使用,加密方 勝出。

    上代碼,多套加密維吉尼亞加密法 [ Virginia ]  

#include <stdio.h>
#include <ctype.h>
#include <string.h>
const int N = 1024;

int main(void)
{
	char txt[N] = "";
	char key[32] = "KING";  // 默認,密鑰Key優化建議,如果出現相同字母去重
	FILE *fp = NULL;
	size_t cnt = 0;
	
	printf("%s","input test file name:> ");  // 如果覺得輸入麻煩,可以省略這一段代碼。
	char file_name[256] = "";
	scanf("%255[^\n]",file_name);
	

    while(
    printf("%s"," KEY :> "),
    scanf("%*[^\n]"), scanf("%*c"), // 清空緩衝區
     scanf("%26[A-Z]",key),
     // KEY : 輸入大寫字母纔有用(想要小寫也簡單,改成 "scanf("%26[a-z]",key)" )。如果前面沒有 scanf 輸入文件名,那麼輸入key 要和 清空緩存區語句 對調順序
      !*key || islower(*key)
    );
    
	fp=fopen( file_name,"r" );
	if( fp!=NULL )
		while( !feof(fp) ){
			char c=fgetc(fp);
			
			if( isupper(c) ){   // 'A' ~ 'Z'
		            txt[ cnt++ ] = c;
			}
			else if( islower(c) ){ // 'a' ~ 'z'
				txt[ cnt++ ] = c;
			}
		}
	else
		printf("fail to open %s\n",file_name);
	fclose(fp);
 
 
        size_t  k_len = strlen(key);
        // 倆種情況. 原文字母大寫小寫分別處理  加密
        printf("%s","密文 :> ");
	for( int i=0; i<cnt; i ++ ){
		if( isupper(txt[i]) ){
	            txt[i] += key[i%k_len] - 'A';
	            txt[i] = (txt[i] - 'A') % 26 + 'A';
	            printf("%c",txt[i]);
		}
		
		else if( islower(txt[i]) )
		{
		    txt[i] = txt[i] + key[i%k_len] - 'A';
	            txt[i] = (txt[i] - 'a') % 26 + 'a';
	            printf("%c",txt[i]);
		}
	}
	
	putchar(10);
        printf(" KEY :> %s\n",key);
	// 解密
	   printf("%s","原文 :> ");
	for( int i=0; i<cnt; i ++ ){
		if( isupper(txt[i]) ){
	            txt[i] = txt[i] - key[i%k_len] + 'A'+26;
	            txt[i] = (txt[i] - 'A') % 26 + 'A';
	            printf("%c",txt[i]);
		}
		
		else if( islower(txt[i]) )
		{
		    txt[i] = txt[i] - key[i%k_len] + 'A'+26;
	            txt[i] = (txt[i] - 'a') % 26 + 'a';
	            printf("%c",txt[i]);
		}
	}
	return 0;
}

// 程序可優化的地方,其一 輸出的時候每個詞間加空格,其二 鑰匙KEY 支持多個單詞,希望指點一下,但不需要指指點點。

鑰匙是一個很重要的概念,現代加密都是以鑰匙爲主。

破解維吉尼亞:  

  1.  從密文中找出拼寫完全相同的字符串,尤其是一些長度大於 4 重複出現的密文。如,一篇幾百個字母的密文中,長度超過 4 ,且重複出現的字母串一共有 4 種,我們就把它們叫做甲乙丙丁。我們舉一個具體的例子見下圖,假如,此時字符串甲是 : "the sun and the man in the moon",鑰匙是KING,密碼文是一串看起來沒什麼規律的字母。這3樣,我們現在都知道。
原文 t h e s u n a n d t h e m a n i n t h e m o o n
KEY K I N G K I N G K I N G K I N G K I N G K I N G
密文 d p r y e v n t n b u k w i a o x b u k w w b t

 原文,有 3 個定冠詞 the,變成密文後,the 有的形式,

  •                                                                    D P R
  •                                                                    B U K
  •                                                                    B U K

       2.  數一次,甲乙丙丁第一次和第二次出現的間隔。如,字符串甲重複間隔爲 20 個字母,ta 代表這段密文對應的鑰匙,是在這 20 個字母中,正好反覆使用了若干次。

  •   如果鑰匙長度爲 2,鑰匙會反覆使用 10 次
  •   如果鑰匙長度爲 4,鑰匙會反覆使用 5 次
  •   如果鑰匙長度爲 5,鑰匙會反覆使用 4 次
  •   如果鑰匙長度爲 10,鑰匙會反覆使用 2 次
  •   如果鑰匙長度爲 20,鑰匙會正好使用 1 次
  •  推出    len = \frac{nums}{time}     
  •  len : 鑰匙長度  、  nums : 間隔字母數量,如 the 間隔 8    、  time : 同樣的密文單詞出現次數,如 the 2次同樣

         字符串乙丙丁處理相同,再查查 ta們 間隔數的因數,找出共同的因數,這個因數對應鑰匙的長度。

         知道鑰匙的長度,那麼對應的密文就變成了單套移位加密法,如,鑰匙是 iskey , 測試出長度爲 5。

          密文的第 1、6、11、16、21、26 ···,這些字母是同一套加密字母,記 A 組

          密文的第 2、7、12、17、22、27 ···,這些字母是同一套加密字母,記 B 組

           ···  ···

        這些關係分析出來,接着用 頻率分析法 解析出現頻率最高的字母。

        可如果原文還沒密鑰長,或者?️重複的單詞,哪怕知道鑰匙長度,用頻率分析也不能破解出來。不過,還有別的方法。  密碼學主要用的場景,原文是很豐富。

        最後,再總結一下破解維吉尼亞加密

        step-0: 找到鑰匙長度

  •     重合互指數法求密鑰 [根據密鑰的長度對密文進行分組,每一組都是一個凱撒加密。計算擬重合指數,通過擬重合指數可以確定每組的移位密鑰,從而求出整個的密鑰]

  •     Kasiski測試法  [搜索長度至少爲2的相鄰的一對對相同的密文段,記下它們之間的距離。而密鑰長度d可能就是這些距離的最大公因子]

  •     上面的倆種求共享代碼推薦資料也行,也歡迎補充新的方法

        step-1: 根據鑰匙長度分組找出位移規則

        step-2: 知道所有位移規則讓維吉尼亞從 N^{M} 種可能性,變成了基礎的移位法。

        step-3: 用頻率分析法即可

       難以破解的原因:同樣的原文對應上億種密文,相同的密文也一樣對應上億種原文。

        N^{M} : N 爲 密文字母數,M 爲 密鑰字母數,如果 N = M = 10 ,那麼 N^{M} = 100 億種可能性。

      相比上文的同音替代法,最多隻能一種原文對應 N 種密文,而且 N 一般不會超過 10 的,但反方向上密文一定只對應一種原文。

代碼我先墊墊,回過頭再寫破解代碼,歡迎交流。得抓主要因素,纔可以提高效率。

這是維吉尼亞的最初版本,ta 第二次改版是以隨機字母充當鑰匙,理論上比起用一個單詞循環匹配,大大提高安全性,可這也是 ta 失敗的一個地方,這個隨機字母 一般選爲文章或者是詩。解密方,哪怕只推動了某個單詞的前幾個字母,也可以猜出來。後來,維吉尼亞又改版,採用真正的隨機字母單鑰匙別名 "單次鑰匙譜"。一戰時,每個國家通訊部中就有一本厚厚的書,裏面全是隨機字母,用完一頁,就用下一頁。數學理論證明,這樣的加密法是無法破解的,不過這樣的加密解密也是相等繁瑣,一戰後,基本沒用過了。

密鑰優化建議如果存儲密鑰的數組有重複單詞,去重只保留一種字符。

複習一下古典加密

加密術 算法原理 鑰匙(密鑰) 實例
移位 明文的各字母按指定的字母數平移 平移的字母數量 愷撒、ASCII碼錶
替換 按照替換表對字母表進行替換 替換表 替代、同音替代、維吉尼亞

 

 初次學習密碼是不是有點燒腦。 沒關係~ 不急的,我們輕鬆一下,和您聊聊小說 ,我小時候看的《神勇小虎隊》

我覺得挺好看的,ta有一小章裏面有一個以密碼爲線索的故事。圖上三個戰友被困住了,他們給出了一串數字給對方。

如 196 03 17 ,這是一個密碼哦。

解密是這樣,他們選擇了一本書《神勇小虎隊》,這串數字前 3 位是這書的第多少頁 即 196頁,第 4~5 位數字 03 代表是這一頁的多少行即 第 3 行,第 6~7 位數字 17 是代表這行第 多少 個字即 第 17字。二戰時的日軍加密和康熙年間的加密同。

簡單吧,ta 就是比爾密碼,1920年到1979年,有一部分美國人超級想破解這些數字因爲這些數字的背後是 4300 萬美元哦,比爾密碼的加密解密是以參照物自定義的。

比爾密碼

        在《比爾密碼》記載中,美國西部淘金熱的時候,1820年,一個小夥子比爾在弗吉尼亞州的一個小旅館住了2個月,當時給人印象挺好的,後來不聲不響走了。2年後他回來了,又住了2個月,臨走時什麼話沒說,給店老闆留下了一個上鎖的盒子。幾個月後,老闆又收到比爾一封信,說盒子裏有他和他合作伙伴非常重要的東西,但暫時拿不回去,請老闆代爲保管10年時間。如果真的超過10年都沒人來領,老闆就可以自行打開了。

這盒子裏除了有一份說明書是可以讀懂的,其他文字都是加密過的,解密的鑰匙在另一個人手裏,這個人會在10年期限到的時候把鑰匙內容告訴店主。
老闆也確實信守承諾,不止等了10年,等了23年纔打開盒子,但那個據說要給他鑰匙的人始終沒有來信。盒子裏除了有份說明書外,還有3封加密的信。店老闆花了17年時間也沒破解密文,在去世前把這盒子東西交給了他的朋友。

到了他朋友手中倒是有所突破,其中第二封信被破解了,其他兩封實在沒搞懂,他就希望大家一起來努力,下面是比爾密碼的第一封信,您可以試着破解,試一下,可不可以拿到 市值 4300萬?

第二封信的鑰匙即選用的書籍是 《獨立宣言》 ,第二封信的內容,如下:

    在1819年到1821年,比爾和另外30個合作伙伴分兩次把2000多磅黃金和5000多磅白銀埋了起來,除此之外還有價值13000美元的珠寶。然後把埋藏地的地形特徵詳細描述了一下,但沒說在哪兒。最後說,具體的埋藏地址在第一封信裏,第三封信寫了30個合作伙伴的詳細信息。

解法

比爾密碼的三份密碼都由1-4位的數字組成。已知的第二密碼以一種替換密碼的方式加密,一般推測其他兩份密碼也是採取類似的加密方式。由於數字的範圍都遠超過字母表的字母數,密碼對應的密鑰可能是某一篇文章。

以第二密碼爲例,每一個數字代表美國《獨立宣言》的文本中的第幾個詞的首字母,如 1 代表第 1 個詞的首字母 “w”,2代表第 2 個詞首字母 “i” 。解密後的文字如下:

I have deposited in the county of Bedford, about four miles from Buford's, in an excavation or vault, six feet below the surface of the ground, the following articles, belonging jointly to the parties whose names are given in number three, herewith:The first deposit consisted of ten hundred and fourteen pounds of gold, and thirty-eight hundred and twelve pounds of silver, deposited Nov. eighteen nineteen. The second was made Dec. eighteen twenty-one, and consisted of nineteen hundred and seven pounds of gold, and twelve hundred and eighty-eight of silver; also jewels, obtained in St. Louis in exchange for silver to save transportation, and valued at thirteen thousand dollars.The above is securely packed in iron pots, with iron covers. The vault is roughly lined with stone, and the vessels rest on solid stone, and are covered with others. Paper number one describes the exact locality of the vault, so that no difficulty will be had in finding it.

中文:
我在貝德福德郡,距 Buford 酒館約 4 英里,6 英尺深的洞穴或地窖埋藏了三號密碼指定的一些人所擁有的物品:1819年11月第一次存入的物品有 1014 磅金和 3812 磅銀。1821年12月第二次存入的物品有 1907 磅金和 1288 磅銀,還有總值約 13000 美元的珠寶,在聖路易斯用銀換取,以便運輸。這些物品儲存在帶有鐵蓋的鐵罐內。洞穴的內壁襯着石頭。容器放置在結實的石壁上,並被其他物件覆蓋。一號密碼指明瞭藏寶處的地點,所以很容易就能找到它。 

             

自此,古典密碼學 學習完畢,我能問您一個問題嘛 ~ 

如果給您一個非常漂亮的妻子[漂亮到全國都知道她的名字],給您一個靠得住的大兄弟[爲了可以去KO欺負您的人]。另外呢,您還會有一個穩定的工作,只是讓您穿越回古代您來對應這些標準,您願意嘛 ~  歡迎評論區留言,不是問您能不能,而是問您想不想。



第四代加密

    機械電子時代的加密解密 

Enigma(恩尼格瑪機) 

      恩尼格瑪機 是一種機械電子式的機器,組成 : 轉子 + 齒輪 + 電線 + 轉盤 + 搖桿 + 編碼器,可以將其簡單分爲三個部分:鍵盤、轉子和顯示器。ta 的加密解密方式類似維吉尼亞的最終版本 [真隨機·鑰匙],因爲 ta 的加密解密無窮是由機器完成,不會加密解密難,同時也保證了不易錯。ta 的鑰匙是由編碼器的齒輪組決定的,每一個齒輪組都由 26 個數字組成,齒輪組越多,鑰匙就更長且有更多的組合,在對付 頻率分析 上十分有用。

維吉尼亞不同版本:

1.0版,鑰匙是隨便想出來的一個詞,然後重複的用;

2.0版,鑰匙長度增加了很多,但爲了便於雙方協同使用,往往是一篇文章、一首詩;

3.0版,鑰匙是純粹隨機字母,而真隨機很難製造,密碼簿又厚到不能用,所以3.0版只停留在理論上,實際幾乎沒人使用。

恩尼格瑪機即 3.0 版本的改進,也叫 "單次鑰匙譜",維吉尼亞3.0版是從數學角度來說不可能破解的加密法。

真隨機數是3.0的關鍵,3.0也應用在以後聊的量子加密中的數學部分以後會再見的。從程序角度講,我們軟件即算法實現的隨機數都是僞隨機數,然依據硬件傳感器的熱量變化、聲音變化、光線變化、壓敏變化等生成的隨機數是真隨機的。

由於內部狀態決定下一個僞隨機數的生成,所以破解在於內部狀態;僞隨機數種子是需要保密的,ta幾乎等同加密術的鑰匙不能告訴別人。因此千萬不能使用易預測的值(用當前時間當種子)

僞隨機數生成器(內部狀態)實現方法:

  1. 亂來法
  2. 線性同餘法(應用廣泛因爲不具備不可測性不能用於加密解密) : 假設我們要生成的僞隨機數列是 R_{1},~R_{2},R_{3},\cdots ,R_{n},第一個僞隨機數種子 R_{1} 是必須先給出來的,接着用一個公式計算下一個僞隨機數:

  R(2) = (A*seed+C)~mod~M,  seed是種子,ta是一個變量 R_{1},~R_{2},R_{3},\cdots ,R_{n},不過因爲計算的是 R_{2},所以seed此時是R_{1}, A 、C、M 都是常量,且 A、C 要小於 M 。

隨機數資料http://m.biancheng.net/view/2043.html

// 這是  C 標準庫 實現的僞隨機數rand函數算法,java的java.util.Random類等也是採用線性同餘法

static unsigned long int next = 1;   // 種子默認值

int rand(void)   // 內部狀態,線性同餘法
{
    next = next * 1103515245 + 12345;
    // A : 1103515245   seed : next    C : 12345  M : 32768

    return (unsigned int) (next>>16) & 32768;
    // next>>16 等同於 next / 65536 , 32768是2的冪,% 換成了 &。   p.s. x & (x-1) == 0 [判斷2的冪 爲0就是]
}

void srand(unsigned int seed) 
{
    next = seed;  // 種子是數字
}
// 調用函數
// srand( (unsigned)time(NULL) ); 採用當前時間做種子,循環產生隨機數是相同的,因爲時間只精確到秒
// int  a = rand()%66+22;
// 稍往安全方面考慮,A、C、M 可以用宏替換,隱藏數字值。

如果不調用 next = 1 就是種子R_{1},rand函數就是內部狀態,因爲ta改變了next的值,種子值變化了。所以,下一次會產生不同的隨機數,需要相同的僞隨機數,只需要讓 next 不變即可。

破解方法 : 利用線性同餘法生成的數列反推 A、C、M,假設推出 A =  1103515245 ,C = 12345 , M = 32768 。這時如果得到任意一個僞隨機數再根據下列公式就可預測下一個僞隨機數。

(A * R + C) mod M = (1103515245 * R + 12345) % 32768  =》 接着把 得到的任意一個僞隨機數代入,可得到下一個僞隨機數

改進版 : 提前打造一個隨機數序列,每當需要種子時,取出對應的長度序列。保護好隨機數序列ta等同於鑰匙,不能給別人,與維吉尼亞加密術的3.0版原理一樣。

幫您打造的字符池的代碼實現~  

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef char* string;                   // 如是C++類型可以直接換成 string

string (pseudo_random) (size_t n)
{
	srand( (size_t)time(NULL) );    // 考慮到安全,種子不要以時間,最好自定義
	// time函數 確定當前的日曆時間。 返回和當前日曆時間最近值,獲取失敗則返回 long的最大值 - 1
    string s = NULL;
    while( ( s = ( string )malloc(sizeof(char) * n) ) == NULL );
        
    for( size_t i = 0; i < n; i ++ )
    {
    int ran = rand( )%3;
        if( ran ==  1 )
            s[i] = (char) (rand( )%26+97);
        else if( ran == 2 )
            s[i] = (char) (rand( )%26+65);
        else
            s[i] = (char) (rand( )%10+48);
        // 生成 low ~ high 之間的隨機數公式 : rand()%(high-low+1)+low
    }
	return s;
}

int main(void)
{
	size_t n = 0;
	printf("請輸入字符串個數:> ");
	scanf("%8d",&n);    // 限制字符串長度-千萬
	
	string str = pseudo_random(n);     
	             printf("%s\n",str);
	 free(str);
         str = NULL;        // 防止野指針
	            
	return 0;
}

     3.  單向散列函數法

     4.  密碼法

     5.  ANSI X9.17


// 這裏有一個由 Marsaglia 首創 Knuth 推薦的方法: 
    #include <stdlib.h>
    #include <math.h>

        double gaussrand()
        {
            static double V1, V2, S;
            static int phase = 0;
            double X;
            if(phase == 0) {
                do {
                    double U1 = (double)rand() / RAND_MAX;  #RAND_MAX 是sodlib.h定義的宏
                    double U2 = (double)rand() / RAND_MAX;
                    V1 = 2 * U1 - 1;
                    V2 = 2 * U2 - 1;
                    S = V1 * V1 + V2 * V2;
                } while(S >= 1 || S == 0);
                X = V1 * sqrt(-2 * log(S) / S);
            } else
                X = V2 * sqrt(-2 * log(S) / S);
     phase = 1 - phase;
return X; 
}

強調 : 維吉尼亞加密術 + 真隨機數 = 理論上以傳統計算機是不可破解的加密術

 

這張圖片是 三組 齒輪(enigma的),可以設置 3 個隨機數,共 26 * 26 * 26 = 17 576  種組合,齒輪組越多組合就越多,頻率分析就會失效。

2 ~ 4 組齒輪的 恩尼格瑪機 多用於商業,二戰中被 圖靈大神和整個布萊切利園 破解的 是八組齒輪的 德軍恩尼格瑪機。

ta 的組合就有 26^{8} ,約等於 2000億 種,這樣的組數讓原文的每個單詞不會匹配同一鑰匙多次。每選擇一個數字,轉子會往下轉一格,轉子的轉動讓連接的線路改變,加密結果自然也不同。哪怕同一個字母敲入多次,加密後密文也不同。八組的就有 2000億 種。這麼多種組合,哪怕信息幾百萬字,用頻率分析也很難找到出現相同的密文單詞。

破解 恩尼格瑪機:

二戰的解密,挽救了世界許多家庭,也讓戰爭年份銳減。這是歷史上極有意義的解密。

1939年中期,波蘭政府因爲德軍撕毀互不侵犯條約後將此破譯方法告知了英國和法國,不過,這只是最簡單的enigma,而 enigma它的廣泛使用就是建立在加密原則上的即 假設解密一方擁有和加密一方同樣的水平,機器和機器原理也被截獲的情況下,對方依然不能解密出來。

圖靈和整個布萊切利園的人們是這樣做到的,主要還是數學呢, 現在的 AI 破解需要 13 min,成本是 10 英鎊。

  • 軍事規律: 德軍的消息經常出現 [ 無特殊情況(Keine besonderen Ereignisse) 、 希特勒萬歲(Heil Hitler)、天氣(wetter) ··· ]
  • 人性弱點: 操作極不規範 [德軍的enigma操作員設定的密碼是三個字母連着一起的因爲方便摁、女朋友名字因爲不會忘 ~]
  • 操作限制: 每組齒輪不能重複出現在同一位置[減少一半組合],接線板只能對調不相臨的字母覺得這樣易被發現[組合再銳減]
  • 波蘭提高的老方法 [波蘭在二戰前 7 年都在研究 3~4 組齒輪的 恩尼格瑪機]
  • 1941年英國海軍捕獲德國U-110潛艇,得到密碼機和密碼本,建立了數學關係
  • MavisBatey收到一個信息,在200個明顯的亂碼中沒有包含一個“w”。 通過使用每個字母不能被替換的知識,她破譯出了密碼
  • 數學大師圖靈以原文和密文建立對照表,[一些數學的知識環和連接數,非常難以理解] 採用數學和上述條件後使 2000億種組合,變成了 105 萬種組合。
  • 製造炸彈機 [圖靈設計] ,以機器的重複計算找到了 105 萬種裏面的德軍密碼。
  • 炸彈機批量生產後,破譯陸軍只需要 1 小時,可是使用 8 組齒輪的海軍,還是不能。
  • 英軍後來在特定地點布 "水雷",信息誘餌,德軍又太相信自己的加密,導致德軍會發送經緯度座標給英軍
  • 英軍演戲那是一流,"水雷" 不會經常用,知道了德軍軍艦的座標也裝什麼都不知道,偷襲前,一定先派偵察機非常正式的去巡航,好像是告訴德軍,你們的軍艦是這個偵察機發現的

破解之後就說德軍配置最高的海軍,

  • 英方盟軍全年軍艦損失減免 60%
  • 德軍軍艦損失從 7% 增到了 50%

 機械加密就說這個非常有意義的 解密 。

 

複習一下機械加密

機械加密術 算法原理 每日/通信 鑰匙 實例
Enigma通信密碼 使用 Enigma密碼機,通過的接線板接線的方式+三個轉子的順序+每個轉子的旋轉位置對字母替換 接線板接線的方式+三個轉子的順序+每個轉子的旋轉位置對字母替換 Enigma通信電文的加密
Enigma通信電文 使用接線板的接線方式+三個轉子的順序固定的 Enigma 密碼機,按照每個轉子的旋轉位置對字母替換 每個轉子的旋轉位置 Enigma通信電文的加密

計算機的前世是中國的算盤,計算機的誕生完全是由圖靈弄出來的,這裏我們簡單的說一些。圖靈與計算機的故事。

在上個世紀30年代中期,圖靈在思考三個問題

  • 第一個問題,世界上是否所有數學問題都有明確的答案?
  •                       問題比答案多,只要一小部分有解
  • 第二個問題,如果有明確的答案,是否可以通過有限步驟的計算得到答案?
  •                       不能完成,哪怕是理想環境
  • 第三個問題纔是,對於那些有可能在有限步驟計算出來的數學問題,能否有一種假想的機械,讓它不斷運動,最後當機器停下來的時候,那個數學問題就解決了?
  •                              電子計算機的電子運動等價於機械運動,計算是確定的,人的意識是不定的

由此,創建了圖靈機[一種數學模型],圖靈也成爲了計算機的鼻祖。需要指出的是,圖靈的境界能想到這一層,首先是受到另一位數學大師希爾伯特的啓發。
希爾伯特在1900年的巴黎國際數學家大會上,提出了 23 個重要的、根本性的數學問題(也被稱爲希爾伯特問題)。

圖靈的念想:把人的思維用邏輯和數學過程描述出來;

圖靈的方法:因爲人的思維是大腦 860億 個神經元相互連接而成,實在太多。因此圖靈想,其實只需大腦的一部分做科學模型描述。

比如說在一個計算過程中,中間會出現很多數字,那麼這些數字都可以視爲單一的狀態,這些狀態都是各自獨立的,而且對於一個生物大腦來說,同時存在於腦中的狀態數量是有限的,所以圖靈就把正在運算的大腦,當做一種數量有限、且狀態間互相離散的機器。
他不試圖還原一個完整的生物大腦,而只在乎某一部分可以用邏輯和數學準確描繪的大腦,看看用邏輯和數學搭建起來的這個思維機器能不能做運算,能不能習得東西,能不能自行找到任意兩個事物間的關聯等……


圖靈的成就很巧,每隔 5年 出現一個,最早的是發生在1935年——圖靈22歲時寫出了《論可計算數及其在判定問題上的應用》,我們之後就簡稱這篇文章爲《可計算數》,它從數學上定義了著名了“圖靈機”。


不過圖靈機很長時間以來一直只存在於論文中,它是一個抽象的可以實施自動計算的思維模型,而它的實體快到1950年時纔出現。
雖然限於篇幅在這節課不能詳細說,但如果一定要形容的話,你可以想象一下:
 

給你一排無限長的紙帶,上面划着格子,格子只有兩種樣式,一種是裏面打了孔的,一種是沒打孔的。

而後你有一個小小的窗口,可以順着紙帶來回的挪動,不但可以查看格子打沒打孔,還可以改變打孔與沒打孔的狀態。除此之外,你還有一個筆記本,可以臨時記錄通過窗口看到的打孔情況。


利用這些窗口,你能幹的事情其實很少,也就是左右挪動、查看、更改打孔狀態。那麼在這幾種操作下,怎麼讓這套設施實現一些具體的運算呢?

紙帶實現出來就是 陰極射線管 或 電子

二戰結束後,圖靈開始研究可以自己下國際象棋的機器,並考慮從機器角度說 "什麼是學習"

時間再往後推 5年,圖靈開始專心於研究自動計算引擎,簡稱ACE。它是圖靈機的改進版,改進的方面非常多:


一方面因爲炸彈機、巨像機這樣的設計是布萊切利園的高級機密,自動計算引擎不能複製它們;


另一方面也是因爲圖靈在這幾年的思考中,接觸到了馮·諾依曼、香農、紐曼、哈代、維特根斯坦這些人的很多論文,從中受到了不少啓發。


圖靈機發展成計算機的主要原因:

  1.  解密需要 : 到了1935年之後,通過手工紙筆解密實在應付不了恩尼格瑪機加密過的東西了。往嚴重的說,情報這種東西如果不搞定,那就是亡國,所以這是一個迫切的需求。
  2.  開發昂貴的新武器,要做可行性分析 : 這些武器投入的資金是巨量的。比如說原子彈,鈾235要發生鏈式反應引爆的臨界質量如果是20噸的話,全球至今都不會有哪個國家打算製造原子彈的,因爲實在造不起。但可行性計算告訴我們,十幾公斤就可以,美國算出來了,所以美國敢造。很多新武器的計算量都很大,除了原子彈、氫彈,還有衝擊波殺傷力、飛機的空氣動力學等。

想了解另一部分,可以看我另外一篇博客 Google食用指南[hacker]。如果想學習這種思維,恐怕得? 我的另外一篇博客 揭示 O(n)

好的,機械時代加密解密,我們聊完了。接下來,纔是最最可貴而鍛鍊思維的現代加密。

 祝豬事順利 ~~

計算機加密記錄,

  • 採用數據結構的二叉樹加密,樹的根節點設爲∞,接着一直延伸二叉樹到 26 個分叉,所有左節點值爲 0 ,所有右節點值爲 1。這樣就可以表示了 26 個字母,加密解密非常快,分支個數也非常多可以擾亂解密方。
  • ... ...

計算機時代的密碼學。嘖嘖,搞不懂 ~

    幾年前人們談起計算機時還會說它笨,甚至嘲笑 Google 什麼都是基於機器算法的服務應付不了很多細微的場景,今天這些人來了一個180度大轉彎,覺得人工智能超級聰明。那麼我們稍稍解釋一下計算機 ~

    歷史: 計算機前身是中國的算盤。爲什麼不是古希臘的算盤呢,ta 比中國的算盤要早呀 ? 我們從現在講起,算盤是計算機的前身,是硅谷的計算機博物館的看法。這家地處硅谷、世界上最大的計算機博物館,一進門自然的出現幾個大字 "計算機 2000 年的歷史",下面放的一把中國的算盤。歷史上,古希臘的算盤實際只是一些小石塊幫助計算時候的計數,但計算的工作還是靠心算,而中國的算盤,ta 除了圓珠子做存儲功能,還有一套珠算口訣來控制算盤操作,這相當於今天控制計算機運行的指令。打算盤的師傅,不需要多好的心算或計算能力,只需要按照這個口訣就可以算出來結果。

     比如,我們都知道一句俗話 "三下五除二",這其實來自於一句珠算口訣。它是做加法時,“加上三”的一種操作指令,意思是說,加三時,可以先把算盤上半部分代表五的珠子落下來,再從下面扣除兩個珠子,加三的計算就完成了。只要把這些口訣背熟,幼稚園的小盆友同樣可以完成。[ 我要是幼稚園的老師,不妨開一個這樣的課。反正都是玩 ]

更多早期計算機:《帶你逛西雅圖活電腦博物館

程序的運行原理:《載入內存,讓程序運行起來

    --------------------------------------------------------------------------------------------------------------

    計算機要處理的信息是多種多樣的,如十進制數、文字、符號、圖形、音頻、視頻等,這些信息在人們的眼裏是不同的。但對於計算機來說,它們在內存中都是一樣的,都是以 二進制 的形式來表示。


    內存條是一個非常精密的部件,包含了上億個電子元器件,它們很小,達到了納米級別。這些元器件,實際上就是電路;電路的電壓會變化,要麼是 0V,要麼是 5V,只有這兩種電壓。5V 是通電,用 1 來表示,0V 是斷電,用 0 來表示。所以,一個元器件有 2 種狀態,0 或者 1。[把 5V 變成 2.5V 也可以,不過會增加電路設計的複雜性,沒必要]

    我們通過電路來控制這些元器件的通斷電,會得到很多 0、1 的組合。例如,8 個元器件有 28 = 256 種不同的組合,16 個元器件有 216 = 65536 種不同的組合。雖然一個元器件只能表示 2 個數值,但是多個結合起來就可以表示很多數值和文字、圖像、音頻、視頻、符號等等。一般情況下我們不一個一個的使用元器件,而是將 8 個元器件看做一個單位,即使表示很小的數,例如 1,也需要 8 個,也就是 00000001。
 

     1 個元器件稱爲 1 比特(Bit)或 1 位,8個元器件稱爲 1 字節(Byte),那麼 16 個元器件就是 2 Byte,32 個就是 4 Byte,以此類推:

  • 8×1024個元器件就是1024Byte,簡寫爲1KB;
  • 8×1024×1024個元器件就是1024KB,簡寫爲1MB;
  • 8×1024×1024×1024個元器件就是1024MB,簡寫爲1GB。

     現在,你知道 1 GB 的內存有多少個元器件了吧。我們通常所說的文件大小是多少 KB、多少 MB,就是這個意思。

單位換算:
  • 8 Bit = 1Byte
  • 1024Byte = 1KB
  • 1024KB = 1MB
  • 1024MB = 1GB
  • 1024GB = 1TB


     您看,在內存中沒有 abc 這樣的字符,也沒有 gif、jpg 這樣的圖片,只有 0 和 1 兩個數字,計算機也只認識 0和1 。所以,計算機使用二進制,而不是我們熟悉的十進制,寫入內存中的數據,都會被轉換成 0 和 1 的組合。

那麼操作位是現在計算機加密解密的開始, 簡單實現一下:

void encode(char *buf, int n)
{
    for( int i = 0; i < n; i ++ )
    {
    	//右移要解釋成無符號數,不然補1
    	unsigned char c = buf[i];
    	// 1個字節全部右移1位 = 自定義,喜歡就行
    	buf[i] = (c >> 1);
    }
}

void decode(char *buf, int n)
{
    for( int i = 0; i < n; i ++ )
    {
    	//右移要解釋成無符號數,不然補1
    	unsigned char c = buf[i];
    	// 1個字節全部左移1位 = 自定義,喜歡就行
    	buf[i] = (c << 1);
    }
}

            ---------------------------------------------------------------------------------------------------------

我們之前加密解密的字母,也全都對應到相應的二進制數,見 ASCII 表。

擴展的ASCII字符滿足了對更多字符的需求。擴展的ASCII包含ASCII中已有的128個字符(數字0–32顯示在下圖中),又增加了128個字符,總共是256個。即使有了這些更多的字符,許多語言還是包含無法壓縮到256個字符中的符號。因此,出現了一些ASCII的變體來囊括地區性字符和符號。例如,許多軟件程序把ASCII表(又稱作ISO8859-1)用於北美、西歐、澳大利亞和非洲的語言。   

 

上圖 字母和符號 ,以十進制看的,有些不明顯哈。我用程序打印出來,稍等。

#include <stdio.h>
#define Type char
void display( Type data )
{
	int bit = 8;
	int i = sizeof(Type) * bit;
	//想看不同字節,改data類型即可, Type 的宏定義換成 size_t 
	while( i-- )
	{
		data & (1<<i) ? putchar('1') : putchar('0');
	    if( !(i % 4) )
	    {
	    if( !(i % 8) )
	        putchar(' ');
	    else
	        putchar('-');
	    }
	}
	putchar(10);
}

int main(void)
{
	for( size_t i = 65; i < 123; i ++)
	    if( i < 91 || i > 96 )
	        putchar(i),
	        putchar(' '), 
	        display(i);
	return 0;
}

古典加密中,無論是手工加密還是機械加密都是以 字母 爲單位操作,使用計算機後,許多信息都可以被數字化,也打亂了語言規律的底層結構,不過,這並不影響加密解密的發揮。改變發生在 0、1 ,所以說計算機使加密解密進入了新時代。

從前的古典加密,加密一方會盡其所能的隱藏好加密的鑰匙、機器、說明書,但計算機加密中,這些都可以公開,就怕你搞不懂。

那麼,到底有多公開??

始於1976 年 11 月,美國國家標準局制定了一套規範的加密系統文檔[IBM研製],簡稱 DES,外號魔王[路西法]。具有極高的安全性、效率較高,到目前爲主,除了使用暴力枚舉搜索[怕量子計算機或超大型分佈式]算法對 DES 算法進行攻擊以外,還沒有發現更有效的辦法[也許有但是設計軍事是不會透露]

 

計算機加密解密分類

1.   對稱加密 與 非對稱加密 

2.  只能加密 與 加密也解密  

  •    對稱加密即 加密 and 解密使用相同的密鑰(同鑰匙,計算機術語),速度快,密鑰不方便管理;
  •    非對稱加密即 加密 and 解密使用不同的密鑰,速度慢,密鑰便於管理;

3.  對稱加密混合非對稱加密  取倆種加密的優點,如 AES + RSA

           AES + RSA 混合加密大致過程:使用 AES 裏的密鑰加密數據得到一個密文,再用 RSA 公鑰加密 AES 的密鑰接着把倆部分的密文組合起來加密起來給接收人;

           AES + RSA 混合解密大致過程:使用 RSA 裏的私鑰解密數據得到AES的密鑰,再用 AES 密鑰解密 AES 的密文;

star... [ 計算機 RSA加密 前置知識 :模、素數、逆元、歐拉定理、費馬定理、模冪運算 、大數運算、加密協議] 

p.s. RSA加密最主要的是模冪運算,迪菲-黑爾曼密鑰交換有舉例,其餘的數論背景知識,寫在數論博客的RSA加密部分。

在 RSA 算法出來的時候,發現者 RSA 三人以非常淺顯的方式表達了算法的真諦。 it 's pretty cool !

那是以一段 3 人的交流而產生的故事。ta 們 分別是 愛麗絲 、鮑勃、伊芙。記住啦,以後您會經常和 ta 們打交道。

現在 伊芙愛麗絲 通信,但發現 鮑勃 想偷聽,於是 ta 們採用 DES 算法加密消息 。

可,DES算法是一個 對稱加密,加密和解密使用相同的密鑰(鑰匙),DES雖然可以保證信息安全,可是交換密鑰是 DES 最大的漏洞。伊芙 和 愛麗絲 怎麼才能 把 每條消息對應的密鑰(鑰匙)  ? 最簡單的是見面告訴對方密鑰,可是如果需要頻繁的間斷通信呢,像歐洲外交官維吉尼亞一樣需要和歐洲其 ta 各國隨時保持通信。難道天天見面,那就不需要 加密解密 了。

那麼,我們是不是可以像維吉尼亞 2.0版本 、比爾密碼 採用 莎士比亞的《威尼斯商人》用字母順序 當密鑰,就不用經常見面了。可是,回想我們的加密原則,假設 解密一方的水平與加密方一致,那麼採用這樣有明確規律的密鑰好嘛 ?

肯定不好,只要 鮑勃 破解了第一次,以後的對話就都知道了。

在這個時代的時候,大銀行就有一個職位,工作人員每天提着保險箱,飛到世界各地給客戶送密鑰。

愛麗絲說,我知道怎麼密鑰保密,您看,

 

                  我(愛麗絲)把 信 放到盒子裏,上一把鎖,盒子寄給 伊芙;

                  伊芙收到盒子後發現沒密鑰打不開,於是按計劃繼續再給盒子裝一把鎖,再寄給 愛麗絲;

                  愛麗絲收到盒子後按計劃把先前自己的鎖解開,現在盒子只剩下伊芙的鎖了,再寄給伊芙就完成了;

這個過程比喻的圓滿,可是工程和科學是有差距的。一套字符使用 A 加密一次,又使用 B 加密一次,我們就必先 B 解密,在用 A 解密,即後加密先解。如果順序不符,解出來就不是原文。我們以簡單的凱撒加密舉例。假設第一把鎖即後移 4 位,第二把鎖後移 6 位。

e.g.  加密 :  China => 後移 4 位 => Clmre => 後移 6 位 => Irsxk

        解密:   Irsxk =>  後移 4 位 => Enotg => 後移 6 位 => Yhina

        解密:   先移 6 位 ,後移 4 位,無論字母大小寫,都不會得到準確的原文

 

回過頭想一想,我們需要的是一種 密文 + 密鑰 推不出 原文的密鑰,生活經驗解決不了,得找找數學上的定義。

數學的 數論 裏面有一種 不可逆 運算,模運算 %。

模 廣泛應用,

  •   時鐘、手錶是 模 12 系統;
  •   計算機的 n 位運算器是 模 n 系統,
  •   算盤 也是一個模系統;
  •   哈希算法本質也是模 運算,讓餘數保持在一個固定的範圍。
  •   我們約定的星期幾,其實也是一個模 7(6) 系統,
  •    Web 編程的分頁也是 模運算
  •    ... ...

加法模,例如時鐘,10 - 4 = 10 - (12 - 4) = 10 + 8 = 6 (mod 12),

               模  7 系統裏, 3 + 3 = 6 (mod 7), 6 + 6 = 5(mod 7),

乘法模,在模 13 系統裏, 11 * 9 = 8 (mod 13),

            11 * 9 = 99 % 13 = 8,

模的另外一種表現形式 : n % p  是否整除可以寫爲  \frac{n}{p} * p = n,還可以擴展代替除法

隨時取模(加法和乘法):

  •  (a+b)%p = (a%p + b%p) % p ,運用這個模性質,不會讓 程序裏的 a + b 溢出;
  •  (a*b)%p = [ (a%p) * (b%p) ] %p,基本同上;

爲什麼 模 不能逆呢 ?

       比如,99 % 13 只會是 8,可是 [0-10000] 之間 % 13 = 8 的,就有 770(加上8) 個數,見下圖,省略了上面一部分。              數學上,當模大到一定程度時,就完全沒有 逆運算 了。但,使用這套方案需要一個大前提---雙方同時在線

[模冪運算 RSA加密核心]

加密裏面把模當成一種加密函數,函數(有一個運算規則如, 7^{x}、+1、*9、等等都可以算在函數裏)

伊芙 和 愛麗絲倆個人都約定使用   7^{x} 這個函數,只不過這裏要約定一個範圍,ta 們用不可逆的模 n 函數。

  1. 愛麗絲和伊芙各給一個數 A 、B,分別代入模 11 函數計算,這裏約定 n = 11;
  2. 假如愛麗絲的數算出來是 N,伊芙算出來的是 M,接着交換結果因爲是模運算的,這個結果不可逆,鮑勃知道這個結果也沒用;
  3. 但愛麗絲和伊芙拿到對方的結果後,可以 以這樣的方式計算出密鑰 -  M^{A} (mod 11) = N^{B} (mod 11),算出來相等的值就是密鑰;

1、2、3合起來被稱爲 "迪菲-黑爾曼密鑰交換"

模擬一下上面的過程:

  1. 愛麗絲給出3,伊芙給出6,代入模 11 函數計算    =>   7^{3}~ mod~11= 343 ~mod ~11 = 2, ~7^{6} ~mod~11 = 117649 ~mod~11 = 4
  2. 愛麗絲算得 2 , 伊芙算得 4 ,接着倆個交換結果[打電話、QQ、Wechat···]
  3. 4^{3} ~mod~ 11 = 2^{6} ~mod~11,結果都是 9 ,用 9 加密;

迪菲-黑爾曼密鑰交換

     不是一種加密算法,而是一種密鑰交換算法,其思想是基於 離散對數 的。它具有兩個吸引力的特徵:

  • 一是僅當需要時才生成密鑰,減小了將密鑰存儲很長一段時間而致使遭受攻擊的機會;
  • 二是除對全局參數的約定外,密鑰交換不需要事先存在的基礎結構;

然而,該技術也存在幾點不足:

  • 一是沒有提供雙方身份的任何信息。
  • 二是它是計算密集性的,因此容易遭受阻塞性攻擊,即對手請求大量的密鑰。受攻擊者花費了相對多的計算資源來求解無用的冪係數而不是在做真正的工作。
  • 三是沒辦法防止重演攻擊。
  • 四是容易遭受中間人的攻擊。

迪菲-黑爾曼密鑰交換:https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange

RSA 算法是現在加密算法裏(MD5、DES、RC4、SHA)用的數學知識(數論)是最多的,MD5、DES、RC4、SHA是以密碼學混亂和擴散原則設計的,我們輪流講,下面請看 魔王露西法加密。


第五代加密

    DES

         DES 是對稱加密,既能加密也能解密。這個規範還有一個外號—— “魔王”(Lucifer),這是最典型的誕生在計算機時代的第五代密碼法。據說,當初設計者一直把這套算法叫做 “示範算法” (Demonstration),但70年代的操作系統對文件名長度有限制,於是只能截取前幾位字母 Demon,而 Demon 又是 “惡魔” 的意思,後來大家就用另一個惡魔的名字——路西法(Lucifer),也就是 “魔王” 來稱呼這個算法了。

DES 加密原理 使用 虛假信息 增加其複雜度的同時分析難道也上升,術語爲 混亂擴散 原則,通過對明文許多的排列和替換來加密,複雜度爲 O(1) 。

  1. 以給定的初始密鑰 K_{0 } 中得到 16 個子密鑰 (K_{1},K_{2},\cdots ,K_{15},K_{16}) 的函數;
  2. 加密明文時,每個子密鑰以規定的位操作按  (K_{1},K_{2},\cdots ,K_{15},K_{16}) 順序迭代 16 次(),每個密鑰一次();解密密文時,每個子
  3. 密鑰以規定的位操作按 (K^{16},K_{15},\cdots ,K_{2},K_{1}) 順序迭代 16 次(),每個密鑰一次();
  •           混亂:爲隱藏任何明文(原文) 同密文或密鑰間的關係
  •           擴散:使明文的有效位密鑰一起組成儘可能多的密文

 

     DES 代碼  倆種,一種是自己實現[跨平臺],一種直接調用 Windows [CryptoAPI]

  • 函數聲明原型 : 入口參數
  1. Key :  7 個字節 56 位 的工作密鑰
  2. Data :  8 個字節 64 位 ,要加密或解密的數據
  3. Mode :   DES 工作方式,加密或解密
  • 變種 DES : 分組加密 3DES 算法,使用 3 條 56 位的密鑰對數據進行 3 次加密,改進 DES 算法 密鑰空間小 的缺陷。
  1.    分組:該算法每次都處理固定長度的數據塊,如果沒達到 這個長度 ,按照 補0(實際情況) 填充 

  • 具體實現[ 看不懂時,跳到大步驟最後,留倆個有流程圖。從上往下,從左至右看 ]

輸入 一個 64位 數據塊 【明文(原文)/密文】   經過一系列的運算(排列和替換) 輸出 另一個 64位 數據塊 【密文(原文)/明文塊】

  1. 初始密鑰 計算出 16 子密鑰 :  

這是 des算法 設定好的 密鑰轉換表 。ta 告訴您,初始密鑰的第 57 位轉換後便是密鑰的第 1 位,初始密鑰第 49 位轉換後便是密鑰第 2 位,其 ta 同理,初始密鑰按?位置數 轉換後就是位置數所佔位置的第幾位。

14 * 4 = 56 位,這樣一個 64位 初始密鑰就變成了 56 位密鑰,接着計算 子密鑰。

  • 將 56 位密鑰 分成 倆個 28位 的組,接着倆組以規定方式 左移 i 位,i 值對應第幾輪即 第幾個子密鑰?,旋轉後重新合併。
  • e.g.  第 1 、2、9、16 輪時,左移 1 位,第 3、4、5、6、7、8、10、11、12、13、14、15 輪,左移 2 位。

   

  • 對重組後的密鑰進行置換,使 56位子密鑰 變成 48 位子密鑰(丟棄8位)

  • 這個置換表的規則同上 ,不過看清楚只有 12 * 4 = 48 位
  • 大致流程如下圖

總結 : 爲保證初始密鑰不同位在每一輪排列中可以用到,上面的 重組 和 置換 都需要進行 16 輪。

 

   2.  對 數據塊 進行 加密或解密

  • 初始置換 : 把 64位 數據塊 置換成 倆組 32位 數據塊 L_{0}R_{0} ,置換規則同上 16 * 4 = 64。

 

  • e.g. 數據塊第 58 位 置換爲 數據塊的 第 1 位,數據塊第 50 位 置換爲 數據塊的 第 2 位;
  • 重複操作 16 輪,操作 i 計算出  L_{i}R_{i} , i 是 1 ~ 16 的整數,每輪是 L_{i-1}R_{i-1} 開始,按下圖置換,置換規則同上,其結果用於下一輪直到 L_{16}R_{16} 結束;

  • 12 * 4 = 48 位,讓 R_{i-1} 從 32 位擴展爲 48位記爲 E。這裏是擴散原則,使數據塊中的 1位 將在下一輪操作中影響更多位。
  • 計算 48位 結果值與這一輪子密鑰 K_{i} 的異或值,得到一個 48位 的中間值 記爲 R_{in t}                                                                          [p.s. 到本輪的操作可以表示爲 : R_{int} = E(R_{i-1}) \oplus K_{i} , \oplus 是 異或。]
  • 接着, R_{in t} 需要通過8個單獨的S盒執行8次替換。每個 S盒j ,j: 1~8 從  R_{in t} 的 6j~6j+6 個位置取6位,並按 S盒j 的置換表查找出一個 4位 的值,將該值寫入緩衝區的 4j 處,見下圖有舉例。

解釋一下,從  R_{in t} 的 6j~6j+6 個位置取6位。

當前盒子j 取 6 位  (6*j ~ 6*j+6)
S0  0~6 
S1  6~12
... 6*j+6
S7 42~48

 

  • e.g. S盒j , j是3時,見上圖,R_{in t} 中第3個6位組是101011,第1位和最後1位是行,中間4位是列。因此 11_{2} = 3_{10}0101_{2} = 5_{10},我們再找下面的 S盒3圖,數表的時候行列都從 0 開始因爲實現出來是一個數組 ~ ,數出來是 9 。
  • 下圖 :  S盒j 替換表,替換規則基本同上 

  • 完成S盒置換後,結果又變成 32位 的值,於是用 P盒 置換,置換方式類似,P盒置換表如下

  • 最終置換: 取消掉初始置換,數據塊重新變成 64位。當 16 輪操作全部結束,將最後的右分組 R_{16} 和 最後的左分組 L_{16} 連接起來,組成一個 64位的 R_{16}L_{16}。最終置換表如下,置換規則類似。

  • 回顧一下,最後一輪左右分組並沒有交換。這樣最後的右分組在左邊而最後的左分組在右邊 ~
  • 大致流程圖如下 

您如果讀懂了,那麼您和程序員的距離也就差編碼實現。14個置換表用14個數組枚舉出來,旋轉i位,寫一個位操作函數即可,加密解密數據塊也是一個函數。和密碼學家的距離也就是加密的理論+數學基礎。如果有心,是可以同時達到的~  當然,如果哪個地方沒讀懂,歡迎交流!des代碼有 java 和 C 的,需要的話,私聊一下Q。

那麼可以寫好函數原型了:

 des_encipher爲加密函數,des_decipher爲解密函數

void des_encipher(const unsigned char *plaintext, 
                  unsigned char *ciphertext,
                  const unsigned char *key);

void des_decipher(const unsigned char *ciphertext, 
                  unsigned char *plaintext,
                  const unsigned char *key);

// plaintext : 明文(原文),ciphertext : 密文
// en加密函數的三個參數分別是 明文、密文、密鑰,而這裏中間參數是存儲函數結果的。e.g. 明文 + 密鑰 = 密文,de解密函數類似。

接着,把 14 個置換表 + 1個轉轉表 按順序實現出來:

  • 前 3 個表是計算 16 子密鑰
  • 再接着 1 個初始化表和 1 個擴展表計算初始數據的置換
  • 8 個 S盒
  • 1 個 P盒
  • 1 個最終置換
static const int DesTransform[56] = 
{
   57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
   10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,
   63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
   14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4
};

static const int DesRotations[16] = 
{
   1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};


static const int DesPermuted[48] = 
{
   14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
   23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
   41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
   44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
};

static const int DesInitial[64] = 
{
   58, 50, 42, 34, 26, 18, 10,  2, 60, 52, 44, 36, 28, 20, 12,  4,
   62, 54, 46, 38, 30, 22, 14,  6, 64, 56, 48, 40, 32, 24, 16,  8,
   57, 49, 41, 33, 25, 17,  9,  1, 59, 51, 43, 35, 27, 19, 11,  3,
   61, 53, 45, 37, 29, 21, 13,  5, 63, 55, 47, 39, 31, 23, 15,  7
};

static const int DesExpansion[48] = 
{
   32,  1,  2,  3,  4,  5,  4,  5,  6,  7,  8,  9,
    8,  9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
   16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
   24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32,  1
};

static const int DesSbox[8][4][16] = 
{
	{
		{14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7},
		{ 0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8},
		{ 4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0},
		{15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13},
	},
	
	{
		{15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10},
		{ 3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5},
		{ 0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15},
		{13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9},
	},
		
	{
		{10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8},
		{13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1},
		{13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7},
		{ 1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12},
	},
		
	{
		{ 7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15},
		{13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9},
		{10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4},
		{ 3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14},
	},
			
	{
		{ 2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9},
		{14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6},
		{ 4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14},
		{11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3},
	},
			
	{
		{12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11},
		{10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8},
		{ 9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6},
		{ 4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13},
	},
				
	{
		{ 4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1},
		{13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6},
		{ 1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2},
		{ 6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12},
	},
				
	{
		{13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7},
		{ 1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2},
		{ 7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8},
		{ 2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11},
	},
};

static const int DesPbox[32] = 
{
   16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
    2,  8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
};

static const int DesFinal[64] = 
{
   40,  8, 48, 16, 56, 24, 64, 32, 39,  7, 47, 15, 55, 23, 63, 31,
   38,  6, 46, 14, 54, 22, 62, 30, 37,  5, 45, 13, 53, 21, 61, 29,
   36,  4, 44, 12, 52, 20, 60, 28, 35,  3, 43, 11, 51, 19, 59, 27,
   34,  2, 42, 10, 50, 18, 58, 26, 33,  1, 41,  9, 49, 17, 57, 25
};

位操作實現出來:

typedef unsigned char  unchar;

int bitGet( const unchar *bits, int pos );
// 按小端模式取一個無符號數的二進制數;

void bitSet( unchar * bits, int pos, size_t state );
// 按小端模式設置一個無符號數的二進制數爲state;

void bitXor( const unchar *bits_1, const unchar *bits_2, unchar *bits_x, size_t size );
// 按參數 size 第幾位 選擇要異或的參數 bits_1 和 bits_2 , 結果交給 bits_x

void bitRotLeft( unchar *bits, size_t size, int cnt );
// 左循環移位 cnt 位,不用宏是因爲宏只能針對一個字節

給出明文、密鑰、密文以實現加密解密函數

    // des_encipher 加密   
    unsigned char destemp[8] = "Admin";                  // 明文(原文)
    unsigned char ciphertext[8],plaintext[8];            // 密文
    // ciphertext : 密文, plaintext : 明文(原文)
    unsigned char deskey[8] = 0xAAFF1133BBB822A5;        // 密鑰
    des_encipher(destemp,ciphertext,deskey);
    /*
	deskey[0] = 0xAA;
	deskey[1] = 0xFF;
	deskey[2] = 0x11;
	deskey[3] = 0x33;
	deskey[4] = 0xBB;
	deskey[5] = 0xB8;
	deskey[6] = 0x22;
	deskey[7] = 0xA5;
    // 隱藏的話,換成輸入 scanf("%14[0-9A-F]",deskey);
    */

    // encipher 解密
        unsigned char destemp[] = "fjlaflka fnlaflkalkf^&*()_&*()&*(jflajflkalkfalkflka";
	unsigned char deskey[8] = 0xAAFF1133BBB822A5;
	unsigned char ciphertext[100],plaintext[100];

	int len = strlen((const char*)destemp) + 1;

	des_encipher(destemp,ciphertext,deskey,len);

置換規則代碼實現,比如第 57 位轉換爲 第 1 位 ··· 

static void permute(unsigned char *bits, const int *mapping, int n) 
{
	unsigned char      temp[8];
	int                i;
	memset(temp, 0, (int)ceil(n / 8));
	for (i = 0; i < n; i++)
		bitSet(temp, i, bit_get(bits, mapping[i] - 1));  // 減 1 是因爲數組下標從 0 開始
	memcpy(bits, temp, (int)ceil(n / 8));
}
// double ceil(double x) math.h 裏,計算一個不小於 x 的最小整數

可根據加密術的原理寫一寫,完整代碼,博客上傳會佔很大的篇幅。需要的話,私聊QQ:23609099,備註是要 java 還是 C 版。15個表的數字,可以自定義。不過,這是一個標準的加密術改動表裏面的數字,其實意義不大~

 DES 的過程總結:

  1. 計算子密鑰:首先初始密鑰通過 初始置換旋轉表,最後經過 一輪置換 得到 16個子密鑰。
  2. 計算數據塊初始數據塊通過 初始置換擴展置換表 得到數據塊,用 8個S盒置換 後結果再同 P盒 置換,最終數據塊變成右左32個重組爲64位後,用 最終置換表 得到最終數據塊。
  3. 注意啦,因爲des算法加密把64位中的第 8、16、24、32、40、48、56、64 位,做奇偶效驗位,在計算密鑰時會忽略這8位,如果輸入的密鑰只是這 8 位有區別,那麼操作就是浪費時間。(心疼計算機 1s)

誒,不過介紹的時候說過,DES 算法的 明文空間小 嗎!現在您知道了嗎(呸呸,哪裏說了你個糟老頭子你壞的很),des 只能對 8個字節64位 的數據塊加密,而生活裏,一條信息不可能只有 4個漢字 的長度。所以,標準推出了分組加密,把數據一段一段(8個字節、4個漢字)的加密,這個過程叫ECB,但不安全因爲ECB的每個分組加密解密都是獨立,如果加密的倆個數據塊出現相同的字母,那麼密文出來也是相同的,這樣可以使用頻率分析。更好的是CBC---密碼分組鏈接,解決了ECB遺留的問題。做法 :  加密一個明文(分組)前,將前一個輸出的密文(分組)與該明文(分組)異或,然後加密;解密時,將前一個輸出的明文(分組)同接下來的待解密的密文(分組)求異或,然後解密。

void cbc_encipher(const unsigned char *plaintext,
				  unsigned char *ciphertext,
				  const unsigned char *key,
				  int size);

void cbc_decipher(const unsigned char *ciphertext,
				  unsigned char *plaintext,
				  const unsigned char *key,
				  int size);
// CBC 比起 ECB 加入了一個參數 size, 用來記錄明文(加密)或密文(解密)的長度

簡單的理解了一下計算機加密,發現全部都是操作二進制位,這裏送上操作二進制代碼函數實現。

int bitGet(const unsigned char *bits, int pos) 
{
	unsigned char      mask;
	int                i;
	mask = 0x80;
	for (i = 0; i < (pos % 8); i++)                             // pos % 8 == pos & 7   建議改成位操作
		mask = mask >> 1;	
	return (((mask & bits[(int)(pos / 8)]) == mask) ? 1 : 0);   // pos / 8 == pos >> 3  建議改成位操作
}

void bitSet(unsigned char *bits, int pos, int state) 
{
	unsigned char      mask;
	int i;
	mask = 0x80;
	for (i = 0; i < (pos % 8); i++)
		mask = mask >> 1;
	if (state)
		bits[pos / 8] = bits[pos / 8] | mask;
	else
		bits[pos / 8] = bits[pos / 8] & (~mask);
}

void bitXor(const unsigned char *bits1, 
			 const unsigned char *bits2,
			 unsigned  char *bitsx, int size) 
{
	int i;
	for (i = 0; i < size; i++) 
	{
		if (bitGet(bits1, i) != bitGet(bits2, i))
			bitSet(bitsx, i, 1);
		else
			bitSet(bitsx, i, 0);
	}
}

void bitRotLeft(unsigned char *bits, int size, int count) 
{
	int fbit,lbit,i,j;
	
	if (size > 0) 
	{
		for (j = 0; j < count; j++) 
		{
			for (i = 0; i <= ((size - 1) / 8); i++)
			{
				lbit = bitGet(&bits[i], 0);
				if (i == 0) 
				{
					fbit = lbit;	
				}
				else
				{
					bitSet(&bits[i - 1], 7, lbit);
				}
				bits[i] = bits[i] << 1;
			}
			bitSet(bits, size - 1, fbit);
		}
	}
}
// 這 4 個函數有說明,我放在上面了,找一找

DES破解 : 在 Shor算法 之後(後面會細說),還是貝爾實驗室,洛夫·格魯夫(Lov Grover)又設計出一種新算法,也是利用量子計算機。專門針對美國國家標準的加密法DES,結果在當前典型的應用級別下,4分鐘就把所有可能的鑰匙試遍,密碼隨之告破。隨着硬件的不斷迭代升級,使用超大型分佈式的傳統計算機同樣可以做到。

XOR異或算法

        XOR異或算法,a ^ a = 0,a ^ 0 = a。

#include <stdio.h>
#include <string.h>
const int N = 32;

void XOR( char *data, char *key ) // data: 放明文或密文   key : 密鑰   
{
	 int datalen = strlen(data);  // datalen: 明文或密文的長度 
	 int keylen = strlen(key);     // keyles: 密鑰的長度
	 
	for( int i = 0; i < datalen; i ++ )
	    data[i] = (char) (data[i] ^ key[ i%keylen ]);
}

int main(void)
{
    char data[N] = "I'm Debroon !";
    char key[N] = "23609099";

     // 加密
    XOR(data, key );
    printf("%s :> %s\n","密文",data);
     // 解密
    XOR(data, key );
    printf("%s :> %s\n","明文",data);
	return 0;
}

異或加密 : 這個算法雖然簡單,可是還需要改進改進。因爲異或有一個性質,a ^ a = 0;  如果密鑰和數據剛剛好是相同的字母或數字,那麼整個加密術都會崩潰。所以,加密解密前先判斷,遇到相同的就跳過。

#include <stdio.h>
#include <string.h>
const int N = 32;

void XORe(char *data, char *key)
{
	int len = strlen(data);
	int n = strlen(key);
	int j = 0;
	for( int i=0; i<len; i++)
    {
    	if(data[i] == key[j])  //考慮相等時a^a=0
    	{
    		j++;
    	}
    	else
    	{
            data[i] ^= key[j++];
             if( j == n )
                j ^= j;      // 相當於 j = 0, 嘿嘿偶爾裝逼也是挺好的;
        }
    }
}


int main(void)
{
	char data[N] = "I'm Debroon !";
	char key[N] = "oo";

     // 加密
    XORe(data, key );
    printf("%s :> %s\n","密文",data);
     // 解密
    XORe(data, key );
    printf("%s :> %s\n","明文",data);
	return 0;
}

異或+循環移位加密實戰:

加密這張圖片~

#include <stdio.h>
#include <string.h>

// 異或+循環位移
void en_de_code(char *buf, int n, char *key) {
int len = strlen(key); 
int j=0;

    for(int i = 0; i < n; i ++)   // n是循環位移數
    {
       buf[i] ^= key[j++];
       if( 0 == j % len )       // 密鑰循環
           j ^= j;     // 同 j = 0
    }
}

int main(void)
{
     FILE *fpr = fopen("Debroon.jpg","rb");
     FILE *fpw = fopen("Debroonee.jpg","wb");

     char buf[2048] = "";            // char buf[1<<11]; 2的11次方是 2048
     char *key = "show code~";
     int n = 0;
     while( (n = fread(buf, 1, 2048, fpr) ) > 0 ) {
       en_de_code( buf, n, key);    // 第一遍加密圖片
       en_de_code( buf, n, key);    // 第二遍解密圖片
     fwrite( buf, n, 1, fpw);
 }
 
    fclose(fpr);
    fclose(fpw);
return 0;
}

 


第六代加密

RSA算法

       下面我們來講 計算機界 聲名遠播的 RSA算法,ta不爲人知的故事和代碼實現 ~

第一個故事愛麗絲和伊芙通信問題,使用不可逆運算,有一個大前提---雙方同時在線。而現實生活裏,我們用微信、QQ、發郵件,一般都不用對方立刻回覆[(大部分)中國人喜歡收到就回復,西方人樂於在一個規定的時間段處理所有消息],解決這個問題使用的加密法就是第六代加密法 --- RSA加密法,手機支付加密、網銀加密都會用到ta。

歷史 :RSA這3個字母,分別代表的是它的3位創立者——Ron Rivest、Adi Shamir、Leonard Adleman。可最早發明這種方法的,並不是 RSA 這三個人,而是詹姆斯·艾利斯(James Ellis)、克里佛·考克斯(Clifford Cocks)和馬爾科姆·威廉森(Malcolm Williamson),我們簡稱他們三人爲 JCM


他們都是英國政府通訊總部的員工。不知道你是不是還記得圖靈破解恩尼格瑪機時,有個龐大的情報部門布萊切利園。當時9000名員工,戰後絕大部分都回到了原來的生活中,只有少數轉去英國通訊總部做了公務員,這3位密碼學家就是這樣留任的。


他們之後的研究也全都帶有軍方項目的性質,所以是國家機密。別看1975年他們就做出了整套非對稱鑰匙加密系統,但直到24年後的1997年,人們才知道這件事。這時不要說和RSA爭奪專利發明權了,連RSA的專利甚至都要過期了。[嘖嘖實在是,悲具了]


其實在1975年,JCM 剛剛做出全套算法的時候,他們曾經向國家通訊總部提出過註冊專利的要求,但總部沒有批。
幾年後他們聽說RSA三個人註冊了專利後,特別失落,因爲他們纔是最早的發明人,專利應該屬於他們。但因爲軍方的限制,他們只好保持沉默。不過,JCM他們並不後悔,其一因爲密碼學是一門特殊的學科,涉及國家安全的東西這是 ta 獨有的文化,是不能泄漏的,其二,他們如果想得到公開的讚揚,那麼就不會去做這份工作(英國??通訊部工作)。

RSA:非對稱加密(加密 和 解密使用不同的密鑰),以 愛麗絲 舉例 :

1. 愛麗絲有很多把鎖,每把鎖都有兩把不同的鑰匙,一把專門用來上鎖而不能開鎖,另一把專門用來開鎖而不能上鎖。


2. 愛麗絲把那把用來上鎖的鑰匙,儘量多的贈送給每個可能和她通信的人。這些人拿到鑰匙後,就可以把要跟愛麗絲說的話寫下來,再用愛麗絲給的鑰匙把鎖鎖上,然後寄給愛麗絲。


3. 愛麗絲拿到鎖好的盒子後,用另外一把只有她纔有的專用鑰匙打開盒子,就可以看到信裏的內容了。

 

這個過程的關鍵點就是,對同一把鎖來說,上鎖和開鎖用的是兩把不同的鑰匙,而之前我們說的DES加密法是使用相同鑰匙的。繼續以 愛麗絲 舉例講解 加密原理 :

愛麗絲展示出來的公開鑰匙,是通過兩個比較大的素數 p和q 相乘得到的一個更大的數 N 得到的。p和q 具體是多少,愛麗絲只要自己知道就行,千萬不要告訴別人。而乘積 N 是公開的,誰都可以知道。(找素數,可以看這個數論文章)
凡是要給愛麗絲髮消息的人,都需要用 N 來加密。加密的過程依然用的是模運算,而且模就是 N。整個數學過程會保證這個模運算不可逆,所以伊芙就算知道 N 也沒用。


那愛麗絲是怎麼解密的呢?她解密時就不需要 N 了,而是要用到 p和q 的具體值,而這兩個值別人都不知道,只有愛麗絲自己知道。具體來說,愛麗絲私下做的另外一個模運算中的模,不是剛纔我們說的 N,而是另外一個值 (p-1)×(q-1)。你看,在這個公式中就必須要知道 p 和 q 到底是多少纔行。(不太清楚原理,可以到計算機加密前面看模運算的介紹)


至於爲什麼一定要是 (p-1)×(q-1),你不用糾結,數學原理保證這樣操作能算出一把新鑰匙,這把新的鑰匙就是愛麗絲自己的私鑰。用這把私鑰,一定可以解出原文。


你說,這樣做就能保證安全嗎?伊芙已經知道了一個大數 N,她難道不能利用精巧的算法,找出N到底是由哪兩個大的質數相乘得到的嗎?


不能的,這種不能是由數學保證的。N 越大,找到 p和q 兩個因數的時間就增加得越誇張。現在銀行使用的 RSA 加密,都要求 N 是一個超過 300位 的大數。想分解這樣一個數,大約需要把全球計算機的算力集中起來算上幾億年纔行。

當 p和q 是一個大素數的時候,從它們的積 pq 去分解因子 p和q,這是一個公認的數學難題。然而,雖然RSA的安全性依賴於大數的因子分解,但並沒有從理論上證明破譯RSA的難度與大數分解難度等價。
雖然RSA加密算法作爲目前最優秀的公鑰方案之一,在發表三十多年的時間裏,經歷了各種攻擊的考驗,逐漸爲人們接受。但是,也不是說RSA沒有任何缺點。由於沒有從理論上證明破譯RSA的難度與大數分解難度的等價性。所以,RSA的重大缺陷是無法從理論上把握它的保密性能如何。
在實踐上,RSA也有一些缺點

1. 產生密鑰很麻煩,受到素數產生技術的限制,因而難以做到一次一密;

2. 分組長度太大,爲保證安全性,n 至少也要 600 bits(位) 以上,使運算代價很高,尤其是速度較慢。

3. 遇到量子計算機,KO。在電子計算機體系下,一個長達n位的大數分解需要的步驟數是n的指數量級,然而在量子計算體系下分解步驟只剩下平方量級。

傳統計算機裏,n稍稍增大,計算量的增加的曲線幾乎是垂直上升的;而量子計算機裏,計算總量增加的趨勢雖然平緩很多,但還是一個陡峭的斜坡。
所以我們現在不用那麼擔心量子計算機出現後,當前的密碼保護會瞬間碎成渣渣。不會這樣的。
只要我們願意把那個大數n再加長很多,比如從300位增加到3000位,保密強度還是足夠應付初期量子計算機破解的。

當然,真正意義上的量子計算機還在研發當中,目前的前沿進展當屬中科大潘建偉教授,用光學量子操控裝置實現了 15 = 3 x 5 的質數分解。具體來說,要破解 RSA 加密法,需要量子計算機和配套的量子算法,即硬件、軟件組合。

不過,現在軟件已經實現,硬件還不夠成熟(也許 10~30 年)。

所以,我們生活在加密法打敗解密法的年代。

不過,加密方,還有量子加密,或許是量子計算機都不能破解的。


算法 VS 算法 

RSA 算法破解 :  Shor(休爾) 算法
從1994年,舒爾提出了Shor 算法,直到2001年,IBM 和斯坦福大學纔在一個 7 個量子比特的量子計算機上首次實現了 Shor 算法,成功地把 15 分解成了 3 和 5,可見質數分解對於量子計算機並不是有算法就能輕易實現的。

現在看來在量子計算領域,各大IT 巨頭紛紛扎堆,中國的進步也很快,不僅科研機構參與(如前面中科大的潘建偉教授),百度和阿里巴巴也都建立了自己的量子計算實驗室。

量子計算機有個致命弱點,由於量子的疊加和糾纏狀態,它是極其脆弱的。

所以,量子計算機通常只能在接近絕對零度的狀態下才能保證量子狀態,正常運行。

看到這溫度不禁讓我想到了“超導”,那科研重要性絕對不亞於量子計算機,可到今天也沒看到大規模商用的曙光,一直趴在實驗室的低溫環境裏面慢慢的“升溫”。

所以對量子計算可以期盼,也不可盲目樂觀,當然它對加密系統的影響更不用過於杞人憂天。

 

Shor 算法:分解因數
1. 選擇任意數字a<N
2. 計算gcd(a, N)。這一步是求 a 和 N 的最大公約數。
3. 若 gcd(a,N) ≠ 1,則我們找到了因數,運算結束。
4. 否則,找到最小的r,使ax(mod N) = ax+r (mod N),這一步就是需要在量子計算機裏實現的步驟。(mod是模運算,根據歐拉定理r一定存在)
5. 若 r 是奇數,回到第一步。
6. 若 ar /2 ≡ -1 (mod N),回到第一步。
7. gcd(ar/2+1,N) 和 gcd(ar/2-1,N) 分別是 N 的因數。

任務完成。

第 4 步,一定得在量子計算機裏完成,其 ta 步驟在傳統計算機中編程即可。

以 N 爲 768 位舉例,這是當年傳統計算機做過分解因數最大的數了。

傳統計算機和量子計算機計算ta,大約是 100萬年1 秒 的差異。

不過現在的量子計算機只在量子計算時,才比傳統計算機快很多倍,普通運算還不如傳統計算機(量子加密時細說)。

也許量子計算機的出現,讓世界不一樣,不過我們只能擁抱技術

RSA 數論 知識:

 1、互爲素數 任何大於1的整數a能被因式分解爲如下唯一形式:

            a = p_{1}*p_{2}...*p_{l} (p_{1}p_{2}…,p_{l} 爲 素數)

 2、模運算

           ①{ [a(mod n)] × [b(mod n)] }mod n ≡ (a×b)(mod n)

           ②如果(a×b) = (a×c)(mod n), a 與 n互素,則 b=c(mod n)

 3、費馬定理 若p是素數,a與p互素,則a^(p-1) mod p = 1.  

 4、歐拉定理 歐拉函數φ(n)表示不大於n且與n互素的正整數的個數。 當n是素數,φ(n)=n-1。n=pq, p,q均爲素數時,則φ(n)= φ(p)φ(q)=(p- 1)(q-1)。 對於互素的a和n,有a^φ(n) mod n =1.

 5、模冪運算   即 x = a ^ b mod n (迪菲-黑爾曼密鑰交換部分有舉例)

原理:         RSA算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作爲加密密鑰。對極大整數做因數分解的難度決定了RSA算法的可靠性。換言之,對一極大整數做因數分解愈困難,RSA算法愈可靠。儘管如此,只有一些RSA算法的變種被證明爲其安全性依賴於因數分解。假如有人找到一種快速因數分解的算法的,那麼RSA安全性還不如des,但找到這樣算法的可能性非常小。

流程:

p.s.  類型全部化成 unsigned long 或者 要實現大數存儲與計算。建模: 加密解密分別定義一個結構體,加密包含e、n,解密包含d、n

typedef unsigned long Huge;

typedef struct RsaPK
{
	Huge e;
	Huge n;
} RsaPK;
typedef struct RsaSK
{
	Huge d;
	Huge n;
}RsaSK;

1、選擇兩個大素數p、q (推薦 1024 位,p和q 一定不能告訴別人)

  • 提示:程序讓用戶選擇隨機產生的素數是多少位用戶輸入 3 ,那麼素數的範圍 100~999,於是n位所有素數寫入文件後打開文件讓用戶自己選擇輸入,文件要刪除因爲不刪除就是告訴別人p和q,刪除文件: remove(路徑文件名)。
void Prime(int num)   // num 是 用戶輸入的數字,ta想要幾位的素數,3位即 100~999
{
	FILE *fp = fopen("prime.txt","w");    // 素數全部寫入文件,需要的時候打開,用完一定記得刪除
	assert(fp != NULL);
	UL base = 1;
	UL top = 9;
	for(int i=0; i<num-1; ++i)
	{
		base = base * 10;  //100
		top = top * 10 + 9; //999
	}

	int k = 1;
	base++;
	while(base <= top)
	{
		if(TestPrime(base))
		{
			fprintf(fp,"%-5d: %-6d",k,base);
			if(k % 5 == 0)
				fprintf(fp,"\n");
			k++;
		}
		base += 2;
	}
	fclose(fp);
}

// 素數測試建議使用 Miller_Rabin素數判定,我數論的博客代碼和原理都寫好了

2、計算n值, n = pq

  • 顯然
Huge n = p * q;

3、選擇一個小的奇數e是公鑰的一部分,要求爲e與(p-1)(q-1)互爲素數關係(沒有相同因子),通常選擇2、3、17、65537、19260817 等作爲 e 的值

  • 可以預先指定,推薦隨機選e
//自動產生隨機數(公鑰e)
void RandomE(Huge f,Huge &e)  
{
	int x,y,r;
	srand(time(0));
	while(1)
	{
		e=rand()%f;

		if(e>1)         
		{                
			y=e;
			x=f;      
			while(y!=1)
			{
				r=x%y; 
				x=y; 
				y=r;  
				if(y==1||y==0)    
					break;          
			}                   
			if(y==1)  
				break;   
		}
	}
}
// 調用 RandomE((p-1)*(q-1),e);

4、計算d值是私鑰的一部分:d = e^-1 mod(p-1)(q-1),可以認爲當 d 爲多少時,滿足ed mod(p-1)(q-1) == 1

  • 求 d 會用到逆元,根據歐拉函數φ(n)和公鑰e求解公鑰d,滿足:(d×e)modφ(n)=1,
    即d與e的乘積除以φ(n)餘數爲1 
Huge Inverses(Huge n, Huge e) 
{
    int x1, x2, x3, y1, y2, y3, t1, t2, t3, temp;
    x1=1;  x2=0;  x3=n;  y1=0;  y2=1;  y3=e;
    while(1)
	{
		if(y3==1)
		{
			if(y2>=0)   return y2;
			else   return (n+y2);
			break;
		}
		temp=x3/y3;
		t1=x1-temp*y1;
		t2=x2-temp*y2;
		t3=x3-temp*y3;
		x1=y1;  x2=y2;  x3=y3;
		y1=t1;  y2=t2;   y3=t3;
	}
}
// Huge d = Inverses((p-1)*(q-1),e);

5、 有了e、d值,將(e,n)做爲公鑰 即 PK = (e,n)   ,(d,n)做私鑰 即 SK = (d,n),加密用PK, 解密用SK。

  • 公鑰只需要求解出 e,私鑰只需要求解出 d

6、 加密方使用 PK 加密數據,解密方使用 SK 解密,爲了保證就算有人知道 PK 也無法推算出 SK , 必須保證 p和q 的值不能泄露

void PK(RsaPK *pk, Huge p, Huge q)    // PK 加密  
{
	FILE *fp = fopen("./key/pk.txt","w");
	assert(fp != NULL);
	Huge n = p * q;
	Huge e;
	RandomE((p-1)*(q-1),e);

	pk->n = n;
	pk->e = e;
	fprintf(fp,"e=%d, n=%d",pk->e,pk->n);
	fclose(fp);
}

void SK(RsaSK *sk, Huge e, Huge p, Huge q)  // SK 解密
{
	FILE *fp = fopen("./key/sk.txt","w");
	Huge n = p * q;
	Huge d = Inverses((p-1)*(q-1),e);
	sk->d = d;
	sk->n = n;
	fprintf(fp,"d=%d, n=%d",sk->d,sk->n);
	fclose(fp);
}

// PK(&pk,p,q);
// SK(&sk,pk.e,p,q);

7、 加密 ciphertext = plaintext ^ PK.e mod PK.n (迪菲-黑爾曼密鑰交換部分有舉例)

  • 一個加密冪模運算
//冪模運算
Huge ModExp(Huge a, Huge b, Huge n)
{
	Huge y = 1;
	while(b != 0)
	{
		if(b & 1)
			y = (y*a)%n;
		a = (a*a)%n;
		b = b>>1;
	}
	return y;
}

void RsaEncrypt(Huge plaintext, Huge *ciphertext, RsaPK *pk) // 加密
{
	//x = a^b%n;
	*ciphertext = ModExp(plaintext,pk->e,pk->n);
}
// RsaEncrypt((Huge)ch,&ciphertext,&pk);

8、 解密 plaintext = ciphertext^SK.d mod SK.n (迪菲-黑爾曼密鑰交換部分有舉例)

  • 一個解密冪模運算
void RsaDecrypt(Huge ciphertext, Huge *plaintext, RsaSK *sk)
{
	*plaintext = ModExp(ciphertext,sk->d,sk->n);
}
// RsaDecrypt(ciphertext,&plaintext,&sk);

複雜度 :  O(1)

實現 :  一種是自實現,還有一種是基於 OpenSSL 庫加解密,ta提供了 8 種對稱加密(AES、DES、Blowfish、CAST、IDEA、RC2、RC5、RC4),4 種非對稱加密(DH、RSA、DSA、橢圓曲線EC)

總結: RSA 是涉及數論知識最多的,但是明白數學原理後實現非常easy. 最重要的就是模冪運算,我有用愛麗絲的故事舉例,您一定要仔細認真品味 !需要完整C代碼Q:23609099, 青睞交流~

MD5、AES等加密算法,先不寫。講點不燒腦的,量子計算機·量子加密。

MD5[破解] 撞庫攻擊:https://cmd5.com/


第七代加密

    量子加密

        目前是第四代配備真空管,晶體管和集成電路的微處理器的計算機。TA們都基於傳統計算,傳統計算基於電路在給定時間處於單一狀態的經典現象,開啓或關閉。隨着電子元件越來越小,計算機日益變小和變快。但這個過程即將達到其物理極限。

        量子計算機基於量子力學現象,即一次可能處於多個狀態的現象。量子同時處於多個狀態並與非常遠的其他粒子相互作用。像疊加和糾纏的現象發生,下面會舉例先學理論。

疊加

In classical computing bits has two possible states either zero or one. In quantum computing, a qubit (short for “quantum bit”) is a unit of quantum information—the quantum analogue to a classical bit. Qubits have special properties that help them solve complex problems much faster than classical bits. One of these properties is superposition, which states that instead of holding one binary value (“0” or “1”) like a classical bit, a qubit can hold a combination of “0” and “1” simultaneously. Qubits have two possible outcomes zero or one but those states are superposition of zero and one. In quantum world qubit don’t have to be in one of those states. It can be in any proportion of those states. As soon as we measure its value its has to decide whether it is zero or one. This is called superposition. It is the ability of the quantum system to be in multiple states at same time.

In classical computing for example there are 4 bytes. The combination of 4 bytes can represent 2^4=16 values in total and one value a given instant. But in a combination 4 qubits all 16 combination are possible at once.


糾纏

Entanglement is an extremely strong correlation that exists between quantum particles — so strong, in fact, that two or more quantum particles can be linked in perfect unison, even if separated by great distances. The particles remain perfectly correlated even if separated by great distances. Two qubits are entangled through the action of laser. Once they have entangled, they are in an indeterminate state. The qubits can then be separated by any distance, they will remain linked. When one of the qubits is manipulated, the manipulation happens instantly to its entangled twin as well.

量子計算機 :現代加密最大的威脅...

怎麼用量子計算機破解 RSA加密算法(軟件) 我們已經說完。現在詳細講一講硬件。

在介紹現在計算機加密時,我告訴了計算機的數據都是由0和1組合,一個字節可以放 Nbits,數據在內存條裏就有 2^{N}種可能,計算機只能在某個時刻操作其中一個, 2^{N}種可能要一個一個的操作。

量子計算機存儲的0和1,總是疊加狀態(所有出現的0和1的可能性融匯在一起)。所以,一個量子存儲單位在某個時刻,可以同時存儲 2^{N} 數據。量子計算機運算一次,會同時對 2^{300} 個狀態操作。所以,TA的一次算力是  2^{300}  臺傳統計算機一次的和。

截至今日,通用、專用的量子計算機都沒出現~

有人聽說過 D-Wave 這個機器,而且也可能知道武器商洛克希德·馬丁 2011 年就訂購過一臺 128 個量子位的量子計算機,但實際上那臺 D-Wave 雖然確實利用量子理論計算,但它只能運行一種特殊的算法,叫做“量子退火算法”。

所以說,D-WAVE 只能算一個實驗性質的計算器,洛克希德·馬丁買它更多是一種嘗試。

後來 D-Wave 還開發出了二代,谷歌和NASA都買過一臺,這臺改進多了,是 512 位的,甚至可以進行一部分傳統運算。

但是運算速度大約跟 1998年 前的計算機的速度差不多,只是在進行特定的量子算法時才比傳統電腦快很多倍。

近景:

硬件發展困難的原因在於: 量子太容易變化。目前我們還沒有特別好的方法,控制量子按照算法的步驟運作。

最經典的例子,就是“光的雙縫干涉實驗”。

一個光源射向一堵障礙物,障礙物上有兩道縫可以透光。

當尺寸合適的時候,你會發現在兩道縫後面的幕布上看到一道明一道暗、一道明一道暗的條紋。

但是,請注意我下面這段話——當人們好奇心大增,想弄清從光源發出來的光的每一個光子,到底通過了哪個縫才射到了幕布的話,我們可以在縫前安放一個偵測光子的設備。

但如果我們這麼做了,在雙縫後面的幕布上明暗條紋就消失了,取而代之的是兩道集中的光斑。

 

所以說,我們的測量行爲會影響量子的狀態

我們一旦在縫隙前設置了裝置,去判斷入射光到底通過了哪道縫,本應該保持的明暗條紋狀態就消失了。


如果那些明暗條紋代表着量子計算機運算到某一時刻的量子位的正確狀態的話,那設計者要盡最大可能維持住這種狀態。

但實際上,這種狀態會被很多因素干擾。


在宏觀世界裏,任何一種物質都有可能和量子發生互動改變他們的狀態,比如說溫度。

所以那些量子計算機,都需要在零下 200 多攝氏度的極低溫才能正常工作。


在 2016 年,美國國家科技委員會曾經發布過《發展量子信息科學》的報告,量子計算機部分的規劃是這樣的:

未來 5 年會有幾十個量子位的原型機誕生……,通用量子計算機是一項長期的挑戰,至少需要10年。

因爲TA的硬件、算法、編程語言、編譯器都嚴重缺失,每一項都是巨大的困難。


量子加密 : 第 7 代加密法。數學 + 物理的量子力學組成。

數學部分:就是古典加密的 "單次鑰匙譜"(維吉尼亞3.0v)。

量子加密保密的數學過程:

第一步:愛麗絲給鮑勃傳送一串光子,其中每一位光信息都用0和1來標註。具體什麼算0,什麼算1,是有兩套測量方法——甲套和乙套。這兩種不同的測量方法,對同一個信號的測量結果是不同的。


第二步:鮑勃收到光信息後開始測量,就測量每個光信息位到底是0還是1。不過鮑勃並不知道愛麗絲那邊說的0或者1,到底是按甲方法測的還是乙方法測的。但沒關係,鮑勃對每個光信號都隨意選用一套方法來測出每個光信號到底是0還是1,就可以了。
所以鮑勃有的時候測出來的結果,肯定是跟愛麗絲髮出來的約定相符的,可有的時候測出來的結果又是不符的。不過這都沒關係,測完了再說。

第三步:畢竟鮑勃有一部分是測錯的,所以這時候兩個人必須打一個電話。這個電話完全不用保密,誰想竊聽都可以。
愛麗絲和鮑勃在電話裏都說什麼呢?就是針對每個信號,到底使用了哪套測量方法。這通電話裏就是按照順位,依次說出測量的方法。第1個信號是用甲方法測的還是乙方法測的,第2個順位用了甲還是乙,第3個順位用了甲方法還是乙方法……所有這些測量方法,由愛麗絲告訴鮑勃。


第四步:鮑勃聽完愛麗絲的這通電話之後,就對照剛剛自己瞎蒙着測的結果,也要回復愛麗絲。回覆的具體內容就是,自己哪幾位的測量方法蒙對了。


對鮑勃來說,自己之前測錯的那些不管,把測對的那幾位挑出來,這串數字就可以作爲他的鑰匙。

對愛麗絲來說,因爲鮑勃告訴了她哪幾位他選對了測量方法,所以愛麗絲也可以把鮑勃選對的那串數字也挑出來。

這個時候兩人挑出來的那串數字是完全相等的,而因爲完全相等,所以就可以作爲兩人的鑰匙了。

它既是鮑勃的鑰匙,也是愛麗絲的鑰匙。

 

整個過程中,鑰匙並沒有在額外的步驟中單獨傳輸。

他們在電話裏說一說,自己分別回去數一數,就能得到同樣的鑰匙。

之所以鑰匙一樣,也是數學原理上保證的,咱們不用糾結於數學原理的細節。

既然沒有單獨的鑰匙傳送環節,所以特工就很難下手。

另外,因爲鮑勃和愛麗絲都是隨機瞎蒙着選用甲套或乙套這兩種測量方式的,所以兩個人恰巧都用了同一種方法的序列挑出來的東西,也是隨機序列,也就是說鑰匙是完全隨機的。

到這裏,鑰匙既不用額外的傳輸,而且本身又是完全隨機的,這下就滿足了單次鑰匙簿加密法,並且改進了傳送鑰匙的薄弱環節。

所以,實際操作時可行性就高了很多。

就算中間有伊芙竊聽了他們的那通電話,伊芙也沒法判斷到底哪幾位應該挑出來當鑰匙,因爲她不知道鮑勃那邊針對每個光子位測量的結果是什麼。

現在,還有一種竊聽途徑——比如說伊芙知道竊聽電話沒用,那就乾脆直接竊聽光纜上的信息。

這樣怎麼辦呢?

這也不用擔心。

首先,光纜上的信息本來就是單次鑰匙簿加密的,就算在使用過程中不遵守隨機的原則,暴露了一些特徵,也不用擔心。

因爲在量子通信中,還會增加一個確認環節,來判斷光路上有沒有人竊聽。

這是怎麼實現的呢?

其實就是我們前面說的物理特性。

因爲人對光的測量會改變光原有的量子態,伊芙竊聽光纜,其實就相當於在雙縫干涉實驗時,在兩道縫前又添加了兩個探測器,這時候幕布上明暗條紋就會不見了。

也就是說,愛麗絲和鮑勃只要發現幕布上的圖案變了,就知道有人在竊聽了。

只要發現有人竊聽,他們就切換到其他線路上,那條被竊聽的線路就廢掉了。

這是量子加密又一個新功能。

在真實的應用下,伊芙竊聽會導致鮑勃收到的信息有錯誤。

但怎麼知道有錯呢?

其實他跟愛麗絲打個電話,覈對一下解碼出來的原文就可以了。

那你說,覈對原文那不就整個都泄密了嗎?

不怕的。

只需要隨機從鮑勃收到的消息中,挑選幾個字母覈對一下是否一致就可以了。

只要有一個不對,就說明這條光纜上有特工竊聽。

覈對的量大概佔原文的多少呢?有這麼一個數字可以參考。


假如從1075個字符裏隨意挑出75個,如果這75個都是一樣的話,基本就能保證這條信息是安全的。

爲什麼說基本呢?

因爲還存在很小很小的概率是它被竊聽了。

但因爲這75個雙方都是一致的,所以竊聽的概率就大概小於一萬億分之一,所以還是非常可信的。


第一次真實的量子加密系統,是1988年在IBM的實驗室做出來的。


它的甲套乙套測量方法,是使用光的偏振方向來呈現量子態。

用上下偏振代表甲套測量方法,用左右偏振代表乙種測量方法。

當時兩臺計算機只相隔30cm,通信成功了。

理論和實踐同時勝利,之後的改進主要就體現在兩臺計算機的通信距離上。


1995年,日內瓦大學可以做到相聚23公里完成量子加密通信。


2012年的時候,咱們國家潘建偉團隊把這個數字推進到了一百公里這個級別。現在這個團隊正在嘗試用空間軌道上的衛星和地面接收站間,實現量子加密信息的傳輸,距離就已經摸到千公里的級別。


只不過實驗中符合條件的光量子態數量實在太少,只有幾個到十幾個數位,遠遠不能承載信息的正文,所以到目前爲止,量子加密只適合給鑰匙加密。

據說白宮和五角大樓已經安裝了量子通信系統,並且已經投入使用。

如果美國可以這樣做,世界其他發達國家,包括中國的那些機要部門,很可能也已經部署了量子加密。


如果量子加密能被破解,就說明在量子理論中,出現了一種對量子態測量後還不改變量子態的方法,而這是違反量子力學基本原理的

所以,在量子加密術這裏,解密一方永遠都解不了。

傳統計算機與量子計算機的區別
傳統計算機 量子計算機
基於電路在給定時間處於單個狀態,開啓或關閉 基於量子力學的現象,例如疊加和糾纏,即一次可能處於多個狀態的現象。
信息存儲和操作基於“bit”,其基於電壓或電荷; 0V電壓(低)爲0,5V電壓(高)爲1 信息存儲和操作基於量子比特或“量子比特”,其基於單個光子的電子自旋或極化。
電路行爲受經典物理學的支配 電路行爲受量子物理學、量子力學的支配。
常規計算使用二進制代碼,即位0或1來表示信息 量子計算使用Qubits即0,1和0和1的疊加狀態來表示信息。
CMOS晶體管是傳統計算機的基本構建模塊 超導量子干涉裝置或SQUID或量子晶體管是量子計算機的基本構建模塊。
在傳統計算機中,數據處理在中央處理單元或CPU中完成,其由算術和邏輯單元ALU,處理器寄存器和控制單元組成 在量子計算機中,數據處理在量子處理單元或QPU中完成,QPU由許多互連的量子位組成。

WIFI 破解

       記錄在 《WIFI 破解》。


【 有錯別字還請指正哦。Thank you ~】

最後,強烈推薦爬蟲專題。https://blog.csdn.net/qq_41739364/column/info/34351

迪菲-黑爾曼密鑰交換https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange

更多的加密解密算法實踐加密解密 點擊文字即可。

另外,精心配備:

資料下載,5個C幣。因爲我沒找到哪調。要不,下一個破解器,或者加Q我發就好。

 

 

  • 資源推薦 《The Ultra Secret》《超級機密》《圖解密碼學》
  • 資源推薦 《計算機安全》                                             GeeksforGeeks
  • 資源推薦 《卓克的密碼學課》                                      得到
  • 資源推薦 《C語言小白變怪獸》                                   C語言中文網
  • 資源推薦 《SHA MD5 DES RC4 RSA 加密解密算法》51CTO
  • 資源推薦 《王桂林的C/C++》                                      能衆官網
  • 資源推薦 《周哥教IT之加密解密》                               騰訊課堂 
  • 資源推薦 《數學聊齋》

我不是打廣告,也沒廣告費,一起學習,加油~ 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章