C# AES加密不';似乎无法解密,对吗?

C# AES加密不';似乎无法解密,对吗?,c#,encryption,aes,C#,Encryption,Aes,我编写这个类是为了允许我对对象的json表示进行加密和解密,但它似乎没有像MSDN文档(此处:)所建议的那样工作 using Newtonsoft.Json; using System; using System.Configuration; using System.IO; using System.Security.Cryptography; using System.Text; using System.Web.Configuration; namespace Core.Data {

我编写这个类是为了允许我对对象的json表示进行加密和解密,但它似乎没有像MSDN文档(此处:)所建议的那样工作

using Newtonsoft.Json;
using System;
using System.Configuration;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;

namespace Core.Data
{
    public class AesCrypto<T> : ICrypto<T>
    {
    string DecryptionKey { get { return ((MachineKeySection)ConfigurationManager.GetSection("system.web/machineKey")).DecryptionKey; } }

    public string Encrypt(T source, string salt)
    {
        var sourceString = JsonConvert.SerializeObject(source);

        using (var aes = new AesManaged())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.GenerateIV();

            using (var stream = new MemoryStream())
            {
                var deriveBytes = new Rfc2898DeriveBytes(DecryptionKey, Encoding.Unicode.GetBytes(salt));
                aes.Key = deriveBytes.GetBytes(128 / 8);
                stream.Write(BitConverter.GetBytes(aes.IV.Length), 0, sizeof(int));
                stream.Write(aes.IV, 0, aes.IV.Length);

                using (var cs = new CryptoStream(stream, aes.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    byte[] rawPlaintext = Encoding.Unicode.GetBytes(sourceString);
                    cs.Write(rawPlaintext, 0, rawPlaintext.Length);
                    cs.FlushFinalBlock();

                    stream.Seek(0, SeekOrigin.Begin);

                    using (var reader = new StreamReader(stream, Encoding.Unicode))
                        return reader.ReadToEnd();
                }
            }
        }
    }

