PBKDF2加密解密 - 前端crypto-js.min.js和后端UE4使用Crypto++互相加解密

UE4中Crypto++库加密解密

第四节:PBKDF2加密解密 - 前端crypto-js.min.js和后端UE4使用Crypto++互相加解密



前言

有一个应用系统包含前端html和UE4后端。其中前端某一部分导航功能需要跳转至后端(简单的说就是前端现在要把后端中的所有功能包含进来),这里就涉及到前端登录后的权限要同步到后端,实现单点登录,针对外部用户就采用了PBKDF2加密的方式验证。实现思路: 在登录前端的情况下,外部用户点击跳转至后端的时候免登录,同步权限。这里是在跳转时,前后端协商采用PBKDF2的加解密对该用户进行校验。后端UE4使用Crypto++库,前端使用crypto-js.min.js进行PBKDF2的对应操作。经过测试,本例中的前后端代码的加密计算结果是能够互相验证成功的。


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

一、前端

前端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">
//--PBKDF2
    function Pbkdf2Encrypt(pbkdf2_content, pbkdf2_key){   //PBKDF2加密
        var srcs = CryptoJS.enc.Utf8.parse(pbkdf2_content);
        var key256Bits = CryptoJS.PBKDF2(srcs, pbkdf2_key, {
            keySize: 256 / 32,  //Key的字节数:32
            iterations: 1024,    //迭代次数
            // hasher: CryptoJS.algo.SHA256     //摘要算法-如果没有写这一行,默认是SHA1摘要算法
        });
        return key256Bits.toString().toUpperCase()  //将Hex格式字符串转换为大写,以方便UE4对比
    }

    function Pbkdf2Authenticate(attemptedPassword, encryptedPassword, pbkdf2_key){  //PBKDF2验证
        var attemptedEncryptedPassword = Pbkdf2Encrypt(attemptedPassword, pbkdf2_key);
        return attemptedEncryptedPassword == encryptedPassword;
    }
</script>

需要注意的是,前端crypto-js.min.js加密的Hex格式密文字符串是小写的,而后端大写的。所以前端发送密文前,需要将小写转换成大写。

二、后端

后端UE4写二个函数,一个是加密函数,使用盐值加密字符串并将密文发送到后端进行验证。另一个是验证函数,需要接收前端发来的密文,对密文进行验证。
以下为实现逻辑:

1. C++代码

PBKDF2LibraryBP.h

#pragma once

#include "../ThirdParty/crypto/include/Win64/config_int.h"
#include "../ThirdParty/crypto/include/Win64/pwdbased.h"
#include "../ThirdParty/crypto/include/Win64/sha.h"
#include "../ThirdParty/crypto/include/Win64/hex.h"
#include "../ThirdParty/crypto/include/Win64/cryptlib.h"
#include "../ThirdParty/crypto/include/Win64/blake2.h"
#include "../ThirdParty/crypto/include/Win64/files.h"
#include "../ThirdParty/crypto/include/Win64/aes.h"
#include "../ThirdParty/crypto/include/Win64/modes.h"
#include "../ThirdParty/crypto/include/Win64/pubkey.h"
using namespace CryptoPP;

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

/**
 * 
 */
UCLASS()
class CRYPTO_API UPBKDF2LibraryBP : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
public:
	//PBKDF2算法
	//验证密码
	UFUNCTION(BlueprintCallable, meta = (DisplayName = "authenticate", Keywords = "authenticate", pbkdf2_key = "0123456789ABCDEF0123456789ABCDEF"), Category = "PBKDF2")
    static bool authenticate(FString attemptedPassword, FString encryptedPassword, FString pbkdf2_key);

	//生成密文
	UFUNCTION(BlueprintCallable, meta = (DisplayName = "getEncryptedPassword", Keywords = "getEncryptedPassword", pbkdf2_key = "0123456789ABCDEF0123456789ABCDEF"), Category= "PBKDF2")
	static  FString getEncryptedPassword(FString pbkdf2_content, FString pbkdf2_key);
};

PBKDF2LibraryBP.cpp

#include "PBKDF2LibraryBP.h"

