简介:这份资源是面向 Delphi 开发者的 LockBox3 密码学组件库源码包,专为 Delphi 12.3 环境做了兼容性适配,适合需要在项目中集成加密解密、数字签名与密钥管理功能的中高级开发者。它涵盖 AES、RSA、SHA 系列等常见对称加密、非对称加密与哈希算法,通过可视化控件和编程接口即可调用,无需深入底层密码学实现。资源包共 243 个文件,约 2.53MB,以 73 个 pas 源码单元、16 个 dpk 与 dproj 工程文件、16 个 cbproj 工程及若干 res、dfm、bmp 资源文件为主,另含少量 cpp、groupproj 与说明文档,结构完整便于按模块查阅。目前已有 32 人学习下载。源码形式分发便于按项目需求定制修改与功能扩展,建议集成前做好单元测试与集成测试,确认特定场景下的功能符合性与性能表现。
1. Delphi 12.3 下 LockBox3 到底解决了什么问题
如果你正在用 Delphi 12.3 维护一套老系统,或者刚接了一个需要做数据加密、签名、密钥交换的桌面项目,大概率会撞上同一个问题:系统自带的System.NetEncoding只能做 Base64 这类编码,真要做 AES、RSA、SHA 这些密码学运算,要么自己找第三方库,要么硬啃 Windows CryptoAPI。LockBox3 就是在这个场景里被反复提起的一套 Delphi 加密组件,它提供源码和控件库两种形态,能直接编进 12.3 的 VCL 和 FireMonkey 工程里。
它解决的不是"加密算法怎么写"这种学术问题,而是"在 Delphi 工程里怎么把加密能力稳定接进去"的工程问题:算法封装、密钥管理、流式加解密、控件化拖拽、跨平台编译。适合两类人——一类是要给现有 Delphi 项目补加密模块的维护者,一类是想把加密逻辑做成可复用控件库的开发者。下面按"先搞清它是什么、再动手接进去、最后避开坑"的顺序讲透。
2. LockBox3 的组件构成与选型理由
2.1 源码包和控件库分别是什么
LockBox3 的交付形态通常分两块。一块是源码包,里面是 Pascal 单元文件,按功能拆成对称加密、非对称加密、哈希、编码、随机数几大类,你可以直接把.pas文件加进工程编译,不依赖任何运行时 DLL。另一块是控件库,把常用能力封装成可视化组件,装进 IDE 的组件面板后可以拖到窗体上,通过 Object Inspector 配属性。
这两块不是二选一的关系。控件库底层调用的就是源码包里的单元,所以你可以只用源码、只用控件、或者混用。我一般建议:核心加密逻辑走源码调用,界面交互层用控件。原因是控件适合快速搭原型,但加密参数一旦要动态变化(比如按用户输入切换算法),代码调用比改属性更可控。
选 LockBox3 而不是其他方案,主要看三点。第一,它是纯 Pascal 实现,不绑 Windows API,Delphi 12.3 的 FireMonkey 跨平台目标(Windows、macOS、Linux、Android、iOS)都能编。第二,源码可见,出问题能跟进去,不像某些闭源加密 DLL 只能当黑匣子。第三,它和 Delphi 12.3 的 Unicode 字符串体系兼容,老版本 LockBox2 在string和AnsiString混用处经常翻车,3 代重写了这块。
2.2 算法覆盖范围和适用边界
LockBox3 覆盖的算法大致分四类,选型时按需求对号入座:
| 类别 | 典型算法 | 适用场景 | 注意点 |
|---|---|---|---|
| 对称加密 | AES、DES、3DES、Blowfish | 大批量数据、文件加密 | AES 优先,DES 已不安全 |
| 非对称加密 | RSA | 密钥交换、数字签名 | 密钥长度至少 2048 位 |
| 哈希 | SHA-1、SHA-256、SHA-512、MD5 | 完整性校验、口令存储 | MD5/SHA-1 不用于安全场景 |
| 编码 | Base64、Hex | 二进制转文本传输 | 编码不是加密 |
这里有个常见误解:把 Base64 当加密用。Base64 只是编码,任何人拿到都能还原,它和加密是两回事。LockBox3 里编码单元和加密单元是分开的,别混。
对称加密适合加密"数据本身",因为速度快、能处理大文件;非对称加密适合加密"密钥"或做签名,因为它慢,不适合直接加密大块数据。实际项目里最常见的组合是:用 AES 加密业务数据,用 RSA 加密那个 AES 密钥,然后把加密后的密钥和数据一起传。这个模式叫混合加密,LockBox3 的两类单元配合就能实现。
2.3 在 Delphi 12.3 里接入源码包的最小步骤
假设你拿到的是源码包,目录里有一堆.pas文件。接入流程如下。
第一步,把源码目录加到工程的搜索路径。打开 Project Options,找到 Delphi Compiler 下的 Search Path,把源码根目录填进去。这样uses里写单元名就能找到。
第二步,在需要加密的单元里引用对应模块。LockBox3 的单元命名一般带Lb前缀,比如对称加密、哈希、编码各有对应单元。具体单元名以你手上的源码为准,用 IDE 的 Code Completion 敲Lb就能列出可用的。
uses LbClass, LbCipher, LbAsym, LbUtils; // 按实际源码单元名调整第三步,写一个最小的 AES 加密函数验证环境是否通。
function AesEncrypt(const PlainText, Key: string): string; var Cipher: TLbAESEncrypt; // 具体类名以源码为准 DataIn, DataOut: TBytes; begin DataIn := TEncoding.UTF8.GetBytes(PlainText); Cipher := TLbAESEncrypt.Create(nil); try Cipher.Key := ...; // 按源码要求设置密钥,注意位数 Cipher.Encrypt(DataIn, DataOut); Result := TNetEncoding.Base64.EncodeBytesToString(DataOut); finally Cipher.Free; end; end;逻辑说明:先把明文字符串按 UTF-8 转成字节数组,因为加密算法处理的是字节不是字符;然后创建加密对象、设密钥、调用加密方法;最后把密文字节用 Base64 转成可打印字符串,方便存数据库或传输。参数说明:密钥长度必须匹配算法要求,AES-128 要 16 字节、AES-256 要 32 字节,长度不对会直接抛异常或产生错误结果,这是新手最常踩的点。
提示:源码包里的类名、方法签名可能因版本不同有差异,以你实际拿到的单元声明为准,上面代码是结构示意,不是逐字可编译版本。
3. 用控件库在 VCL 和 FireMonkey 里落地加密
3.1 控件安装到 IDE 组件面板
控件库的安装走 Delphi 标准的包安装流程。打开源码里的.dpk包文件(Design Package),在 Project Manager 里右键 Install。编译通过后,组件会出现在 Tool Palette 的自定义页签下。Delphi 12.3 对包编译比较严格,如果报找不到单元,八成是 Search Path 没配全,回到上一节把路径补上。
安装成功后,你会在面板上看到加密、哈希、编码几组控件。拖一个到窗体上,选中它,Object Inspector 里能看到算法类型、密钥、输入输出等属性。控件方式的优势是可视化配置,劣势是运行时改参数不如代码灵活。
3.2 VCL 工程里的加密按钮实现
在 VCL 窗体上放一个TMemo输入明文、一个按钮触发加密、一个TMemo显示密文。按钮的OnClick里调用控件方法:
procedure TForm1.btnEncryptClick(Sender: TObject); var PlainBytes, CipherBytes: TBytes; begin PlainBytes := TEncoding.UTF8.GetBytes(memoInput.Text); // 假设控件名为 LbAES1,方法名以实际控件为准 LbAES1.Key := edtKey.Text; LbAES1.Encrypt(PlainBytes, CipherBytes); memoOutput.Text := TNetEncoding.Base64.EncodeBytesToString(CipherBytes); end;逻辑说明:输入框内容转字节、设密钥、加密、Base64 输出,和源码调用流程一致,只是把对象换成了窗体上的控件实例。参数说明:Key属性直接赋字符串时,控件内部会做长度校验,如果密钥位数不对,可能在赋值时就报错,也可能在加密时才报,取决于控件实现,调试时留意异常抛出的位置。
3.3 FireMonkey 跨平台工程的差异点
FireMonkey 工程用同一套控件,但有几个地方要注意。第一,FMX 的字符串处理在某些平台(尤其是移动端)对编码更敏感,统一用TEncoding.UTF8转换,别依赖默认编码。第二,移动端没有 Windows 的 CryptoAPI 兜底,所有运算都走 LockBox3 自己的 Pascal 实现,性能会比桌面端低,加密大文件时要评估耗时。第三,Android 和 iOS 上文件路径规则不同,如果加密结果要落盘,用TPath系列函数拼路径,别硬编码反斜杠。
// FMX 下跨平台安全的字节转换 function SafeUtf8Bytes(const S: string): TBytes; begin Result := TEncoding.UTF8.GetBytes(S); end;逻辑说明:把编码转换封装成函数,避免每个平台各写一套。参数说明:TEncoding.UTF8是 Delphi 内置的 UTF-8 编码器,不依赖 LockBox3,但和 LockBox3 的字节接口能直接对接。
3.4 密钥和 IV 的管理方式
对称加密里,密钥(Key)和初始化向量(IV)是两个必须正确管理的参数。密钥是保密的,IV 不需要保密但必须每次加密都不同(对于 CBC 模式)。常见错误是 IV 写死成全零或固定值,这样相同明文每次加密结果一样,泄露了数据模式。
正确做法:IV 用 LockBox3 的随机数单元生成,每次加密随机产生,然后把 IV 和密文一起存或传。解密时先取出 IV,再解密密文。
// IV 随机生成示意 var IV: TBytes; begin SetLength(IV, 16); // AES 块大小 16 字节 // 用 LockBox3 随机数单元填充 IV,具体函数名以源码为准 RandomFillBytes(IV); end;逻辑说明:先按算法块大小分配 IV 空间,再用密码学安全的随机数填充。参数说明:AES 的块大小固定 16 字节,IV 长度必须等于块大小,长度不对加密会失败。别用Random()这种普通随机函数,它的随机性不够,密码学场景要用专门的随机数源。
4. 避坑与排查:LockBox3 接入时最容易翻车的地方
4.1 密钥长度不匹配导致加密结果错误
现象:加密不报错,但解密出来是乱码,或者加密结果每次不一样(在固定 IV 的情况下本应一样)。
原因:密钥字符串直接赋值时,控件或类内部按某种规则截断或填充,实际使用的密钥和你以为的不一致。比如你输入 "123456",AES-256 需要 32 字节,内部可能补零也可能截断,导致加解密双方密钥不一致。
解决:显式把密钥转成固定长度的字节数组,用哈希函数(如 SHA-256)把任意长度口令派生成固定长度密钥。这样输入什么口令都能得到合规密钥,且加解密双方算法一致。
function DeriveKey(const Password: string): TBytes; var Hash: TLbSHA256; // 以实际源码类名为准 begin Hash := TLbSHA256.Create(nil); try Result := Hash.HashOf(TEncoding.UTF8.GetBytes(Password)); finally Hash.Free; end; end;逻辑说明:用 SHA-256 把任意长度口令映射成 32 字节,正好是 AES-256 的密钥长度。参数说明:哈希输出长度固定,SHA-256 是 32 字节,SHA-512 是 64 字节,按你选的算法挑。
4.2 字符串编码不一致导致中文乱码
现象:英文加解密正常,中文一加就乱。
原因:加密前把string按系统默认编码(GBK 或 ANSI)转字节,解密后按 UTF-8 还原,两边编码不一致。
解决:全程统一 UTF-8。加密前TEncoding.UTF8.GetBytes,解密后TEncoding.UTF8.GetString,中间不经过任何隐式转换。Delphi 12.3 默认string是 UnicodeString,但和字节数组互转时如果不指定编码,行为依赖系统区域设置。
4.3 大文件加密内存爆掉
现象:加密几十 MB 的文件时程序卡死或抛内存异常。
原因:一次性把整个文件读进内存再加密,内存占用是文件大小的两倍以上(原文加密文)。
解决:用流式加密,分块读写。LockBox3 的加密类一般支持流接口,开一个输入流和一个输出流,循环读块、加密、写块。
// 流式加密示意 var InStream, OutStream: TFileStream; Buffer: TBytes; ReadCount: Integer; begin InStream := TFileStream.Create('input.dat', fmOpenRead); OutStream := TFileStream.Create('output.dat', fmCreate); try SetLength(Buffer, 64 * 1024); // 64KB 块 repeat ReadCount := InStream.Read(Buffer[0], Length(Buffer)); if ReadCount > 0 then begin // 对 Buffer 前 ReadCount 字节加密后写入 OutStream end; until ReadCount = 0; finally InStream.Free; OutStream.Free; end; end;逻辑说明:每次只读 64KB,加密后立即写出,内存占用恒定。参数说明:块大小 64KB 是经验值,太小会增加调用开销,太大失去流式意义。注意最后一块可能不满,加密时要按实际读取长度处理。
4.4 编译期找不到单元或重复定义
现象:编译报 "Unit not found" 或 "Identifier redeclared"。
原因:Search Path 没配全,或者工程里同时引用了 LockBox2 和 LockBox3 的同名单元。
解决:检查 Project Options 的 Search Path,确保源码目录在里面;检查uses列表,把老版本 LockBox 的单元清掉。如果两个版本单元名冲突,给其中一个加命名空间前缀,或者干脆只保留一个版本。
4.5 移动端随机数不可用
现象:Android 或 iOS 上加密正常,但 IV 每次一样,或者随机数生成报错。
原因:某些随机数源依赖桌面平台的系统调用,移动端没有对应实现。
解决:用 LockBox3 自带的跨平台随机数单元,别直接调平台 API。如果源码里的随机数单元在移动端有问题,退而求其次用时间戳加进程 ID 加计数器组合做种子,但这只适合非安全场景,安全场景必须用密码学随机源。
5. 进阶:把 LockBox3 封装成可复用的加密服务层
5.1 为什么要再包一层
直接在业务代码里到处Create加密对象、设密钥、转编码,短期能跑,长期是维护灾难。密钥散落各处、算法切换要改几十个文件、单元测试没法 mock。我一般会在 LockBox3 之上封一个服务层,对外只暴露Encrypt、Decrypt、Sign、Verify几个方法,内部管密钥、管编码、管异常。
type ICryptoService = interface function Encrypt(const PlainText: string): string; function Decrypt(const CipherText: string): string; end; TLockBoxCryptoService = class(TInterfacedObject, ICryptoService) private FKey: TBytes; FIV: TBytes; public constructor Create(const Password: string); function Encrypt(const PlainText: string): string; function Decrypt(const CipherText: string): string; end;逻辑说明:接口定义契约,实现类持有密钥和 IV。构造函数里用口令派生密钥,加密方法内部完成字节转换、加密、Base64 编码。参数说明:Password是用户级口令,FKey是派生后的固定长度密钥,两者不要混用。
5.2 加密结果的版本兼容设计
加密数据一旦落库或发给第三方,格式就固定了。以后换算法、换密钥长度,老数据还得能解。常见做法是在密文前面加一个版本头,比如第一个字节标识算法版本,解密时先读版本号再选对应解密路径。
| 版本字节 | 算法 | 密钥派生 | 说明 |
|---|---|---|---|
| 0x01 | AES-128-CBC | SHA-256 前 16 字节 | 初版 |
| 0x02 | AES-256-CBC | SHA-256 全 32 字节 | 升级版 |
| 0x03 | AES-256-GCM | SHA-256 全 32 字节 | 带认证 |
这样新老数据共存,升级时不用一次性迁移。代价是密文多一个字节,可忽略。
5.3 用测试用例验证加解密闭环
封装完必须写测试,否则你不知道哪天改参数把老数据解不开了。最小闭环测试:加密一个已知字符串,解密回来比对;改一个字节的密文,确认解密失败或抛异常;用错误密钥解密,确认失败。
procedure TestRoundTrip; var Svc: ICryptoService; Original, Encrypted, Decrypted: string; begin Svc := TLockBoxCryptoService.Create('test-password'); Original := '中文测试 ABC 123'; Encrypted := Svc.Encrypt(Original); Decrypted := Svc.Decrypt(Encrypted); Assert(Decrypted = Original, '加解密闭环失败'); end;逻辑说明:构造服务、加密、解密、断言相等。参数说明:测试口令固定,保证可重复;测试数据包含中文和英文,覆盖编码转换路径。这个测试跑通,说明密钥派生、编码、加解密全链路一致。
5.4 我踩过的那个坑
最后说个血泪经验。早期我把 IV 和密文分开存,数据库两个字段,结果某次数据迁移只导了密文字段,IV 丢了,那批数据全部解不开,没有后悔药。后来改成 IV 拼在密文前面一起存,格式是[IV][密文],解密时先切前 16 字节当 IV。这样只要密文字段在,IV 就在。如果你现在还在分开存,趁数据量小赶紧改。
另一个习惯:密钥永远不硬编码在源码里,用配置文件或环境变量,源码进版本库时密钥不进。这个习惯帮我避免过好几次密钥泄露。
希望帮到你。
本文还有配套的精品资源,点击获取