☰
Delphi XE10.2加密组件实战:TMS Cryptography Pack安装与AES/SHA应用
2026/10/15 17:33:37 网站建设 项目流程

简介:TMS Cryptography Pack 3.4.0.1 是面向 Delphi XE10.2 开发者的专业加密组件库,适合需要在桌面与移动应用中集成数据保护、安全通信的中高级开发者。它覆盖 AES、DES、3DES、Blowfish、Twofish、RC4、RSA 等对称与非对称算法,并提供 MD5、SHA 系列哈希、HMAC 消息认证码、RSA/DSA 数字签名、X.509 证书管理、PKCS#7 兼容处理以及加密级随机数生成器,可满足文件加密、密钥交换、S/MIME 邮件与 SSL/TLS 通信等场景。资源包共 210 个文件,约 71.54MB,以 107 个 dcu 编译单元、48 个 o 目标文件、8 个 dcp 与 3 个 bpl 运行库为主,另含 dpk/dproj 安装工程、res/bmp 资源、xml 配置及 pdf 说明文档,结构完整便于直接编译引用。目前已有 270 人学习下载,可帮助开发者快速理解各加密模块的 API 设计与集成方式,减少自行封装底层算法的时间成本。

1. 拆开一个 Delphi 加密包:TMS Cryptography Pack 3.4.0.1 到底装了什么

如果你手上维护着 Delphi XE10.2 的老项目,又恰好被要求加一套对称加密、哈希校验或者数字签名,大概率会搜到这个包:TMS_Cryptography_Pack_3.4.0.1_for_XE10.2。它不是某个单一算法的 Demo,而是一整套面向 Delphi/C++Builder 的加密组件库,覆盖 AES、RSA、SHA、HMAC、Base64 等常见能力,封装成可直接拖到窗体上的组件。适合谁?适合不想自己从 OpenSSL 命令行绕一圈、又需要在 Pascal 代码里直接调加密逻辑的从业者。我第一次接触它是在一个需要给配置文件做 AES 加密的维护项目里,当时试过手写 WinCrypt 调用,代码又长又容易在密钥长度上翻车,换成这套组件后,核心逻辑压缩到十几行。下面按“它是什么、怎么装、怎么用、坑在哪”的顺序拆一遍。

2. 环境准备与包安装:从 XE10.2 的搜索路径说起

2.1 为什么是 XE10.2 这个版本

Delphi 的包管理对版本极其敏感。TMS Cryptography Pack 3.4.0.1 标注 for XE10.2,意味着它的编译产物(.bpl、.dcp)和源码单元是针对 XE10.2 的 RTL 编译的。如果你拿它去装 XE11 或者更老的 XE7,最常见的现象是“单元找不到”或者“bpl 加载失败”。这不是包坏了,而是 Delphi 的二进制接口在版本间不保证兼容。常见做法是:确认你的 IDE 版本号,在 Help → About 里看到 “Embarcadero RAD Studio 10.2”,再动手。如果版本对不上,要么找对应版本,要么只把源码单元(.pas)拷进项目里手动编译,放弃设计期组件面板。

2.2 安装步骤与搜索路径配置

安装分两种:设计期包安装和源码引用。前者能让你在工具箱里看到组件,后者更轻量。我一般先走源码引用,确认能用再决定要不要装设计期包。

# 假设解压后的目录结构如下(路径按你实际解压位置替换) # TMS_Cryptography_Pack_3.4.0.1/ # ├── Source/ # 核心 .pas 单元 # ├── Packages/ # XE10.2 的 .dpk / .bpl # └── Demos/ # 示例工程 # 第一步:把 Source 目录加入项目搜索路径 # IDE 菜单:Project → Options → Delphi Compiler → Search Path # 追加:<你的解压路径>\Source
// 第二步:在单元里直接引用,验证源码可用 uses uTPLb_AES, // 对称加密相关单元(名称以实际包内为准) uTPLb_Hash; // 哈希相关单元 procedure TForm1.TestCompile; var Cipher: IBlockCipher; begin // 这里只做编译验证,不执行实际加密 Cipher := nil; if Cipher = nil then Caption := '单元引用成功'; end;

上面这段代码的目的不是跑加密,而是让编译器去解析单元路径。如果 Search Path 配错,编译会直接报 “Unit uTPLb_AES not found”。参数说明:Search Path 里不要用中文路径,Delphi 对非 ASCII 路径的处理在部分版本上会出玄学问题;路径末尾不要多加反斜杠,否则可能拼出双斜杠导致找不到文件。

2.3 设计期包安装的取舍

如果你确实需要组件面板,走 Packages 目录下的 .dpk。打开 dpk 后先 Build 再 Install。Build 阶段如果报 “Required package ‘rtl’ not found”,说明你的 IDE 版本和包不匹配,别硬装。Install 成功后,工具箱会多出一页 TMS Cryptography。这里有个血泪经验:装完设计期包后,如果之后又手动改了 Source 里的单元,记得重新 Build 包,否则 IDE 里用的还是旧 bpl,运行结果和你改的代码对不上,排查起来像黑匣子。

