凯撒加密与仿射加密

凯撒加密与仿射加密

二者都是古典密码学比较有代表的算法。

凯撒加密

加密原理

​ 凯撒加密的原理是约定一个密钥为k,设明文字母为x,则密文y y = x + k ( m o d 26 ) y = x + k (mod 26) y=x+k(mod26) 。注意当 x + k x + k x+k 超过字母 z 时就要像循环队列那样中返回头部的字母 a 继续移位。

如:明文是 t ,k = 10,则密文是 d 。

解密原理

​ 将上面的公式 y = x + k ( m o d 26 ) y = x + k (mod 26) y=x+k(mod26) 移项即可,目标是已知y求出x,因此解密公式为 x = y − k ( m o d 26 ) x = y - k (mod 26) x=yk(mod26)

加密代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
     int k = 10;
     string mingwen, miwen;
     getline(cin, miwen);
     for (int i = 0; i < mingwen.size(); i++)
     {
          if (mingwen[i] >= 'A' && mingwen[i] <= 'Z')
               miwen += (mingwen[i] + k - 'A') % 26 + 'A';//这一步比较难理解,(mingwen[i] + k - 'A')可以看成是明文向右移位k位之后距离a的位置,模26相当于循环一个周期,此时再用a加上(mingwen[i] + k - 'A') % 26就是密文了
          else if (mingwen[i] >= 'a' && mingwen[i] <= 'z')
               miwen += (mingwen[i] + k - 'a') % 26 + 'a';
          else
               miwen += mingwen[i];
     }
     cout << miwen;
     return 0;
}

解密代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int k = 10;
    string mingwen, miwen;
    getline(cin, miwen);
    for (int i = 0; i < miwen.size(); i++)
    {
        if (miwen[i] >= 'A' && miwen[i] <= 'Z')
            mingwen += 'Z' - ('Z' - (miwen[i] - k)) % 26;//这一步比较难理解,可以理解为是用('Z' - (miwen[i] - k))计算出密文向左移位k位后距离字母z的位置,之后再模26,就相当于循环了一整个周期,此时再用字母z减去('Z' - (miwen[i] - k)) % 26的结果就是明文了
        else if (miwen[i] >= 'a' && miwen[i] <= 'z')
            mingwen += 'z' - ('z' - (miwen[i] - k)) % 26;
        else
            mingwen += miwen[i];
    }
    cout << mingwen;
    return 0;
}

仿射加密

​ 在这里先放书出通用的加解密公式,再解释其中的原理。

​ 给定密钥 ( α , β ) (\alpha, \beta) (α,β) 其中 α , β ∈ [ 0 , 26 ) \alpha,\beta \in [0,26) α,β[0,26) 并为整数,给定明文x,密文y

加密:
𝑦 = α 𝑥 + β ( m o d    26 ) 𝑦=\alpha𝑥+\beta(mod \ \ 26) y=αx+β(mod  26)
解密:
x = 1 α ( y − β ) ( m o d    26 ) x = \frac{1}{\alpha}(y - \beta)(mod \ \ 26) x=α1(yβ)(mod  26)
其中 1 α \frac{1}{\alpha} α1 是逆元,而不是 α \alpha α 的倒数。

逆元的定义及求解

α \alpha α 对于模数n的逆元是满足 α γ = 1 ( m o d    n ) \alpha\gamma=1(mod\ \ n) αγ=1(mod  n) γ \gamma γ 的值,即找出 α γ % n = 1 \alpha\gamma\%n=1 αγ%n=1 γ \gamma γ 的值,这个 γ \gamma γ 就是 α \alpha α 的逆元。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WomVt8tn-1663850205133)(C:\Users\YYYYYKN\AppData\Roaming\Typora\typora-user-images\image-20220922162429488.png)]

定理:对于 α \alpha α 如果相对模数 n 存在逆元,则需要满足 g c d ( α , n ) = 1 gcd(\alpha, n) = 1 gcd(α,n)=1

​ 这里取 n = 26,为了使得 g c d ( α , n ) = 1 gcd(\alpha, n) = 1 gcd(α,n)=1 ,则 α \alpha α 必须与26互质。因为如果不是互质,则在加密的 y = α x + β y = \alpha x+\beta y=αx+β就不是单射。例如如 y = ( 13 x + β ) m o d   26 y = (13x+\beta)mod\ 26 y=(13x+β)mod 26,x 取2、4、6……的字母结果都是 y = β y = \beta y=β ,则就有很多个明文字母同时对应一个密文字母。

