C# 发送 byte[] 到服务器,解密内容并将其作为 byte[] 发送回客户端
C# Send byte[] to server, decrypt content and send it as byte[] back to client
我的问题如下:
我有一个 client/server 应用程序通过套接字连接。我客户的任务是按字节向服务器发送文件。服务器获取字节,解密它们,将其发送回客户端,然后他将它们写入磁盘上的新文件中。
我每次都会在这行代码处遇到服务器端异常(System.Security.Cryptography.Exception:填充无效且无法删除):plaintext = sr.ReadToEnd();
有人可以帮我解决我的问题吗?
这里是解密代码:
public byte[] Dec(byte[] content, byte[] Key, byte[] IV, int fileLength, string filepath, int chunkSize, int bytesToRead)
{
byte[] contentDec;
string plaintext = null;
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream ms = new MemoryStream(content))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plaintext = sr.ReadToEnd();
cs.FlushFinalBlock();
}
contentDec = encoding.GetBytes(plaintext);
}
}
}
return contentDec;
}
这是我的加密代码:
public byte[] Enc(byte[] content,byte[] Key, byte[] IV, int fileLength,string filepath, int chunkSize, int bytesToRead)
{
byte[] contentEnc;
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(content);
}
contentEnc = ms.ToArray();
}
}
}
return contentEnc;
}
在客户端我调用这样的加密方法
int chunkSize = 1024;
byte[] chunk = new byte[chunkSize];
using (FileStream fileReader = new FileStream(plainPath, FileMode.Open, FileAccess.Read))
using (FileStream filewriter = new FileStream(pathEncrypt, FileMode.Create, FileAccess.ReadWrite))
using (BinaryReader binaryReader = new BinaryReader(fileReader))
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
myRijndael.GenerateKey();
myRijndael.GenerateIV();
Key = myRijndael.Key;
IV = myRijndael.IV;
int bytesToRead = (int)fileReader.Length;
do
{
chunk = service.Enc(binaryReader.ReadBytes(chunkSize), Key, IV,(int)fileReader.Length,
fileReader.Name, chunkSize, bytesToRead);
filewriter.Write(chunk, 0, chunk.Length);
bytesToRead -= chunkSize;
} while (bytesToRead > 0);
}
Key 和 IV 声明为私有字节[]
在客户端我调用这样的解密方法
int chunkSize = 1024;
byte[] chunk = new byte[chunkSize];
using (FileStream fileReader = new FileStream(pathEncrypt, FileMode.Open, FileAccess.Read))
using (FileStream filewriter = new FileStream(pathDecrypt, FileMode.Create, FileAccess.ReadWrite))
using (BinaryReader binaryReader = new BinaryReader(fileReader))
{
int bytesToRead = (int)fileReader.Length;
do
{
chunk = service.Dec(binaryReader.ReadBytes(chunkSize), Key, IV, (int)fileReader.Length,
fileReader.Name, chunkSize, bytesToRead);
filewriter.Write(chunk, 0, chunk.Length);
bytesToRead -= chunkSize;
} while (bytesToRead > 0);
}
编辑:这是我在客户端和服务器之间建立的连接。
服务器:
var host = new ServiceHost(类型(服务),
新 Uri("net.pipe://localhost"));
host.AddServiceEndpoint(typeof(TiService),
new NetNamedPipeBinding(), "TestService");
host.Open();
Console.WriteLine("Server connection established...");
Console.ReadKey();
客户:
var callback = new Callback();
var context = new InstanceContext(callback);
var pipeFactory =
new DuplexChannelFactory<TiService>(context,
new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/TestService"));
service = pipeFactory.CreateChannel();
service.Connect();
您的问题始于在加密中使用 StreamWriter。它用于编写文本文件,而不是任意文件。当您调用 sw.Write(content) 时,它只是调用 content.ToString(),即 return "System.Byte[]",而不是您可能期望的数组的每个字节。要修复它,只需编写 CryptoStream,无需使用 StreamWriter,如下所示:
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(content, 0, content.Length);
}
contentEnc = ms.ToArray();
}
}
您可能注意到我使用的是 AesCng 而不是 RijndaelManaged。为什么?因为它在我的测试中要快得多,除非你真的需要非标准块,否则使用 RijndaelManaged 没有任何好处。另外,我使用无参数的 CreateEncryptor,因为您已经在前面的行中设置了密钥和 IV。
解密同理。您不应该将它们视为文本,因此:
var buffer = new byte[content.Length]; //at first its size is actual size+padding
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(content))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
var actualSize = cs.Read(buffer, 0, content.Length);
//we write the decrypted content to the buffer, and get the actual size
Array.Resize(ref buffer, actualSize);
//then we resize the buffer to the actual size
}
}
}
return buffer;
此外,您对 Enc 和 Dec 的使用过于复杂。它已经能够自己处理整个文件。所以要加密文件,只需使用
var original = File.ReadAllBytes("originalPath");
var enc = Enc(original, rM.Key, rM.IV);
File.WriteAllBytes("encryptedPath", enc);
要解密文件,只需使用
var enc = File.ReadAllBytes("encryptedPath");
var dec = Dec(enc, rM.Key, rM.IV);
File.WriteAllBytes("decryptedPath", dec);
如您所见,我在 Enc 和 Dec 上丢弃了 fileLength、filepath、chunkSize 和 bytesToRead,因为您当前的代码实际上并没有使用它们。我已经在 ASCII、Unicode 和 UTF-8 上尝试了带有短文本文件的代码,以及带有大型二进制文件的代码,所有这些都在最终解密的文件上使用相同的哈希值成功加密和解密。
编辑:
将代码变成直接的文件流写入,实际上让一切变得如此简单。
public static void Transform(string source, string target, ICryptoTransform transf)
{
var bufferSize = 65536;
var buffer = new byte[bufferSize];
using (var sourceStream = new FileStream(source, FileMode.Open))
{
using (var targetStream = new FileStream(target, FileMode.OpenOrCreate))
{
using (CryptoStream cs = new CryptoStream(targetStream, transf, CryptoStreamMode.Write))
{
var bytesRead = 0;
do
{
bytesRead = sourceStream.Read(buffer, 0, bufferSize);
cs.Write(buffer, 0, bytesRead);
} while (bytesRead != 0);
}
}
}
}
public static void Enc(string source, byte[] Key, byte[] IV, string target)
{
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor();
Transform(source, target, encryptor);
}
}
public static void Dec(string source, byte[] Key, byte[] IV, string target)
{
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor();
Transform(source, target, decryptor);
}
}
用法是:
Enc(@"originalPath", key, iv, @"encryptedPath");
Dec(@"encrypedPath", key, iv, @"decryptedPath");
我的问题如下:
我有一个 client/server 应用程序通过套接字连接。我客户的任务是按字节向服务器发送文件。服务器获取字节,解密它们,将其发送回客户端,然后他将它们写入磁盘上的新文件中。
我每次都会在这行代码处遇到服务器端异常(System.Security.Cryptography.Exception:填充无效且无法删除):plaintext = sr.ReadToEnd();
有人可以帮我解决我的问题吗?
这里是解密代码:
public byte[] Dec(byte[] content, byte[] Key, byte[] IV, int fileLength, string filepath, int chunkSize, int bytesToRead)
{
byte[] contentDec;
string plaintext = null;
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream ms = new MemoryStream(content))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plaintext = sr.ReadToEnd();
cs.FlushFinalBlock();
}
contentDec = encoding.GetBytes(plaintext);
}
}
}
return contentDec;
}
这是我的加密代码:
public byte[] Enc(byte[] content,byte[] Key, byte[] IV, int fileLength,string filepath, int chunkSize, int bytesToRead)
{
byte[] contentEnc;
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(content);
}
contentEnc = ms.ToArray();
}
}
}
return contentEnc;
}
在客户端我调用这样的加密方法
int chunkSize = 1024;
byte[] chunk = new byte[chunkSize];
using (FileStream fileReader = new FileStream(plainPath, FileMode.Open, FileAccess.Read))
using (FileStream filewriter = new FileStream(pathEncrypt, FileMode.Create, FileAccess.ReadWrite))
using (BinaryReader binaryReader = new BinaryReader(fileReader))
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
myRijndael.GenerateKey();
myRijndael.GenerateIV();
Key = myRijndael.Key;
IV = myRijndael.IV;
int bytesToRead = (int)fileReader.Length;
do
{
chunk = service.Enc(binaryReader.ReadBytes(chunkSize), Key, IV,(int)fileReader.Length,
fileReader.Name, chunkSize, bytesToRead);
filewriter.Write(chunk, 0, chunk.Length);
bytesToRead -= chunkSize;
} while (bytesToRead > 0);
}
Key 和 IV 声明为私有字节[]
在客户端我调用这样的解密方法
int chunkSize = 1024;
byte[] chunk = new byte[chunkSize];
using (FileStream fileReader = new FileStream(pathEncrypt, FileMode.Open, FileAccess.Read))
using (FileStream filewriter = new FileStream(pathDecrypt, FileMode.Create, FileAccess.ReadWrite))
using (BinaryReader binaryReader = new BinaryReader(fileReader))
{
int bytesToRead = (int)fileReader.Length;
do
{
chunk = service.Dec(binaryReader.ReadBytes(chunkSize), Key, IV, (int)fileReader.Length,
fileReader.Name, chunkSize, bytesToRead);
filewriter.Write(chunk, 0, chunk.Length);
bytesToRead -= chunkSize;
} while (bytesToRead > 0);
}
编辑:这是我在客户端和服务器之间建立的连接。
服务器: var host = new ServiceHost(类型(服务), 新 Uri("net.pipe://localhost"));
host.AddServiceEndpoint(typeof(TiService),
new NetNamedPipeBinding(), "TestService");
host.Open();
Console.WriteLine("Server connection established...");
Console.ReadKey();
客户:
var callback = new Callback();
var context = new InstanceContext(callback);
var pipeFactory =
new DuplexChannelFactory<TiService>(context,
new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/TestService"));
service = pipeFactory.CreateChannel();
service.Connect();
您的问题始于在加密中使用 StreamWriter。它用于编写文本文件,而不是任意文件。当您调用 sw.Write(content) 时,它只是调用 content.ToString(),即 return "System.Byte[]",而不是您可能期望的数组的每个字节。要修复它,只需编写 CryptoStream,无需使用 StreamWriter,如下所示:
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(content, 0, content.Length);
}
contentEnc = ms.ToArray();
}
}
您可能注意到我使用的是 AesCng 而不是 RijndaelManaged。为什么?因为它在我的测试中要快得多,除非你真的需要非标准块,否则使用 RijndaelManaged 没有任何好处。另外,我使用无参数的 CreateEncryptor,因为您已经在前面的行中设置了密钥和 IV。
解密同理。您不应该将它们视为文本,因此:
var buffer = new byte[content.Length]; //at first its size is actual size+padding
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(content))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
var actualSize = cs.Read(buffer, 0, content.Length);
//we write the decrypted content to the buffer, and get the actual size
Array.Resize(ref buffer, actualSize);
//then we resize the buffer to the actual size
}
}
}
return buffer;
此外,您对 Enc 和 Dec 的使用过于复杂。它已经能够自己处理整个文件。所以要加密文件,只需使用
var original = File.ReadAllBytes("originalPath");
var enc = Enc(original, rM.Key, rM.IV);
File.WriteAllBytes("encryptedPath", enc);
要解密文件,只需使用
var enc = File.ReadAllBytes("encryptedPath");
var dec = Dec(enc, rM.Key, rM.IV);
File.WriteAllBytes("decryptedPath", dec);
如您所见,我在 Enc 和 Dec 上丢弃了 fileLength、filepath、chunkSize 和 bytesToRead,因为您当前的代码实际上并没有使用它们。我已经在 ASCII、Unicode 和 UTF-8 上尝试了带有短文本文件的代码,以及带有大型二进制文件的代码,所有这些都在最终解密的文件上使用相同的哈希值成功加密和解密。
编辑:
将代码变成直接的文件流写入,实际上让一切变得如此简单。
public static void Transform(string source, string target, ICryptoTransform transf)
{
var bufferSize = 65536;
var buffer = new byte[bufferSize];
using (var sourceStream = new FileStream(source, FileMode.Open))
{
using (var targetStream = new FileStream(target, FileMode.OpenOrCreate))
{
using (CryptoStream cs = new CryptoStream(targetStream, transf, CryptoStreamMode.Write))
{
var bytesRead = 0;
do
{
bytesRead = sourceStream.Read(buffer, 0, bufferSize);
cs.Write(buffer, 0, bytesRead);
} while (bytesRead != 0);
}
}
}
}
public static void Enc(string source, byte[] Key, byte[] IV, string target)
{
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor();
Transform(source, target, encryptor);
}
}
public static void Dec(string source, byte[] Key, byte[] IV, string target)
{
using (var rijAlg = new AesCng())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor();
Transform(source, target, decryptor);
}
}
用法是:
Enc(@"originalPath", key, iv, @"encryptedPath");
Dec(@"encrypedPath", key, iv, @"decryptedPath");