目录
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函数的内部实现方式,可能改变了指针所指向的内存的大小,发生了内存溢出。
四.参考文献
2.C4996错误