简介面向 Delphi 12.3/XE12 开发者的 DCPcrypt2 加密库压缩包用于在 Object Pascal 项目中快速集成数据加密、哈希与消息摘要能力避免重复实现底层算法并减少由此引入的安全风险。该库覆盖对称加密与散列/消息摘要两大类别既适合独立工具开发也可嵌入大型业务系统。压缩包共 74 个文件核心是 34 个 .pas 源码与 15 个 .inc 公共包含文件这些源码直接提供 AES、DES、Blowfish、CAST、RC2/4/5/6、IDEA、Twofish、Serpent 等对称算法以及 MD5、SHA1/SHA256/SHA512 等散列算法另有 4 个 .dpk、4 个 .dproj 工程与包文件、4 个 .html 文档、3 个 .txt 说明/许可证还包含 dpr/dfm/dcr/res/png 等配套资源整体仅 122KB目录结构清晰。目前已有 160 人学习/下载。除完整源码外包内还附带 FileEncrypt、FileHash 两个可直接运行的演示工程、MIT 许可证和 HTML API 文档目录按 Ciphers、Hashes、Demos 等模块划分便于将库加入 Delphi 12.3 工程后对照示例快速验证加解密与文件哈希效果同时 .inc 公共头文件降低了多版本适配的复杂度适合需要增强应用数据安全的中高级开发者作为基础加密组件选型参考。1. DCPcrypt2-XE12.7zDelphi 12.3 加密控件的压缩包到底值不值得装做 Delphi 的老项目最怕半路杀出个安全需求用户密码不能明文入库接口数据要加密传输日志里的敏感信息得先打码。这些事如果借道外部 DLL 或者 HTTP 加密服务部署时又多一堆要解释的麻烦。这个标题里的 DCPcrypt2-XE12.7z就是 Delphi 12.3 能直接用的纯 Object Pascal 加密控件包解压、编译、装入 IDE然后你的 exe 就能自己算 AES、Blowfish、SHA-256不依赖任何第三方运行时库。我刚开始用它的头两天没少翻车主要问题集中在安装路径配不对、加密和解密用的状态没重置、字符串编码没统一。这篇笔记就顺着“安装—选算法—写代码—排错”这条路走一遍不管你是刚接触 Delphi 还是从老版本升级至少能少走一半弯路。2. 从 7z 到 IDE安装 DCPcrypt2 的完整链路与版本选择安装这个控件包最重要的不是双击某个 install.exe而是先把压缩包里的目录结构和编译顺序搞清楚。很多人失败不是因为控件本身有问题而是把旧版本的.bpl直接拖进了 Delphi 12.3结果 IDE 启动就报“找不到包”。下面这套流程是我在 Windows 10 Delphi 12.3 上反复验证过的你也可以把它当成一次可复现的部署步骤。2.1 解压后先看什么BPL、DCP 和 source 目录先找一个纯英文路径比如D:\Components\DCPcrypt2用 7-Zip 把压缩包解压进去然后立刻列一下文件清单。Delphi 的编译器和 Windows 的文件搜索在某些情况下对中文路径并不友好这个玄学问题你在首次编译报错时就会明白。# 解压并列出核心源码与包文件 7z x DCPcrypt2-XE12.7z -oD:\Components\DCPcrypt2 -y Get-ChildItem D:\Components\DCPcrypt2 -Recurse -Include *.pas,*.dpk,*.bpl | Select-Object FullName这里7z x是解压并保留目录结构-o指定输出目录-y跳过交互确认。解压之后重点找两类文件一是source目录下的DCPrijndael.pas、DCPbase64.pas这类以DCP开头的单元二是packages目录下的.dpk文件比如dcpCrypt.dpk。.bpl是预编译好的运行时包但老版本预编译包很难直接在 Delphi 12.3 里加载所以后面会坚持从.pas源码编译。如果解压后只看到.pas没有.dpk也不要立刻放弃。你可以用 RAD Studio 的File New Package手工建一个包把所有DCP*.pas加进去。只是那样要手动处理requires列表排错成本高。文本里能搜到.dpk的话优先用现成的。2.2 把源码路径加进 Delphi 12.3 的 Library Path打开 Delphi 12.3选择Tools Options Language Delphi Library在Library path中把刚才的D:\Components\DCPcrypt2\source加进去。这个动作必须在编译.dpk之前完成否则后续会报“找不到 DCPrijndael”之类的单元不存在错误。# 确认源码文件确实已经躺在指定位置Windows PowerShell 下执行 Test-Path D:\Components\DCPcrypt2\source\DCPrijndael.pas Test-Path D:\Components\DCPcrypt2\source\DCPbase64.pasTest-Path返回True说明路径存在但别急着高兴。这里面有个 Delphi 12.3 特有的坑Library path是编译时用的Search path是 IDE 在打开项目时找源文件用的。如果你只在Search path里加了路径编译.dpk时还是会提示找不到单元。两个路径最好都加上同一个目录如果你不想每次换电脑都重新配可以写到一个.env脚本里但 IDE 全局设置还是得手动点一遍。2.3 编译并安装设计期包那些需要手工勾选的依赖打开packages目录下的dcpCrypt.dpk先不要直接在 IDE 里点 Install。我先用命令行编译一次因为命令行能一次性把所有错误打出来不容易被 IDE 的弹窗打断思路。:: 进入包目录用 Delphi 12.3 的命令行编译器编译设计期包 cd /d D:\Components\DCPcrypt2\packages dcc32.exe dcpCrypt.dpk -B -Q-B是全部重新编译忽略已有的.dcu-Q是安静模式只在有错误时输出。如果dcc32.exe不在系统 PATH 里就用 RAD Studio 自带的 “RAD Studio Command Prompt” 打开终端或者直接把dcc32.exe的完整路径写上去。编译输出中可能会出现 “Unit DCPSha1 not found” 这样的错误这通常不是缺少文件而是你添加的Library path没有被命令行编译器的环境变量读到。检查一下是否把source目录加进了系统环境变量BDSLIB或者直接在命令行里用-U参数指定单元路径。dcc32.exe dcpCrypt.dpk -B -Q -UD:\Components\DCPcrypt2\source其中-U表示在给定的目录里搜索单元。命令行编译通过后再回到 IDE 里打开同一个.dpk文件右键Compile然后右键Install。这里经常出现一个提示“Package ... doesnt contain any components to install。” 这不算错误因为 DCPcrypt2 本身就是非可视组件它只提供类不拖控件到窗体上。看到这个提示直接继续IDE 会把它注册成可用的库包。如果你不想每次安装都要在 IDE 里手动点也可以直接把编译好的.bpl放进C:\Users\你的用户名\Documents\Embarcadero\Studio\23.0\Bpl目录但前提是.bpl的编译版本必须和 IDE 完全一致否则会变成“已安装的包与当前 IDE 不兼容”。我一般不会走这条路因为预编译包一旦遇到 Delphi 小版本升级就废掉源码包反而省心。安装完成后的验证很简单新建一个空白 VCL 项目在 uses 里写DCPrijndael如果能编译通过说明源码路径已经生效。如果要在多个项目里反复使用还可以把source目录加到默认 VCL 项目的Search path里这样以后新建项目不用重新引。3. 选算法之前先懂 DCPcrypt2 的对象模型DCP_rijndael、密钥和 IV很多人拿到 DCPcrypt2 第一反应就是找 “加密函数” 在哪里但这个库设计的是对象模型不是普通函数集。你必须先理解它把加密算法封装成了一个带生命周期和内部状态的组件否则很容易在二次加密时得到全乱码的密文。这一章就把对象关系、密钥长度和链模式讲透后面写代码时你才知道自己每行参数在干什么。3.1 对称加密基类 TCipher 与 DCP_rijndaelDCPcrypt2 里所有对称加密算法都继承自一个抽象基类TCipher。这个基类规定了算法对象必须实现的初始化、加密、解密、重置接口。你在代码里直接使用的是它的具体子类比如TDCP_rijndaelAES、TDCP_blowfish、TDCP_twofish。每个对象都需要Create实例化用完后Free。uses DCPcrypt2, DCPrijndael, DCPSha256; var Cipher: TDCP_rijndael; begin Cipher : TDCP_rijndael.Create(nil); try // 这里会执行初始化、加解密操作 finally Cipher.Free; end; end;Create(nil)的参数是组件所有者这里传nil表示自己管理生命周期。TDCP_rijndael内部维护着密钥调度表、当前 IV、上次加密的位置所以同一对象不能直接连续做两次不同的加密除非先调用初始化方法重新设置密钥。我看到很多老代码在循环里复用同一个Cipher对象第一次正常第二次开始就解密失败原因就在这。对象模型里另一个重要方法是InitStr它接受一个字符串口令和一个哈希类内部先把口令用哈希算法扩展成固定长度的密钥。比如你习惯写Cipher.InitStr(mypassword, TDCP_sha256)它并不是直接用mypassword当 AES 密钥而是先算mypassword的 SHA-256 摘要再用这个摘要作为 256 位密钥。这个设计好处是口令可以任意长度坏处是你和别人联调时如果对方直接用原始字节做密钥两边的加密结果就对不上。3.2 密钥长度、IV 与链模式三个最容易配错的参数对于TDCP_rijndaelAES密钥长度只能取 16、24、32 字节对应 128、192、256 位。很多新手会在Init里直接传256这实际上是告诉库你要 256 字节密钥而 AES 根本不支持那么长。DCPcrypt2 的Init方法签名里密钥长度参数单位是字节不是位。这是最容易踩的第一个坑。var KeyBytes: array[0..31] of Byte; IVBytes: array[0..15] of Byte; begin FillChar(KeyBytes, SizeOf(KeyBytes), 0); FillChar(IVBytes, SizeOf(IVBytes), 0); Cipher.Init(KeyBytes[0], SizeOf(KeyBytes) * 8, nil, 0, ctCBC, True); end;这里SizeOf(KeyBytes) * 8是把字节数转成位数传给库。有些版本的 DCPcrypt2 参数定义不同你拿到一个包之后打开DCPrijndael.pas搜procedure Init看它声明里第二参数叫KeySize还是ASize并在注释里确认单位。我见到过好几个来源的 DCPcrypt2 都在这一点上不一样所以别凭记忆硬写花一分钟看看声明。IV 是初始化向量作用是把明文块和上一块密文做异或避免相同明文产生相同密文。DCPcrypt2 的Init方法里IV是一个指针如果传nil表示全零。全零 IV 在安全上是弱配置但很多老项目为了和第三方接口保持一致就这么干。真要对外联调建议把 IV 固定成一个 16 字节的随机串并写进协议头或者用InitStr时让库用密钥哈希的固定位置做 IV具体要看实现。链模式决定了块与块之间的关联方式。ECB 模式最简单每块独立加密同样的明文块会得到同样的密文块软件里有经验的同事一眼就能看出规律CBC 模式用前一块密文参与当前块加密安全性好得多。DCPcrypt2 支持通过ctCBC、ctECB等枚举指定。默认值如果没设置通常是 ECB这一点在跨端互操作时非常致命。你和 Java 用AES/CBC/PKCS5Padding对接你的 Delphi 端却是 ECB密文怎么都对不上。所以再强调一遍联调时先把链模式写进接口文档。3.3 哈希对象 DCP_sha256 与数据完整性校验DCPcrypt2 里哈希对象和加密对象是平级的常用的是TDCP_sha256。它的核心用法不是拿来直接做加密而是做数据完整性校验以及刚才提到的InitStr口令扩展。哈希对象同样有三个关键方法Init重置状态Update喂数据Final输出结果。uses DCPSha256, DCPcrypt2; var Hasher: TDCP_sha256; Digest: array[0..31] of Byte; InputBuf: RawByteString; begin InputBuf : UTF8String(待哈希内容); Hasher : TDCP_sha256.Create(nil); try Hasher.Init; Hasher.Update(InputBuf[1], Length(InputBuf)); Hasher.Final(Digest); // Digest 就是 32 字节的 SHA-256 摘要 finally Hasher.Free; end; end;注意Update的第一个参数是Pointer或者const不能把 Delphi 的string直接传进去因为string在 Unicode 版本里是 UTF-16哈希出来的字节和别人用 UTF-8 算出来的不一样。所以上面代码先把输入转成了UTF8String这是血泪经验。Final(Digest)会把结果填到 32 字节的缓冲区里。这里的Digest长度必须至少是哈希输出的字节数SHA-256 固定 32 字节如果你写array[0..15]运行时会越界写坏堆栈。哈希还有一个容易被忽视的用途做密码存储。用户注册时把口令加盐后算SHA-256存摘要而不是存明文。DCPcrypt2 的哈希对象和加密对象耦合度低你可以在一个项目里只用哈希不用加密它不会强制安装加密包。理解了这些对象之间的边界接下来写 AES 加解密就简单了。真正的难点是处理 Delphi 的字符串编码和二进制缓冲区的对接这也是很多翻车现场的起点。4. 用 DCPcrypt2 在 Delphi 12.3 里跑通 AES-256字符串与文件两种写法这一章是抄作业章节。我会给你两套直接能用的代码骨架一套加密字符串一套加密文件。两套代码都基于TDCP_rijndael密钥由口令经过TDCP_sha256扩展得到链模式选 CBCIV 固定 16 字节。注意以下代码的 API 基于我手上的 DCPcrypt2 2.x 版本你安装包里如果有差异以DCPrijndael.pas里的方法声明为准。4.1 字符串加解密从 String 到 RawByteString 的转换Delphi 12.3 的默认string是 UnicodeString里面是 UTF-16 编码字节序列和你预期的不一样。直接加密string解密回来去显示没问题但传输给其他语言就会因为字节序问题解不开。所以第一步永远是把字符串转成 UTF-8 字节流再交给加密对象。uses System.SysUtils, System.NetEncoding, System.Classes, DCPcrypt2, DCPrijndael, DCPSha256; function DCPEncryptString(const APlainText, APassword: string): string; var Cipher: TDCP_rijndael; PlainBytes, CipherBytes: TBytes; InputMem, OutputMem: TMemoryStream; Base64: TBase64Encoding; begin PlainBytes : TEncoding.UTF8.GetBytes(APlainText); InputMem : TMemoryStream.Create; OutputMem : TMemoryStream.Create; Cipher : TDCP_rijndael.Create(nil); Base64 : TBase64Encoding.Create(0); try InputMem.Write(PlainBytes[0], Length(PlainBytes)); InputMem.Position : 0; Cipher.InitStr(APassword, TDCP_sha256); Cipher.EncryptStream(InputMem, OutputMem); OutputMem.Position : 0; SetLength(CipherBytes, OutputMem.Size); OutputMem.Read(CipherBytes[0], OutputMem.Size); Result : Base64.EncodeBytesToString(CipherBytes); finally Base64.Free; Cipher.Free; OutputMem.Free; InputMem.Free; end; end;这个函数的关键点有三个。第一TEncoding.UTF8.GetBytes把 Unicode 字符串转成 UTF-8 字节这样和 Java、Python 的utf-8编码一致。第二EncryptStream把输入流里的所有字节加密后写入输出流流内部会按 CBC 链模式一块一块处理不需要你手动去对齐 16 字节块。第三密文是二进制不能直接塞进 JSON 或 XML所以用TBase64Encoding做了 Base64 编码方便传输和存文本字段。TBase64Encoding.Create(0)里的 0 表示不加换行符如果你要和某些老系统对接需要 76 字符换行改成76就行。对应的解密函数必须对称。由于EncryptStream输出的密文长度可能和输入不一样解密时要先把 Base64 解码成原始密文字节再交给DecryptStreamfunction DCPDecryptString(const ACipherText, APassword: string): string; var Cipher: TDCP_rijndael; CipherBytes, PlainBytes: TBytes; InputMem, OutputMem: TMemoryStream; Base64: TBase64Encoding; begin Base64 : TBase64Encoding.Create(0); CipherBytes : Base64.DecodeStringToBytes(ACipherText); InputMem : TMemoryStream.Create; OutputMem : TMemoryStream.Create; Cipher : TDCP_rijndael.Create(nil); try InputMem.Write(CipherBytes[0], Length(CipherBytes)); InputMem.Position : 0; Cipher.InitStr(APassword, TDCP_sha256); Cipher.DecryptStream(InputMem, OutputMem); OutputMem.Position : 0; SetLength(PlainBytes, OutputMem.Size); OutputMem.Read(PlainBytes[0], OutputMem.Size); Result : TEncoding.UTF8.GetString(PlainBytes); finally Cipher.Free; OutputMem.Free; InputMem.Free; Base64.Free; end; end;注意这里的InitStr用的是同一个口令库内部通过哈希扩展成同样的 256 位密钥所以能配对。如果你在加密时用了InitStr解密时千万别改成手动拼接的密钥否则结果是“黑匣子”式的乱码很难排查。4.2 文件加解密流式读入与密文落盘文件加密最直观的写法是EncryptFile它底层按块读取文件不会一次性把几百 MB 文件加载进内存。对于日志文件、数据库备份这类大文件这是正确的选择。下面这个函数把源文件加密后写到目标文件并在最后删除原文。procedure DCPEncryptFile(const AInFile, AOutFile, APassword: string); var Cipher: TDCP_rijndael; begin Cipher : TDCP_rijndael.Create(nil); try Cipher.InitStr(APassword, TDCP_sha256); Cipher.EncryptFile(AInFile, AOutFile); finally Cipher.Free; end; end;EncryptFile内部的处理细节是它从输入文件读一块、写一块到输出文件并且在输出文件头部写了标识吗答案是不写DCPcrypt2 的文件加密流没有额外头如果你把加密后的文件改成别的扩展名解密时还是用同一个方法只要密码对就能还原。如果你想让别人看不出这是密文可以在加密前先压缩或加盐但那不是这个库的事。解密时调DecryptFile(AInFile, AOutFile)参数顺序和文件名自己约定一致。有一个容易忽略的问题EncryptFile会直接覆盖目标文件如果目标文件已经存在它不会问你是否覆盖。我一般会在调用前用FileExists检查或者自己生成唯一文件名防止重要资料被无意识地冲掉。4.3 用 Base64 与 Hex 编码让密文可传输文件加密产物是二进制但很多时候你需要把它放进配置项或者数据库的VARCHAR字段里。EncryptFile写入的文件不适合直接转 Base64因为你要先读完整个文件再编码大文件又回去了。常见做法是加密时使用EncryptStream把密文流到一个TMemoryStream再整体转 Base64。uses System.NetEncoding, System.Classes; function StreamToBase64(AStream: TMemoryStream): string; var Base64: TBase64Encoding; begin AStream.Position : 0; Base64 : TBase64Encoding.Create(0); try Result : Base64.EncodeBytesToString(AStream.Memory, AStream.Size); finally Base64.Free; end; end;这里的AStream.Memory直接指向流内部缓冲区Size是字节数。要注意TBase64Encoding.EncodeBytesToString有两个重载一个接受TBytes一个接受Pointer和Size。这里用指针版本避免了复制大流到TBytes的额外开销。如果你偏好 Hex 编码Delphi 12.3 也有System.SysUtils里的TEncoding辅助函数或者用 DCPcrypt2 自带的DCPbase64单元。但DCPbase64里的全局函数是 Ansi 时代的和string混用时容易出坑我建议直接用System.NetEncoding它在所有新版本 Delphi 里都有维护。字符串、文件、Base64 这几种形态合起来已经能覆盖大多数项目需求。如果你要对接 OpenSSL 命令行或者 JavaCipher更要注意下一章的那些细节——它们才是跨语言互通的真正拦路虎。5. DCPcrypt2 实际使用中的 5 个避坑记录从编译错误到解密失败每个问题都是我在项目现场或给同事排查时真实遇到过的。这里按“现象 → 原因 → 解决”写方便你直接对号入座。5.1 编译提示 “Unit DCPrijndael not found”现象新建项目后在uses里写了DCPrijndael编译时报错找不到单元。原因DCPcrypt2 的源码路径没有被当前项目搜索到。很多人只在 Delphi 的全局Library path里加了路径但打开的那个项目如果设置了自己的Search path项目级配置会覆盖全局配置。解决在Project Options Delphi Compiler Search path里加上D:\Components\DCPcrypt2\source。如果加完还报错检查是不是路径里写了中文或空格Delphi 对带空格的路径有时能容忍但命令行编译时往往不行。另外确保没有在多个目录里放了多个DCPrijndael.pasDelphi 遇到同名文件会优先加载最前面的路径版本混用会引发更奇怪的编译错误比如“重定义”。5.2 字符串解密后末尾出现乱码现象用EncryptStream加密字符串解密后原文本正常但尾部多出几个不可见字符或#0。原因加密前把string转 UTF-8 字节时长度是正确的。但解密后TEncoding.UTF8.GetString把 O 字节也当成了字符串内容。如果加密串的字节长度不是 16 的整数倍DCPcrypt2 在 CBC 模式下会做 PKCS7 padding 吗这个版本不一定自动加有些发布版本需要你自己保证明文长度是块大小的整数倍否则最后一块缺位解密时读回来就带上了 padding 的原始字节。解决最简单的方法是不自己处理明文对齐而是用EncryptStream时确保输入流的位置在 0流库会按自身的块大小处理。如果解密后仍然有尾巴换用TDCP_rijndael的InitStr并在加密前手动补0到 16 字节整数倍解密后再按原始长度截断。最稳的方法是在密文前面额外存一个 4 字节的原文明文长度解密时先读长度再截取。不同的打包版本对 padding 的支持不一样所以用之前花 30 秒在DCPrijndael.pas里搜一下Padding相关的声明有padding参数的Init重载就传True没有就走手动补位路线。5.3 密钥与 IV 混用导致多端互解失败现象Delphi 端加密的数据Java 或 Python 端解不开反过来OpenSSL 命令行加密的密文Delphi 端解密后是乱码。原因InitStr(Password, TDCP_sha256)的密钥扩展算法和别人约定的不一样。比如 Java 开发习惯直接取Password.getBytes(StandardCharsets.UTF_8)的前 32 字节当 AES-256 密钥而 DCPcrypt2 是先算 SHA-256 摘要两边密钥字节完全不同。IV 也一样DCPcrypt2 的InitStr可能使用固定的全零 IV而对方用的是随机 IV 并把它拼接在密文头部。解决跨语言联调前把密钥生成规则写死成一种。常见的做法是约定“口令的 UTF-8 字节先做 SHA-256摘要作为 32 字节密钥IV 固定为000102030405060708090a0b0c0d0e0f这 16 个十六进制字节”。然后在 Delphi 端显式初始化密钥和 IV不要用InitStr隐式派生。具体方法是取出密钥摘要后调用Init并传入IV指针。IV 可以定义为一个array[0..15]每个字节赋予你约定的值。这样两边只要遵循相同约定密文就能互通。5.4 用 DCP_rijndael 加解密大文件时内存暴涨甚至内存不足现象加密一个 1 GB 以上的备份文件时程序内存占用持续上升到数百 MB接着报Out of memory。原因用了EncryptBuffer一次性把整个文件读进TMemoryStream然后再加密。DCPcrypt2 的EncryptBuffer是同步方案要求输入输出缓冲区准备好全部数据这对大文件不友好。解决改用EncryptFile或EncryptStream它内部用固定大小的分块循环处理。如果加密网络流要自己控制一块一块调用EncryptBuffer每次处理 16 KB 到 1 MB 不等。注意这时必须正确处理链模式的连续性TDCP_rijndael对象内部会保存上一个密文块连续调用EncryptBuffer时不要重新Init全部喂完后如果要给下一个文件用再调用Reset或Burn清状态。5.5 设计期包安装后 IDE 启动变慢且报错现象安装完 DCPcrypt2Delphi 12.3 每次启动都卡几十秒还会弹“Package ... is already installed”或“找不到 bpl”。原因之前残留了不同编译版本的.bpl或者同一个包既装了源码版又装了预编译版。Delphi 在启动时会把设计期包全部加载包之间有重复单元声明时就会异常。解决打开 Delphi 的Component Install Packages找到所有带 DCP 字样的包先 Remove然后重启 IDE。接着去 BPL 目录手动删除旧的DCPcrypt2*.bpl文件。最后回到源码包只编译一次并安装。如果你已经按本文 2.3 节通过命令行编译过一次就不要再同时装一份 IDE 编译的包二选一。另外不要把运行时包和设计期包混放在同一个目录虽然它们都能被找到但很容易让 IDE 加载重复单元。6. 让 DCPcrypt2 真正融入项目封装单例、兼容 OpenSSL 数据与自检脚本到这里DCPcrypt2 已经能在你的 Delphi 12.3 项目里跑起来了但开发完算法、调通流程后你多半会考虑“怎么让团队里别人不用每次看我这篇笔记也能安全使用”以及“如何保证升级后没退化”。这一章给出三个收尾动作。6.1 用 class 封装加解密避免到处 new每次用都Create一个TDCP_rijndael并没有毛病但团队里有人容易忘记释放或者在下一次加密前没有重新初始化。我习惯做一个静态工具类把字符串和文件加解密暴露成两个静态方法内部管理对象生命周期和异常处理。class function TCryptHelper.AESEncrypt(const Value, Password: string): string; begin // 实现见 4.1 节这里省略重复代码 end;静态方法的好处是调用方不需要知道 DCPcrypt2 的对象细节出问题时只需要调TCryptHelper。另外一个好处是可以集中统一 Base64 编码、统一密码默认值以后换算法时只改一个文件。6.2 和 OpenSSL 互解格式约定与一个最小验证如果你要和 Linux 服务器上的 OpenSSL 命令互传密文先用下面的命令行自己做一次交叉验证。# 先用 OpenSSL 加密一个测试文件 openssl enc -aes-256-cbc -K 32字节十六进制密钥 -iv 16字节十六进制IV -in plain.txt -out cipher.bin然后写一个 Delphi 小工具用同样的密钥和 IV 调用DCPcrypt2解密这个cipher.bin。如果解出来的内容和plain.txt一致说明你的链模式、密钥长度、填充方式都和 OpenSSL 对齐了。这里最容易错的是填充方式OpenSSL 默认用 PKCS7DCPcrypt2 如果初始化时没设置 padding就需要你自己处理。最简单的办法是在密文末尾多试几种填充方案或者干脆用OpenSSL命令行工具加密时加上-nopad两端都不填充但要保证明文长度是 16 的整数倍。这种“乖巧”的边界条件是联调时必须写进接口文档的。6.3 一个自检用例跑完再上线最后在你封装的单元里写一个自检函数包含“空字符串”“中文长文”“正好 16 字节明文”“图片文件”这四类样本。每次改动完算法或换个 Delphi 版本先跑一遍自检。我吃过一次亏升级 Delphi 12.2 到 12.3 后某些老代码没变但解密结果多了一个尾字节最后发现是TEncoding.UTF8.GetString的行为变化。有了自检用例这种问题在提交代码前就能发现。这组技巧本身不难难的是养成“加密算法改动前先回归”的习惯。毕竟加密这行最怕的不是算法弱而是你都不知道它在哪个版本里换了脾气。希望这一套流程能帮你在 Delphi 12.3 上把 DCPcrypt2 用得更踏实。本文还有配套的精品资源点击获取