我有一个在 php 7.2 上运行的应用程序,我需要使用以下条件加密一个字符串:
我已经知道我应该得到的输出,但是我的脚本返回不同的字符串,我认为是因为 IV(openssl_random_pseude_bytes),我无法真正理解它的逻辑。我在加密方面没有太多经验,所以我无法弄清楚。
$string = 'my-string';
$cipher = 'BF-CFB';
$key = 'my-secret-key';
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$encrypted = base64_encode(openssl_encrypt($string, $cipher, $key, OPENSSL_RAW_DATA, $iv));
示例
此加密的目标是 API 访问,并且提供了一个用 C# 编写的示例用于加密方法。问题是该脚本每次都生成相同的字符串,这与我的不同。我必须构建我的脚本,以便获得与提供的官方示例相同的结果(这是一个代码片段:)
public new int Encrypt(
byte[] dataIn,
int posIn,
byte[] dataOut,
int posOut,
int count)
{
int end = posIn + count;
byte[] iv = this.iv;
int ivBytesLeft = this.ivBytesLeft;
int ivPos = iv.Length - ivBytesLeft;
// consume what's left in the IV buffer, but make sure to keep the new
// ciphertext in a round-robin fashion (since it represents the new IV)
if (ivBytesLeft >= count)
{
// what we have is enough to deal with the request
for (; posIn < end; posIn++, posOut++, ivPos++)
{
iv[ivPos] = dataOut[posOut] = (byte)(dataIn[posIn] ^ iv[ivPos]);
}
this.ivBytesLeft = iv.Length - ivPos;
return count;
}
for (; ivPos < BLOCK_SIZE; posIn++, posOut++, ivPos++)
{
iv[ivPos] = dataOut[posOut] = (byte)(dataIn[posIn] ^ iv[ivPos]);
}
count -= ivBytesLeft;
uint[] sbox1 = this.sbox1;
uint[] sbox2 = this.sbox2;
uint[] sbox3 = this.sbox3;
uint[] sbox4 = this.sbox4;
uint[] pbox = this.pbox;
uint pbox00 = pbox[0];
uint pbox01 = pbox[1];
uint pbox02 = pbox[2];
uint pbox03 = pbox[3];
uint pbox04 = pbox[4];
uint pbox05 = pbox[5];
uint pbox06 = pbox[6];
uint pbox07 = pbox[7];
uint pbox08 = pbox[8];
uint pbox09 = pbox[9];
uint pbox10 = pbox[10];
uint pbox11 = pbox[11];
uint pbox12 = pbox[12];
uint pbox13 = pbox[13];
uint pbox14 = pbox[14];
uint pbox15 = pbox[15];
uint pbox16 = pbox[16];
uint pbox17 = pbox[17];
// now load the current IV into 32bit integers for speed
uint hi = (((uint)iv[0]) << 24) |
(((uint)iv[1]) << 16) |
(((uint)iv[2]) << 8) |
iv[3];
uint lo = (((uint)iv[4]) << 24) |
(((uint)iv[5]) << 16) |
(((uint)iv[6]) << 8) |
iv[7];
// we deal with the even part first
int rest = count % BLOCK_SIZE;
end -= rest;
for (; ; )
{
// need to create new IV material no matter what
hi ^= pbox00;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox01;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox02;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox03;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox04;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox05;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox06;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox07;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox08;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox09;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox10;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox11;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox12;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox13;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox14;
lo ^= (((sbox1[(int)(hi >> 24)] + sbox2[(int)((hi >> 16) & 0x0ff)]) ^ sbox3[(int)((hi >> 8) & 0x0ff)]) + sbox4[(int)(hi & 0x0ff)]) ^ pbox15;
hi ^= (((sbox1[(int)(lo >> 24)] + sbox2[(int)((lo >> 16) & 0x0ff)]) ^ sbox3[(int)((lo >> 8) & 0x0ff)]) + sbox4[(int)(lo & 0x0ff)]) ^ pbox16;
uint swap = lo ^ pbox17;
lo = hi;
hi = swap;
if (posIn >= end)
{
// exit right in the middle so we always have new IV material for the rest below
break;
}
hi ^= (((uint)dataIn[posIn]) << 24) |
(((uint)dataIn[posIn + 1]) << 16) |
(((uint)dataIn[posIn + 2]) << 8) |
dataIn[posIn + 3];
lo ^= (((uint)dataIn[posIn + 4]) << 24) |
(((uint)dataIn[posIn + 5]) << 16) |
(((uint)dataIn[posIn + 6]) << 8) |
dataIn[posIn + 7];
posIn += 8;
// now stream out the whole block
dataOut[posOut] = (byte)(hi >> 24);
dataOut[posOut + 1] = (byte)(hi >> 16);
dataOut[posOut + 2] = (byte)(hi >> 8);
dataOut[posOut + 3] = (byte)hi;
dataOut[posOut + 4] = (byte)(lo >> 24);
dataOut[posOut + 5] = (byte)(lo >> 16);
dataOut[posOut + 6] = (byte)(lo >> 8);
dataOut[posOut + 7] = (byte)lo;
posOut += 8;
}
// store back the new IV
iv[0] = (byte)(hi >> 24);
iv[1] = (byte)(hi >> 16);
iv[2] = (byte)(hi >> 8);
iv[3] = (byte)hi;
iv[4] = (byte)(lo >> 24);
iv[5] = (byte)(lo >> 16);
iv[6] = (byte)(lo >> 8);
iv[7] = (byte)lo;
// emit the rest
for (int i = 0; i < rest; i++)
{
iv[i] = dataOut[posOut + i] = (byte)(dataIn[posIn + i] ^ iv[i]);
}
this.ivBytesLeft = iv.Length - rest;
return count;
}
最佳答案
这就是您的 PHP 代码所期望的。 CFB 模式将分组密码转换为流密码。由于 semantical security (或 randomized encryption ),对于相同 key 下的每个加密,您需要不同的 IV。否则,一旦攻击者注意到 IV 被重用,攻击者就可以像在 One-Time-Pad 中一样使用两次 time-pad 攻击。
您应该始终新鲜生成 IV。
$iv = openssl_random_pseudo_bytes($ivlen);
注意: 仍然存在一个问题,如果 key 使用过多,您可能会为同一个 key 生成两次相同的 IV。 IV 重用的最简单缓解方法是使用增量 IV 或使用 LFSR 生成 IV,这是常见的做法。如果您要为每个加密更改 key ,那么 IV 重用不是问题,但是,更改 IV 比更改 key 更容易。
更新: 我通过查看评论找到了你的 C# source code
// consume what's left in the IV buffer, but make sure to keep the new
这段代码的作者说
/// Useful if you don't want to deal with padding of blocks (in comparsion to CBC), however
/// a safe initialization vector (IV) is still needed.
此代码目前使用不安全。
您可以使用
SetIV(value, 0);
函数使用来自 PHP 加密的
value
初始化 IV。关于c# - PHP 使用河豚加密字符串,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57883645/