【文章标题】: 密码学入门系列(三) 之 希尔密码(古典)
【文章作者】: jackozoo
【作者邮箱】: jackozoo@163.com
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  系列声明: 见(一)
  
  希尔密码(Hill Cipher)简介: 希尔密码是基于矩阵的线性变换, 希尔密码相对于前面介绍的移位密码以及仿射密码而言, 其最大的好处就是隐藏了字符的频率信息, 使得传统的通过字频来破译密文的方法失效. 
  安全性: 希尔密码不是足够安全的, 如今已被证实, 关于希尔密码的破解不在本文范围内, 有兴趣的朋友可以研读相关书籍以了解相关破译方法.  
  希尔密码所需要掌握的前置知识:
  1) 线性代数基础知识.
  2) 初等数论基础知识.
  
  坦白来说, 大部分密码学都要用到线性代数以及初等数论中的知识, 所以我希望大家可以自行找来相关书籍完成基础知识的学习, 所以关于什么是矩阵,什么是单位矩阵我不打算细讲. 在希尔密码中, 具体的话, 会涉及到矩阵的运算, 及其初等变化等.
  
  约定:
  1) 希尔密码常使用Z26字母表, 在此贴中, 我们也以Z26最为字母表进行讲解.在附带源码中有两种字母表选择. 
  2) 大家都知道最小的质数是2, 1 既不是质数也不是合数. 在此我们定义1对任何质数的模逆为其本身.
     因为对于任意质数n, 有: 1*1 % n = 1 的. 也应该是很好理解的.
  
  相关概念:
  线性代数中的逆矩阵: 在线性代数中, 大家都知道,对于一个n阶矩阵 M , 如果存在一个n阶矩阵 N ,使得 M * N = E (其中:
  E为n阶单位矩阵), 则称矩阵 N 为 矩阵 M 的逆矩阵, 并记为 M^-1.
  
  比如 2阶矩阵 M = [3,6] , 则很容易得知其逆矩阵 :
                   [2,7] 
  
      M^-1 = [7/9, -2/3]
            [-2/9, 1/3] .
  
  关于这个逆矩阵是如何计算出的, 通常的有两种方法:
  一是使用伴随矩阵, 通过计算行列式得到. 所用公式为: M^-1 = M^* / D . (其中M^*为M的伴随矩阵, D为M的行列式的值)
  二是通过增广矩阵, 在M右侧附加一个n阶单位矩阵, 再通过初等变换将增广矩阵的左侧变换为一个n阶单位矩阵, 这时右
      侧便是所求的逆矩阵.
  
  打住!! 我们到此先打住! 我们返回到希尔密码.
  
  希尔密码原理:
  加密者在对明文加密前会选择一个加密秘匙, 这个秘匙最终会以一个m矩阵的形式参与到加密算法中的. 在加密者选定了加密秘匙后, m便得到了确定,
  这时,加密者将明文按m个字母一组的形式分成多组, 最后一组不足m个字母的按特定的方式补齐. 这样就形成了很多组由m个字母组成的单个向量, 然后
  对每一个m阶向量, 我们用它去乘以确定好了的秘匙.
  
  如下为其中的一个分组A向量加密后变为B向量的过程:
  
   [A1,A2,A3 ... Am] * M = [B1,B2,B3 ... Bm] .
  
  我们将所有相乘后的向量连在一起, 便得到了密文. 这便是希尔密码的加密. 
  
  加密是非常简单的, 我们接下来来看一下解密部分, 解密部分要比加密部分稍微复杂一点点.
  
  上面我们提到了矩阵的逆矩阵. 大家可能会想, 既然明文A向量乘以秘匙M矩阵就得到了密文B向量, 那么我们将B向量乘以M的逆矩阵, 不就可以得到A了吗?
  
  大家的想法不错, 但是请注意:
  
  我们上面的那个例子 矩阵[3,6]的逆矩阵为[7/9, -2/3] , 发现了吧, 我们如果硬是去按常规方法计算M的逆矩阵的话, 你得到的
                         [2,7]          [-2/9, 1/3] 
  很可能是一个含有分式的矩阵. 这显然是不符合要求的.(为什么? )
  __asm
  {
      cmp you, "想知道为什么"
      jnz @F
  ]
  有的人会说,就算有分式又怎么样? 虽然分式在计算机中以浮点数体现, 但我还是让B乘以这个浮点数表示的M^1, 然后对结果进行
  四舍五入, 不久OK了? 不错这样是可以达到效果. 但是! 有以下几个缺点:
  1): 平白无辜的扯到了浮点运算, 还要进行四舍五入, 降低了算法效率使其看起来相当愚蠢.
  2): 解密秘匙体现的局限性, 其实是这个意思: 假如现在为二战时期, 我们需要派一位特工在盟军的两个司令部之间传达密钥. 而且
      规定密钥只能以A~Z这26个字母的形式体现. 也即你的秘匙只能是字母构成的, 接受方得到秘匙后按照Z26表对应将A当作0,B当作1,
      ... Z当作25 来翻译, 然后解密. 这种情况下, 上面的分式就不好表示了. 当然在真实情况下, 密钥是怎么个传输法, 那还要区
      别对待.
  
  
  @@:
  于是, 我们想对于一个矩阵能否有另外一种的逆使得其各元素皆为Z26范围中的元素同时可以顺利地完成解密了?  当然有.
  
  方法一: 最小公倍数法
  
  这种方法是在前面的矩阵逆的基础上来做文章的. 如下.
  
  我们接着上面那个带分式的M^-1来说, 大家观察一下, 很容易知道, 其中的分母9 其实为 原矩阵M的行列式值: 9 = 3*7 - 2*6;
  那我们将M^-1乘以9, 不就可以消掉分母了吗? 呵呵. 不行的. 
  
  我们要想消掉分母, 肯定得乘以一个数, 那到底要乘以多少了.  这里因为我们是Z26的字母表. 我们要保证乘以一个数之后, 原来的明文
  字母所增大的部分一定得是26的整数倍. 也即如下
  
  第一步:
  设a为明文中的一个字母. x 为 需要对当前的M^-1乘以的倍数. t为任意整数.
  
  ax = a + 26t. 恒成立. ==>>   t = a(x-1)/26  . 
  要想t为整数, 则 x = 26p+1 .p >=1. 这里我们一般取p =1 即可. 因此 x = 27.(及字母表个数加一)
  
  第二步:
  要消掉分母, 我们必须乘以分母D(M)的倍数. 其中D(M)为M的行列式值.
  
  得结果:
  
  所求 x = 最小公倍数( 27, D(M) )  .
  
  具体到上例中, x = 最小公倍数(27,9) = 27.
  
  我们将上面的M^-1 乘以27 得到: [21, -18]
                                [-6, 9  ]
  
  到了这一步, 我们得到了含负数的希尔逆矩阵.(注意: 从这里开始我们区别对待两种逆矩阵).
  而负数还是不能用Z26中的字母表示, 怎么办? 没关系, 对于负数我们加上26即可.  因为我们加上的是26,
  所以对于最终的取模是没有影响的. 因此我们得到:
  
  希尔逆矩阵 M^-1 = [21,8]
                    [20,9]
  
  
  方法二:纯整数初等变化法 (这个名字和上面那个最小公倍数法都是我自己想出来的名字, 可能不好听. 呵呵.)
  
  这一种方法的思想就是元素的模逆. 因为我们这里是Z26, 我们不关心元素的实际大小, 只关心它对26取模后的数值.
  
  因此, 在对原矩阵M求逆时, 我们先将M变为增广矩阵A, 再对A的每一列进行循环, 在第j列中, 从第j行开始, 每个元素
  遍历, 依次检查是否对26存在模逆. 否的话, 检查下一个, 是的话,乘以其模逆, 于是该元素结果得1, 再得到其行数为 i , 
  将此行与第j行互换(目的就是为了形成对角线的n个1), 然后对余下的行, 用此行乘以余下行的第j个元素的值去依次减余下的行,
  这样就使得当前第j列的n-1个0得以生成. 如果某列一直检查下去都没有元素存在模逆的话, 则该矩阵M不存在希尔逆矩阵.
  
  文字有时还是不如代码好说话, 看代码吧:
  
  (这次的希尔密码辅助软件,我使用的是C#.我嫌用C弄一些框框太麻烦,所以选择了简单的C#,弄一些框框是为了看中间过程.
   同时, 也能布置大家一个作业: 即读懂附件中的C#代码, 用C或C++重写之. 呵呵, 我想未装.NET Framework的非Vista朋友
  如果为了使用附件中的bin的话, 还是得自己用其他语言重写一边的吧 (-_*,坏笑中 ~~~))
  
  //检查元素a是否对n存在模逆
  

