RSA加解密 - 前端JSEncrypt和后端UE4使用Crypto++互相加解密

UE4中Crypto++库加密解密

第一节:RSA加密解密 - 前端JSEncrypt和后端UE4使用Crypto++互相加解密



前言

登录系统时,用户的密码用明文传输,太不安全,应该加密传输,怎么做呢,对称加密一旦秘钥丢失则形同虚设,最好使用非对称加密的方式,由后端事先生成公钥和私钥,公钥发给前端页面,私钥后端自己保留,前端进行认证时,把密码原文用公钥加密再发给后端,后端用私钥解密而得到原文。非对称加密算法常用RSA算法,秘钥使用hex编码成字符串,后端UE4使用Crypto++库,前端使用jsencrypt.js进行RSA的对应操作。经过测试,本例中的前后端代码的加密解密计算结果是一致的。


提示:以下是本篇文章正文内容,下面案例可供参考

一、前端

前端html写两个函数,一个是加密函数,使用公钥加密字符串并将密文发送到后端进行解密。另一个是解密函数,需要接收后端发来的密文,并使用私钥将密文解密成明文。

<script src="./crypto-js.min.js"></script>
<script src="https://cdn.bootcss.com/jsencrypt/3.0.0-beta.1/jsencrypt.js"></script>

<script type = "text/javascript">
function RsaEncrypt(content){
        //公钥
        var words = CryptoJS.enc.Hex.parse('30819D300D06092A864886F70D01010ABCD1050003818B00B30818702818100A2829C189C6177E3018C2ACBB377968F4609A7F77DDCE70A0E664F6E4632C2093323A71C4EAB8FA5DBE164D506164CDE4371C9868A7300C6382B02627B87173D3086FAFC915590B33EA43FBC5FA91223676E3DF34F327D44BFBDAA3ACFEEE41F6E456D8AD52F3DF12A29E846BE8078E8B6D43D692D3446B801C00A6098EF7181020111')//密文已被修改过,需要使用自己生成的密钥
        var PUBLIC_KEY = CryptoJS.enc.Base64.stringify(words);
        console.log('Base64公钥:%o', PUBLIC_KEY);

        //使用公钥加密
        var encrypt = new JSEncrypt();
        encrypt.setPublicKey('-----BEGIN PUBLIC KEY-----' + PUBLIC_KEY + '-----END PUBLIC KEY-----');
        
        var encrypted = encrypt.encrypt(content);
        console.log('加密前数据:%o', content);
        console.log('加密后数据:%o', encrypted);

        //将结果转换成Hex编码格式
        var str = CryptoJS.enc.Base64.parse(encrypted)  
        var hexStr = CryptoJS.enc.Hex.stringify(str)
        return hexStr
    }

    function RsaDecrypt(content){
        //将hex编码格式密文转换成base64格式
        console.log('hex格式密文:%o', content);
        var str = CryptoJS.enc.Hex.parse(content)
        var base64Str = CryptoJS.enc.Base64.stringify(str)
        console.log('base64格式密文:%o', base64Str);

        //私钥
        var words = CryptoJS.enc.Hex.parse('30820276020100300D06092A864886F70D0B1010105000ABCD48202603082025C02010002818100A2829C189C6177E3018C2ACBB377968F4609A7F77DDCE70A0E664F6E4632C2093323A71C4EAB8FA5DBE164D506164CDE4371C9868A7300C6382B02627B87173D3086FAFC915590B33EA43FBC5FA91223676E3DF34F327D44BFBDAA3ACFEEE41F6E456D8AD52F3DF12A29E846BE8078E8B6D43D692D3446B801C00A6098EF718102011102818017E607E58068AFBF803A42875E282CBAB779E3FEBFAF8B635C787517BF0776E33C39F2ED93193AC59146CB1044A8ED2FBE9FCAD0054D25C2CC06532C99B1FBE31DF749ECBB7824E7D8A983B8764A2EE3A5DB29A4DB0A667CCB9554A95920DB16227EBC2B3D3A900F76AD596AF5D93629CE069894B9B104693BD3767766A64323024100C9C0BB380A8013176FCCCEF8C9E226C533A548476BA2CA726734AEB16BE78F87A437EFB389921CBCFFB33D06CDB0FCA666044C46B34880A43878BD3960D119A3024100CE34AF7ABF3E1C400D89F110716812EBCBF6A74ADEE29381BCF7EF09D2C182CE12AFB17E11A880CB03757E689C3DA58C716FE3958B380E7B65A95AC817B3C28B024100B20468F5367101C962A5A79039A96D80D3377C02C8627664F1A6F47E6E44C9F0277CA6531F0873B5D28F17BAB57E0C1A5A03CAD4F88B4454AA4C6ABA2840078F024100A9D108FBAC8D80AD38537B3AB7BF1EA41161B6F25D3310107D80E2F907EAA7F50054740D77F42DD45D3395471741D3A0D5E3AC5D093D391A53B8A51D40B227BD02410084532715AE57C49C758039A71E22814D07E058A44FEDAF1FE0FEF7CA03DB09BBB2843CEB8329F07AE2E48B7EABC0C527980C33B38B72D35DB04B4493D019A1F3')//密文已被修改过,需要使用自己生成的密钥
        var PRIVATE_KEY = CryptoJS.enc.Base64.stringify(words)
        console.log('base64格式PRIVATE_KEY:%o', PRIVATE_KEY);

        //使用私钥解密
        var decrypt = new JSEncrypt();
        decrypt.setPrivateKey('-----BEGIN RSA PRIVATE KEY-----'+PRIVATE_KEY+'-----END RSA PRIVATE KEY-----');
        var uncrypted = decrypt.decrypt(base64Str);
        console.log('解密后数据:%o', uncrypted);

        return uncrypted;
    }

