RSA 加密算法在C++中的实现 面向初学者(附代码)

news2024/12/25 9:35:38

概述

博文的一,二部分基础知识的铺垫。分别从密码学,数论两个方面为理解RSA算法做好了准备。第三部分是对RSA加密过程的具体介绍,主要涉及其密钥对(key-pair)的获取。前三个部分与编程实践无关,可以当作独立的关于RSA加密算法的介绍。第四部分开始介绍在编程层面实现RSA算法的基础知识,主要涉及一些算法,如拓展欧几里得算法米勒-拉宾素性检验算法,是为C++中实现RSA加密所作的铺垫。第五部分阐述了面向初学者实现RSA算法的思路,以及其局限,可改善之处。第六部分为提供的参考代码。

一. RSA算法的密码学基础

  密钥:明文转换为密文,对于窃听者来说,密钥和明文等价

  对称加密(symmetric cryptograph),特征在于加密和解密使用同一个密钥。

  非对称加密(asymmetric cryptography),也被称作公钥加密(public-key cryptography)。最主要的特征在于使用公钥加密,私钥解密

  下面我们通过一个例子,来简述非对称加密的过程,假设A,B两人进行公钥加密通信,则整个通信的过程,由信息接受者B启动(A为信息发出者)。

  B首先通过一定算法生成包含公钥,私钥的密钥对(key-pair),然后将公钥发送给A自己保留私钥,请求A利用这个公钥对信息进行加密。

  A利用该公钥对信息进行加密后,将密文传送给B,B利用自己的私钥对密文进行解密。

  值得注意的是:首先公钥是可公开的,因为光凭借公钥只能加密,而并不能解密,所以不用担心公钥传输过程中被窃听者截获,同理也不用担心密文被截获,因为唯一能够破解密文的密钥在信息接收者处。

  RSA算法(Rivest-Shamir-Adleman 取自开发者首字母),正是一种公钥密码算法

二. RSA算法的数论基础

(看之前需要理解同余符号的含义)

1.欧拉函数

对于正整数n,不大于n,且与n互素的数的个数记为\phi (n)

2.欧拉定理

