前言
最近需要搞一个基于虚幻引擎的国密sm4加密插件,记录下研究的过程。
插件编写
这里计划写一个插件来进行适配,插件编写可以参考 https://docs.unrealengine.com/5.1/zh-CN/integrating-third-party-libraries-into-unreal-engine/
教程主要是针对编译好的库,如何引用头文件,以及引入lib库等。
我这里还有一种方案,如何第三方库比较轻量,可以把这个文件夹包含进来。
Unreal Build Tool (UBT) 会根据你的项目文件结构自动发现并编译源代码文件。在 Unreal Engine 项目中组织代码时,遵循一定的目录结构是很重要的,这不仅有助于 UBT 正确地识别和编译代码,还能使项目保持清晰和易于管理。以下是一些组织示例,展示了如何安排你的C++和C源代码文件。
虽然Unreal Engine主要使用C++,你仍然可以在项目中包含C源代码文件。将C文件放在合适的目录下(例如Private目录),并确保它们的命名和组织与你的C++文件一致。UBT会自动编译这些C源文件,并将它们链接到最终的可执行文件或库中。即我们将文件放置在Private目录下即可自动编译了。
我们是基于MIRACL进行构建,所以直接把整个文件拷贝到Private目录即可。
另外,还需要在build.sc中添加如下内容bEnableUndefinedIdentifierWarnings = false;
不然会有一个报错,报错如下
错误 C4668 没有将“INLINE_ASM”定义为预处理器宏,用“0”替换“#if/#elif”
设置private目录设置为环境目录
在 Unreal Engine 的 Build.cs 文件中,你无法直接将 Private 目录下的一个子目录设置为“环境目录”(即直接影响编译器的包含路径),因为 Unreal Build Tool (UBT) 自动处理源代码文件的查找和编译,它默认包含了 Public 和 Private 目录及其所有子目录作为编译时的包含路径。
然而,你可以通过修改 Build.cs 文件来显式地添加额外的包含路径(Include Paths),这些路径可以是项目内的任何目录,包括位于 Private 目录下的子目录。这样做可以让编译器在编译过程中查找头文件时,包括这些额外指定的目录。
如何添加额外的包含路径 在你的模块的 Build.cs 文件中,你可以使用 PublicIncludePaths 和 PrivateIncludePaths 属性来添加额外的头文件搜索路径。例如,如果你想要添加 Private/SomeSubDirectory 作为一个包含路径,你可以这样做:
PublicIncludePaths.AddRange(
new string[] {
Path.Combine(ModuleDirectory, "Private/SomeSubDirectory")
}
);
PrivateIncludePaths.AddRange(
new string[] {
Path.Combine(ModuleDirectory, "Private/SomeSubDirectory")
}
);
这将指示编译器在查找头文件时,也搜索 Private/SomeSubDirectory 目录。请注意,PublicIncludePaths 是对模块外部可见的路径,而 PrivateIncludePaths 是仅在模块内部使用的路径。在大多数情况下,将私有子目录添加到 PrivateIncludePaths 就足够了。
sm4填充
SM4加密算法是一种分组加密标准,它使用128位的分组大小和128位的密钥长度。在SM4加密中,无论输入数据的大小如何,每次处理的数据块都是固定的128位(16字节)。这意味着输入到SM4加密函数的数据需要被分割成16字节的块。如果最后一个数据块不足16字节,通常需要进行填充(padding)以确保其大小为16字节。
所以PKCS#7填充的基本原理是在数据的末尾添加一定数量的字节,使得填充后的数据长度符合块的整数倍。填充的字节值等于缺少的字节数量。
基于上面两个可以判断出sm4的填充的值永远都是固定的。都是补足为16字节。
16进制字符串互转
由于我的需求是输入16进制字符串的加密数据,所以编写了一个16进制互转的库函数
TArray<uint8> USMFunctionLibrary::HexStringToBytes(const FString& HexString)
{
TArray<uint8> Bytes;
// 确保输入字符串长度是偶数
if (HexString.Len() % 2 != 0)
{
UE_LOG(LogTemp, Warning, TEXT("HexStringToBytes requires an even-length input string."));
return Bytes;
}
// 每两个字符表示一个字节
for (int32 Index = 0; Index < HexString.Len(); Index += 2)
{
// 截取两个字符的子字符串
FString ByteSubstring = HexString.Mid(Index, 2);
// 将子字符串转换为16进制数值
uint8 ByteValue = FParse::HexNumber(*ByteSubstring);
// 添加到字节数组
Bytes.Add(ByteValue);
}
return Bytes;
}
FString USMFunctionLibrary::BytesToHexString(const TArray<uint8>& Bytes)
{
FString HexString;
for (uint8 Byte : Bytes)
{
// 对于每个字节,将其转换为16进制字符串并追加到结果字符串中
HexString += FString::Printf(TEXT("%02X"), Byte);
}
return HexString;
}
解密算法编写
由于sm4每次只能加密16个字节,所以我们需要对加密的内容进行分割,每次加密16个字节,另外还需要对其进行填充,以保证其可以满足每次16字节的加密。整个实现思路如下
FString USMFunctionLibrary::Sm4Encrypt(FString HexStringKey, FString Input)
{
auto key = HexStringToBytes(HexStringKey);
auto inputLen = Input.Len();
auto letf16 = inputLen % 16;
auto addLength = 16 - letf16;
auto totalLen = inputLen + addLength;
TArray<uint8> CipherBytes;
CipherBytes.Init(0, totalLen);
FTCHARToUTF8 Convert(*Input);
const uint8* UTF8Data = (uint8*)Convert.Get();
TArray<uint8> PlainBytes;
PlainBytes.Empty(16);
int index = 0;
for (int i = 0; i < totalLen; i++)
{
if (i >= inputLen)
{
PlainBytes.Add(addLength);
}
else
{
PlainBytes.Add(UTF8Data[i]);
}
if (16 == PlainBytes.Num())
{
SM4_Encrypt(key.GetData(), PlainBytes.GetData(), CipherBytes.GetData()+ index*16);
index++;
PlainBytes.Empty(16);
}
}
return BytesToHexString(CipherBytes);
}
总结
这个插件目前根据业务需求,我只实现了sm4的解密算法,实际上可以实现全套sm的加解密,这个可以后续进行完善。想要插件源码,可以加入我的知识星球。https://t.zsxq.com/18NDXtAue
我的知识星球
请关注公众号g0415shenw 加入知识星球。
星球地址 https://t.zsxq.com/15EvfoA7n
星球有本人经验心得全部总结 涵盖音视频,gb28181、虚幻引擎、其他编程工具等等。另外还可以在星球提问,我会尽力答复,等于给您多了一个引路人。
原文始发于微信公众号(g0415shenw):虚幻引擎国密sm4加密插件
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/272449.html