</script>

需要注意的是,前端jsencrypt.js加密的密文是base64格式的,而后端Crypto++是hex格式的。所以前端发送密文前,需要将密文转换为hex格式。前端接收密文后,需要将密文转换为base64格式。

二、后端

后端UE4写三个函数,一个是生成公私密钥函数。一个是加密函数,使用公钥加密字符串并将密文发送到前端进行解密。另一个是解密函数,需要接收前端发来的密文,并使用私钥将密文解密成明文。
以下为实现逻辑:

1. C++代码

Crypto.h

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"

#include "../../../ThirdParty/crypto/include/Win64/osrng.h"
#include "../../../ThirdParty/crypto/include/Win64/rsa.h"
#include "../../../ThirdParty/crypto/include/Win64/hex.h"
#include "../../../ThirdParty/crypto/include/Win64/files.h"
#include "../../../ThirdParty/crypto/include/Win64/modes.h"

#include "Crypto.generated.h"

using namespace std;
using namespace CryptoPP;

/**
 * 
 */
UCLASS()
class TEST_API UCrypto : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
	//生成RSA公钥和私钥
	UFUNCTION(BlueprintCallable, meta = (DisplayName = "GenerateRSAKey_ue4", Wordkeys = "GenerateRSAKey_ue4"), Category= "RSA")
	static bool GenerateRSAKey_ue4(int keyLength, FString privFilename, FString pubFilename);

	//RSA使用公钥加密
	UFUNCTION(BlueprintCallable, meta = (DisplayName = "RSAEncryptString_ue4", Wordkeys = "RSAEncryptString_ue4"), Category= "RSA")
	static FString RSAEncryptString_ue4(FString pubFilename, FString message);

	//RSA使用私钥解密
	UFUNCTION(BlueprintCallable, meta = (DisplayName = "RSADecryptString_ue4", Wordkeys = "RSADecryptString_ue4"), Category= "RSA")
	static FString RSADecryptString_ue4(FString privFilename, FString ciphertext);
 
private:
	//随机种子
	static AutoSeededRandomPool & getRng();
};

Crypto.cpp

