Skip to content

Instantly share code, notes, and snippets.

@electricessence
Last active February 26, 2023 08:29
Show Gist options
  • Save electricessence/16b1b2c52a93cff13a1c099cf0a8df5a to your computer and use it in GitHub Desktop.
Save electricessence/16b1b2c52a93cff13a1c099cf0a8df5a to your computer and use it in GitHub Desktop.
Some basic extensions for encrypting and decrypting JSON.
using System;
using System.Buffers;
using System.IO;
using System.Security.Cryptography;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
public static class SymmetricAlgorithmExtensions
{
class MemoryOwner : IMemoryOwner<byte>
{
private readonly Action? _onDispose;
public MemoryOwner(Memory<byte> Memory, Action? onDispose = null)
{
this.Memory = Memory;
this._onDispose = onDispose;
}
public Memory<byte> Memory { get; }
public void Dispose() => _onDispose?.Invoke();
}
public static async ValueTask<IMemoryOwner<byte>> EncryptAsync<T>(this SymmetricAlgorithm algo, T content, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
{
// Create a CryptoStream that writes to a new byte array
using var encryptedStream = new MemoryStream();
using var cs = new CryptoStream(encryptedStream, algo.CreateEncryptor(), CryptoStreamMode.Write);
// Serialize the content to JSON and write it to the CryptoStream
await JsonSerializer.SerializeAsync(cs, content, options, cancellationToken);
// Flush the CryptoStream to ensure all data is written
await cs.FlushFinalBlockAsync(cancellationToken);
// Return the encrypted data
var len = encryptedStream.Length;
if (len > int.MaxValue)
return new MemoryOwner(encryptedStream.ToArray());
var iLen = (int)encryptedStream.Length;
var mo = MemoryPool<byte>.Shared.Rent(iLen);
var buffer = mo.Memory;
await encryptedStream.ReadAsync(buffer, cancellationToken);
return new MemoryOwner(mo.Memory[..iLen], mo.Dispose);
}
public static async ValueTask<T?> DecryptAsync<T>(this SymmetricAlgorithm algo, Stream encryptedData, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
{
// Create a CryptoStream that reads from the encrypted data
using var cs = new CryptoStream(encryptedData, algo.CreateDecryptor(), CryptoStreamMode.Read);
// Deserialize the JSON from the CryptoStream and return the result
return await JsonSerializer.DeserializeAsync<T>(cs, options, cancellationToken);
}
public static async ValueTask<T?> DecryptAsync<T>(this SymmetricAlgorithm algo, byte[] encryptedData, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
{
using var encryptedStream = new MemoryStream(encryptedData);
return await algo.DecryptAsync<T>(encryptedStream, options, cancellationToken);
}
public static async ValueTask<T?> DecryptAsync<T>(this SymmetricAlgorithm algo, ReadOnlyMemory<byte> encryptedData, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
{
using var encryptedStream = new MemoryStream(encryptedData.Length);
await encryptedStream.WriteAsync(encryptedData, cancellationToken);
return await algo.DecryptAsync<T>(encryptedStream, options, cancellationToken);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment