PHP使用河豚加密字符串

Joh*_*Doe 2 php c# encryption cryptography

我有一个在php 7.2上运行的应用程序,需要使用以下条件对字符串进行加密:

  • 密码:NCFB
  • 输出编码:Base64
  • 初始化向量(IV)= 8

我已经知道应该得到的输出,但是我的脚本会返回所有不同的字符串,由于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));
Run Code Online (Sandbox Code Playgroud)

加密的目的是为了访问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;
}
Run Code Online (Sandbox Code Playgroud)

kel*_*aka 6

这就是您的PHP代码所期望的。CFB模式将分组密码转换为流密码。由于语义安全性(或随机加密),对于同一密钥下的每种加密,您需要不同的IV。否则,一旦攻击者注意到IV的重复使用,攻击者就可以像一次性垫一样使用两次垫式攻击。

您应该始终重新生成IV。

$iv = openssl_random_pseudo_bytes($ivlen);
Run Code Online (Sandbox Code Playgroud)

注意:仍然存在一个问题,如果密钥使用过多,您可能会为同一密钥两次生成相同的IV。IV重用最简单的缓解方法是使用增量IV或通过使用LFSR生成IV。如果您要更改每种加密的密钥,那么IV重用就不是问题,但是,更改IV比更改密钥更容易。

更新:我已经通过查看注释找到了您的C#源代码

// consume what's left in the IV buffer, but make sure to keep the new
Run Code Online (Sandbox Code Playgroud)

该代码的作者说

/// 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.
Run Code Online (Sandbox Code Playgroud)

此代码当前使用不安全。

您可以使用

SetIV(value, 0);
Run Code Online (Sandbox Code Playgroud)

函数value通过使用PHP加密来初始化IV 。