    public T Decrypt(string sourceString, string salt)
    {
        using (Aes aes = new AesManaged())
        {
            aes.Padding = PaddingMode.PKCS7;
            var deriveBytes = new Rfc2898DeriveBytes(DecryptionKey, Encoding.Unicode.GetBytes(salt));
            aes.Key = deriveBytes.GetBytes(128 / 8);

            using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(sourceString)))
            {
                stream.Seek(0, SeekOrigin.Begin);

                // Get the initialization vector from the encrypted stream
                aes.IV = ReadIV(stream);

                using (var reader = new StreamReader(new CryptoStream(stream, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read), Encoding.Unicode))
                {
                    var resultString = reader.ReadToEnd();
                    return JsonConvert.DeserializeObject<T>(resultString);
                }
            }
        }
    }

    byte[] ReadIV(Stream s)
    {
        byte[] rawLength = new byte[sizeof(int)];
        if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
        {
            throw new SystemException("Stream did not contain properly formatted byte array");
        }

        byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
        if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
        {
            throw new SystemException("Did not read byte array properly");
        }

        return buffer;
    }
}
使用Newtonsoft.Json;
使用制度;
使用系统配置;
使用System.IO;
使用System.Security.Cryptography;
使用系统文本;
使用System.Web.Configuration;
名称空间Core.Data
{
公共类加密:ICrypto
{
字符串DecryptionKey{get{return((MachineKeySection)ConfigurationManager.GetSection(“system.web/machineKey”)).DecryptionKey;}
公共字符串加密(T源,字符串salt)
{
var sourceString=JsonConvert.SerializeObject(源);
使用(var aes=new AesManaged())
{
aes.Padding=PaddingMode.PKCS7;
aes.GenerateIV();
使用(var stream=new MemoryStream())
{
var deriveBytes=new Rfc2898DeriveBytes(DecryptionKey,Encoding.Unicode.GetBytes(salt));
aes.Key=deriveBytes.GetBytes(128/8);
stream.Write(BitConverter.GetBytes(aes.IV.Length),0,sizeof(int));
流写入(aes.IV,0,aes.IV.Length);
使用(var cs=new CryptoStream(stream,aes.CreateEncryptor(),CryptoStreamMode.Write))
{
byte[]rawPlaintext=Encoding.Unicode.GetBytes(sourceString);
cs.Write(rawPlaintext,0,rawPlaintext.Length);
cs.FlushFinalBlock();
stream.Seek(0,SeekOrigin.Begin);
使用(var reader=newstreamreader(stream,Encoding.Unicode))
返回reader.ReadToEnd();
}
}
}
}
公共T解密(字符串源字符串、字符串盐)
{
使用(Aes=新Aes管理())
{
aes.Padding=PaddingMode.PKCS7;
var deriveBytes=new Rfc2898DeriveBytes(DecryptionKey,Encoding.Unicode.GetBytes(salt));
aes.Key=deriveBytes.GetBytes(128/8);
使用(var stream=newmemoryStream(Encoding.Unicode.GetBytes(sourceString)))
{
stream.Seek(0,SeekOrigin.Begin);
//从加密流中获取初始化向量
aes.IV=读取IV(流);
使用(var reader=newstreamreader(newcryptostream(stream,aes.CreateDecryptor(aes.Key,aes.IV),CryptoStreamMode.Read),Encoding.Unicode))
{
var resultString=reader.ReadToEnd();
返回JsonConvert.DeserializeObject(resultString);
}
}
}
}
字节[]ReadIV(流s)
{
字节[]rawLength=新字节[sizeof(int)];
if(s.Read(rawLength,0,rawLength.Length)!=rawLength.Length)
{
抛出新的SystemException(“流未包含格式正确的字节数组”);
}
字节[]缓冲区=新字节[BitConverter.ToInt32(rawLength,0)];
if(s.Read(buffer,0,buffer.Length)!=buffer.Length)
{
抛出新的SystemException(“未正确读取字节数组”);
}
返回缓冲区;
}
}
}

我编写了以下单元测试来测试此功能

using Core.Data;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace Core.Tests
{
    [TestClass]
    public class CryptoTests
    {
        class EncryptableObject
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public DateTimeOffset When { get; set; }
        }

        [TestMethod]
        public void TestAesCrypto()
        {
            var testInput = new EncryptableObject { Id = 123, Name = "Victim", When = DateTimeOffset.UtcNow };
            var crypto = new AesCrypto<EncryptableObject>();

            var testSalt = "testtest";

            var magicString = crypto.Encrypt(testInput, testSalt);
            var testOutput = crypto.Decrypt(magicString, testSalt);

            Assert.AreEqual(testInput.Id, testOutput.Id);
            Assert.AreEqual(testInput.Name, testOutput.Name);
            Assert.AreEqual(testInput.When, testOutput.When);
        }
    }
}
使用Core.Data;
使用Microsoft.VisualStudio.TestTools.UnitTesting;
使用制度;
名称空间核心测试
{
[测试类]
公开类密码测试
{
类EncryptableObject
{
公共int Id{get;set;}
公共字符串名称{get;set;}
当{get;set;}时的公共DateTimeOffset
}
[测试方法]
公共无效测试脚本()
{
var testInput=newencryptableobject{Id=123,Name=“受害者”,When=DateTimeOffset.UtcNow};
var crypto=新AesCrypto();
var testSalt=“testtest”;
var magicString=crypto.Encrypt(testInput,testSalt);
var testOutput=crypto.Decrypt(magicString,testSalt);
AreEqual(testInput.Id、testOutput.Id);
AreEqual(testInput.Name、testOutput.Name);
AreEqual(testInput.When、testOutput.When);
}
}
}
问题似乎无穷无尽

  • 出于某种原因,我在“var resultString=reader.ReadToEnd();”行上的decrypt方法中得到了json输出中似乎是中文字符的内容
  • 每次运行时,输出都不同
  • 当我不附加 调试器,但在反序列化json失败时引发异常
  • 不确定为什么它从错误的配置值读取(但这可能与加密不起作用无关)

我做错了什么?

好吧,我发现这基本上是编码问题,所以进一步,我从@jbtule(谢谢James)over@

抓到“Aesenthnmac”课程后,我就可以写这个了

public class AesCrypto<T> : ICrypto<T>
{
    public string Encrypt(T source, string key)
    {
        var e = Encoding.UTF8;
        var rawData = e.GetBytes(JsonConvert.SerializeObject(source));
        var cipherData = AESThenHMAC.SimpleEncryptWithPassword(rawData, key);
        return Convert.ToBase64String(cipherData);
    }

    public T Decrypt(string source, string key)
    {
        var e = Encoding.UTF8;
        var decryptedBytes = AESThenHMAC.SimpleDecryptWithPassword(Convert.FromBase64String(source), key);
        return JsonConvert.DeserializeObject<T>(e.GetString(decryptedBytes));
    }
}
公共类AesCrypto:ICrypto
{
公共字符串加密(T源,字符串密钥)
{
var e=Encoding.UTF8;
var rawData=e.GetBytes(JsonConvert.SerializeObject(源));
var cipherData=AESThenHMAC.SimpleEncryptWithPassword(原始数据,密钥);
返回Convert.tobase64字符串(cipherData);
}
公共T解密(字符串源、字符串密钥)
{
var e=Encoding.UTF8;
var decryptedBytes=aessenhmac.SimpleDecryptWithPassword(Convert.FromBase64String(源),key);
返回JsonConvert.DeserializeObject(e.GetString(decryptedBytes));
}
}

。。。它完美地通过了上面的单元测试:)

好的,我发现这基本上是编码问题,所以进一步,我从@jbtule(感谢James)的示例中获取了代码