Поиск в моем MemoryStream приводит к нежелательным результатам

Я работаю над переписыванием нашего класса шифрования, чтобы он соответствовал требованиям FIPS, и при этом мне приходится заново работать над тем, как мы обрабатываем несекретные данные полезной нагрузки. В данный момент я записываю размер моей несекретной полезной нагрузки, а затем записываю размер моего IV. Я продолжаю в том же духе, записывая свою несекретную полезную нагрузку и IV, причем все эти записи разделяют BinaryWriter, Наконец, я тогда разделяю то же самое MemoryStream и записать мои данные должны быть зашифрованы в CryptoStream,

Вот как класс в настоящее время выглядит:

public class Encryption
{
    private const int SaltBlockSize = 8;
    private const int SaltBitSize = 64;
    private const int KeyBitSize = 256;
    private const int SaltIterations = 10000;

    private const int nonSecretPayloadOffsetInPayload = 0;
    private const int ivOffsetInPayload = 1;

    public byte[] GetNonSecretPayload(byte[] completePayload)
    {
        byte[] nonSecretPayload;
        using (var memoryStream = new MemoryStream(completePayload))
        {
            using (var binaryReader = new BinaryReader(memoryStream))
            {
                int nonSecretPayloadLength = binaryReader.ReadInt32();
                binaryReader.BaseStream.Position = 3;
                nonSecretPayload = binaryReader.ReadBytes(nonSecretPayloadLength);
            }
        }

        return nonSecretPayload;
    }

    public byte[] EncryptMessageWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
    {
        if (string.IsNullOrEmpty(password))
        {
            throw new InvalidOperationException("You can not provide an empty password, you must give a string that is at least 12 characters in size. If you just want to obfuscate the message without any protection, an alternative way is to use a Base64 String");
        }
        else if (password.Length < 12)
        {
            throw new InvalidOperationException("The minimum size your password can be is 12 characters.");
        }

        byte[] saltHash;
        byte[] saltKey = this.CreateSaltKeysFromPassword(password, 0, out saltHash);

        byte[] encryptedValue = null;

        using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
        {
            aesProvider.Key = saltKey;
            aesProvider.Mode = CipherMode.CBC;
            aesProvider.Padding = PaddingMode.PKCS7;
            aesProvider.GenerateIV();
            using (MemoryStream memoryStream = new MemoryStream())
            {
                // Write our IV out first so we can pull the IV off later during decryption.
                // The IV does not need to be encrypted, it is safe to store as as unencrypted buffer in the encrypted byte array.
                using (BinaryWriter ivWriter = new BinaryWriter(memoryStream, Encoding.UTF8, true))
                {
                    // The first two writes to the stream should be the size of the non-secret payload
                    // and the size of the IV. If no payload exists, then we write 0.
                    if (nonSecretPayload == null || nonSecretPayload.Length == 0)
                    {
                        ivWriter.Write(0);
                    }
                    else
                    {
                        ivWriter.Write(nonSecretPayload.Length);
                    }
                    ivWriter.Write(aesProvider.IV.Length);

                    // If we have a payload, write it out.
                    if (nonSecretPayload != null && nonSecretPayload.Length > 0)
                    {
                        ivWriter.Write(nonSecretPayload);
                    }

                    // Write the Initialization Vector.
                    ivWriter.Write(aesProvider.IV);
                }

                // Create our encryptor and write the secret message to the encryptor stream.
                var encryptor = aesProvider.CreateEncryptor(saltKey, aesProvider.IV);
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(secretMessage, 0, secretMessage.Length);
                    cryptoStream.FlushFinalBlock();
                }

                // Get the non-secret payload, IV, payload and IV lengths and encrypted data back as an array of bytes.
                encryptedValue = memoryStream.ToArray();
            }
        }

        return encryptedValue;
    }

    public string EncryptMessageWithPassword(string secretMessage, string password, byte[] nonSecretPayLoad = null)
    {
        byte[] secreteMessageBytes = Encoding.UTF8.GetBytes(secretMessage);
        byte[] encryptedMessage = this.EncryptMessageWithPassword(secreteMessageBytes, password, nonSecretPayLoad);
        return Convert.ToBase64String(encryptedMessage);
    }

    private byte[] CreateSaltKeysFromPassword(string password, int nonSecretPayloadSize, out byte[] saltHash)
    {
        byte[] saltKey;

        //Use Random Salt to prevent pre-generated weak password attacks.
        using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / SaltBlockSize, SaltIterations))
        {
            // Get a generated salt derived from the user password, hashed n-times where n = SaltIterations
            saltHash = generator.Salt;

            //Generate Keys
            saltKey = generator.GetBytes(KeyBitSize / SaltBlockSize);
        }

        return saltKey;
    }
}

Я бы ожидал в моем GetNonSecretPayload(byte[] payload); что, установив положение, или используя binaryReader.BaseStream.Seek(2); чтобы пропустить элемент длины IV, я бы пропустил запись размера IV в массиве byte[] и смог бы прочитать байты, связанные с фактическими несекретными данными. Это не работает, хотя, вероятно, потому что это не массив под крышками, который я могу просто переместить к следующему элементу в массиве, пропуская длину IV, записанную первоначально.

У меня есть следующий юнит-тест.

[TestClass]
public class EncryptionTests
{
    private const string _ContentToEncrypt = "This is a test to make sure the encryption Type actually encrypts the data right.";

    private const string _Password = "EncryptedPassword1";

    [TestMethod]
    public void Extract_non_secret_payload_content_from_encrypted_string()
    {
        // Arrange
        var encryption = new Encryption();
        string nonSecretData = "My payload is not considered secret and can be pulled out of the payload without decrypting";

        // Convert the secret and non-secret data into a byte array
        byte[] payload = Encoding.UTF8.GetBytes(nonSecretData);
        byte[] encodedBytes = Encoding.UTF8.GetBytes(_ContentToEncrypt);

        // Encrypt the secret data while injecting the nonsecret payload into the encrypted stream.
        byte[] encryptedValue = encryption.EncryptMessageWithPassword(encodedBytes, _Password, payload);

        // Act
        // Pull the non-secret payload out of the encrypted message - without having to decrypt it.
        byte[] UnencryptedPayloadWithinEncryptedArray = encryption.GetNonSecretPayload(encryptedValue);
        string payloadContent = Encoding.UTF8.GetString(UnencryptedPayloadWithinEncryptedArray);

        // Assert
        Assert.AreEqual(nonSecretData, payloadContent);
    }
}

Что я получу с моим текущим binaryReader.BaseStream.Position = 3 является

"\0\u0010\0\0\0Моя полезная нагрузка не считается секретной и может быть извлечена из полезной нагрузки без расшифровки"

В прошлом я читал и писал такие данные, используя BinaryWriter, но мне никогда не приходилось искать их, чтобы пропустить данные. Что я здесь не так делаю?

0 ответов

Другие вопросы по тегам