我没有安全背景,而且我处理密码学的最多是使用从网上获取的代码段。经过一些阅读后,很明显我需要更好地理解这些算法的工作原理。所以请忍受任何天真。
我试图实现一些期望完全初始化SymmetricAlgorithm
实例的通用/抽象方法。据我所知,转换方法很好,而调用代码却没有。我得到以下例外:
The input data is not a complete block.
Length of the data to decrypt is invalid.
USAGE:
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
private static void Main (string [] args)
{
var cycled = "";
Exception exception = null;
var output = new byte [] { };
var encoding = Encoding.UTF8;
var source = "Dude, where's my car?!";
var algorithm = RijndaelManaged.Create();
algorithm.GenerateIV();
algorithm.GenerateKey();
algorithm.Mode = CipherMode.CBC; // CipherMode.OFB;
// I've tried multiple combinations of algorithms and modes, which all result in exceptions.
if (SecurityUtilities.Encrypt(source, encoding, algorithm, out output, out exception))
{
if (SecurityUtilities.Decrypt(output, encoding, algorithm, out cycled, out exception))
{
var r = cycled == source;
Console.Write("{0} = {1} == {2}.", r, source, cycled);
}
else
{
Console.Write(exception);
}
}
else
{
Console.Write(exception);
}
Console.ReadKey();
}
}
通用代码:
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public static class SecurityUtilities
{
public static bool Encrypt (string source, Encoding encoding, SymmetricAlgorithm algorithm, out string output, out Exception exception)
{
var result = false;
output = "";
exception = null;
if (source == null) { throw (new ArgumentNullException("source")); }
if (encoding == null) { throw (new ArgumentNullException("encoding")); }
if (algorithm == null) { throw (new ArgumentNullException("algorithm")); }
try
{
var bytesEncrypted = new byte [] { };
var bytesSource = encoding.GetBytes(source);
if (SecurityUtilities.Encrypt(bytesSource, algorithm, out bytesEncrypted, out exception))
{
output = encoding.GetString(bytesEncrypted);
result = true;
}
}
catch (Exception e)
{
exception = e;
}
return (result);
}
public static bool Encrypt (string source, Encoding encoding, SymmetricAlgorithm algorithm, out byte [] output, out Exception exception)
{
var result = false;
output = null;
exception = null;
if (source == null) { throw (new ArgumentNullException("source")); }
if (encoding == null) { throw (new ArgumentNullException("encoding")); }
if (algorithm == null) { throw (new ArgumentNullException("algorithm")); }
try
{
var bytesEncrypted = new byte [] { };
var bytesSource = encoding.GetBytes(source);
if (SecurityUtilities.Encrypt(bytesSource, algorithm, out bytesEncrypted, out exception))
{
output = bytesEncrypted;
result = true;
}
}
catch (Exception e)
{
exception = e;
}
return (result);
}
public static bool Encrypt (byte [] source, SymmetricAlgorithm algorithm, out byte [] output, out Exception exception)
{
var result = false;
output = null;
exception = null;
if (source == null) { throw (new ArgumentNullException("source")); }
if (algorithm == null) { throw (new ArgumentNullException("algorithm")); }
try
{
using (var memoryStream = new MemoryStream())
{
using (var transform = algorithm.CreateEncryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(source, 0, source.Length);
}
}
output = memoryStream.ToArray();
}
result = true;
}
catch (Exception e)
{
exception = e;
}
return (result);
}
public static bool Decrypt (string source, Encoding encoding, SymmetricAlgorithm algorithm, out string output, out Exception exception)
{
var result = false;
output = "";
exception = null;
if (source == null) { throw (new ArgumentNullException("source")); }
if (encoding == null) { throw (new ArgumentNullException("encoding")); }
if (algorithm == null) { throw (new ArgumentNullException("algorithm")); }
try
{
var bytesDecrypted = new byte [] { };
var bytesSource = encoding.GetBytes(source);
if (SecurityUtilities.Decrypt(bytesSource, algorithm, out bytesDecrypted, out exception))
{
output = encoding.GetString(bytesDecrypted);
result = true;
}
}
catch (Exception e)
{
exception = e;
}
return (result);
}
public static bool Decrypt (byte [] source, Encoding encoding, SymmetricAlgorithm algorithm, out string output, out Exception exception)
{
var result = false;
output = "";
exception = null;
if (source == null) { throw (new ArgumentNullException("source")); }
if (encoding == null) { throw (new ArgumentNullException("encoding")); }
if (algorithm == null) { throw (new ArgumentNullException("algorithm")); }
try
{
var bytesSource = source;
var bytesDecrypted = new byte [] { };
if (SecurityUtilities.Decrypt(bytesSource, algorithm, out bytesDecrypted, out exception))
{
output = encoding.GetString(bytesDecrypted);
result = true;
}
}
catch (Exception e)
{
exception = e;
}
return (result);
}
public static bool Decrypt (byte [] source, SymmetricAlgorithm algorithm, out byte [] output, out Exception exception)
{
var result = false;
output = null;
exception = null;
if (source == null) { throw (new ArgumentNullException("source")); }
if (algorithm == null) { throw (new ArgumentNullException("algorithm")); }
try
{
using (var memoryStream = new MemoryStream(source))
{
using (var transform = algorithm.CreateDecryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
{
cryptoStream.Read(source, 0, source.Length);
}
}
output = memoryStream.ToArray();
}
result = true;
}
catch (Exception e)
{
exception = e;
}
return (result);
}
}
核心问题是,Encrypt/Decrypt
方法中是否存在错误,如果没有,调用代码中所需的最小变化是什么?
答案 0 :(得分:2)
您提到的两个错误是同一问题的结果 - 许多对称算法是“分组密码”,即它们在特定大小的一个或多个块上工作。使用分组密码加密任意长度数据时,必须对其进行填充,使其长度为块大小的倍数,类似地,在解密时,必须删除填充以提供原始的未填充明文。
填充明文有多种不同的方法,使用的方法由Padding
的{{1}}属性决定。如果设置为SymmetricAlgorithm
,那么明文的长度必须是PaddingMode.None
的倍数。
如果我们假设BlockSize
实例设置为使用除SymmetricAlgorithm
以外的填充模式,则必须告诉算法您已完成加密并且应该添加填充(或者何时添加填充)解密你已经读过所有的密文,它应该删除填充)。使用None
时,这是使用CryptoStream
方法实现的,并且应该在将所有数据写入流后立即执行:
FlushFinalBlock()
在using (var memoryStream = new MemoryStream())
{
using (var transform = algorithm.CreateEncryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(source, 0, source.Length);
// Tell the CryptoStream we've written all the data and padding should be applied
cryptoStream.FlushFinalBlock();
}
}
output = memoryStream.ToArray();
}
中,Decrypt(byte[], SymmetricAlgorithm, ...)
使用memoryStream.ToArray()
作为output
,但memoryStream
包含密文,不生成的纯文本。您应该使用与加密相同的构造,除了使用transform.CreateDecryptor()
而不是transform.CreateEncryptor()
:
using (var memoryStream = new MemoryStream())
{
using (var transform = algorithm.CreateDecryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(source, 0, source.Length);
// Tell the CryptoStream we've written all the data and padding should be removed
cryptoStream.FlushFinalBlock();
}
}
output = memoryStream.ToArray();
}
最后,在您的Encrypt (string, Encoding, ...)
中,您使用二进制密文并使用encoding.GetString()
将其转换为字符串,这是无效的。如果您希望将二进制数据表示为字符串,则应使用类似Convert.ToBase64String()
的内容。同样在Decrypt(string, Encoding, ...)
中,您应该使用Convert.FromBase64String()
将传入的Base64编码密文字符串转换为解密所需的字节数组。