/**
* 对输入的password进行验证
* 
* @param attemptedPassword
*            待验证的password
* @param encryptedPassword
*            密文
* @param salt
*            盐值
* @return 是否验证成功
*/
#pragma region PBKDF2
bool UPBKDF2LibraryBP::authenticate(FString attemptedPassword, FString encryptedPassword, FString salt)
{
	// 用同样的盐值对用户输入的password进行加密
	FString encryptedAttemptedPassword = getEncryptedPassword(attemptedPassword, salt);
	// 把加密后的密文和原密文进行比較,同样则验证成功。否则失败
	return encryptedAttemptedPassword.Equals(encryptedPassword);
}

/**
* 生成密文
* 
* @param password
*            明文password
* @param salt
*            盐值
* @return
*/
FString UPBKDF2LibraryBP::getEncryptedPassword(FString pbkdf2_content, FString pbkdf2_key)
{
	std::string _pbkdf2_content = TCHAR_TO_UTF8(*pbkdf2_content);
	byte password[AES::MAX_KEYLENGTH];
	size_t plen = _pbkdf2_content.size();
	plen <= AES::MAX_KEYLENGTH ? memcpy(password, _pbkdf2_content.c_str(), plen) : memcpy(password, _pbkdf2_content.c_str(), AES::MAX_KEYLENGTH);

	std::string _pbkdf2_key = TCHAR_TO_UTF8(*pbkdf2_key);
	byte salt[AES::MAX_KEYLENGTH];
	size_t slen = _pbkdf2_key.size();
	slen <= AES::MAX_KEYLENGTH ? memcpy(salt, _pbkdf2_key.c_str(), slen) : memcpy(salt, _pbkdf2_key.c_str(), AES::MAX_KEYLENGTH);

	byte derived[SHA256::DIGESTSIZE];	//密文长度要和前端一样

	PKCS5_PBKDF2_HMAC<SHA1> pbkdf;		//填充方式要和前端一样
	byte unused = 0;

	pbkdf.DeriveKey(derived, sizeof(derived), unused, password, plen, salt, slen, 1024, 0.0f);

	std::string result;
	HexEncoder encoder(new StringSink(result));

	encoder.Put(derived, sizeof(derived));
	encoder.MessageEnd();

	return UTF8_TO_TCHAR(result.c_str());
}
#pragma endregion

2. 蓝图

在这里插入图片描述

测试结果

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

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

  2. 前端html加密,后端UE4验证
    在这里插入图片描述
    完美实现前后端加密和验证。


总结

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

  1. 前后端字符串大小写不一样
    前端加密的结果传到后端,或者后端加密的结果传到前端,对方无法对比成功识别接收到的字符串。主要原因在于双方的字符串大小写不一样,前端的是小写,后端的是大写,这样就需要进行大小写转换。
  2. 填充方式不一样
    在这里,前端加密库使用PKCS5填充,所以后端也要使用PKCS5方式填充。
  3. 摘要算法不一样
    在这里,前端加密库默认使用SHA1摘要算法,所以后端也要使用SHA1摘要算法。

参考

PKCS#1、PKCS#5、PKCS#7区别
crypto 前后端加解密简单了解
PBKDF2-JS演示

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PBKDF2是一种密码学中的密钥派生函数,它可以将一个较弱的密码转换为一个强密钥,从而增加密码的安全性。在Java中,可以使用javax.crypto.SecretKeyFactory类来实现PBKDF2算法的加密和解密。 下面是一个简单的Java代码示例,演示如何使用PBKDF2算法进行加密和解密: ```java import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import java.security.spec.KeySpec; import java.util.Arrays; public class PBKDF2Example { public static void main(String[] args) throws Exception { String password = "myPassword123"; byte[] salt = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; int iterations = 10000; int keyLength = 256; // 加密 byte[] encrypted = encrypt(password, salt, iterations, keyLength); System.out.println("Encrypted: " + Arrays.toString(encrypted)); // 解密 boolean isMatch = decrypt(password, salt, iterations, keyLength, encrypted); System.out.println("Is match: " + isMatch); } public static byte[] encrypt(String password, byte[] salt, int iterations, int keyLength) throws Exception { KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keyLength); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); return factory.generateSecret(spec).getEncoded(); } public static boolean decrypt(String password, byte[] salt, int iterations, int keyLength, byte[] encrypted) throws Exception { byte[] decrypted = encrypt(password, salt, iterations, keyLength); return Arrays.equals(encrypted, decrypted); } } ``` 在上面的代码中,encrypt()方法使用PBKDF2算法对密码进行加密,decrypt()方法使用相同的算法对密码进行解密,并比较解密后的结果是否与加密前的密码相同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值