如何实现与给定PHP函数兼容的Delphi AES-256-CBC加解密功能
Delphi与PHP互通的AES-256-CBC加解密实现(适配GDPR合规)
原PHP实现核心逻辑(需1:1对齐)
function encryptString($plaintext, $password, $encoding = null) { $iv = openssl_random_pseudo_bytes(16); $ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv); $hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true); return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext); } function decryptString($ciphertext, $password, $encoding = null) { $ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext); if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null; return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16)); }
加密流程
- 生成16字节密码学安全的随机IV
- 对输入密码做SHA256哈希,取32字节原始二进制值作为AES-256密钥
- 用AES-256-CBC模式加密明文,输出原始二进制密文
- 用相同密钥对「密文 + IV」计算SHA256 HMAC,得到32字节原始二进制校验值
- 按
16字节IV + 32字节HMAC + 变长密文顺序拼接,最后按参数返回原始二进制、十六进制字符串或Base64字符串
解密流程
- 按编码类型将输入转换为原始二进制
- 拆分二进制内容:前16字节为IV,16-48位为HMAC校验值,48位之后为密文
- 用相同密钥重新计算「密文 + IV」的HMAC,与取出的HMAC做恒定时间比较(防时序攻击),校验失败返回空
- 校验通过后用AES-256-CBC解密密文,返回明文
Delphi 对应实现(兼容Delphi 10.3及以上版本)
依赖单元:System.SysUtils, System.Hash, System.NetEncoding, System.Cipher, System.Classes
辅助恒定时间比较函数(对齐PHP hash_equals 能力)
function HashEquals(const A, B: TBytes): Boolean; var I, Diff: Byte; begin Result := False; if Length(A) <> Length(B) then Exit; Diff := 0; for I := 0 to Length(A) - 1 do Diff := Diff or (A[I] xor B[I]); Result := Diff = 0; end;
加密函数
type TEncodingType = (etRaw, etHex, etBase64); function EncryptString(const PlainText, Password: string; EncodingType: TEncodingType = etBase64): string; var AESKey, IV, CipherText, HMACVal, Combined: TBytes; AES: TAESCipher; HMAC: THMACSHA256; begin // 计算AES密钥:对密码做SHA256取原始32字节 AESKey := THashSHA2.GetHashBytes(TEncoding.UTF8.GetBytes(Password), THashSHA2.TSHA2Version.SHA256); // 生成16字节随机IV SetLength(IV, 16); TNRGGenerator.GenerateRandomBytes(IV); // AES-256-CBC加密,明文统一转UTF-8 AES := TAESCipher.Create(AESKey, TAESCipherMode.mCBC, IV); CipherText := AES.Encrypt(TEncoding.UTF8.GetBytes(PlainText)); // 计算HMAC,输入顺序为密文+IV HMAC := THMACSHA256.Create(AESKey); HMAC.Update(CipherText); HMAC.Update(IV); HMACVal := HMAC.HashFinal; // 拼接IV + HMAC + 密文 Combined := Concat(IV, HMACVal, CipherText); // 按指定编码返回结果 case EncodingType of etRaw: Result := TEncoding.ASCII.GetString(Combined); etHex: Result := TBitConverter.ToString(Combined).Replace('-', '').ToLower; etBase64: Result := TNetEncoding.Base64.EncodeBytesToString(Combined); end; end;
解密函数
function DecryptString(const CipherText, Password: string; EncodingType: TEncodingType = etBase64): string; var AESKey, RawData, IV, HMACVal, CipherRaw, CalcHMAC: TBytes; AES: TAESCipher; HMAC: THMACSHA256; begin Result := ''; // 计算AES密钥 AESKey := THashSHA2.GetHashBytes(TEncoding.UTF8.GetBytes(Password), THashSHA2.TSHA2Version.SHA256); // 输入转原始二进制 case EncodingType of etRaw: RawData := TEncoding.ASCII.GetBytes(CipherText); etHex: begin SetLength(RawData, Length(CipherText) div 2); for var I := 0 to Length(RawData) - 1 do RawData[I] := StrToInt('$' + Copy(CipherText, I*2 + 1, 2)); end; etBase64: RawData := TNetEncoding.Base64.DecodeStringToBytes(CipherText); end; // 最小长度校验:至少包含16字节IV+32字节HMAC if Length(RawData) < 48 then Exit; // 拆分各段数据 IV := Copy(RawData, 0, 16); HMACVal := Copy(RawData, 16, 32); CipherRaw := Copy(RawData, 48, Length(RawData) - 48); // HMAC校验 HMAC := THMACSHA256.Create(AESKey); HMAC.Update(CipherRaw); HMAC.Update(IV); CalcHMAC := HMAC.HashFinal; if not HashEquals(CalcHMAC, HMACVal) then Exit; // 解密后转UTF-8字符串 AES := TAESCipher.Create(AESKey, TAESCipherMode.mCBC, IV); Result := TEncoding.UTF8.GetString(AES.Decrypt(CipherRaw)); end;
互通验证说明
- 两端明文统一使用UTF-8编码,Delphi不要直接传入默认UTF-16格式的字符串
- 密钥计算全程使用原始二进制值,不要转成十六进制或Base64后再传入加密接口
- HMAC的输入顺序必须为「密文+IV」,顺序调换会导致校验失败
- 本实现使用密码学安全的随机IV、带完整性校验,符合GDPR对个人数据加密的合规要求
内容的提问来源于stack exchange,提问作者delphirules
相关产品推荐
相关产品推荐

