Rail Fence Cipher 籬笆式位移密碼,很接近劉伯溫燒餅歌內的藏頭詩的排列,來認識英文單字Rail Fence[籬笆]順便練習C#語法。
與凱撒、維吉尼亞加密法中的替代字母方式不同,Rail Fence Cipher是一種位移式密碼,從Wiki的案例秒懂:
原文:'WE ARE DISCOVERED. FLEE AT ONCE'
原文排列: 上到下,下到上~
加密後字串:
WECRL TEERD SOEEF EAOCA IVDEN
需要一個厲害的迴圈並且可以輸入列數Rails來處理:
string RailFenceEncryption(string PlainText, int RailCount)
{
//Transposition cipher
//(1)依照輸入參數建立籬笆列
List<List<string>> Rails = buildRails(RailCount);
//(2)計算一圈路徑的長度
int PLen = GetPathLength(RailCount);
//(3)按照籬笆排列字母
for (int i = 0; i < PlainText.Length; i++)
{
//取字元位置/路徑長度餘數大小來判讀是去程還是回程
int j = i % PLen;
if (j < RailCount) //去程
{
Rails[j].Add(PlainText.Substring(i, 1));
}
else //回程
{
Rails[PLen - j].Add(PlainText.Substring(i, 1));
}
}
//(4)取出每一個籬笆排列內的字元
StringBuilder sb = new StringBuilder();
foreach (var Rail in Rails)
{
foreach (var s in Rail)
{
sb.Append(s);
}
}
return sb.ToString();
}
會使用到的方法:
public List<List<string>> buildRails(int RailCount)
{
List<List<string>> Rails = new List<List<string>>();
//依照輸入參數建立籬笆列
for (int k = 0; k < RailCount; k++)
{
Rails.Add(new List<string> { });
}
return Rails;
}
public int GetPathLength(int RailCount)
{
//計算一圈路徑的長度(扣除出發點+結束點=2) 1->2->3->4->3->2->1
return (RailCount * 2) - 2;
}
加密測試程式:
string PlainTetx1 = "WEAREDISCOVEREDFLEEATONCE";
string CipherText1 = RailFenceEncryption(PlainTetx1, 3);
Console.WriteLine("PlainTetx:{0}", PlainTetx1);
Console.WriteLine("CipherText:{0}", CipherText1);
string PlainTetx2 = "123412341234";
string CipherText2 = RailFenceEncryption(PlainTetx2, 3);
Console.WriteLine("PlainTetx:{0}", PlainTetx2);
Console.WriteLine("CipherText:{0}", CipherText2);
string PlainTetx3 = "12345612345612345612345";
string CipherText3 = RailFenceEncryption(PlainTetx3, 4);
Console.WriteLine("PlainTetx:{0}", PlainTetx3);
Console.WriteLine("CipherText:{0}", CipherText3);
string PlainTetx4 = "12345678123456781234567812345";
string CipherText4 = RailFenceEncryption(PlainTetx4, 5);
Console.WriteLine("PlainTetx:{0}", PlainTetx4);
Console.WriteLine("CipherText:{0}", CipherText4);
加密測試結果:
這個網站真感心還有繪圖:
今天是新年後第一天上班,事情超多,晚上下班後頭昏昏,寫不出解密方程式,明晚再來補解密。
解密來了:
//(1)依照輸入參數建立籬笆列
List<List<string>> Rails = buildRails(RailCount);
//(2)計算一圈路徑的長度
int PLen = GetPathLength(RailCount);
//(3)計算商
int quotient = CipherText.Length / PLen;
//(4)計算餘數:不足一圈的單位距離
int remainder = CipherText.Length % PLen;
//(5)將字元填入每一個籬笆列
int offset = 0;
for (int i = 0; i < RailCount; i++)
{
//籬笆排數
int j = i + 1;
//是否為第一排籬笆及最後一排籬笆
bool isFirstAndLast = (j == 1 || j == RailCount);
//第一排籬笆及最後一排籬笆(一個循環圈中,路徑只會走一次)
int length = isFirstAndLast ? quotient : quotient * 2;
//餘數
if (j <= remainder)
{
length++; //去程
if (!isFirstAndLast) //回程
{
//(中間點 - 出發點) * 2 = 折返距離 + 出發點 <= 餘數
if (((RailCount - j) * 2) + j <= remainder)
{
length++;
}
}
}
//依照個別籬笆長度放入字元
for (int k = 0; k < length; k++)
{
Rails[i].Add(CipherText.Substring(offset, 1));
offset++;
}
}
StringBuilder sb = new StringBuilder();
//(6)按照籬笆排列取出字元
for (int i = 0; i < CipherText.Length; i++)
{
//取字元位置/路徑長度餘數大小來判讀是去程還是回程
int j = i % PLen;
if (j < RailCount) //去程
{
if (Rails[j].Count > 0)
{
sb.Append(Rails[j][0]);
Rails[j].RemoveAt(0);
}
}
else //回程
{
if (Rails[PLen - j].Count > 0)
{
sb.Append(Rails[PLen - j][0]);
Rails[PLen - j].RemoveAt(0);
}
}
}
return sb.ToString();
解密測試方法: 分別測試3、4及5排。
string CipherText1 = "WECRLTEERDSOEEFEAOCAIVDEN";
string PlainTetx1 = RailFenceDecryption(CipherText1, 3);
Console.WriteLine("CipherText:{0}", CipherText1);
Console.WriteLine("PlainTetx:{0}", PlainTetx1);
string CipherText2 = "11112626262353535354444";
string PlainTetx2 = RailFenceDecryption(CipherText2, 4);
Console.WriteLine("CipherText:{0}", CipherText2);
Console.WriteLine("PlainTetx:{0}", PlainTetx2);
string CipherText3 = "11112828282373737346464645555";
string PlainTetx3 = RailFenceDecryption(CipherText3, 5);
Console.WriteLine("CipherText:{0}", CipherText3);
Console.WriteLine("PlainTetx:{0}", PlainTetx3);
解密比較: 5 Rails。相符!
小結:
多學會一種移位式密碼(Transposition cipher)
古典又經典密碼學分類: 呼....
Rail Fence
參考: