Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save viruseg/d30cdca9df952c798e4da56e1b01428f to your computer and use it in GitHub Desktop.
Save viruseg/d30cdca9df952c798e4da56e1b01428f to your computer and use it in GitHub Desktop.
Encrypting & Decrypting (file, string, byte array) in C#, NET 8.0 and Unity
//The code is based on the code from here: https://stackoverflow.com/a/10177020/1221983
//And adapted for NET 8 and Unity.
//Reduced memory consumption.
//Added possibility to encrypt byte arrays, not only strings.
//You can also attach custom data that will not be encrypted.
var password = "nso%dhfkl$siohf";
var str = "This is test string!№;%^#@^&";
var bytesArr = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 128, 129, 130, 250, 255 };
var strE = Encryptor.Encrypt(str, password, "custom data");
var bytesArrE = Encryptor.Encrypt(bytesArr, password, new byte[] { 9, 65, 14 });
{
var customData = Encryptor.GetCustomData(strE);
var size = Encryptor.GetDataSize(strE);
}
{
var customData = Encryptor.GetCustomData(bytesArrE);
var size = Encryptor.GetDataSize(bytesArrE);
}
{
var strD = Encryptor.Decrypt(strE, password, out var customData);
var bytesArrD = Encryptor.Decrypt(bytesArrE, password, out var customDataArr);
}
{
var fileBytes = File.ReadAllBytes(@"F:\Test.jpg");
var fileBytesE = Encryptor.Encrypt(fileBytes, password, null);
File.WriteAllBytes(@"F:\Encrypted.enc", fileBytesE);
}
{
var fileBytes = File.ReadAllBytes(@"F:\Encrypted.enc");
var fileBytesD = Encryptor.Decrypt(fileBytes, password, out _);
File.WriteAllBytes(@"F:\Decrypted.jpg", fileBytesD);
}
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace EncryptorLib
{
public static unsafe class Encryptor
{
private const int BUFFER_SIZE = 128 / 8;
private const int DERIVATION_ITERATIONS = 1000;
public static string Encrypt(string text, string password, string? customData)
{
var textAsBytes = Encoding.Default.GetBytes(text);
var customDataAsBytes = customData != null ? Encoding.Default.GetBytes(customData) : Array.Empty<byte>();
var cipherTextBytes = Encrypt(textAsBytes, password, customDataAsBytes);
return Convert.ToBase64String(cipherTextBytes);
}
public static byte[] Encrypt(byte[] bytes, string password, byte[] customData)
{
return Encrypt(new ReadOnlySpan<byte>(bytes), password, new ReadOnlySpan<byte>(customData));
}
public static byte[] Encrypt(ReadOnlySpan<byte> bytes, string password, ReadOnlySpan<byte> customData)
{
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
using var passwordKey = new Rfc2898DeriveBytes(password, saltStringBytes, DERIVATION_ITERATIONS, HashAlgorithmName.SHA512);
var keyBytes = passwordKey.GetBytes(BUFFER_SIZE);
using var symmetricKey = Aes.Create();
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes);
using var memoryStream = new MemoryStream();
using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(bytes);
cryptoStream.FlushFinalBlock();
var customDataSize = customData.Length;
var result = new byte[sizeof(int) + sizeof(int) + customDataSize + BUFFER_SIZE + BUFFER_SIZE + memoryStream.Length];
fixed (byte* customDataArrPtr = customData)
fixed (byte* saltStringBytesArrPtr = saltStringBytes)
fixed (byte* ivStringBytesArrPtr = ivStringBytes)
fixed (byte* resultPtr = result)
{
var sizePtr = (int*) resultPtr;
var customDataSizePtr = (int*) ((byte*) sizePtr + sizeof(int));
var customDataPtr = (byte*) customDataSizePtr + sizeof(int);
var saltStringBytesPtr = customDataPtr + customDataSize;
var ivStringBytesPtr = saltStringBytesPtr + BUFFER_SIZE;
*sizePtr = bytes.Length;
*customDataSizePtr = customDataSize;
Buffer.MemoryCopy(customDataArrPtr, customDataPtr, customDataSize, customDataSize);
Buffer.MemoryCopy(saltStringBytesArrPtr, saltStringBytesPtr, BUFFER_SIZE, BUFFER_SIZE);
Buffer.MemoryCopy(ivStringBytesArrPtr, ivStringBytesPtr, BUFFER_SIZE, BUFFER_SIZE);
memoryStream.Position = 0;
#if UNITY_2021_3_OR_NEWER
memoryStream.Read(result, sizeof(int) + sizeof(int) + customDataSize + BUFFER_SIZE + BUFFER_SIZE, (int) memoryStream.Length);
#else
memoryStream.ReadExactly(result, sizeof(int) + sizeof(int) + customDataSize + BUFFER_SIZE + BUFFER_SIZE, (int) memoryStream.Length);
#endif
}
return result;
}
public static string GetCustomData(string cipherText)
{
var encryptedBytes = Convert.FromBase64String(cipherText);
encryptedBytes = GetCustomData(encryptedBytes);
return Encoding.Default.GetString(encryptedBytes);
}
public static byte[] GetCustomData(byte[] encryptedBytes)
{
return GetCustomData(new ReadOnlySpan<byte>(encryptedBytes));
}
public static byte[] GetCustomData(ReadOnlySpan<byte> encryptedBytes)
{
fixed (byte* encryptedBytesPtr = encryptedBytes)
{
var sizePtr = (int*) encryptedBytesPtr;
var customDataSizePtr = (int*) ((byte*) sizePtr + sizeof(int));
var customDataPtr = (byte*) customDataSizePtr + sizeof(int);
var customData = *customDataSizePtr == 0 ? Array.Empty<byte>() : new byte[*customDataSizePtr];
fixed (byte* customDataArrPtr = customData)
Buffer.MemoryCopy(customDataPtr, customDataArrPtr, *customDataSizePtr, *customDataSizePtr);
return customData;
}
}
/// <summary>
/// Source string size
/// </summary>
public static int GetDataSize(string cipherText)
{
var encryptedBytes = Convert.FromBase64String(cipherText);
return GetDataSize(encryptedBytes) / sizeof(char);
}
/// <summary>
/// Length of the source array
/// </summary>
public static int GetDataSize(byte[] encryptedBytes)
{
return GetDataSize(new ReadOnlySpan<byte>(encryptedBytes));
}
/// <summary>
/// Length of the source array
/// </summary>
public static int GetDataSize(ReadOnlySpan<byte> encryptedBytes)
{
fixed (byte* encryptedBytesPtr = encryptedBytes)
{
var sizePtr = (int*) encryptedBytesPtr;
return *sizePtr;
}
}
public static string Decrypt(string cipherText, string password, out string customData)
{
var encryptedBytes = Convert.FromBase64String(cipherText);
encryptedBytes = Decrypt(encryptedBytes, password, out var customDataBytes);
customData = Encoding.Default.GetString(customDataBytes);
return Encoding.Default.GetString(encryptedBytes);
}
public static byte[] Decrypt(byte[] encryptedBytes, string password, out byte[] customData)
{
return Decrypt(new ReadOnlySpan<byte>(encryptedBytes), password, out customData);
}
public static byte[] Decrypt(ReadOnlySpan<byte> encryptedBytes, string password, out byte[] customData)
{
int size;
var saltStringBytes = new byte[BUFFER_SIZE];
var ivStringBytes = new byte[BUFFER_SIZE];
ReadOnlySpan<byte> encryptedBytesASpan;
fixed (byte* saltStringBytesArrPtr = saltStringBytes)
fixed (byte* ivStringBytesArrPtr = ivStringBytes)
fixed (byte* encryptedBytesPtr = encryptedBytes)
{
var sizePtr = (int*) encryptedBytesPtr;
var customDataSizePtr = (int*) ((byte*) sizePtr + sizeof(int));
var customDataPtr = (byte*) customDataSizePtr + sizeof(int);
var saltStringBytesPtr = customDataPtr + *customDataSizePtr;
var ivStringBytesPtr = saltStringBytesPtr + BUFFER_SIZE;
size = *sizePtr;
Buffer.MemoryCopy(saltStringBytesPtr, saltStringBytesArrPtr, BUFFER_SIZE, BUFFER_SIZE);
Buffer.MemoryCopy(ivStringBytesPtr, ivStringBytesArrPtr, BUFFER_SIZE, BUFFER_SIZE);
customData = *customDataSizePtr == 0 ? Array.Empty<byte>() : new byte[*customDataSizePtr];
fixed (byte* customDataArrPtr = customData)
Buffer.MemoryCopy(customDataPtr, customDataArrPtr, *customDataSizePtr, *customDataSizePtr);
var dataOffset = sizeof(int) + sizeof(int) + customData.Length + BUFFER_SIZE + BUFFER_SIZE;
encryptedBytesASpan = new ReadOnlySpan<byte>(encryptedBytesPtr + dataOffset, encryptedBytes.Length - dataOffset);
}
using var passwordKey = new Rfc2898DeriveBytes(password, saltStringBytes, DERIVATION_ITERATIONS, HashAlgorithmName.SHA512);
var keyBytes = passwordKey.GetBytes(BUFFER_SIZE);
using var symmetricKey = Aes.Create();
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes);
using var memoryStream = new MemoryStream(encryptedBytesASpan.Length);
memoryStream.Write(encryptedBytesASpan);
memoryStream.Position = 0;
using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
var result = new byte[size];
try
{
#if UNITY_2021_3_OR_NEWER
cryptoStream.Read(result, 0, size);
#else
cryptoStream.ReadExactly(result, 0, size);
#endif
}
catch (Exception e)
{
// ignored
}
return result;
}
private static byte[] Generate256BitsOfRandomEntropy()
{
#if UNITY_2021_3_OR_NEWER
var randomBytes = new byte[BUFFER_SIZE];
using var rngCsp = new RNGCryptoServiceProvider();
rngCsp.GetBytes(randomBytes);
return randomBytes;
#else
return RandomNumberGenerator.GetBytes(BUFFER_SIZE);
#endif
}
}
}
@muhammetozeski
Copy link

thank you so much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment