課本上所授的案例只說到了模26值時的加密方式,若要想模到任意模值,以256為例,考慮如何將其實現加密,解密,在此基礎上再實現分組鏈接模式,即(CBC)。
首先來探討算法,Hill密碼的加密實現取決於一對可逆矩陣的變換。
核心公式為:
C=E(K,P)=PK mod 26;
P=D(K,C)=CK^-1 mod 26 =PKK^-1 =P;
可逆矩陣的性質滿足於(K*K^-1) mod 26=E(單位矩陣,僅角平分線上值為1);
其模值換成256也是一樣的,我們將鍵盤輸入的明文用ASCII碼來表示,查表可得ASCII正好有256位。第0個正好就是'\0',為空值,鍵盤輸入不得。將輸入與其做差即可。
若分組時候,明文不能做到整除,課采用填充的方式,填入0即可,解密的時候只需刪除即可得到原明文。
緊接着,我們來探討可逆矩陣的求法,這對矩陣滿足右式:(K*k^-1)%256=E;
我們用matlab軟件來幫助實現。
矩陣求法如下:
為求出合適的矩陣K,在一個n*n階矩陣中,任設二元素的值為x、y,其余元素給出具體的正整數數字,並令其行列式的值為+1或-1,可得一個二元一次或二元二次不定方程,可求其正整數解。如下例:
取K={{4 x 8 y},{12 1 6 9},{3 6 4 6},{2 11 3 8}};令|K|=1,得:-105x+187y=761,可借助matlab語言得其一正整數解:x=7,y=8;下面開始求其逆矩陣
K^-1:
設置為有理格式 : >>format rat
輸入矩陣K的值: >>K=[4 7 8 8;12 1 6 9;3 6 4 6;2 11 3 8];
用 >>det(K) 命令驗證矩陣K的行列式的值為1;
用 >>K1=inv(K)從數學角度求矩陣的逆K1
K1=[-112 -34 371 -128; -105 -32 348 -120; -39 -12 130 -45; 187 57 -620 214];
由於他的元素出現負整數,故將他的每一個元素加上256的若干倍,使他的元素全為正整數,再模256,
用>>K2=mod(K1+2560,256)命令得:
K2=[144 222 115 128; 151 224 92 136; 217 244 130 211; 187 57 148 214];
不難驗證K*K2mod256=[1 0 0 0;0 1 0 0;0 0 1 0; 0 0 0 1];
故K2就是矩陣K的逆矩陣K^-1;
接下來K與K2用於加密解密過程即可。
在此我們采用分組鏈接的方式
分組鏈接:(CBC模式)在CBC模式中,后一組的密文都受前一組密文影響,第一組密文受初始向量IV影響,從而明文的重復排列不會反應在密文中。
假設e~k~()表示用密鑰k進行的分組加密;e~k~^-1^()對應解密。 x~i~,y~i~表示長度為b的位字符串,IV表示長度為b的初始向量,(長度不夠的字符串會按照指定的填充規則進行填充) 加密:y~1~=e~k~(x~1~ ⊕ IV) y~i~=e~k~(x~i~ ⊕ y~i-1~) ,i≥2 解密:x~1~=e~k~^-1^(y~1~)⊕ IV x~i~=e~k~^-1^(y~i~) ⊕ y~i-1~ ,i≥2
如果每次加密是都選擇一個新的IV,則CBC模式就變成了一個概率加密方案,即相同的明文加密會生成不同的密文。
需要說明的是,如果每次選擇IV的數值得當,則代換攻擊會徹底失效,因為監聽者無法識別加密的模式。但是如果若干次加密使用相同的IV,則監聽者還是可以識別加密模式。
雖然(承接ECB的例子)這一次直接將我們的賬戶的密文替換上去已經不能實現向我們賬戶轉賬的效果,因為我們代換,會導致第四、五組消息都被破壞,但是有可能解密后,賬戶和金額仍然具有意義呢?(雖然可能性似乎很低)只是是隨機的,不再可控了而已。但這仍然是銀行所不可接受的。所以消息驗證碼和數字簽名的作用顯得尤為重要。
缺陷
因為某些原因,使得某一組密文中出現了錯誤的bit,將影響這一組(整組)及其之后一組(相應位)分組的解密。由此產生了CBC反轉字節攻擊。另外與下面所講的CFB模式一樣,CBC模式也不能抵御重放攻擊。
注意向量IV插入的位置,層層疊加,最終我們就實現了采用分組鏈接方式的Hillmod256算法,代碼如下:
main.cpp:
#include <iostream> #include <string> #include "Hill.h" using namespace std; int main() { //cin >> str; string str = "paymoremoney"; vector<int> num1; for (int i = 0;i < str.size();i++) { num1.push_back(str[i] - '\0'); } vector<int> num2 = Hencryption(num1); /*string str3 = ""; for (int i = 0;i < num2.size();i++) { str3 += '\0' + num2[i]; } cout << str3 << endl;*/ vector<int> num3 = Hdecryption(num2); string str2 = ""; for (int i = 0;i < num3.size();i++) { str2+= '\0' + num3[i]; } cout << str2 << endl; system("pause"); return 0; }
Hill.h:
#pragma once #include <iostream> #include <vector> using namespace std; vector<int> Hencryption(vector<int> num); vector<int> Hdecryption(vector<int> num);
Hill.cpp:
#include "Hill.h" vector<int> V = { 0,0,0,0 }; vector<int> Hencryption(vector<int> num) { vector<vector<int>> K = { {4,7,8,8},{12,1,6,9},{3,6,4,6},{2,11,3,8} }; vector<int> res; if (num.size() % 4 != 0) { int temp = num.size() % 4; switch (temp) { case 1: num.push_back(0); num.push_back(0); num.push_back(0); break; case 2: num.push_back(0); num.push_back(0); break; case 3: num.push_back(0); break; default: break; } } for (int i = 0;i < num.size() / 4;i++) { //進行分組鏈接加密 if (i == 0) { //異或 int a1 = V[0] ^ num[0]; int b1 = V[1] ^ num[1]; int c1 = V[2] ^ num[2]; int d1 = V[3] ^ num[3]; //加密 int a = a1 * K[0][0] + b1 * K[1][0] + c1 * K[2][0] + d1 * K[3][0]; int b = a1 * K[0][1] + b1 * K[1][1] + c1 * K[2][1] + d1 * K[3][1]; int c = a1 * K[0][2] + b1 * K[1][2] + c1 * K[2][2] + d1 * K[3][2]; int d = a1 * K[0][3] + b1 * K[1][3] + c1 * K[2][3] + d1 * K[3][3]; res.push_back(a % 256); res.push_back(b % 256); res.push_back(c % 256); res.push_back(d % 256); } else { num[i * 4 + 0] = res[(i - 1) * 4 + 0] ^ num[i * 4 + 0]; num[i * 4 + 1] = res[(i - 1) * 4 + 1] ^ num[i * 4 + 1]; num[i * 4 + 2] = res[(i - 1) * 4 + 2] ^ num[i * 4 + 2]; num[i * 4 + 3] = res[(i - 1) * 4 + 3] ^ num[i * 4 + 3]; //分組鏈接再加密,鏈接的是上一組的密文分組 int a = num[i * 4 + 0] * K[0][0] + num[i * 4 + 1] * K[1][0] + num[i * 4 + 2] * K[2][0] + num[i * 4 + 3] * K[3][0]; int b = num[i * 4 + 0] * K[0][1] + num[i * 4 + 1] * K[1][1] + num[i * 4 + 2] * K[2][1] + num[i * 4 + 3] * K[3][1]; int c = num[i * 4 + 0] * K[0][2] + num[i * 4 + 1] * K[1][2] + num[i * 4 + 2] * K[2][2] + num[i * 4 + 3] * K[3][2]; int d = num[i * 4 + 0] * K[0][3] + num[i * 4 + 1] * K[1][3] + num[i * 4 + 2] * K[2][3] + num[i * 4 + 3] * K[3][3]; res.push_back(a % 256); res.push_back(b % 256); res.push_back(c % 256); res.push_back(d % 256); } } //明文多出一兩個怎么修改? return res; } vector<int> Hdecryption(vector<int> num) { vector<vector<int>> K = { { 144,222,115,128},{ 151,224,92,136 },{ 217,244,130,211 },{187,57,148,214} }; vector<int> res; for (int i = 0;i < num.size() / 4;i++) { int a = num[(i)* 4 + 0] * K[0][0] + num[(i)* 4 + 1] * K[1][0] + num[(i)* 4 + 2] * K[2][0] + num[(i)* 4 + 3] * K[3][0]; int b = num[(i)* 4 + 0] * K[0][1] + num[(i)* 4 + 1] * K[1][1] + num[(i)* 4 + 2] * K[2][1] + num[(i)* 4 + 3] * K[3][1]; int c = num[(i)* 4 + 0] * K[0][2] + num[(i)* 4 + 1] * K[1][2] + num[(i)* 4 + 2] * K[2][2] + num[(i)* 4 + 3] * K[3][2]; int d = num[(i)* 4 + 0] * K[0][3] + num[(i)* 4 + 1] * K[1][3] + num[(i)* 4 + 2] * K[2][3] + num[(i)* 4 + 3] * K[3][3]; res.push_back(a % 256); res.push_back(b % 256); res.push_back(c % 256); res.push_back(d % 256); } //到這里為止,相當於每一個密文分組都過了解密那一步,接下來是與本組的密文分組異或得到明文分組 //解密組存在於res中,密文在num中,res此時是解密分組,不是最終的明文分組 for (int i = 0;i < num.size() / 4;i++) { if (i == 0) { res[0] = V[0] ^ res[0]; res[1] = V[1] ^ res[1]; res[2] = V[2] ^ res[2]; res[3] = V[3] ^ res[3]; } else { res[i * 4 + 0] = num[(i - 1) * 4 + 0] ^ res[i * 4 + 0]; res[i * 4 + 1] = num[(i - 1) * 4 + 1] ^ res[i * 4 + 1]; res[i * 4 + 2] = num[(i - 1) * 4 + 2] ^ res[i * 4 + 2]; res[i * 4 + 3] = num[(i - 1) * 4 + 3] ^ res[i * 4 + 3]; } } int j = res.size() - 1; while (res[j] == 0) { res.pop_back(); j--; } return res; }