#include "Crypto.h"

//生成公私密钥
bool UCrypto::GenerateRSAKey_ue4(int keyLength, FString privFilename, FString pubFilename)
{
    RSAES_PKCS1v15_Decryptor priv(getRng(), keyLength);
    HexEncoder privFile(new FileSink(TCHAR_TO_ANSI(*privFilename)));
    priv.AccessMaterial().Save(privFile);
    privFile.MessageEnd();
 
    RSAES_PKCS1v15_Encryptor pub(priv);
    HexEncoder pubFile(new FileSink(TCHAR_TO_ANSI(*pubFilename)));
    pub.AccessMaterial().Save(pubFile);
    pubFile.MessageEnd();

    return true;
}
 
//加密
FString UCrypto::RSAEncryptString_ue4( FString pubFilename, FString message)
{
    FileSource pubFile(TCHAR_TO_ANSI(*pubFilename), true, new HexDecoder);
    RSAES_PKCS1v15_Encryptor pub(pubFile);
    string result;
 
    //避免加密错误导致程序异常退出
    try {
        StringSource(TCHAR_TO_ANSI(*message), true,
                new PK_EncryptorFilter(getRng(), pub,
                        new HexEncoder(new StringSink(result))));
    }
    catch (CryptoPP::Exception& e) {
        result = " "; 
        cout << "Crypto::RSAEncryptString_ue4 Error: " << e.what() << endl;       
    }
 
    return UTF8_TO_TCHAR(result.c_str());
}
 
//解密
FString UCrypto::RSADecryptString_ue4( FString privFilename, FString ciphertext)
{
    FileSource privFile(TCHAR_TO_ANSI(*privFilename), true, new HexDecoder);
 
    //因为前端加密库 jsencypt.js 使用的RSA_PKCS1_PADDING标准填充,所以这里也要使用RSA_PKCS1_PADDING
    RSAES_PKCS1v15_Decryptor priv(privFile);
 
    string result;
 
    //避免解密错误导致程序异常退出
    try {
        StringSource(TCHAR_TO_ANSI(*ciphertext), true,
                new HexDecoder(
                        new PK_DecryptorFilter(getRng(), priv,
                                new StringSink(result))));
    }
    catch ( CryptoPP::Exception& e ){
        result = " ";
        cout << "Crypto::RSADecryptString Error: " << e.what() << endl;
    }
 
    return UTF8_TO_TCHAR(result.c_str());
}

//自动生成随机种子
AutoSeededRandomPool & UCrypto::getRng()
{
    static AutoSeededRandomPool m_Rng;
    return m_Rng;
}

2. 蓝图

在这里插入图片描述

测试结果

将html页面加载到UE4中,运行效果如下:

  1. 后端UE4加密,前端html解密
    在这里插入图片描述

  2. 前端html加密,后端UE4解密
    在这里插入图片描述

完美实现前后端加解密。


总结

一般情况下,前端自己实现加解密没有问题,后端自己实现加解密也没有问题。问题主要出在2个方面:

  1. 前后端编码格式不一样
    前端加密的结果传到后端,或者后端加密的结果传到前端,对方无法识别接收到的字符串。主要原因在于双方的字符串编码格式不一样,前端的是base64格式,后端的是hex格式,这样就需要将格式进行转换。
  2. 填充方式不一样
    因为前端加密库 jsencypt.js 使用的RSA_PKCS1_PADDING标准填充,所以后端也要使用RSA_PKCS1_PADDING方式填充。

参考

RSA前端 jsencrypt 加密,后端 c++ crypto++库解密
JSEncrypt前端加密以及java后端解密
JS 使用RSA加密解密
记录一下前端使用CryptoJS的几种加密方式
jsencrypt源码
RSA官方文档
C/C++ Crypto密码库调用的实现方法
Crypto++库实现AES和RSA加密解密
string to SecByteBlock转换
基于Crypto++库的RSA非对称加密实现对数据的加解密
Crypto++的安装及使用

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值