​ 由于 α \alpha α 与26互质且小于26,所以 α \alpha α 可能的取值是{1,3,5,7,9,11,15,17,19,21,23,25},而在定义中可知 β ∈ [ 0 , 26 ) \beta \in [0,26) β[0,26)

​ 所以对于仿射加密所有的密钥可能只有 12x26 = 312 种。

求逆元程序:

int qiuniyuan(int a, int n)
{
    for (int r = 0; r < n; r++)
    {
        if (a * r % n == 1)
            return r;
    }
    return -1;
}

解释为什么 α , β ∈ [ 0 , 26 ) \alpha,\beta \in [0,26) α,β[0,26)

模算术运算的性质

(1)[(a mod n) + (b mod n)]mod n = (a + b)mod n

(2)[(a mod n) – (b mod n)]mod n = (a - b)mod n

(3)[(a mod n) x (b mod n)]mod n = (a x b)mod n

具体解释

​ 以加密 𝑦 = α 𝑥 + β ( m o d    26 ) 𝑦=\alpha𝑥+\beta(mod \ \ 26) y=αx+β(mod  26) 为例, 𝑦 = α 𝑥 + β ( m o d    26 ) = α ( % 26 )   x ( % 26 ) + β ( % 26 ) 𝑦=\alpha𝑥+\beta(mod \ \ 26) = \alpha(\%26)\ x(\%26) + \beta(\% 26) y=αx+β(mod  26)=α(%26) x(%26)+β(%26) ,则 α , β \alpha,\beta α,β 超过了26也会被模回去,所以 α , β ∈ [ 0 , 26 ) \alpha,\beta \in [0,26) α,β[0,26)

使用暴力的方式对仿射加密进行破解

​ 由于 α \alpha α 可能的取值是{1,3,5,7,9,11,15,17,19,21,23,25},而在定义中可知 β ∈ [ 0 , 26 ) \beta \in [0,26) β[0,26) ,所以可以遍历所有得密钥组合 ( α , β ) (\alpha, \beta) (α,β) 看解出得明文哪个比较像人话,对应的密钥就是正确的密钥。

//代码中加入了计算时间的函数
#include <bits/stdc++.h>
using namespace std;
int qiuniyuan(int a, int n)//求逆元的函数
{
    for (int r = 0; r < n; r++)
    {
        if (a * r % n == 1)
            return r;
    }
    return -1;
}
int main()
{
    clock_t start,end;
    start = clock();
    ofstream ofile;//将所有解密出的明文放在文件里
    ofile.open("D:\\CPP\\output1.txt");
    int A[] = {1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25};
    for (int a = 0; a < 12; a++)//遍历密钥(a,b)中的a
    {
        for (int b = 0; b < 26; b++)//遍历密钥(a,b)中的b
        {
            ifstream ifile;
            ifile.open("input.txt");
            ofile << "(" << A[a] << ", " << b << ")" << endl;//输出此时的密钥
            string miwen;
            int r = qiuniyuan(A[a], 26);
            while (getline(ifile, miwen))
            {
                for (int i = 0; i < miwen.size(); i++)
                {
                    if (miwen[i] >= 'A' && miwen[i] <= 'Z')
                        ofile << char(r * (miwen[i] - 'A' - b + 26) % 26 + 'A'); //注意这里要+26,因为可能y-b有负数
                    else if (miwen[i] >= 'a' && miwen[i] <= 'z')
                        ofile << char(r * (miwen[i] - 'a' - b + 26) % 26 + 'a'); //注意这里要+26,因为可能y-b有负数
                    else
                        ofile << miwen[i];
                }
                ofile << endl;
            }
        }
    }
    end = clock();
    cout << end - start << "ms" << endl;
    return 0;
}

使用词频统计的方式对仿射加密进行破解

这种方式只对于较长的密文有效果,对于很短的密文可能会出错。

step1:统计密文中字母的频率

step2:与标准字母频率比较,找到两个明文-密文的映射(因为仿射加密是线性映射,所以明文和对应(长篇)密文出现的频率是一致的。)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwKhRg4B-1663850205134)(C:\Users\YYYYYKN\AppData\Roaming\Typora\typora-user-images\image-20220922171246919.png)]

step3

实际求解思路(代码里的思路)