3. 核心能力实战:AES、哈希与 Base64 的 Pascal 写法

3.1 AES 对称加密:密钥长度与模式选择

AES 是这套包里最常用的能力。Delphi 里调 AES,核心就三件事:密钥、模式、填充。密钥长度支持 128/192/256 位,对应 16/24/32 字节。模式常见 CBC 和 ECB,ECB 相同明文块产出相同密文块,不适合有结构的数据;CBC 需要 IV,安全性更好。填充用 PKCS7 最省心。

uses System.SysUtils, System.Classes, uTPLb_CryptographicLibrary, uTPLb_Codec, uTPLb_BlockCipher; function AESEncrypt(const PlainText, Key, IV: string): string; var Lib: TCryptographicLibrary; Codec: TCodec; InStream, OutStream: TStringStream; begin Lib := TCryptographicLibrary.Create(nil); Codec := TCodec.Create(nil); try Codec.CryptoLibrary := Lib; // 选择 AES-256 的 CBC 模式 Codec.StreamCipherId := 'native.AES-256'; Codec.BlockCipherId := 'native.CBC'; Codec.ChainModeId := 'native.CBC'; // 密钥和 IV 都按 UTF8 字节传入,长度必须匹配 Codec.Password := Key; // 32 字节对应 AES-256 InStream := TStringStream.Create(PlainText, TEncoding.UTF8); OutStream := TStringStream.Create('', TEncoding.ANSI); try // 这里用 Codec 的加密方法,IV 通过属性设置 Codec.InitFromPassword(Key, IV); Codec.Begin_EncryptMemory(0); // 实际项目里按流式或内存方式调用,此处示意参数位置 Result := OutStream.DataString; finally InStream.Free; OutStream.Free; end; finally Codec.Free; Lib.Free; end; end;

逻辑说明:先建加密库和编解码器对象,再指定算法标识。参数上,StreamCipherId决定算法和密钥长度,ChainModeId决定分组模式。密钥用InitFromPassword传入时,内部会做一次派生,如果你要的是“原始密钥直接当 AES 密钥”,得用设置二进制密钥的接口,别用密码派生,否则两边对不上。IV 在 CBC 下必须和加密端一致,常见翻车是加密用了 IV、解密忘了传,结果解出来是乱码。

3.2 哈希与 HMAC:校验文件完整性

哈希用来做完整性校验,SHA-256 是当前常见选择。HMAC 则是带密钥的哈希,适合做接口签名。

uses uTPLb_Hash, uTPLb_CryptographicLibrary; function CalcSHA256(const FilePath: string): string; var Lib: TCryptographicLibrary; Hash: THash; FS: TFileStream; Digest: TBytes; begin Lib := TCryptographicLibrary.Create(nil); Hash := THash.Create(nil); try Hash.CryptoLibrary := Lib; Hash.HashId := 'native.SHA-256'; FS := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyNone); try Hash.Begin_Hash; Hash.HashStream(FS); // 流式读取,大文件不会爆内存 Hash.End_Hash; Digest := Hash.HashValue; Result := BytesToHex(Digest); finally FS.Free; end; finally Hash.Free; Lib.Free; end; end;

参数说明:HashId换成native.SHA-1或native.MD5就切换算法,但新项目别再用 MD5 做安全校验。HashStream是流式接口,处理几百 MB 的文件时内存占用平稳。注意HashValue返回的是字节数组,转十六进制字符串时大小写要统一,否则和对方系统比对时会出现“明明一样却对不上”的假故障。

3.3 Base64 与编码转换的边界

Base64 严格说不是加密,是编码。但加密后的密文经常要转 Base64 才能安全地放进文本协议里。包里通常带 Base64 编解码单元。

uses System.NetEncoding; // XE10.2 自带,也可用包内实现 function SafeBase64Encode(const Raw: TBytes): string; begin Result := TNetEncoding.Base64.EncodeBytesToString(Raw); // 去掉换行,避免放进 JSON 时多出 \r\n Result := Result.Replace(#13, '').Replace(#10, ''); end;

这里的关键点是换行。很多 Base64 实现默认每 76 字符插入换行,直接塞进 JSON 或 URL 参数就会出问题。参数上,如果用于 URL,还要把+和/替换成-和_,这就是 URL-safe Base64。别小看这一步,接口联调时因为一个+变成空格导致签名失败的案例不少。

4. 避坑与排查:五个真实翻车记录

4.1 现象:编译报“找不到单元”,原因:搜索路径没加或版本不符

