数论及其应用——密码学中的数论
密码学,是一门古老而又年轻的学科,在《模仿游戏》中Benedict Cumberbatch饰演的图灵,就是二战时期颇有造诣的密码学大师。虽然涉猎不深,但是笔者还是认为密码学同数论、组合数学一样,都是非常好的数学游戏,那么这篇文章,我们就来介绍一下一些简单的和数论有一定关联的加密方式。
最为古老的一种加密方式——凯撒密码,其实就是字符密码的一种方式。
在密码学中,有两个最基本的名词——明文、密文。明文即包含了加密者真正想表达的东西,而密文则是通过某种加密方式,由明文生成。而这种加密方式,就是明文中的基本字母(或者其他符号),与密文中的字母(或者其他符号)一一对应关系。
基于对上面概念的简单理解,我们就可以来看凯撒密码了。
明文:A B C D ……
密文:D E F G ……
即明文中的大写字母对应该字母在字母表位置中+3的那个大写字母。
凯撒密码就是一种利用移位变换来加密的密码,那么推广起来,它遵循这样的公式:C = P + k(mod 26) , 其中C为密文字符,P为明文字符。
基于对这个很简单的加密方式的理解,我们通过一个题目尝试来用代码来实现它。(Problem source : pku 3749)
在编程实现上,简单地对字母进行ASCII值的运算即可,这里基于C++中输入输出流的特点,我们可以仅仅通过一个字符变量来实现一条凯撒消息的翻译。
参考代码如下。
#include<stdlib.h> #include<stdio.h> #include<iostream> #include<string.h> using namespace std; int main() { char a[15],c; while(1) { while(cin >> a && strcmp(a,"START")) {if(strcmp(a,"ENDOFINPUT") == 0) break;} if(strcmp(a,"ENDOFINPUT") == 0) break; getchar(); while((c = getchar()) != '\n') { if(c >= 'A' && c <= 'Z') { c -= 5 ; if(c < 'A') c += 26;} cout << c; }cout << endl; while(cin >> a && strcmp(a,"END")); } return 0; }
今天介绍一种强大的加密方式——RSA,关于这种加密方式,我们直接通过一个具体的题目来了解一下。(Problem source:hdu 1211)
> choose two large prime integer p, q > calculate n = p × q, calculate F(n) = (p - 1) × (q - 1) > choose an integer e(1 < e < F(n)), making gcd(e, F(n)) = 1, e will be the public key > calculate d, making d × e mod F(n) = 1 mod F(n), and d will be the private key
You can encrypt data with this method :
C = E(m) = me mod n
When you want to decrypt data, use this method :
M = D(c) = cd mod n
Here, c is an integer ASCII value of a letter of cryptograph and m is an integer ASCII value of a letter of plain text.
Now given p, q, e and some cryptograph, your task is to "translate" the cryptograph into plain text.
题目大意:首先给出了RSA加密和解密的方法。给出了两个大素数p、q,计算n = p * q , F(n) = (p - 1)(q - 1)。选择一个整数e,是的gcd(e,F(n) ) = 1。计算d,使其满足d x e = 1 (mod F(n))。
设c是密文中字符的ASCII值,m是明文中字符的ASCII值,那么生成密文的方式为:
c = m^e(mod n).
则解密的方式为:
m = c^d mod n.
现在给出参数p、q、e、和一段密文,请你输出解密后的信息。
数理分析: 值得注意的是,既然题目中已经给出了解密公式,其实题目的难度就大大降低了。我们需要知道的是,一种加密方式是对应着一种解密方式的。人们可以自定义加密信息的方式,而解密公式则需要根据加密公式以及参数之间的关系式进行推导。
而在这个问题中,我们运用解密公式的时候需要求得d的数值,这里就需要用到RSA机制中 d x e = 1(mod F(n))这一条件来求解d了。即e关于模F(n)的逆元,自然而然我们想到了先前学过的拓展欧几里得算法。
编程实现:基于对算法流程的分析,剩下的部分则需要处理一些模拟过程中的小细节了,值得注意的是,这里并没有给出密文的长度的限制,因此我们需要处理字符串的技巧。
参考代码如下。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int extgcd(int a , int b , int &x , int &y) { if(b == 0) {x = 1;y = 0;return a;} int d = extgcd(b , a%b,x,y); int t = x; x = y; y = t - a/b*y; return d; } int pon(int x , int y,int mo) { int m = 1; while(y) { if(y&1) m = (long long )m*x%mo; x = (long long )x*x%mo; y >>= 1; } return m; } int main() { int n , p , q , e , d , m , c , l; while(cin >> p >> q >> e >> l) { n = p * q; p = (p - 1)*(q - 1); int y; extgcd(e , p , d , y); d = (d%p + p)%p; while(l--) { cin >> c; m = pon(c , d , n); printf("%c",m%128); } printf("\n"); } return 0; }
我们在这里再介绍一种加密方法——流密码。我们通过一个题目来引入这种加密技术的简单模型。(Problem source : pku 1026)
Description
The length of the message is always less or equal than n. If the message is shorter than n, then spaces are added to the end of the message to get the message with the length n.
Help Alice and Bob and write program which reads the key and then a sequence of pairs consisting of k and message to be encoded k times and produces a list of encoded messages.
Input
Output
#include<cstdlib> #include<string.h> #include<stdio.h> #include<iostream> using namespace std; int main() { int k , n , len , cir[210],dec[210]; char c , s[210],en[210]; bool flag[210]; while(scanf("%d",&n) && n) { for(int i = 1;i <= n;i++) scanf("%d",&dec[i]); memset(flag , 0 , sizeof(flag)); for(int i = 1;i <= n;i++) if(!flag[i]) { int q = 0 , j = i; while(!flag[j]) { q++; flag[j] = 1; j = dec[j]; } cir[i] = q; for(j = dec[i];j != i;j=dec[j]) cir[j] = q; } memset(s,' ',sizeof(s)); while(scanf("%d",&k) && k) { getchar(); len = 1; while(scanf("%c",&c) && c!='\n') { s[len] = c; len++; } memset(en,' ',sizeof(en)); for(int i = 1;i < len;i++) { int j = k%cir[i]; int po = i; while(j--) po = dec[po]; en[po] = s[i]; } en[n+1] = '\0'; printf("%s\n",en + 1); } printf("\n"); } }
另外放个小彩蛋,按照按照上文给出密匙,按照次数为930的加密,结果显示明文和密文是一样的!是笔者一次试出来的彩蛋!密码学是不是好神奇。