RC4(原理+代码+调用openssl库+报错分析)
目录
一、原理
1.流密码的基本思想
2.RC4流密码算法的原理
1.初始化数据表S和T
2.初始置换数据表S(密钥调度算法)
3.生成密钥流(伪随机数生成算法)
二、代码实现
三、调用openssl库实现RC4
1.代码实现
2.调用openssl库遇到的问题
1.如何调用库
2. 出现C4996错误
3.无法解析的外部符号
4.出现错误:HEAP CORRUPTION DETECTED: after Normal block
四.参考文献
一、原理
1.流密码的基本思想
序列密码,也称流密码,是一种对称加密算法,加密和解密双方使用相同的伪随机数据流作为密钥,每次将明文的一位与密钥的一位使用异或操作加密,得到密文。
序列密码具有实现简单、便于硬件实施、加解密处理速度快、没有或只有有限的错误传播等特点。 1949年Shannon证明了一次一密的密码体制是绝对安全的,而“一次一密”的密码方案是序列密码的雏形。如果序列密码所使用的是真正随机方式的、与消息流长度相同的密钥流,则此时的序列密码就是一次一密的密码体制。
加解密运算是简单的模二加运算,密码安全强度主要依赖密钥流的安全性。
序列密码的关键是密钥序列产生器,密钥序列产生器一般由线性反馈移位寄存器和非线性序列两部分组成。
2.RC4流密码算法的原理
RC4(Ron Rivest Cipher 4)是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,属于对称加密算法,是使用最广泛的序列密码。RC4是一种基于非线性数据表变换的序列密码。它以一个足够大的数据表(S盒)为基础,对表进行非线性变换,产生非线性的密钥流序列。它是一个可变密钥长度、面向字节操作的序列密码,该算法以随机置换作为基础。
RC4的S盒大小根据参数的值而变化。种子密钥长度小于等于S盒的长度大小,用来初始化S盒。RC4有两种主要算法,密钥调度算法和伪随机数生成算法。其中密钥调度算法用于对S盒的分线性排列,伪随机数生成算法用于生成密钥流。
其中的步骤主要为:初始化数据表S,初始置换数据表S,密钥流的生成。
1.初始化数据表S和T
2.初始置换数据表S(密钥调度算法)
使用数据表T初始置换数据表S,置换伪码如下:
j = 0;for (i = 0 ; i < 256 ; i++){(当参数n为8时,S盒大小为256)j = (j + S[i] + T[i]) mod 256;swap(S[i] , S[j]);}
初始置换之后S表中包含还是0-255这256个元素。
3.生成密钥流(伪随机数生成算法)
密钥流生成伪码如下
i , j = 0;while (true){i = (i + 1) mod 256;j = (j + S[i]) mod 256;swap(S[i] , S[j]);t = (S[i] + S[j]) mod 256;k = S[t];}
反复进行该过程,直到生成的二进制的数量等于明文位的数量。加密时将k与明文中的一个字节异或,解密时将k与密文中的一个字节异或。
二、代码实现
RC4main.cpp
#include "Operation.h"int main() {while (1) {show(); //菜单界面keyDown(); //按键处理system("pause");system("cls");}
}
head.h
#pragma once
#include<cstdio>
#include<iostream>
#include<string>
#include<Windows.h>
#include<vector>
using namespace std;
RC4.h
#pragma once
#include"head.h"
void RC4_init(vector<int>& s, vector<int>& t, string key);
void RC4_exchange(vector<int>& s, vector<int>& t);
void RC4_crypt(vector<int>& s, string& m);
void print(vector<int>& s);
RC4.cpp
#include "RC4.h"void RC4_init(vector<int>& s, vector<int>& t, string key)
{for (int i = 0; i < s.size(); i++) {s[i] = i;t[i] = key[i % key.size()];}
}void RC4_exchange(vector<int>& s, vector<int>& t)
{int j = 0;for (int i = 0; i < s.size(); i++) {j = (j + s[i] + t[i]) % s.size();swap(s[i], s[j]);}
}void RC4_crypt(vector<int>& s, string& m)
{int i = 0, j = 0, t = 0;unsigned long k = 0;char temp;for (k = 0; k < m.size(); k++){i = (i + 1) % s.size();j = (j + s[i]) % s.size();temp = s[i];s[i] = s[j];s[j] = temp;t = (s[i] + s[j]) % s.size();m[k] ^= s[t];}
}void print(vector<int>& s) {for (int i = 0; i < s.size(); i++) {cout << s[i] << " ";}cout << endl;
}
Operation.h
#pragma once
#include"RC4.h"
void show();
void keyDown();
void readFile();
void saveFile();
void encrypt();
void decrypt();
Operation.cpp
#include "Operation.h"
string fileStr;void init() {fileStr = "";
}void show()
{cout << "*****************RC4密码*****************" << endl;cout << "\t\t1.加密文件" << endl;cout << "\t\t2.解密文件" << endl;cout << "\t\t3.退出" << endl;cout << "******************************************" << endl;}void keyDown()//按键处理
{int userkey = 0;cin >> userkey;switch (userkey) {case 1:cout << "-----------------加密文件-----------------" << endl;init();readFile();encrypt();saveFile();break;case 2:cout << "-----------------解密文件-----------------" << endl;init();readFile();decrypt();saveFile();break;case 3:exit(0);break;}
}void readFile()//读取文件
{cout << "请输入文件名:" << endl;string fileName;cin >> fileName;FILE* fp = fopen(fileName.c_str(), "r+");if (fp == nullptr) {cout << "未找到相关文件" << endl;return;}else {cout << "成功打开文件" << endl;}char ch;int pos = 0;while ((ch = fgetc(fp)) != EOF) {fileStr += ch;}cout << endl << "待处理的文件为:" << endl;cout << fileStr << endl;fclose(fp);
}void saveFile()//保存文件
{string fileName;cout << endl << "请输入要保存信息的文件名:" << endl;cin >> fileName;FILE* fp = fopen(fileName.c_str(), "w+");if (fp == nullptr) {cout << endl << "保存文件失败" << endl;return;}else {cout << endl << "保存成功" << endl;}fprintf(fp, "%s", fileStr.c_str());fclose(fp);}void encrypt()//加密文件
{int n = 0;string key = "";cout << endl << "请输入密钥:" << endl;cin >> key;cout << endl << "请输入S盒的参数(S盒的大小):" << endl;cin >> n;vector<int> s(n);vector<int> t(n);RC4_init(s, t, key);RC4_exchange(s, t);cout << endl << "S盒为:" << endl;print(s);RC4_crypt(s, fileStr);cout << endl << "初始化成功,按下任意键进行加密" << endl;char ch = getchar(); ch = getchar();cout << endl << "得到的密文为:" << endl;cout << fileStr << endl;
}void decrypt()//解密文件
{int n = 0;string key = "";cout << endl << "请输入密钥:" << endl;cin >> key;cout << endl << "请输入S盒的参数(S盒的大小):" << endl;cin >> n;vector<int> s(n);vector<int> t(n);RC4_init(s, t, key);RC4_exchange(s, t);cout << endl << "S盒为:" << endl;print(s);RC4_crypt(s, fileStr);cout << endl << "初始化成功,按下任意键进行解密" << endl;char ch = getchar(); ch = getchar();cout << endl << "得到的明文为:" << endl;cout << fileStr << endl;
}
RC4算法的核心代码在RC4.h和RC4.cpp中,实现起来还是比较简单的。
三、调用openssl库实现RC4
1.代码实现
openssl库中与RC4相关的函数如下所示:
RC4_KEY s;RC4_set_key(&s, keylength, (unsigned char *)key); //初始化RC4(&s, datalength, (unsigned char *)data, buffer); //加解密
RC4_set_key为初始化密钥生成序列的函数,其中key为种子密钥,s为密钥,通过种子密钥key来初始化密钥s。
RC4为加解密函数,其中data为要加解密的数据,buffer为得到的加解密数据提供存放的缓冲区。由于加解密使用的是简单的异或模二加运算,所以加解密的函数是一样的。
需要注意的是在加密或者解密的过程中,RC4_KEY在加解密前后是会改变的,所以要有一份密钥的备份,才能实现加解密两个操作。
RC4_KEY函数过程应该包含S盒的置换过程,在加解密之后会发生交换,没看过源码,只是一种猜测。
代码实现起来比较简单,代码如下:
#include<openssl/rc4.h>
#include<iostream>
using namespace std;
int main() {
cout << "-------使用openssl库实现RC4密钥算法-------" << endl;cout << endl << "请输入密钥的长度:" << endl;int n = 0;cin >> n;
cout << endl << "请输入密钥:" << endl;unsigned char* key = new unsigned char[n + 1];cin >> key;string m;cout << endl << "请输入明文:" << endl;cin >> m;
unsigned char* mm = new unsigned char[m.size() + 1];//将明文从string转变为unsigned char* 适应函数
for (int i = 0; i < m.size(); i++) {mm[i] = (unsigned char)m[i];}
unsigned char* buffer1 = new unsigned char[m.size() + 1];//存放加密后的密文unsigned char* buffer2 = new unsigned char[m.size() + 1];//存放解密后的明文
RC4_KEY enkey,dekey;//需要密钥的备份RC4_set_key(&enkey, n + 1, key);//初始化密钥RC4_set_key(&dekey, n + 1, key);
RC4(&enkey, m.size() + 1, mm, buffer1);//加密
buffer1[m.size()] = '\0';cout << endl << buffer1 << endl;//输出密文
RC4(&dekey, m.size() + 1, buffer1, buffer2);//解密
buffer2[m.size()] = '\0';cout << endl << buffer2 << endl;//输出明文
delete[] key;//释放空间delete [] mm;delete [] buffer1;delete [] buffer2;
return 0;
}
2.调用openssl库遇到的问题
1.如何调用库
在VC++包含目录或者C/C++附加包含目录中填写附加头文件所在的目录,目的是为了在预处理阶段可以找到需要的头文件。
在VC++库目录或链接器的常规中填写附加库目录,填入附加依赖项所在的库目录,以便链接时可以找到附加依赖项文件。
之后在链接器输入里面填写附加依赖项的名字xxx.lib。这里是libssl.lib和libcrypto.lib。
到此基本的环境配置就完成了。
2. 出现C4996错误
运行代码后出现C4996错误。
VS的在线文档:代码使用标记为已弃用的函数、类成员、变量或 typedef。 使用 修饰符或属性弃用符号。 实际的C4996警告消息是由 deprecated 声明的 修饰符或 属性指定的。此警告始终是声明符号的头文件的作者的有意消息。 请勿在不了解后果的情况下使用已弃用符号。若要解决 C4996 问题,通常建议更改代码。 请改为使用建议的函数和全局变量。 如果需要出于可移植性原因使用现有函数或变量,可以关闭警告。
猜测应该是VS编译器的问题,直接关闭该警告。
关闭C4996警告的方法:
若要关闭特定代码行的警告,请使用 warning杂则。
#pragma warning(suppress : 4996)
若要在文件中关闭以下所有项的警告,请使用警告杂则 。
#pragma warning(disable : 4996)
关闭项目中项目的警告,若要关闭 IDE 中整个项目的警告,Visual Studio:
打开 项目的"属性页 "对话框。
选择" 配置属性>""C/C++>""高级 "属性页。
编辑" 禁用特定警告" 属性以添加
4996
。 选择 " 确定"以应用更改。
使用预处理器宏禁用警告,若要定义预处理器宏,Visual Studio:
打开 项目的"属性页 "对话框
展开 "配置属性 > ""C/C++ > 预处理器"。
在 "预处理器定义"属性 中,添加宏名称。 选择“确定” 进行保存,然后重新生成项目。
3.无法解析的外部符号
运行代码之后可能会出现 error LNK2019: 无法解析的外部符号 错误。
但是已经将所需要的文件都包含进去了,为什么还会出现这个错误。
由于我下载的openssl是64位的,但是我使用的项目的解决方案平台为x86,即32位的,所以运行时程序报错,将活动解决方案平台改为x64就解决了,如果没有的话打开配置管理器在新建里面寻找。
4.出现错误:HEAP CORRUPTION DETECTED: after Normal block
这是典型的内存溢出错误,常在内存的delete处发生,而且一般在debug版本中可能出现,release版本中可能并不报错。
出现这个错误的原因一般都是操作new申请的内存溢出,因为在c++中,如果用new分配一段内存,操作的时候改变了该部分的大小,在delete时就会出错。比如说修改了申请的内存以外的内存,从而导致释放指针指向的堆内存出现错误。
发生这个问题的主要原因是不知道RC4函数的内部实现方式,可能改变了指针所指向的内存的大小,发生了内存溢出。
四.参考文献
1.error LNK2019: 无法解析的外部符号
2.C4996错误
3.VS在线文档C4996
4.HEAP CORRUPTION DETECTED: after Normal block
RC4(原理+代码+调用openssl库+报错分析)相关推荐
- 【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 使用 addr2line 命令行工具查找动态库中的报错代码位置 )
文章目录 一.从 Tombstone 报错日志中查找报错动态库 二.addr2line 命令行工具使用 64 位动态库使用的 aarch64-linux-android-addr2line.exe 工 ...
- 三方库报错真的就没有办法了吗?
前言 项目最近适配了安卓10,而安卓10废弃了某些api,导致调用后就报错,比如: val var5: TelephonyManager = getSystemService("phone& ...
- 已解决(Python爬虫requests库报错 请求异常SSL错误,证书认证失败问题)requests.exceptions.SSLError: HTTPSConnectionPool
成功解决(Python爬虫requests库报错 请求异常,SSL错误,证书认证失败问题)requests.exceptions.SSLError: HTTPSConnectionPool(host= ...
- python3中调用map函数报错map object at 0x000001EF004D97B8
python3中调用map函数报错<map object at 0x000001EF004D97B8> 在python中这样的一段代码报错: a=map(int,input().split ...
- 遇到代码不生效或者报错不要慌
以轮播图为例: 当遇到控制台报错 这种情况就需要看控制台报的错误是什么. 比如上面这种就是说变量i未定义,直接使用,网页就会报错. 当控制台没有报错,轮播图没有实现自动播放的功能 这时候需要检查自己那 ...
- QT DLL库报错:file not recognized: File format not recognized
QT DLL库报错:file not recognized: File format not recognized 我的解决方式是把debug32改成64了 不过网上还有一种说法就是切换编译器: MS ...
- 身份证阅读器Android相片解码库报错libwlt2bmp.so:has text relocations解决方案
身份证阅读器Android相片解码库报错libwlt2bmp.so:has text relocations,如下图的几种情况. libwlt2bmp.so是部里的相片解码库,身份证芯片里面存储的相片 ...
- SAP QM 执行事务代码QP01,系统报错 -Material type FOOD is not defined for task list type Q-
SAP QM 执行事务代码QP01,系统报错 -Material type FOOD is not defined for task list type Q- 商品主数据755,物料类型是FOOD,基 ...
- python下载库报错_下载python中Crypto库报错:ModuleNotFoundError: No module named ‘Crypto’的解决...
下载python中Crypto库报错:ModuleNotFoundError: No module named 'Crypto'的解决 前言 最近在网上找了很多下载Crypto的方法,感觉作用都不算很 ...
最新文章
- 把握芯片科技发展趋势 促进半导体产业创新突破
- Spring Cloud Feign如何实现JWT令牌中继以传递认证信息
- 【错误记录】AS 编译报错 ( Android Support plugin 版本太高 | 升级 Android Studio 到最新版本 )
- java和ssm是什么关系,JAVA --- SSH和SSM的区别
- 利用Word2010给汉字添加汉语拼音
- 使用 .NET 进行游戏开发
- 平衡树 - FHQ 学习笔记
- day5 Java中的方法与重载
- Linux设备驱动程序学习(2)-调试技术
- 一个比较完整的pytorch项目
- 【UVa11178】Morley's Theorem(向量旋转+直线交点)
- Windows API一日一练(17-18)DialogBox DialogBoxParam EndDialog函数
- 计算机辅助翻译技术包含,计算机辅助翻译方法技术
- LNMP(nginx php-fpm mysql) 环境部署——php
- 系统封装教程(Win10案例)
- dot com过时了,个性域名“钱”景看好
- 使用conda安装pytorch时出现问题CondaSSLError: OpenSSL appears to be unavailable on this machine.
- CVPR 2020 论文大盘点-人脸技术篇
- 辅警小丁同志 灵宝西站派出所第一警务区赵桥
- vue mvc与mvvm