【问题标题】:wrong password in image steganography图像隐写术中的错误密码
【发布时间】:2013-02-25 21:26:03
【问题描述】:

我有一个关于图像隐写术的项目(用于无损图像)。我正在尝试了解它背后的过程。但是当我运行它来提取消息时,会为 wrong password 以及 empty-image(no data encrypted) 生成 exception .它随机选择像素并使用散列集。但是中间的一些步骤我不明白。我怀疑这些步骤中的任何错误是否会导致异常。此外,在某些情况下,该项目即使密码错误也会提取隐藏消息。我该如何解决这些问题?

为这个长代码道歉,但希望有一个解决方案.. 感谢您的帮助。

代码如下:

//encoding data in image

private void EncodeByte(Bitmap bm, Bitmap visible_bm, Random rand,
        byte value, HashSet<string> used_positions)
    {
        for (int i = 0; i < 8; i++)
        {
            // Pick a position for the ith bit.
            int row, col, pix;
            PickPosition(bm, rand, used_positions, out row, out col, out pix);

            // Get the color's pixel components.
            Color clr = bm.GetPixel(row, col);
            byte r = clr.R;
            byte g = clr.G;
            byte b = clr.B;

疑问:下一个要存储的位是如何在下面显示的“接下来的两行”中获取的?

// Get the next bit to store.
            int bit = 0;
            if ((value & 1) == 1) bit = 1;  //’value’ is the byte to be encoded

// Update the color.
            switch (pix)
            {
                case 0:
                    r = (byte)((r & 0xFE) | bit);
                    break;
                case 1:
                    g = (byte)((g & 0xFE) | bit);
                    break;
                case 2:
                    b = (byte)((b & 0xFE) | bit);
                    break;
            }
            clr = Color.FromArgb(clr.A, r, g, b);
            bm.SetPixel(row, col, clr);

  // Move to the next bit in the value.
            value >>= 1;
        }
    }

  //decoding  image
  private string DecodeMessageInImage(Bitmap bm, string password)
    {
        // Initialize a random number generator.
        Random rand = new Random(obj.NumericPassword(password));

        // Create a new HashSet.
        HashSet<string> used_positions = new HashSet<string>();

        // Make a byte array big enough to hold the message length.
        int len = 0;
        byte[] bytes = BitConverter.GetBytes(len);

   // Decode the message length.
        for (int i = 0; i < bytes.Length; i++)
        {
            bytes[i] = DecodeByte(bm, rand, used_positions);
        }
        len = BitConverter.ToInt32(bytes, 0);

疑问:以下检查(如果 len>10000)是否正确?

 // Sanity check.

        if(len>10000)
        {
            throw new InvalidDataException(
            "Message length " + len.ToString() +
                " is too big to make sense. Invalid password.");
        }

        // Decode the message bytes.
        char[] chars = new char[len];
        for (int i = 0; i < chars.Length; i++)
        {
            chars[i] = (char)DecodeByte(bm, rand, used_positions);
        }
        return new string(chars);
    }

    // Decode a byte.
    private byte DecodeByte(Bitmap bm, Random rand, HashSet<string>   used_positions)
    {

        byte value = 0;
        byte value_mask = 1;
        for (int i = 0; i < 8; i++)
        {
            // Find the position for the ith bit.
            int row, col, pix;
           obj.PickPosition(bm, rand, used_positions, out row, out col, out pix);

            // Get the color component value.
            byte color_value = 0;
            switch (pix)
            {
                case 0:
                    color_value = bm.GetPixel(row, col).R;
                    break;
                case 1:
                    color_value = bm.GetPixel(row, col).G;
                    break;
                case 2:
                    color_value = bm.GetPixel(row, col).B;
                    break;
            }

疑问:你能告诉我以下是如何工作的吗:

// Set the next bit if appropriate.
            if ((color_value & 1) == 1)
            {
                // Set the bit.
                value = (byte)(value | value_mask);
            }

            // Move to the next bit.
            value_mask <<= 1;
        }

        return value;
      }