代码:
 
          public bool CheckReverse(int a)
          {
              int n = (int)zt;
              int p = 2;
              while(p*p<n)
              {
                  if (a%p == n%p &&  0 == a%p)
                  {
                      return false;
                  }
                  p++;
              }
              //when a equals with 1 , it's also reversiable
              return true;
          }
  
  //得到元素a对n的模逆
  
代码:
public int GetReverse(int a)
          {
              int n = (int)zt;
              int q, p, t;
              int x = 0, y = 1, z;
              q = n;
              p = a;
              z = (int)q / p;
              while (1 != p && 1 != q)
              {
  
                  t = p;
                  p = q % p;
                  q = t;
  
                  t = y;
                  y = x - y * z;
                  x = t;
                  z = (int)q / p;
              }
              y = y % n;
              if (y < 0)
              {
                  y += n;
              }
              //when a equals with 1 , it return 1.
              return y;
          }
  
  //使用纯整数初等变换法计算M的希尔逆矩阵.
  
代码:
        public bool Calc_M_1()
          {
              int[,] A = new int[nRank, nRank * 2];
              int[] T = new int[nRank*2];
              int i,j,k;
              //construct the [M|E] matrix A
              for (i = 0; i < nRank;++i)
              {
                  for (j = 0; j < nRank * 2;++j)
                  {
                      if (j<nRank)
                      {
                          A[i, j] = nMatrix[i, j];
                      }
                      else
                      {
                          if (nRank == j-i)
                          {
                              A[i, j] = 1;
                          }
                          else
                          {
                              A[i, j] = 0;
                          }
                      }
                  }
              }
  
              //begin to metamorphose A
              int a_1 = 0;
              for (j = 0; j < nRank;++j)
              {
                  //step1: get one reversiable element
                  for (i = j; i < nRank /*+ 1*/; ++i)
                  {
                      if (CheckReverse(A[i,j]))
                      {
                          a_1 = GetReverse(A[i, j]);
                          for (k = 0; k < nRank * 2;++k)
                          {
                              A[i, k] *= a_1;
                              A[i, k] %= (int)zt; 
                              T[k] = A[i, k];
                              A[i, k] = A[j, k];
                              A[j, k] = T[k];
                          }
                          goto step2;
  
                      }
                      if (nRank - 1 == i) //last element of the column, still no one is reversiable
                      {
                          return false;
                      }
  
                  }
  
              step2:    //create the n-1 zeros of the column
                  for (i = 0; i < nRank ; ++i)
                  {
                      if (i != j)
                      {
                          int t = A[i, j]; //first element of Row i .
                          for (k = 0; k < nRank * 2; k++)
                          {                            
                              A[i, k] -= t * A[j, k];
                              A[i, k] %= (int)zt;
                              if (A[i, k]<0)
                              {
                                  A[i, k] += (int)zt;
                              }
                          }
                      }
                  }
              }
              //construct M_1
              for (i = 0; i < nRank;++i)
              {
                  for (j = 0; j < nRank;++j)
                  {
                      nDeMatrix[i,j] = A[i,j+nRank];
                  }
              }
              return true;
          }
  
  
  效果图:
  我们来截几张图看看:
  
  n阶希尔逆矩阵的计算:
  http://bbs.pediy.com/picture.php?albumid=7&pictureid=78
  
  加密测试:(注意明文中的3个O分别变为了O,S,A . 很好地隐藏了字频信息.)
  
  http://bbs.pediy.com/picture.php?albumid=7&pictureid=79
  
  
  总结: 大概就讲这么多吧. 附件为辅助软件和C#源码.大家可以对这源码看文章. 也希望大家指出不足之处. 谢谢.
  
  
  

  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年05月21日 PM 11:55:39
上传的附件 HillEnc_bin.rar
HillEnc_src.rar