我想使用密钥加密.NET Core中的字符串。我有一个客户端/服务器方案,并希望加密客户端上的字符串,将其发送到服务器并解密它。
由于.NET Core仍处于早期阶段(例如Rijndael尚未推出),我的选择是什么?
答案 0 :(得分:27)
你真的不应该在.NET中使用Rijndael / RijndaelManaged。如果您使用的BlockSize值为128(默认值),那么您使用的是AES explained in a similar question。
.NET Core中提供的对称加密选项包括:
对于非对称加密
特别是在.NET Core上,工厂是最好的方法,因为它们会返回一个对象,该对象可以在当前正在执行的操作系统上运行。例如,RSACng是一种公共类型,但仅适用于Windows;和RSAOpenSsl是公共类型,但仅在Linux和macOS上受支持。
答案 1 :(得分:18)
已经有了答案,但我认为我们可以提供更简单的解决方案。
如果您只是想保护您的数据,那么在.NET Core中就可以实现这一功能,让您免于加密的麻烦; DataProtectionProvider
。
在Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection(); //Add this
[..]
services.AddMvc();
}
如果您愿意,可以指定用于加密和验证的算法(使用Microsoft.AspNetCore.DataProtection
),如下所示:
services.AddDataProtection()
.UseCryptographicAlgorithms(new AuthenticatedEncryptionSettings()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_GCM,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
然后使用服务加密/解密:
public class CipherService : ICipherService
{
private readonly IDataProtectionProvider _dataProtectionProvider;
private const string Key = "my-very-long-key-of-no-exact-size";
public CipherService(IDataProtectionProvider dataProtectionProvider)
{
_dataProtectionProvider = dataProtectionProvider;
}
public string Encrypt(string input)
{
var protector = _dataProtectionProvider.CreateProtector(Key);
return protector.Protect(input);
}
public string Decrypt(string cipherText)
{
var protector = _dataProtectionProvider.CreateProtector(Key);
return protector.Unprotect(cipherText);
}
}
修改强> 正如下面的评论中提到的那样,理解使用像这样的DataProtectionProvider只能在存储在本地磁盘上的密钥的同一台机器上工作是个好主意。
答案 2 :(得分:14)
以下是一个简单的样本没有身份验证:
var text = "Hello World";
var buffer = Encoding.UTF8.GetBytes(text);
var iv = GetRandomData(128);
var keyAes = GetRandomData(256);
byte[] result;
using (var aes = Aes.Create())
{
aes.Key = keyAes;
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, encryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(buffer))
{
plainStream.CopyTo(aesStream);
}
result = resultStream.ToArray();
}
}
用于密钥生成:
private static byte[] GetRandomData(int bits)
{
var result = new byte[bits / 8];
RandomNumberGenerator.Create().GetBytes(result);
return result;
}
答案 3 :(得分:5)
我有一种不同的方法,我想用一个密钥加密一个字符串,然后得到一个加扰的字符串,我可以用相同的密钥再次解密它。请参阅以下扩展方法:
public static string Encrypt(this string text, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(text))
throw new ArgumentException("The text must have valid value.", nameof(text));
var buffer = Encoding.UTF8.GetBytes(text);
var hash = new SHA512CryptoServiceProvider();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, encryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(buffer))
{
plainStream.CopyTo(aesStream);
}
var result = resultStream.ToArray();
var combined = new byte[aes.IV.Length + result.Length];
Array.ConstrainedCopy(aes.IV, 0, combined, 0, aes.IV.Length);
Array.ConstrainedCopy(result, 0, combined, aes.IV.Length, result.Length);
return Convert.ToBase64String(combined);
}
}
}
public static string Decrypt(this string encryptedText, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(encryptedText))
throw new ArgumentException("The encrypted text must have valid value.", nameof(encryptedText));
var combined = Convert.FromBase64String(encryptedText);
var buffer = new byte[combined.Length];
var hash = new SHA512CryptoServiceProvider();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
var iv = new byte[aes.IV.Length];
var ciphertext = new byte[buffer.Length - iv.Length];
Array.ConstrainedCopy(combined, 0, iv, 0, iv.Length);
Array.ConstrainedCopy(combined, iv.Length, ciphertext, 0, ciphertext.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, decryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(ciphertext))
{
plainStream.CopyTo(aesStream);
}
return Encoding.UTF8.GetString(resultStream.ToArray());
}
}
}
答案 4 :(得分:3)
默认情况下,ASP.NET Core应用程序启用了数据保护系统。除非您想重新配置默认密钥存储位置或密钥的生存时间,否则您甚至不需要在StartUp方法中执行任何操作。在这种情况下,您需要在ConfigureServices方法中执行以下操作:
services.ConfigureDataProtection(dp =>
{
dp.PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys"));
dp.SetDefaultKeyLifetime(TimeSpan.FromDays(14));
});
由于数据保护系统默认位于应用程序的服务集合中,因此可以通过依赖注入使其可用。以下是如何将IDataProtectionProvider注入控制器,然后使用它在控制器的构造函数中创建IDataProtector的实例:
public class HomeController : Controller
{
IDataProtector _protector;
public HomeController(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector(GetType().FullName);
}
}
然后,您可以调用保护程序来加密内容,如下所示:
public IActionResult Index()
{
var model = _service.GetAll().Select(c => new ContractViewModel {
Id = _protector.Protect(c.Id.ToString()),
Name = c.Name }).ToList();
return View(model);
}
我希望这会有所帮助:)
答案 5 :(得分:0)
您可以使用System.Security.Cryptography
string keyString = "encrypt123456789";
var key = Encoding.UTF8.GetBytes(keyString);//16 bit or 32 bit key string
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var iv = aesAlg.IV;
var decryptedContent = msEncrypt.ToArray();
var result = new byte[iv.Length + decryptedContent.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);
return Convert.ToBase64String(result);
}
}
}
用于解密
var fullCipher = Convert.FromBase64String(cipherText);
var iv = new byte[16];
var cipher = new byte[16];
Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, iv.Length);
var key = Encoding.UTF8.GetBytes(keyString);//same key string
using (var aesAlg = Aes.Create())
{
using (var decryptor = aesAlg.CreateDecryptor(key, iv))
{
string result;
using (var msDecrypt = new MemoryStream(cipher))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
return result;
}
}
答案 6 :(得分:0)
对于使用@sundarraj 发布的上述解决方案并获得 System.Security.Cryptography.CryptographicException 的用户:“填充无效且无法删除。”解密时出错,使用这些代码行修复解密:
var cipher = new byte[full.Length - iv.Length];
Buffer.BlockCopy(full, 0, iv, 0, iv.Length);
Buffer.BlockCopy(full, iv.Length, cipher, 0, cipher.Length);