在 dotnet core 中使用密码和盐对字符串进行编码和解码 [英] Encode and decode a string, with password and salt, in dotnet core

查看:39
本文介绍了在 dotnet core 中使用密码和盐对字符串进行编码和解码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在为 dotnet 核心开发一个简单的助手,它应该根据用户提供的密码(密钥)和盐对字符串进行编码和解码.

I've been working on a simple helper for dotnet core that should encode and decode a string, based on a user provided password (key) and a salt.

与完整的 .NET 框架相反,dotnet 核心目前没有 RijndaelManaged 类的实现.几乎每个像样的 C# 加密/解密示例都基于此类,使其对 dotnet 核心毫无用处.在 GitHub 上的 CoreFX 存储库 上有很多争论.

In contradiction to the full .NET Framework, dotnet core currently does not have an implementation for the RijndaelManaged class. Pretty much every decent encrypt/decrypt sample for C# is based on this class, rendering it useless for dotnet core. There is a lot of debate on it on the CoreFX repo on GitHub.

但是,结合(旧) 关于 AesManaged 的​​ MSDN 文章Troy Hunt 的文章 - 更不用说一些重构了 - 我能够编写以下半工作示例:

However, with the combination of both the (old) MSDN article on AesManaged and an article by Troy Hunt - not to mention some refactoring - I was able to brew the following semi-working example:

internal class CryptographyHelpers
{
    internal static string Decrypt(string password, string salt, string encrypted_value)
    {
        string decrypted;

        using (var aes = Aes.Create())
        {
            var keys = GetAesKeyAndIV(password, salt, aes);
            aes.Key = keys.Item1;
            aes.IV = keys.Item2;

            // create a decryptor to perform the stream transform.
            var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

            // create the streams used for encryption.
            var encrypted_bytes = ToByteArray(encrypted_value);
            using (var memory_stream = new MemoryStream(encrypted_bytes))
            {
                using (var crypto_stream = new CryptoStream(memory_stream, decryptor, CryptoStreamMode.Read))
                {
                    using (var reader = new StreamReader(crypto_stream))
                    {
                        decrypted = reader.ReadToEnd();
                    }
                }
            }
        }

        return decrypted;
    }

    internal static string Encrypt(string password, string salt, string plain_text)
    {
        string encrypted;

        using (var aes = Aes.Create())
        {
            var keys = GetAesKeyAndIV(password, salt, aes);
            aes.Key = keys.Item1;
            aes.IV = keys.Item2;

            var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            using (var memory_stream = new MemoryStream())
            {
                using (var crypto_stream = new CryptoStream(memory_stream, encryptor, CryptoStreamMode.Write))
                {
                    using (var writer = new StreamWriter(crypto_stream))
                    {
                        writer.Write(plain_text);
                    }

                    var encrypted_bytes = memory_stream.ToArray();
                    encrypted = ToString(encrypted_bytes);
                }
            }
        }

        return encrypted;
    }

    private static byte[] ToByteArray(string input)
    {
        return Encoding.Unicode.GetBytes(input);
    }

    private static string ToString(byte[] input)
    {
        return Encoding.Unicode.GetString(input);
    }

    private static Tuple<byte[], byte[]> GetAesKeyAndIV(string password, string salt, SymmetricAlgorithm symmetricAlgorithm)
    {
        const int bits = 8;
        var key = new byte[16];
        var iv = new byte[16];

        var derive_bytes = new Rfc2898DeriveBytes(password, ToByteArray(salt));
        key = derive_bytes.GetBytes(symmetricAlgorithm.KeySize / bits);
        iv = derive_bytes.GetBytes(symmetricAlgorithm.BlockSize / bits);

        return new Tuple<byte[], byte[]>(key, iv);
    }

}

我说的是半工作示例,这意味着它有效......有时.这就是问题所在.当我任意运行我的测试时,它大部分时间都会成功.随机地,大约四分之一,它失败并显示以下消息:

I said semi-working example, which means it works... sometimes. And there lies the problem. When I run my test arbitrarily, it succeeds most of the time. Randomly, about one out of four times, it fails with the following message:

消息:预期字符串为lorem ipsum dom dolor sat amet",但" R k6 o do Lr sat amet" 在 "> R" 附近不同(索引 0).

Message: Expected string to be "lorem ipsum dom dolor sit amet", but " �R��k6��o��do�Lr sit amet" differs near ">�R" (index 0).

看起来是unicode的问题,但是把helpers中的Encoding.Unicode改成Encoding.UTF8,导致报错:

It looks like a unicode problem, but changing the Encoding.Unicode in the helpers to Encoding.UTF8, leads to the error:

System.Security.Cryptography.CryptographicException : 输入数据不是一个完整的块.

System.Security.Cryptography.CryptographicException : The input data is not a complete block.

将编码更改为 Encoding.ASCII 会导致以下错误:

Changing the encoding to Encoding.ASCII leads to the following error:

System.Security.Cryptography.CryptographicException : 指定的填充模式对此算法无效.

System.Security.Cryptography.CryptographicException : Specified padding mode is not valid for this algorithm.

我使用的测试是:

[Fact]
public void Decrypt_Should_Decode_An_Encrypted_String()
{
    // arrange
    var key = Guid.NewGuid().ToString();
    var salt = Guid.NewGuid().ToString();
    var original_value = "lorem ipsum dom dolor sit amet";
    var encrypted_value = CryptographyHelpers.Encrypt(key, salt, original_value);

    // act
    var target = CryptographyHelpers.Decrypt(key, salt, encrypted_value);

    // assert
    target.Should().NotBeNullOrEmpty();
    target.Should().Be(original_value);
}

所以,我的主要问题是:为什么我的实施(测试)有时会失败?

So, my primary question is: why does my imlementation (test) sometimes fail?

我绝对不是密码学专家,但在高层次上我知道一些基本概念.此外,发布了一个类似的问题 如何将 Rijndael 加密与 .Net Core 类库一起使用?" 已发布,但唯一的答案停留在概念层面,缺乏具体实施.

I am definitely not an expert on cryptography, but on a high level am aware of some basic concepts. Also, a similar question "How to use Rijndael encryption with a .Net Core class library?" was posted, but the only answer stays on a conceptual level, lacking concrete implementation.

关于这是否是一个足够好"的实现的任何提示和论点也将不胜感激.

Any tips and arguments as to if this is a 'good enough' implementation would also be very much appreciated.

谢谢!

推荐答案

你的问题与密码学无关,而是这对函数引起的

Your problem has nothing to do with cryptography and instead is caused by this pair of functions

private static byte[] ToByteArray(string input)
{
    return Encoding.Unicode.GetBytes(input);
}

private static string ToString(byte[] input)
{
    return Encoding.Unicode.GetString(input);
}

不允许在任意字节数组上调用 GetString,否则会导致信息丢失.您需要使用允许任意字节数组的编码,例如 Base64:

You are not allowed to call GetString on arbitrary byte arrays, you will cause a loss of information if you do. You need to use a encoding that allows for arbitrary byte arrays, for example Base64:

private static byte[] ToByteArray(string input)
{
    return Convert.FromBase64String(input);
}

private static string ToString(byte[] input)
{
    return Convert.ToBase64String(input);
}

这篇关于在 dotnet core 中使用密码和盐对字符串进行编码和解码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