疑问:这个无限循环是否异常?这是一个正确的条件吗?

  // Pick an unused (r, c, pixel) combination.
    public void PickPosition(Bitmap bm, Random rand,
        HashSet<string> used_positions,
        out int row, out int col, out int pix)
    {
        for (; ; )
        {
            // Pick random r, c, and pix.
            row = rand.Next(0, bm.Width);
            col = rand.Next(0, bm.Height);
            pix = rand.Next(0, 3);

            // See if this location is available.
            string key =
                row.ToString() + "/" +
                col.ToString() + "/" +
                pix.ToString();
            if (!used_positions.Contains(key))
            {
                used_positions.Add(key);
                return;
            }
        }
      }

  // Convert a string password into a numeric value.
    public int NumericPassword(string password)
    {
        // Initialize the shift values to different non-zero values.
        int shift1 = 3;
        int shift2 = 17;

        // Process the message.
        char[] chars = password.ToCharArray();
        int value = 0;
        for (int i = 1; i < password.Length; i++)
        {
            // Add the next letter.
            int ch_value = (int)chars[i];
            value ^= (ch_value << shift1);
            value ^= (ch_value << shift2);

            // Change the shifts.
            shift1 = (shift1 + 7) % 19;
            shift2 = (shift2 + 13) % 23;
        }
        return value;
    }

【问题讨论】:

    标签: c#-3.0 steganography


    【解决方案1】:

    首先,为什么会接受错误的密码:

    NumericPassword 可能会为两个不同的密码返回相同的值(“hash collission”)。 NumericPassword 基本上是一个哈希函数,因此容易受到碰撞攻击。您可以通过为哈希使用更多字节来改善这种情况,尽管这需要不同的算法和能够处理更大种子的随机数生成器。

    在这种情况下,随机数生成器会为不同的密码生成相同的数字序列,如果它们产生相同的哈希值,即导致对隐写内容的“解密”。

    可以通过使用密码作为密钥的加密算法对数据本身进行加密来规避这种情况。

    关于算法

    有关如何将值打包/解包到图像中的问题,请查看Bitwise Operators

    • 例如:

      if ((value & 1) == 1) bit = 1;  //’value’ is the byte to be encoded
      

      将取值的最低有效位并将其与 1 进行比较。如果是 1,则 bit 设置为 1。这就是将字节值分解为单个位的方式,然后将这些位写入图像中的颜色值与您展示的案例除外。

      bitwise and (&amp;) 运算符的示例:具有二进制值 00101010,我们可以将 bitwise and00010111 结合使用,这将得到 00000010

    • 还使用了位移运算符 (&lt;&lt;)。在您的情况下(&lt;&lt;= 1),它将整数中的位向左移动(即它们的重要性向上)。那里的掩码是用来屏蔽当前读取的位必须放入值的地方。

      基本上,在编码和解码函数中,从低到高遍历字节中的所有位。然后,通过将图像打包到像素的最低有效位中,一个接一个地写入或读取图像。

    • 长度检查是明智的,但我会选择不同的条件(即取决于图像大小的条件,即容器的实际大小)。有必要通过检查图像中编码的数据是否可以使用给定的密码解码来检测可能错误的密码(完整性检查)。

      对于错误的密码,提取的长度可能会非常高。但是,这不是检测错误密码的安全检查(错误的密码也可能产生低长度的数字)。

    • PickPosition 中的“无限”循环。这个循环可能确实是无限的,即如果图像中没有任何位置。就我个人而言,我会选择一种算法,该算法首先遍历“坏”(即已使用)像素的邻居,然后再获取一个新的随机值。

      这样可能需要很长时间才能找到下一个有效位置。此外,它还没有检查是否留下了任何有效位置,但这可能发生在您未显示的部分代码中。

    给定算法可以很容易地计算出可以存储在图像中的位数。该算法将信息存储在每个颜色通道的最低有效位中,因此我们得到每个像素三个位。因此,图像中的总位数为:

    capacityInBits = width * height * 3
    

    【讨论】:

    • 非常感谢。现在我明白了。但是还有一个疑问。这个语句的含义是什么 'r = (byte)((r & 0xFE) | bit)' 。 0xFE 是 254 的,但是为什么用在这里呢?
    • 也是按位运算。使用 r&0xFE,将最低位显式设置为 0,然后将最低位或(使用 |)与所需值相或。它是bitwise andor 的组合。我建议使用r 的一些值和支持按位运算的计算器(或例如python shell)。
    • 好的...我使用密码加密了数据,使其正常工作。现在我可以显示错误密码/图像的消息。谢谢你..
    • 我们如何计算图像可以包含的总位数?
    • 好吧,看看算法是如何存储数据的。它使用每个颜色通道的最低有效位,因此每个像素有 3 位。将其与图像区域(宽度×高度)相乘,就可以得到可以存储的位数。除以八,您显然会得到字节数。将其编辑到我的答案中。
    猜你喜欢
    • 1970-01-01
    • 2015-06-02
    • 2011-02-17
    • 2014-10-31
    • 2014-01-24
    • 2014-02-18
    • 2018-07-02
    • 2011-01-01
    • 2021-03-03
    相关资源
    最近更新 更多