实现 RSA 算法之 C 语言实现(第二章)(老物)
第二章 如何实现应用RSA算法
趁着白天在自家店里的闲暇时间来写写第二章了,假设记住了第一章的各种定理之后,我们又该如何实现RSA密码的加密解密呢?也懒得废话了,直接进入正题吧。
先回顾几个知识点:
1.模运算的性质:
结合律:(a % p * b) % p = (a * b) % p
可知当a == b时,(a % p * a) % p = (a * a) % p
2.欧拉定理
a^φ(n) ≡ 1 (mod n)
3.乘法逆元性质
e * d ≡ 1 (mod n) => e * d ≡ 1 + k * n(k为任意整数)
注:且当a与n互质时b有解。
注:如何去除带(mod)式子,令k = 0,即忽略其他同余式。
接着我们要证明一种现象,根据欧拉定理与乘法逆元性质外带一点模运算的结合律运算列出如下算式:
M ^ (e * d) = M ^ (1 + k * φ(n)) = M * M ^ (φ(n) * k) ≡ M * (1) ^ k ≡ M (mod n)
由此可知 M ^ (e * d) ≡ M (mod n)
注:M ^ φ(n) ≡ 1 (mod n)
(欧拉定理)
基于上述请诸位看以下算式:
C ≡ M ^ e (mod n) ①
M ≡ C ^ d (mod n) ②
于是我们这样做,将①代入②可得:M ≡ M ^ (e * d) (mod n)
,是不是和上述的现象的结果一致呢?
至此我们也可以看出,其实C就是一个中间密文。
那么我们来看看RSA运作流程:
假如A和B两人各自拿着e与d。
首先A要给B解密用的密钥d时必然得满足乘法逆元性质e和n要互质,否则得不到d。
其次对于A要加密一个B可以解开的密文必然满足M < n,否则M不满足②式也就不唯一了,同样B也要满足C < n,否则A也解不对B的数据。
最后利用①与②算式分别生成中间密文交由对方来还原数据。
综上所述,RSA算法描述就到此结束了。
以下是算法实现过程:
一.算法所需参数
1.密文数据规模 n = φ(n)
2.生成两把密钥 e 与 d
二.算法实现代码
一开始想着分C++和C版本代码,后来一想,既然都写了C代码何必浪费时间去写吃力不讨好的C++呢?= -=
于是现在直接给出完整的C代码了,因为第三章想基于第二章的代码部分进行讲解,尤其是一些可以优化本质的代码(即不在语法上改进),说不定出了第二章后就不想写第三章了,所以把代码放出来是为了以后好找理由偷懒吧(“都把代码给你了你怎么还要我来讲解(说笑的= -=)”)
事不宜迟贴代码吧!建议想学习的同学复制后拿到自己的编译器里运行测试一下~
#ifndef RSA
#define RSA
// 欧拉函数:φ(x) = x ∏ (1-1/p)(P是数N的质因数)
int Eular(int n)
{
int ret = 1, i;
for(i = 2; i*i <= n; i++)
{
if(0 == n%i)
{
n /= i, ret *= i-1;
while(0 == n%i)
n /= i, ret *= i;
}
}
if(n > 1)
ret *= n-1;
return ret;
}
// 欧几里何函数:Gcd(a, b)= Gcd(b, a%b)
int Gcd(int a, int b)
{
return 0 == b ? a : Gcd(b, a%b);
}
// 拓展欧几里何函数:a*x + b*y = gcd(a, b)
// gcd(a, b) = gcd(b, a%b)
// a%b = a-(a/b)*b
// a*x + b*y = gcd(a, b) = gcd(b, a%b) = b*x1 + (a-(a/b)*b)*y1
// = b*x1 + a*y1–(a/b)*b*y1
// = a*y1 + b*(x1–a/b*y1)
int Egcd(int a, int b, int *x, int *y)
{
int result, t;
// 递归终止条件
if (0 == b)
{
*x = 1, *y = 0;
return a;
}
result = Egcd(b, a%b, x, y);
t = *x, *x = *y, *y = t-a/b*(*y);
return result;
}
// 求解乘法逆元函数 b * (return value) ≡ 1 (mod a)
int Inverse(int a, int b)
{
int x = 0, y = 0;
if (Egcd(a, b, &x, &y) != 1)
return -1;
// 确保余数与被除数符号一致
if (b < 0)
a = -a;
if ((y %= a) <= 0)
y += a;
return y;
}
// 幂模运算a^b%k
// 通常思维暴力求解版本
unsigned PowMod(unsigned a, unsigned b, unsigned c)
{
unsigned ans = a;
while(b--)
ans *= a;
return ans % c;
}
// 幂模运算a^b%k
// 递归版本 a ^ b (mod c)
// 模运算结合律:(a * a) mod c =( (a mod c) * a ) mod c
// C 语言表达:((a * b) % p = (a % p * b) % p)
unsigned RecursionPowMod(unsigned a, unsigned b, unsigned c)
{
return b ? a * RecursionPowMod(a, b - 1, c) % c : 1;
}
// 幂模运算a^b%k
// Montgomery 版本
unsigned FastPowMod(unsigned a, unsigned b, unsigned c)
{
unsigned ans;
for (ans = 1; b; a = a*a%c, b >>= 1)
if (b & 1)
ans = ans*a%c;// LSB位为 1
return ans;
}
#endif // RSA
#include "stdio.h"
#include "stdlib.h"
int main()
{
// N 数据规模, En = φ(N)(欧拉函数结果), Encrypt 与 Decrypt 分别为两把密钥
int N = 0x1F, En = Eular(N), Encrypt = 2, Decrypt;
// 生成RSA参数
do
{
// 选取 Encrypt 与 En 互质的数
while(1 != Gcd(En, Encrypt))
Encrypt++;
// 求解其乘法逆元,若 Decrypt == -1 则表示 En 与 Encrypt 不互质
Decrypt = Inverse(En, Encrypt);
}while(-1 == Decrypt);
// 预览 RSA 参数
printf("En:%4d\tE:%4d\tD:%4d\n", En, Encrypt, Decrypt);
// 功能测试, 若陷入死循环必然是因为没能还原回原数据导致无法进入下一索引(++),可自行测试大于N的数据规模
puts("Test Encrypt Key");
for(int src = 0, goal; src < N; src++)
{
printf("src: %4d\t", src);
goal = RecursionPowMod(src, Encrypt, N);
printf("goal: %4d\t", goal);
src = RecursionPowMod(goal, Decrypt, N);
printf("src: %4d\n", src);
}
puts("Test Decrypt Key");
for(int src = 0, goal; src < N; src++)
{
printf("src: %4d\t", src);
goal = RecursionPowMod(src, Decrypt, N);
printf("goal: %4d\t", goal);
src = RecursionPowMod(goal, Encrypt, N);
printf("src: %4d\n", src);
}
return 0;
}
顺便也贴一下运行结果= -=~标题就是我的IDE路径,采用32位mingw编译器
最后的最后,这章也没什么好谈的了,代码说明了一切,看一句代码比废话十句更有效吧我认为~
如果有疑问或想要进一步学习的同学可以私聊我=- = 反正我晚上一直都很有空(偶尔会跑去打魔兽吧~)
现在代码放在我的 Github 了,我翻翻 CryptFunction ,包含 ECC 算法的实现(好像)。
实现 RSA 算法之 C 语言实现(第二章)(老物)的更多相关文章
- 算法竞赛入门经典_第二章:循环结构程序设计_上机练习_MyAnswer
习题2-1 位数 输入一个不超过109的正整数,输出它的位数.例如12735的位数是5.请不要使用任何数学函数,只用四则运算和循环语句实现. #include<stdio.h> int m ...
- 最小正子序列(序列之和最小,同时满足和值要最小)(数据结构与算法分析——C语言描述第二章习题2.12第二问)
#include "stdio.h" #include "stdlib.h" #define random(x) (rand()%x) void creat_a ...
- C语言学习第二章
今天开始学习常量,变量,基本数据类型,printf()函数和scanf()函数,算术运算符. 首先常量:是在程序中保持不变的量 变量:编写程序时,常常需要将数据存储在内存中,方便后面使用这个数据或者修 ...
- C程序设计语言练习 第二章
2.3 常量 strlen函数:返回s的长度 int strlenn(char s[]) { int i=0; while(s[i] != '\0') ++i; return i; } 2.7 类型转 ...
- C语言数据结构——第二章 线性表
二.线性表 2.1-线性表简介 2.1.1-线性表的定义 线性表是由若干个相同特性的数据元素组成的有限序列.若该线性表不包含任何元素,则称为空表,此时长度为0,当线性表不为空时,表中的元素的个数就是线 ...
- RSA算法详解及C语言实现
RSA算法它是第一个既能用于数据加密也能用于数字签名的算法.它易于理解和操作,也很流行.算法的名字以发明者的名字命名:Ron Rivest, Adi Shamir 和Leonard Adleman.但 ...
- 跨越千年的RSA算法
转载自http://www.matrix67.com/blog/archives/5100 数论,数学中的皇冠,最纯粹的数学.早在古希腊时代,人们就开始痴迷地研究数字,沉浸于这个几乎没有任何实用价值的 ...
- 【转】基于RSA算法实现软件注册码原理初讨
1 前言 目前,商用软件和共享软件绝大部份都是采用注册码授权的方式来保证软件本身不被盗用,以保证自身的利益.尽管很多常用的许多软件系统的某些版本已经被别人破解,但对于软件特殊行业而言,注册码授权的方式 ...
- [转载]RSA算法详解
原文:http://www.matrix67.com/blog/archives/5100 数论,数学中的皇冠,最纯粹的数学.早在古希腊时代,人们就开始痴迷地研究数字,沉浸于这个几乎没有任何实用价值的 ...
随机推荐
- linux 文件目录介绍
网上的资源 太多了,copy 过来 留给自己用吧! 摘自 : http:/ 使用linux也有一年多时间了 最近也是一直在维护网站系统主机 下面是linux目录结构说明 本人使用的是centos系 ...
- kaliXSSbeef的使用
Kali中Beef的安装和使用: 先打开终端输入 apt-get install beef-xss 然后切换到beef的安装目录 cd /usr/share/beef-xss 然后启动beef ./b ...
- Implement TensorFlow's next_batch for own data
The version of numpy data import numpy as np class Dataset: def __init__(self, data): self._index_in ...
- LeetCode 第 3 题:无重复字符的最长子串(滑动窗口)
LeetCode 第 3 题:无重复字符的最长子串 (滑动窗口) 方法:滑动窗口 滑动窗口模板问题:右指针先走,满足了一定条件以后,左指针向前走,直到不满足条件. 特点:左右指针的方向是一致的,并且是 ...
- 装win7英文版装系统学习
1:语言变换,下载Vistalizator ,详细的方法点击:方法见.需要安装语言包,语言包地址请点击:语言包.
- CentOS 7,使用yum安装Nginx
https://www.centos.bz/2018/01/centos-7%EF%BC%8C%E4%BD%BF%E7%94%A8yum%E5%AE%89%E8%A3%85nginx/ 文章目录 [隐 ...
- “fatal error: hdf5.h: 没有那个文件或目录”解决方法
問題一: Installing Caffe without CUDA: fatal error: cublas_v2.h No such file: 在Makefile.config中修改,將CPU_ ...
- 如何使用EF?
方法1: 新建好项目之后 → 右击类库 → 新建项 → ADO.NET实体数据模型(在Visual C#项中) → 从数据库生成 → 选择你要映射的数据库的数据源(将 『是,在连接字符串中包含敏感数据 ...
- Dubbo Monitor Simple 监控中心
下载项目源码(其中的 dubbo-registry-simple 代表简单的注册中心,用于开发测试,生产环境一般用zookeeper) https://github.com/apache/incuba ...
- 八、MD5加密并封装,并调用封装方法
一.MD5加密 封装Md5 public class Md5 { //十六进制下数字到字符的映射数组 private static final char hexDigits[] = { '0', '1 ...