我使用rijndael密码解密从PHP发送到Delphi的字符串时遇到问题。 我在PHP端使用mcrypt,在Delphi端使用DCP_rijndael。
目前我有以下代码。
PHP:
function encRJ($key, $iv, $data)
{
$r = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
$r = base64_encode($r);
return $r;
}
在德尔斐:
function decRJ(Data: string; Key: string; IV: string): string;
var ciph: TDCP_rijndael;
begin
Data := Base64DecodeStr(Data);
ciph:= TDCP_rijndael.Create(Self);
ciph.Init(Key[1], 256, @IV[1]);
ciph.DecryptCBC(Data[1], Data[1], Length(Data));
ciph.Free;
Result := Data;
end;
我尝试在互联网上使用几个单元来实现密码,并且发现大多数人都在谈论DCP组件。即便如此,我还是没有设法正确解密。我已经尝试使用Byte数组作为参数,AnsiStrings,WideStrings等,但遗憾的是没有运气。
打扰一下,如果我在这里遗漏了一些非常明显的东西,因为在经过几个小时的搜索之后,我的思绪状况并不理想。
答案 0 :(得分:8)
我似乎花了太长时间,但......
您的问题是块大小。 TDCP_rijndael相当于MCRYPT_RIJNDAEL_128(不是_256)。 ciph.Init(...)调用中的'256'值仍然是正确的。除此之外,它看起来非常好。也就是说,假设你正在使用关键/ iv的ansistrings或者你正在使用非unicode Delphi。
对于unicode Delphi版本,我倾向于使用TBytes和key [0] / iv [0]。
填充可能仍然是一个问题。如果是这样,那么这就是我基于PHP手册页和一些反复试验而破坏的内容。
PHP:
function Encrypt($src, $key, $iv)
{
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
//echo "Block size: " . $block . "\r\n";
$pad = $block - (strlen($src) % $block);
$src .= str_repeat(chr($pad), $pad);
$enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $src, MCRYPT_MODE_CBC, $iv);
$r = base64_encode($enc);
return $r;
}
function Decrypt($src, $key, $iv)
{
$enc = base64_decode($src);
$dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv);
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
$pad = ord($dec[($len = strlen($dec)) - 1]);
return substr($dec, 0, strlen($dec) - $pad);
}
的Delphi:
function DecryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
key, iv, src, dest: TBytes;
cipher: TDCP_rijndael;
slen, pad: integer;
begin
//key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
//iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
key := TEncoding.ASCII.GetBytes(AKey);
iv := TEncoding.ASCII.GetBytes(AIv);
src := Base64DecodeBytes(TEncoding.UTF8.GetBytes(Data));
cipher := TDCP_rijndael.Create(nil);
try
cipher.CipherMode := cmCBC;
slen := Length(src);
SetLength(dest, slen);
cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES
cipher.Decrypt(src[0], dest[0], slen);
// Remove the padding. Get the numerical value of the last byte and remove
// that number of bytes
pad := dest[slen - 1];
SetLength(dest, slen - pad);
// Base64 encode it
result := TEncoding.Default.GetString(dest);
finally
cipher.Free;
end;
end;
function EncryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
cipher: TDCP_rijndael;
key, iv, src, dest, b64: TBytes;
index, slen, bsize, pad: integer;
begin
//key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
//iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
key := TEncoding.ASCII.GetBytes(AKey);
iv := TEncoding.ASCII.GetBytes(AIv);
src := TEncoding.UTF8.GetBytes(Data);
cipher := TDCP_rijndael.Create(nil);
try
cipher.CipherMode := cmCBC;
// Add padding.
// Resize the Value array to make it a multiple of the block length.
// If it's already an exact multiple then add a full block of padding.
slen := Length(src);
bsize := (cipher.BlockSize div 8);
pad := bsize - (slen mod bsize);
Inc(slen, pad);
SetLength(src, slen);
for index := pad downto 1 do
begin
src[slen - index] := pad;
end;
SetLength(dest, slen);
cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES
cipher.Encrypt(src[0], dest[0], slen);
b64 := Base64EncodeBytes(dest);
result := TEncoding.Default.GetString(b64);
finally
cipher.Free;
end;
end;
PHP和Delphi函数现在给出了相同的答案。
修改
Base64DecodeBytes是我添加到DCP Base64单元的一些代码:
function Base64DecodeBytes(Input: TBytes): TBytes;
var
ilen, rlen: integer;
begin
ilen := Length(Input);
SetLength(result, (ilen div 4) * 3);
rlen := Base64Decode(@Input[0], @result[0], ilen);
// Adjust the length of the output buffer according to the number of valid
// b64 characters
SetLength(result, rlen);
end;
2018年编辑(提升死者......):
根据要求,这是编码方法,取消选中并直接从我找到的旧源文件中提取。
免责声明:它已经许多岁并且在最近的内存中未经测试,自Delphi 2010以来未被使用过。现在可能有许多更好的选择。使用风险自负。
function Base64EncodeBytes(Input: TBytes): TBytes;
var
ilen: integer;
begin
ilen := Length(Input);
SetLength(result, ((ilen + 2) div 3) * 4);
Base64Encode(@Input[0], @result[0], ilen);
end;
答案 1 :(得分:0)
您的PHP或Delphi方法似乎都没有指定任何填充。如果默认填充不同,那么您将遇到问题。明确指定两者的PKCS7(或PKCS5)。
GregS'关于解码Base64的结果的评论是正确的。您正在为decRJ()方法提供加密的密文。这将是随机出现的字节。试图将其转换为UTF-8会使其无法解密。传入的密文必须从Base64直接转换为字节数组。 Cyphertext不是字符串,这就是为什么它需要转换为Base64才能作为文本传输。只有再次将文字后,它才会被解密回明文。