a^{\phi (n)}\equiv 1(mod n) (其弱化形式,即在n为素数时,变为费马小定理

证明需要用到群论知识,与RSA算法关联不大,故在此不加赘述,可参考:

 欧拉定理 证明及推论_有钱哥哥家的的博客-CSDN博客_欧拉定理证明

3.同余的一些基本性质:

/1:乘积同余:即两数乘积,与两数模n的余数的乘积,关于模n同余,证明可以通过将两数写作kn+q(q为余数)的形式,比较其乘积与q1,q2乘积在模n时的结果。

/2:幂运算同余:a\equiv b(mod n),则a^{p}\equiv b^{p}(mod n),证明可以通过移项,由因式定理可知,其必有a-b这个因式

4.逆元

ab\equiv 1(mod n),则称a为b,关于模n的逆元

三.RSA算法介绍

我们用A来代表明文,B代表经过RSA算法加密后的密文。则可以用一个等式来阐明A,B间的关系:B\equiv A^{e}(mod n),且B< n,即B为A的e次方后除以n的余数。其中(e,n)为公钥。

(d,n)为私钥,则私钥满足的关系为A\equiv B^{d}(mod n)

下面我们来看如何得到公钥和私钥组成的密钥对(需要用到二.介绍的数学知识)。

1.得到公钥:

选取两个充分大的素数p,q, 其乘积的值即为n,在得到n后,计算其欧拉函数的值,即在1到n-1中有多少数与n互素。

因为n包含两个质因子p,q,所以在1到n-1中包含p,q因子的数均与n不互素。

包含p因子的有p,2p,3p一直到p(q-1),同理含q的有q到q(p-1)。一共p+q-2个数

则在这n-1个数中与n互素的数一共有n-1-(p+q-2)=n-p-q+1,且n可以写作p*q,可以得到:\phi (n)=(p-1)(q-1)

我们选取与\phi (n)互素的小于\phi (n)的数e,则(e,n)组成公钥。

2.得到私钥:

取e关于\phi (n)的逆元为d,则得到(d,n)私钥。

下面来证明为何(d,n)为私钥

即证明:A\equiv B^{d}(mod n)

两侧同时取e次幂可以得到A^{e}\equiv B^{ed}(mod n)

因为d为e的逆元,所以ed=k\phi (n)+1,将该等式带入到上式中,我们可以得到:

A^{e}\equiv B^{k\phi (n)}\times B(modn)

欧拉定理可知B^{\phi (n)}\equiv 1(modn),由同余的性质中的幂运算同余知,两侧同时取k次幂,可以得到:

B^{k\phi (n)}\equiv 1(mod n),再由同余基本性质中的乘积同余,可知B\equiv A^{e}(mod n),此即为公钥的条件,于是我们发现在d取e 关于\phi (n)的逆元时,两者等价,即私钥条件成立。

上述生成的公钥与密钥组成的密钥对便可用于加密。

RSA算法核心在于,对于一个大数的质因数分解是很困难的,一旦能够发现对于大数质因数的高效算法,RSA就能够被破译。

四.RSA在C++实现的算法基础

在利用c++实现RSA加密时,含需要一些补充的算法知识:

1.裴蜀定理(Bezout’s lemma):

一定存在整数x,y,使得线性方程组ax+by=gcd(a,b)成立。而ax+by=gcd(a,b)则称为裴蜀等式。

2.拓展欧几里得算法(extended Euclidean algorithm):

欧几里得算法(辗转相除法)常用于求算最大公约数(gcd),而拓展欧几里得算法则是在具备欧几里得算法的功能前提下,增加了求解裴蜀等式的功能。而在我们通过公钥(e,n)计算私钥(d,n)时,就需要用到拓展欧几里得算法。

因为ed\equiv 1(mod \phi (n)),可以写作(e)d+(-k)\phi (n)=1

又因为gcd(e,\phi (n))=1,故上式可化为(d)e+(-k)\phi (n)=gcd(e,\phi (n)),符合裴蜀等式。

d与-k分别为欲求的x,y,故可以使用拓展欧几里得算法来求解

 关于拓展欧几里得算法的细节可以参考:扩展欧几里得算法详解__Warning_的博客-CSDN博客_扩展欧几里得

3.米勒-拉宾素性检验(Miller-Rabin prime test)

RSA加密的关键在于其最初生成的两个充分大的素数p,q,其大小决定了密码破译的难度。但是要随机生成两个大素数是比较困难的,所以RSA算法中大多都采用通过米勒-拉宾素性检验的伪素数来作为p,q。

米勒-拉宾素性检验是基于费马小定理,对给定的任意奇数进行检验,检验通过则代表其有概率为素数,在进行多次检验后,若都通过,则其为素数的概率会非常高,可以作为素数使用。

五.RSA算法在C++中的实现

基本思路:

1.随机素数p,q的获取:利用数组,生成一定范围内一定质数的数表,产生随机数i,j对应素数数组的下标,由此达到在数表中随机选取素数的功能。

2.密钥的获取:利用拓展欧几里得算法获取d的值。

3.加密过程:将输入字符的ASCII码值进行RSA加密,密文为一串数字,实现方法是用字符数组与整型数组间的值传递。

局限与改善:

1.没有使用米勒-拉宾素性检验来获取大素数,而是用素数数表产生的素数对,其构成的密钥空间小,一旦数表范围被获取,则密钥极有可能被破解。

2.加密文本的读入没有涉及文件的读写层面,需要依靠人为输入,较为不方便。

3.部分计算没有考虑在选取充分大的素数时可能产生的数据溢出问题。

六.C++代码

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;

int exgcd(int a, int b,int *x,int *y)                                     //拓展欧几里得算法
{
    if(b==0)
    {
        *x=1;
        *y=0;
        return a;
    }
    int gcd=exgcd(b,a%b,x,y);
    int temp=*x;
    *x=*y;
    *y=temp-a/b*(*y);
    return gcd;
}

int isprime(int a)                                                          //素数判断
{
    int i;
    for(i=2;i<1+(a/2);i++){
        if(a%i==0)return 1;
    }
    return 0;
}

void primegenerator(int prime[10])                             //生成素数表
{
    int i,j=0;
    for(i=91;i<=1000;i++){
        if(isprime(i)==0){
            prime[j]=i;
            j++;
        }
      if(j>9)break;
    }
}


int main()
{
    int prime[10];
    primegenerator(prime);
    int seed,p,q;
    seed=time(0);
    srand((unsigned int)seed);                              //生成在范围内的随机素数p,q
    p=rand()%9;
    do{
        q=rand()%9;
    }while(q==p);
    int e,d,n,fi_n,r,nu,w1,w2;


    int a;
    cout<<"请选择加密/解密"<<endl;
    cout<<"输入0代表加密"<<' '<<"输入1代表解密"<<endl;
    cin>>a;
    char minwen[1000];

    int i,j,mi;
    if(a==0){
         n=prime[p]*prime[q];
         fi_n=(prime[p]-1)*(prime[q]-1);
         for(r=fi_n/2;n>=1;r--){                                                 //求得公钥
           if(exgcd(r,fi_n,&w1,&w2)==1){
             e=r;
              break;
          }
       }
       r=exgcd(e,fi_n,&d,&nu);
        cout<<"请输入明文"<<endl;
        scanf("%s",minwen);
        int shuma_minwen[strlen(minwen)];
        for(i=0;i<strlen(minwen);i++){
            shuma_minwen[i]=minwen[i];
        }
        int shuma_miwen[strlen(minwen)];                                         //录入结束,开始加密
        for(i=0;i<strlen(minwen);i++){
                mi=shuma_minwen[i];
                shuma_miwen[i]=1;
            for(j=1;j<=e;j++){
                shuma_miwen[i]=(shuma_miwen[i]*mi)%n;
            }
        }
        cout<<"密文为"<<endl;
        for(i=0;i<strlen(minwen);i++){
            cout<<shuma_miwen[i]<<' ';                                       //加密结束,输出密文,私钥
        }
        cout<<endl<<"密文长度为"<<i<<endl;
        cout<<endl<<"解密私钥为"<<endl;
        cout<<d<<' '<<n<<endl;
    }
    else if(a==1){
        int shuma_jiemiwen[10000];

         cout<<"请输入密文长度"<<endl;
        int k;
        cin>>k;
        cout<<"请输入密文"<<endl;                                                      //录入密文
        int t=0;
        for(i=0;i<k;i++){
            cin>>shuma_jiemiwen[i];
        }
        int sizel=k;

        cout<<"请输入私钥(d,n) (分别输入d,n用空格隔开)"<<endl;
        int d1,n1;
        cin>>d1>>n1;
        int ming;
        int shuma_jieminwen[sizel];                                                    //开始解密
        for(i=0;i<sizel;i++){
                ming=shuma_jiemiwen[i];
                shuma_jieminwen[i]=1;
            for(j=0;j<d1;j++){
                shuma_jieminwen[i]=shuma_jieminwen[i]*ming%n1;
            }
        }

        char jieminwen[sizel];
        for(i=0;i<sizel;i++){
            jieminwen[i]=shuma_jieminwen[i];
        }
        cout<<"明文为"<<endl;                                                        //输出明文
        for(i=0;i<sizel;i++){
            cout<<jieminwen[i];
        }
    }
    return 0;
}
 

这里提供的代码部分参考于以下链接:

C语言实现简单的RSA加解密算法_♡Starry.的博客-CSDN博客_rsa加密算法代码c语言

 但是做了一些改善

1.由随机数与素数表的对应关系,给出素数对(p,q),无需人为输入

2.计算逆元d时,采用拓展欧几里得算法,时间复杂度更低

但仍有一些点可以继续改进

1.没有使用米勒-拉宾素性检验来获取大素数,密码安全性较低

2.加密文本的读入没有涉及文件的读写层面,较为不方便。

此代码可以供初学者简单体会RSA加密的基本过程,以及主要算法,进一步优化有待探讨。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/530780.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# | 内存池

内存池 文章目录 内存池前言什么是内存池内存池的优点内存池的缺点 实现思路示例代码结束语 前言 在上一篇文章中&#xff0c;我们介绍了对象池的概念和实现方式。对象池通过重复利用对象&#xff0c;避免了频繁地创建和销毁对象&#xff0c;提高了系统的性能和稳定性。 今天我…

你真的了解索引吗

当我们学习存储算法和索引算法时&#xff0c;他们可以深入了解如何在系统中存储和查询数据。因为存储和查询数据是许多系统的核心功能之一&#xff0c;例如数据库、搜索引擎等。理解这些算法可以帮助程序员更好地设计和优化系统架构&#xff0c;提高系统的可扩展性、可用性和性…

玩转Google开源C++单元测试框架Google Test系列(gtest)之二 - 断言

一、前言 这篇文章主要总结gtest中的所有断言相关的宏。 gtest中&#xff0c;断言的宏可以理解为分为两类&#xff0c;一类是ASSERT系列&#xff0c;一类是EXPECT系列。一个直观的解释就是&#xff1a; 1. ASSERT_* 系列的断言&#xff0c;当检查点失败时&#xff0c;退出当前…

大数据之光:Apache Spark 实用指南 大数据实战详解【上进小菜猪大数据】

上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货。 本文将深入探讨Apache Spark作为一种强大的大数据处理框架的基本概念、特点和应用。我们将详细介绍Spark的核心组件&#xff0c;包括Spark Core、Spark SQL、Spark Streaming和Spa…

百子作业 —— 中国邮递员问题

题目 严老师和宋老板去勘测武威市区的道路网&#xff0c;每一条路都需要勘测&#xff0c;且需要两人合作.武威市区可以近似地看成六横六纵组成的道路网&#xff0c;自西向东依次为学府路、民勤路、西关路、中关路、富民路、滨河路&#xff1b;自北向南依次为雷海路、宣武路、祁…

Redis基本数据类型及使用(2)

书接上回&#xff0c;这节讲讲其余的基本数据结构使用 集合&#xff0c;有序集合以及遍历和事务的使用 Set集合&#xff0c;无序不重复的成员 表现形式&#xff1a; key1string1string2key2string1string2 常用的基本操作&#xff1a; sadd key string1 [string2..]添加1…

第二十届宁波大学程序设计竞赛(同步赛)

A-0-1翻转_第二十届宁波大学程序设计竞赛&#xff08;同步赛&#xff09; (nowcoder.com) 思路&#xff1a; 我们观察发现&#xff0c;奇数位与偶数位的1每次操作一定时同时增加或者减少的&#xff0c;我们无法做到同时删除奇数位的两个1.。不满足相等则情况无解那么&#xf…

【谷粒商城之订单服务-支付】

本笔记内容为尚硅谷谷粒商城订单服务支付部分 目录 一、支付宝沙箱 沙箱环境 二、公钥、私钥、加密、加签、验签 1、公钥私钥 2、加密和数字签名 3、对称加密和非对称加密 三、内网穿透 四、整合支付 1、导入支付宝SDK依赖 2、封装工具类和PayVo 3、前端访问支付接…

python汉诺塔编程代码

汉诺塔问题是一个经典的递归问题。以下是使用Python实现汉诺塔的一个简单方法&#xff1a; python def hanoi(n, source, target, auxiliary): if n > 0: # 把 n-1 个盘子从 source 移动到 auxiliary hanoi(n-1, source, auxiliary, target) # 把第 n 个盘子从 source 移动到…

三十四、服务治理、实现负载均衡、

1、服务治理介绍 先来思考一个问题 通过上一章的操作&#xff0c;我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址 &#xff08;ip&#xff0c;端口&#xff09;等硬编码到了代码中&#xff0c;这种做法存在许多问题&#xff1a; l 一旦服务提供者地址变化…

浪潮之巅第一章 — 帝国的余辉(ATT)(一) 阅读笔记

在这十几年间&#xff0c;它们代表着科技的浪潮&#xff0c;直到下一波浪潮的来临。 从一百年前算起&#xff0c;AT&T 公司、IBM 公司、苹果公司 (Apple)、英特尔 (Intel) 公司、微软 (Microsoft) 公司、思科公司 (Cisco) 公司、雅虎 (Yahoo) 公司和谷歌 (Google) 公司都先…

相见恨晚的Matlab编程小技巧(2)-代码怎么做到逻辑清晰?——巧用注释符“%“

本文将以教程的形式详细介绍Matlab中两个常用符号“%”和“%%”的作用。初学者可以通过此文掌握这两个符号的用法&#xff0c;为Matlab编程打下坚实的基础。 一、什么是“%”符号&#xff1f; 在 Matlab 中&#xff0c;“%” 符号是注释符号&#xff0c;它后面的文本被视为注释…

GEE:下载MODIS海表温度影像

作者:CSDN @ _养乐多_ 本篇博客将介绍如何使用Google Earth Engine(GEE)平台下载MODIS(Moderate Resolution Imaging Spectroradiometer)海表温度影像数据。MODIS是一种遥感传感器,用于监测地球表面的温度变化。我们将展示如何获取MODIS数据集,并选择特定感兴趣区域进行…

C#操作Redis明细内容 C#调用redis c#使用redis业务 C# Redis操作类 C#中Redis封装的类 C#和Redis客户端

谈下你对 Redis 的了解&#xff1f; 1&#xff09;Redis是一种基于键值对的NoSQL数据库&#xff08;非关系型数据库&#xff09;&#xff1b;是一个key-value存储系统 2&#xff09;高性能、可靠性 Redis将数据存储在内存中&#xff0c;读写性能高&#xff1b;Redis提供了 R…

第四十天学习记录:C语言进阶:笔试题整理Ⅰ

#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int main() {unsigned long pulArray[] { 6,7,8,9,10 };unsigned long* pulPtr;pulPtr pulArray;*(pulPtr 3) 3;printf("%d,%d\n", *pulPtr, *(pulPtr 3));//6 12return 0; }输出&#xff1a;6&#…

哈工大软件过程与工具作业1(100以内加减法练习小软件)

softwareProcess-lab1-master 哈工大软件过程与工具作业1 100以内加减法练习小软件 地址&#xff1a;https://github.com/944613709/Addition-and-subtraction-practice-small-software 项目概述 &#xff08;1&#xff09;项目名称&#xff1a;100以内加减法练习小软件 &…

一图看懂 markupsafe 模块:为 Python 实现 XML/HTML/XHTML 标记安全字符串,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 markupsafe 模块&#xff1a;为 Python 实现 XML/HTML/XHTML 标记安全字符串&#xff0c;资料整理笔记&#xff08;大全&#xff09; &#x1f9ca;摘要&#x1f9ca;模块图…

【JAVA】Java中的类型转换

目录 1.自动类型转换&#xff08;隐式转换&#xff0c;小类型转换为大类型&#xff09; 2.强制类型转换&#xff08;显示转换&#xff0c;大类型转换为小类型&#xff09; 3.小于4字节的类型转换问题 3.1 byte<->int 3.2 char<->int 3.3 String<->int …

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-4

深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践-总结-4 垃圾收集器与内存分配策略经典垃圾收集器Serial Old收集器CMS收集器Garbage First收集器 垃圾收集器与内存分配策略 经典垃圾收集器 Serial Old收集器 Serial Old是Serial收集器的老年代版本&#xff0c;它同样…

Liunx基础命令 - find命令

find命令 – 根据路径和条件搜索指定文件 find命令的功能是用于根据给定的路径和条件查找相关文件或目录&#xff0c;参数灵活方便&#xff0c;且支持正则表达式&#xff0c;结合管道符后能够实现更加复杂的功能&#xff0c;是Linux系统运维人员日常工作必须掌握的命令之一。 …