这是最高频的问题。现象是Unit uTPLb_xxx not found。原因通常是解压后只把包装了,但项目没引用 Source 路径,或者 IDE 版本和包版本不一致。解决:先确认 IDE 版本,再把 Source 目录加进 Project → Options → Delphi Compiler → Search Path,注意选 All configurations 而不是只选 Debug。

4.2 现象:加密正常、解密乱码,原因:IV 或填充模式不一致

加密端用 CBC + PKCS7,解密端如果用了 ECB 或者 NoPadding,结果就是乱码或尾部多出怪字符。解决:把模式、填充、IV 三样东西在两端写成同一份配置,最好抽成一个常量单元,别在两处各写一遍。IV 可以随密文一起传输,但必须是随机生成且每次不同。

4.3 现象:大文件哈希时内存飙升,原因:一次性读入

用ReadFile把整个文件读进内存再哈希,几百 MB 的文件会让进程占用暴涨。解决:用流式接口,像 3.2 里那样传 TFileStream,分块读取。参数上注意流的 Position 要归零,否则第二次哈希会从文件末尾开始,算出空值。

4.4 现象:设计期组件拖上去后运行报错,原因:bpl 与源码不同步

改了 Source 里的单元但没重新 Build 设计期包,IDE 加载的还是旧 bpl。解决:改源码后,打开 dpk 重新 Build,再 Install。如果还不行,把 IDE 里已安装的包先 Remove 再重新加。这个坑的隐蔽性在于编译能过,运行时行为却和代码对不上。

4.5 现象:密钥长度报错,原因:字符串字节数和算法要求不匹配

AES-256 要 32 字节密钥,如果你传了一个 10 个字符的字符串,某些接口会报密钥长度错误,某些则会静默派生。解决:明确你要的是“原始密钥”还是“密码派生”。原始密钥就用字节数组接口,长度严格对齐;密码派生就用InitFromPassword,但两端派生参数要一致。别混用。

5. 进阶技巧:把加密逻辑收进一个可复用的单元

5.1 封装成独立单元,隔离算法细节

维护老项目时,最怕加密调用散落在几十个窗体里。我的习惯是建一个uCryptoHelper.pas,把 AES、SHA、Base64 各包一层,业务代码只调EncryptConfig/DecryptConfig这种语义化函数。这样换算法或换密钥长度时,只改一个文件。

unit uCryptoHelper; interface uses System.SysUtils; type TCryptoHelper = class public // 统一入口:加密配置字符串 class function EncryptConfig(const Plain, Key, IV: string): string; // 统一入口:解密配置字符串 class function DecryptConfig(const Cipher, Key, IV: string): string; // 文件完整性校验 class function FileSHA256(const Path: string): string; end; implementation uses uTPLb_CryptographicLibrary, uTPLb_Codec, uTPLb_Hash; class function TCryptoHelper.EncryptConfig(const Plain, Key, IV: string): string; begin // 内部走 AES-256-CBC,返回 Base64 // 具体实现按第 3 章的方式组装 Result := ''; end; class function TCryptoHelper.DecryptConfig(const Cipher, Key, IV: string): string; begin // 与 EncryptConfig 对称 Result := ''; end; class function TCryptoHelper.FileSHA256(const Path: string): string; begin // 流式哈希,返回十六进制 Result := ''; end; end.

逻辑说明:把算法标识、密钥处理、编码转换都关在这个单元里。参数上,Key 和 IV 建议从配置文件或环境变量读取,不要硬编码在源码里。业务层只关心“给我明文,还我密文”,不关心底层是 AES 还是别的。

5.2 验证方法:用已知向量做回归测试

加密代码最怕“看起来能跑但结果不对”。我一般会准备一组已知的测试向量:固定明文、固定密钥、固定 IV,记录期望的密文十六进制。每次改完代码跑一遍,对不上就说明动了不该动的地方。

测试项输入期望输出(示意)用途
AES-256-CBC明文 "test" + 固定 Key/IV固定密文 Hex验证算法与填充
SHA-256空字符串e3b0c442...验证哈希实现
Base64字节 0x00 0xFFAP8=验证编码边界
HMAC-SHA256固定 Key + 消息固定摘要验证签名逻辑

这张表不用写进代码,放在测试工程里当断言用。参数上,测试向量一旦确定就不要改,它是你判断“改动是否引入回归”的唯一依据。

5.3 一个具体技巧:密钥不要写在源码里

我见过太多项目把 AES 密钥直接写在const里,编译出来的 exe 用字符串搜索就能找到。常见做法是把密钥放在外部配置文件,并且配置文件本身用另一套机制保护;或者用 Windows 的 DPAPI 做本地加密存储。Delphi 里调 DPAPI 需要额外封装,但至少比明文强。从那以后我每次做加密相关改动,都强制走一遍“密钥来源检查 + 测试向量回归 + 两端模式比对”这三步,少一步都可能在上线后收到“解密失败”的反馈。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询