alin的学习之路:RSA非对称加密算法哈希算法demo
demo代码
.h 头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <QCryptographicHash>
#include <QDebug>
#include <QFile>
#include <QDir>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
//hash test
void hashTest();
//非对称加密
void generateRsa();
//公钥加密
QString rsaPublicEncrypt(QString msg);
//私钥解密
QString rsaPrivateDecrypt(QString msg);
//数字签名与校验
bool rsaSignAndVerify();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
.cpp 源文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "rsacrypto.h"
extern "C"
{
// 在编译源文件的时候, applink.c也会参与到编译的过程中
#include <openssl/applink.c>
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//hashTest();
//generateRsa();
// QString sec = rsaPublicEncrypt("密文长度 == 秘钥长度== 分组的长度");
// qDebug() << "解密后:" << rsaPrivateDecrypt(sec);
// qDebug() << rsaSignAndVerify();
rsacrypto rsa;
rsa.generateRsakey(2048);
QString msg = rsa.rsaPubKeyEncrypt("我爱学习");
qDebug() << rsa.rsaPriKeyDecrypt(msg);
// 数字签名
QString text = "我特别爱学习";
QString encStr = rsa.rsaSign(text, Level6);
// 签名校验
bool bl = rsa.rsaVerify(text, encStr, Level6);
qDebug() << "签名校验结果: " << bl;
// 获取私钥
qDebug() << rsa.publicPemKey(PubFileName);
// 获取公钥
qDebug() << rsa.privatePemKey(PriFileName);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::hashTest()
{
QCryptographicHash hash(QCryptographicHash::Sha224);
//将字符串使用哈希函数转换为哈希值
hash.addData("hello");
hash.addData("world");
QByteArray array = hash.result().toHex();
qDebug() << "sha224:" << array;
//直接将文件转换为哈希值
//:/image-20200611153139152.png
QFile file(":/image-20200611153139152.png");
file.open(QFile::ReadOnly);
hash.reset();
hash.addData(&file);
array = hash.result().toHex();
qDebug() << "sha224:" << array;
//使用静态函数直接将字符串转换为哈希值,不通过添加的方式
array = QCryptographicHash::hash("helloworld", QCryptographicHash::Sha224).toHex();
qDebug() << "sha224:" << array;
}
void MainWindow::generateRsa()
{
//生成密钥对,并存储在内存中
RSA* rsa = RSA_new();
BIGNUM* e = BN_new();
//初始化变量e
BN_set_word(e, 12345);
RSA_generate_key_ex(rsa, 1024, e, NULL);
#if 0
//持久化存储密钥 方式1
BIO* pubbio = BIO_new_file("public.pem", "w");
PEM_write_bio_RSAPublicKey(pubbio, rsa);
BIO* pribio = BIO_new_file("private.pem", "w");
PEM_write_bio_RSAPrivateKey(pribio, rsa, NULL, NULL, 0, NULL, NULL);
BIO_free(pubbio);
BIO_free(pribio);
#else
FILE* publicfile = fopen("public2.pem", "w");
PEM_write_RSAPublicKey(publicfile, rsa);
FILE* privatefile = fopen("private2.pem", "w");
PEM_write_RSAPrivateKey(privatefile, rsa, NULL, NULL, 0, NULL, NULL);
fclose(publicfile);
fclose(privatefile);
#endif
//释放内存
RSA_free(rsa);
BN_free(e);
}
QString MainWindow::rsaPublicEncrypt(QString msg)
{
BIO* bio = BIO_new_file("public.pem", "r");
RSA* rsa = RSA_new();
//从磁盘文件中加载公钥,此时rsa中只有公钥
if(PEM_read_bio_RSAPublicKey(bio, &rsa, NULL, NULL) == NULL)
{
qDebug() << "PEM_read_bio_RSAPublicKey() error" ;
return QString();
}
//使用公钥对数据进行加密
int keylen = RSA_size(rsa);
QByteArray array = msg.toUtf8();
unsigned char* to = new unsigned char[keylen];
//公钥加密核心函数,返回的是密文的长度
//参1是原始数据的长度
int ret = RSA_public_encrypt(array.size(), (unsigned char*)array.data(), to, rsa, RSA_PKCS1_PADDING);
//因为二进制的字符串中间可能有 '\0',所以不能用strlen求长度
array = QByteArray((char*)to, ret).toBase64();
BIO_free(bio);
RSA_free(rsa);
delete[] to;
return array;
}
QString MainWindow::rsaPrivateDecrypt(QString msg)
{
BIO* bio = BIO_new_file("private.pem", "r");
RSA* rsa = RSA_new();
//获得私钥
if(PEM_read_bio_RSAPrivateKey(bio, &rsa, NULL, NULL) == NULL)
{
qDebug() << "PEM_read_bio_RSAPrivateKey() error" ;
return QString();
}
//解码base64
QByteArray array = msg.toUtf8();
array = QByteArray::fromBase64(array);
int keylen = RSA_size(rsa);
//使用私钥解密
unsigned char* to = new unsigned char[keylen];
int ret = RSA_private_decrypt(array.size(), (unsigned char*)array.data(), to, rsa, RSA_PKCS1_PADDING);
array = QByteArray((char*)to, ret);
BIO_free(bio);
RSA_free(rsa);
delete[] to;
return array;
}
bool MainWindow::rsaSignAndVerify()
{
BIO* bio = BIO_new_file("private.pem", "r");
RSA* rsa = RSA_new();
if(PEM_read_bio_RSAPrivateKey(bio, &rsa, NULL, NULL) == NULL)
{
qDebug() << "PEM_read_bio_RSAPrivateKey() error" ;
return false;
}
int keylen = RSA_size(rsa);
unsigned char* sigret = new unsigned char[keylen];
unsigned int siglen;
QString msg = "密文长度 == 秘钥长度== 分组的长度";
QByteArray array = msg.toUtf8();
RSA_sign(NID_sha224, (unsigned char*)array.data(), array.size(), sigret, &siglen, rsa);
//转base64
QString value = QByteArray((char*)sigret, siglen).toBase64();
//到此签名完成
// 签名校验
// 获取公钥
bio = BIO_new_file("public.pem", "r");
rsa = RSA_new();
if(PEM_read_bio_RSAPublicKey(bio, &rsa, NULL, NULL) == NULL)
{
qDebug() << "PEM_read_bio_RSAPublicKey() error" ;
return false;
}
//对数据进行base64解码
QByteArray array1 = QByteArray::fromBase64(value.toUtf8());
int ret = RSA_verify(NID_sha224, (unsigned char*)array.data(), array.size(), (unsigned char*)array1.data(), array1.size(), rsa);
return ret == 1;
}
注意事项
-
在Qt中需要非常注意类型的转换,
QByteArray
,QString
,std::string
,char*
之间的相互转换 -
数字签名的时候注意签名的数据的大小,不能大于密钥长度-11
-
密文长度 == 秘钥长度== 分组的长度
-
数字签名后得到的是一个二进制串,如果发生了数据中转,需要使用base64