​ 计算对应的 α 、 β \alpha、\beta αβ ,计算方法如下:

​ 如:明文和密文的对应关系是e->v,t->w

​ 由于:e:4,t:19,v:21,w:22,用 y = α x + β ( m o d    26 ) y=\alpha x+\beta(mod \ \ 26) y=αx+β(mod  26) 加密

​ 所以我们可得两个线性方程组:
{ 21 = 4 α + β ( m o d    26 )     即     26 m 1 + 21 = 4 α + β 22 = 19 α + β ( m o d    26 )     即     26 m 2 + 22 = 19 α + β \left\{ \begin{matrix} 21 = 4α +β(mod\ \ 26) \ \ \ \ 即 \ \ \ \ 26m_1 + 21 = 4α +β \\ 22 = 19α+β(mod\ \ 26) \ \ \ \ 即 \ \ \ \ 26m_2 + 22 = 19α +β \\ \end{matrix} \right. {21=4α+β(mod  26)        26m1+21=4α+β22=19α+β(mod  26)        26m2+22=19α+β

​ 上下相减得 26 ( m 2 − m 1 ) + 1 = 5 α 26(m_2 - m_1) + 1 = 5\alpha 26(m2m1)+1=5α ,用 m = m 2 − m 1 m = m_2 - m_1 m=m2m1 代替,因为 α \alpha α 可能的取值是{1,3,5,7,9,11,15,17,19,21,23,25},所以遍历 α \alpha α 可能的取值来求出整数的解 m m m(因为 m 1 、 m 2 m_1、m_2 m1m2 也是整数),第一个求解出来整数解 m 的 α \alpha α 就是密钥中的 α \alpha α

m m m 代替 m 1 m_1 m1 ,求出 β \beta β能这么代替的原因是:若不代替则最后的结果是
β = ( 26 m 1 + 21 − 4 α ) % 26 \beta = (26m_1 + 21 - 4α) \%26 β=(26m1+214α)%26

​ 代替后最后的结果应该是
β = ( 26 ( m 2 − m 1 ) + 21 − 4 α ) % 26 \beta = (26(m_2 - m_1) + 21 - 4α) \%26 β=(26(m2m1)+214α)%26

​ 根据模运算的法则,若 ( 26 ( m 2 − m 1 ) + 21 − 4 α ) (26(m_2 - m_1) + 21 - 4α) (26(m2m1)+214α) 小于0,则在模26的时候会先加上一个26的倍数c(即 ( 26 ( m 2 − m 1 ) + 21 − 4 α + 26 c ) = ( 26 ( m 2 − m 1 + c ) + 21 − 4 α ) (26(m_2 - m_1) + 21 - 4α + 26c) = (26(m_2 - m_1 + c) + 21 - 4α) (26(m2m1)+214α+26c)=(26(m2m1+c)+214α) ),让它变成正的之后再取模,所以
( 26 ( m 2 − m 1 + c ) + 21 − 4 α ) % 26 = ( ( 26 ( m 2 − m 1 + c ) ) % 26 + ( 21 − 4 α ) % 26 ) = ( 0 + ( 21 − 4 α ) % 26 ) (26(m_2 - m_1 + c) + 21 - 4α) \% 26 = ((26(m_2 - m_1 + c))\%26 + (21 - 4\alpha)\%26) = (0 + (21 - 4\alpha)\%26) (26(m2m1+c)+214α)%26=((26(m2m1+c))%26+(214α)%26)=(0+(214α)%26)
( 26 ( m 2 − m 1 ) + 21 − 4 α ) (26(m_2 - m_1) + 21 - 4\alpha) (26(m2m1)+214α) 大于0,则

( 26 ( m 2 − m 1 ) + 21 − 4 α ) % 26 = ( ( 26 ( m 2 − m 1 ) ) % 26 + ( 21 − 4 α ) % 26 ) = ( 0 + ( 21 − 4 α ) % 26 ) (26(m_2 - m_1) + 21 - 4\alpha)\%26 = ((26(m_2 - m_1))\%26 + (21 - 4\alpha)\%26) = (0 + (21 - 4\alpha)\%26) (26(m2m1)+214α)%26=((26(m2m1))%26+(214α)%26)=(0+(214α)%26)

可以发现代替后求出来的 β \beta β 都一样,所以可以直接代替加快求解速度。

里面的精髓就是26乘任何数的结果再模26都等于0。

​ 综上,可以直接用 m m m 代替 m 1 m_1 m1 带回 26 m 1 + 21 = 4 α + β 26m_1 + 21 = 4α +β 26m1+21=4α+β 求出 β \beta β

对于这一题求解出 α = 7 , β = 19 \alpha=7,\beta=19 α=7,β=19

书面上的书写

​ 上面都是实际求解过程,书面上我觉得应该这么写:

step3

​ 计算对应的 α 、 β \alpha、\beta αβ ,计算方法如下:

​ 如:明文密文的对应关系是e–>v,t–>w

​ 由于:e:4,t:19,v:21,w:22,用 𝑦 = α x + β ( m o d    26 ) 𝑦=\alpha x+\beta(mod \ \ 26) y=αx+β(mod  26) 加密

​ 所以我们可得两个线性方程组:
{ 21 = 4 α + β ( m o d    26 )     即     26 m + 21 = 4 α + β 22 = 19 α + β ( m o d    26 )     即     26 m + 22 = 19 α + β \left\{ \begin{matrix} 21 = 4\alpha +\beta(mod\ \ 26) \ \ \ \ 即 \ \ \ \ 26m + 21 = 4\alpha +\beta \\ 22 = 19\alpha+\beta(mod\ \ 26) \ \ \ \ 即 \ \ \ \ 26m + 22 = 19\alpha +\beta \\ \end{matrix} \right. {21=4α+β(mod  26)        26m+21=4α+β22=19α+β(mod  26)        26m+22=19α+β

​ 其中: m ∈ [ 0 , 26 ) m \in [0,26) m[0,26) ,这是因为若 26 m + 21 = 4 α + β 26m + 21 = 4α +β 26m+21=4α+β ,则 ( 26 m + 21 ) % 26 = ( 4 α + β ) % 26 (26m + 21)\%26 = (4α +β)\%26 (26m+21)%26=(4α+β)%26 ,所以 m 超过了26也会被模回去到 [ 0 , 26 ) [0,26) [0,26) 里。

​ 则遍历 m ,若求出唯一解 ( α , β ) (\alpha, \beta) (α,β) ,则这个 ( α , β ) (\alpha, \beta) (α,β) 就是对应的正确的密钥,若 m m m 从 0~25 如果都求不出 ( α , β ) (\alpha, \beta) (α,β) 说明这个明文-密文映射关系就错了。

对于这一题,当 m = 1 m=1 m=1 时有唯一解即 α = 7 , β = 19 \alpha=7,\beta=19 α=7,β=19

实际破解时,假定的密钥(a, b)可能不止一组,但是对于当前计算机的计算能力,即使是使用穷举法也很快就能破解仿射加密

//代码还加入了计算时间的函数
#include <bits/stdc++.h>
using namespace std;
int qiuniyuan(int a, int n)//求逆元
{
     for (int r = 0; r < n; r++)
     {
          if (a * r % n == 1)
               return r;
     }
     return -1;
}

int *tongjicipin()//统计密文的词频
{
     int cipin[26] = {0};//记录每个字母的词频
     ifstream ifile;
     ifile.open("input.txt");
     string miwen;
     while (getline(ifile, miwen))
     {
          for (int i = 0; i < miwen.size(); i++)
          {
               if (miwen[i] >= 'A' && miwen[i] <= 'Z')
                    cipin[miwen[i] - 'A']++;
               else if (miwen[i] >= 'a' && miwen[i] <= 'z')
                    cipin[miwen[i] - 'a']++;
          }
     }
     int a = 0, b = 0;//a是词频最大的字母序号,b是第二大的
     for (int i = 1; i < 26; i++)
     {
          if (cipin[i] > cipin[a])
          {
               b = a;
               a = i;
          }
          else if (cipin[i] > cipin[b])
               b = i;
     }
     int *result = new int(2);
     result[0] = a;
     result[1] = b;
     return result;
}

int *qiujiemiyao(int x, int y, int z, int t)//求解密钥的函数
{
     int A[] = {1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25};
     int *result = new int(2);
     for (int a = 0; a < 12; a++)
     {
          if ((A[a] * (t - y) - (z - x)) % 26 == 0)
          {
               int m = (A[a] * (t - y) - (z - x)) / 26;
               result[0] = A[a];
               result[1] = 26 * m + x - A[a] * y;
               result[1] %= 26;
               return result;
          }
     }
     result[0] = result[1] = -1;
     return result;
}
int main()
{
     clock_t start, end;
     start = clock();
     int zimucipin[] = {4, 19, 14, 0, 13, 8, 17, 18, 7, 3, 11, 2, 20, 12, 15, 24, 22, 6, 1, 21, 10, 23, 9, 16, 25};
     ofstream ofile;
     ofile.open("D:\\CPP\\output2.txt");
     int *cipin = tongjicipin();
     int *miyao = qiujiemiyao(cipin[0], zimucipin[0], cipin[1], zimucipin[1]);
     if (miyao[0] != -1 && miyao[1] != -1)
     {
          ifstream ifile;
          ifile.open("input.txt");
          ofile << "(" << miyao[0] << ", " << miyao[1] << ")" << endl;
          string miwen;
          int r = qiuniyuan(miyao[0], 26);

          //解密
          while (getline(ifile, miwen))
          {
               for (int i = 0; i < miwen.size(); i++)
               {
                    if (miwen[i] >= 'A' && miwen[i] <= 'Z')
                         ofile << char(r * (miwen[i] - 'A' - miyao[1] + 26) % 26 + 'A'); //注意这里要+26,因为可能y-b有负数
                    else if (miwen[i] >= 'a' && miwen[i] <= 'z')
                         ofile << char(r * (miwen[i] - 'a' - miyao[1] + 26) % 26 + 'a'); //注意这里要+26,因为可能y-b有负数
                    else
                         ofile << miwen[i];
               }
               ofile << endl;
          }
     }
     end = clock();
     cout << end - start << "ms" << endl;
     return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
仿射密码是一种基于数学原理的加密算法,它可以通过随机生成密钥a、b来加密和解密任意满足条件的数据。下面是一个基于C语言实现的仿射加密算法的示例代码: ```c #include <stdio.h> // 求两数的最大公约数 int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } // 求a模N=26的逆 int exgcd(int a, int n) { int p = a, q = n; int x = 0, y = 1; int z = q / p; while (p != 1 && q != 1) { int t = p; p = q % p; q = t; t = y; y = x - y * z; x = t; z = q / p; } y %= n; if (y < 0) y += n; return y; } // 加密函数 void encrypt(char *plaintext, char *ciphertext, int a, int b) { int i; for (i = 0; plaintext[i] != '\0'; i++) { if (plaintext[i] >= 'a' && plaintext[i] <= 'z') { ciphertext[i] = (a * (plaintext[i] - 'a') + b) % 26 + 'a'; } else if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') { ciphertext[i] = (a * (plaintext[i] - 'A') + b) % 26 + 'A'; } else { ciphertext[i] = plaintext[i]; } } ciphertext[i] = '\0'; } // 解密函数 void decrypt(char *ciphertext, char *plaintext, int a, int b) { int i, a_inv = exgcd(a, 26); for (i = 0; ciphertext[i] != '\0'; i++) { if (ciphertext[i] >= 'a' && ciphertext[i] <= 'z') { plaintext[i] = (a_inv * (ciphertext[i] - 'a' - b + 26)) % 26 + 'a'; } else if (ciphertext[i] >= 'A' && ciphertext[i] <= 'Z') { plaintext[i] = (a_inv * (ciphertext[i] - 'A' - b + 26)) % 26 + 'A'; } else { plaintext[i] = ciphertext[i]; } } plaintext[i] = '\0'; } int main() { char plaintext[100], ciphertext[100], decrypted[100]; int a, b; printf("请输入密钥a和b(用逗号隔开):"); scanf("%d,%d", &a, &b); if (gcd(a, 26) != 1) { printf("密钥a必须与26互质!\n"); return 0; } printf("请输入明文:"); scanf("%s", plaintext); encrypt(plaintext, ciphertext, a, b); printf("加密后的密文为:%s\n", ciphertext); decrypt(ciphertext, decrypted, a, b); printf("解密后的明文为:%s\n", decrypted); return 0; } ``` 在这个示例代码中,我们定义了两个函数encrypt和decrypt,分别用于加密和解密数据。在加密时,我们将明文中的每个字符都转换为一个数字,然后使用公式(a * x + b) % 26来计算密文中的每个字符。在解密时,我们使用公式(a' * (y - b) % 26)来计算明文中的每个字符,其中a'是a模26的逆元。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值