Encrypt in C# and decrypt using Apex - c#

I want to pass a field in the query string from asp .net to Apex . I want to encrypt the value of the field and then pass it in the query string.
I am not sure how to approach this, are there any example code /links for same ?
Basically i want to encrypt in C# and decrypt using Apex.
In C#
static string key = "eU5WzoFgU4n8Apu5PYxcNGRZswRDZJWDEMdbQVU85gw=";
static string IV = "9ehY9gYtfIGlLRgwqg6F2g==";
static void Main(string[] args)
{
string source = "test";
string encrypted = EncryptStringToBytes_Aes(source, Convert.FromBase64String(key), Convert.FromBase64String(IV));
Console.ReadLine();
}
static string EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
string encrypted;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = Convert.ToBase64String(msEncrypt.ToArray());// ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
In Apex:
string cryptoKey='eU5WzoFgU4n8Apu5PYxcNGRZswRDZJWDEMdbQVU85gw=';
String det= System.currentPageReference().getParameters().get('Det');
Blob decryptedData = Crypto.decryptWithManagedIV('AES256', EncodingUtil.base64Decode(cryptoKey), EncodingUtil.base64Decode(det));
But this doesn't work, decryptedData.toString() does not come as 'test' (the original text). How do i decrypt it back ?

Why? All communication with SF is done via SSL anyway (https://salesforce.stackexchange.com/questions/8273/data-loader-cli-and-encryption for example).
If you absolutely have to - Apex has Crypto class that supports several algorithms. Hopefully you'll find matching ones in C# libraries.
There's also EncodingUtil class if you need to pass some binary data (as base64 for example).

The person who created the above example was VERY close. They probably would have realized it if they had encrypted a longer string than "Test" -- they just needed to insert some padding in front of what they were encrypting:
string spadding = "paddingxxxxxxxxx";
string source = spadding + "Opportunity0065";
-- Output would have been "Opportunity0065"

Related

how many bits generate aes.create() method in C#

I'm actually trying to make a secure file transfer program
and I would like to encrypt the sent file with the c# Aes.Create() method
but I wanted a AES-256 encryption and I'm not sure that the method does a 256 bits key
so I searched on Microsoft docs and many sketchy websites but I did find nothing.
So, how many bits generate Aes.Create()?
There is my code:
using System.Security.Cryptography;
namespace ConsoleApp1
{
internal class Program
{
public static void Main()
{
string original = File.ReadAllText(#"C:\SomePath");
// Create a new instance of the Aes
// class. This generates a new key and initialization
// vector (IV).
using (Aes myAes = Aes.Create())
{
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);
// Decrypt the bytes to a string.
string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
//Display the original data and the decrypted data.
Console.WriteLine("Original: {0}", original);
Console.WriteLine("Encrypted: {0}", System.Text.Encoding.Default.GetString(encrypted));
Console.WriteLine("Round Trip: {0}", roundtrip);
}
}
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the string used to hold
// the decrypted text.
string? plaintext = null;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
}
Yes, it's a modified version of Microsoft docs on Aes class: https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes
AES is a block cipher. That means it encrypts a fixed-sized block of clear text bytes into a same-sized block of cipher text bytes (hence the term block cipher). AES uses 128-bit blocks, i.e. 16 bytes long. This is irrespective of key size.
To be able to encrypt data of an arbitrary length, block ciphers use different modes of operation. Depending on the mode, padding is applied, an initialization vector may be used, salt prepended, and dependencies between blocks are employed.
Hence, as a result, the total size of encrypted data may be slightly bigger than the original size of the unencrypted data. The difference accounts for (at least) the length of the initialization vector and/or salt and any padding to the nearest multiple of the cipher's block size.

Encrypt and Decrypt a List of objects to byte[]

Current solution
So, I got a reliable AES ( Rijndael ) decryption and encryption methods for C# that can transform strings into an array of bytes ( byte[] ).
This is very useful, since it allows me to send and receive information secured by symmetric cryptography to the network.
Method for encryption ( string to byte[] ):
private byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
Method for decryption ( byte[] to string ):
private string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
string plaintext = null;
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
try
{
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
catch (Exception) //if decription doesnt work, do normal conversion
{
plaintext = Encoding.Unicode.GetString(cipherText);
}
}
return plaintext;
}
Problem
Now the necessity is to send and receive an entire List of custom objects over the network, while still using AES cryptography.
I need to encrypt an entire List< CustomObject> to byte[] and then later be able to decrypt it from byte[] to List< CustomObject>. Keep in mind that CustomObject is a example / Foo class.
But I don't know how I could modify these methods to use my List< CustomObject> instead of string. Nothing seems to work so far...
The List< CustomObject> I want to convert:
public List<CustomObject> ListCustomObjects = new List<CustomObject>();
And this is the CustomObject class inside the List<>:
public class CustomObject
{
public string NameFoo { get; set; }
public float NumberFoo { get; set; }
public bool ConditionFoo { get; set; }
}
Any solutions? Converting the list of objects to an encrypted byte array and be able to convert it back to list when needed is vital to this project.
How about you serialize it to a string first and then encrypt.
Then on the other side, decrypt and deserialize.
One format to represent an object as a string is JSON, and serialize/deserialize to JSON is build into dotnet since dotnet core 3. You can also use Newtonsoft.JSON (which used to be the default json serializer for c#)
So you can add the following code:
// using System.Text.Json;
// using System.Text.Json.Serialization;
private byte[] EncryptObjectToBytes<T>(T inputObject, byte[] Key, byte[] IV) {
var jsonString = JsonSerializer.Serialize(inputObject);
return EncryptStringToBytes(jsonString, Key, IV);
}
// and for the other side
private T DecryptObjectFromBytes<T>(byte[] cipherText, byte[] Key, byte[] IV) {
var jsonString = DecryptStringFromBytes(cipherText, Key, IV);
return JsonSerializer.Deserialize<T>(jsonString);
}
// Call the decryption like:
DecryptObjectFromBytes<YourClassName>(...);
// or for collections
DecryptObjectFromBytes<List<YourClassName>>(...);
You can even optimize the above code (because you can also (de)serialize to and from a byte array, which would make it even faster. And you can also use the async SerializeAsync and DeserializeAsync methods, but this code should get you started.
Other serialization methods
Using JSON is preferred (by me) because of the fact that you as a human can also read the data that is inside and it's less bytes then xml. You can also consider these formats.
MessagePack (serialization to a by humans none readable byte array) = way smaller for big objects.
XML this is what developers used to use before they invented json. Looks like html, but needs way more charcters to serialize a message.

The type name 'Create' does not exist for type Aes

I get an error in VS Code (running on Ubuntu) in the following code
class Program
{
public static void Main()
{
string originalText = "Test";
try
{
using (Aes myAes = new Aes.Create())
{
string encrypted = StringCipher.Encrypt(originalText, myAes);
string decrypted = StringCipher.Decrypt(encrypted, myAes);
Console.WriteLine(String.Format("Original {0}", originalText));
Console.WriteLine(String.Format("Encrypted {0}", encrypted));
Console.WriteLine(String.Format("Decrypted {0}", decrypted));
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
The VS Code intellisense is stating that type name 'Create' doesn't exist in type Aes.
Not sure if it's my bad coding but I am essentially following the guide here.
The only difference being I have implemented the encryption / decryption is being implemented in a separate code file here:
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Linq;
namespace EncryptionConsole
{
public static class StringCipher
{
public static string Encrypt(string plainText, Aes aes)
{
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes_Aes(plainText, aes.Key, aes.IV);
string result = Encoding.UTF8.GetString(encrypted);
return result;
}
public static string Decrypt(string cipherText, Aes aes)
{
byte[] cipherTextArray = Encoding.UTF8.GetBytes(cipherText);
// Decrypt the bytes to a string.
string roundtrip = DecryptStringFromBytes_Aes(cipherTextArray, aes.Key, aes.IV);
return roundtrip;
}
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
}
I appreciate there may be some glaring mistakes in the above code, this is not really of a concern as I am just exploring at this point.
Any help is greatly appreciated.
Aes.Create() is a static method. You do not need to instanciate it by using the keyword new. If you look at the docs (https://msdn.microsoft.com/en-us/library/bb351532(v=vs.110).aspx), you will see that this method actually returns a newly created AES object for you. This is referred to as "Static Factory Method" in programming terms, and is a common practice/pattern for initializing objects.
Basically calling Aes myAes = new Aes.Create() will try to instanciate the class Create from the namespace or class Aes which explains the error you experience. As stated above you can just use Aes myAes = Aes.Create().

How to create Aes 256bit Encryption with key in C# Windows Phone application?

I'm trying to create Aes 256bit Encryption with key in login screen. I need a large encrypted string as i'm using 256bit But it result in small encrypted string.I have checked many samples But all are for Windows desktop application not for windows Phone application. Please help regarding this.
This is my code
namespace SampleEncription
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
byte[] encryptedPassword;
// Create a new instance of the RijndaelManaged
// class. This generates a new key and initialization
// vector (IV).
using (var algorithm = new AesManaged())
{
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
// Encrypt the string to an array of bytes.
encryptedPassword = Cryptology.EncryptStringToBytes("Password", algorithm.Key, algorithm.IV);
//string chars = encryptedPassword.Aggregate(string.Empty, (current, b) => current + b.ToString());
string chars = System.Convert.ToBase64String(encryptedPassword);
Debug.WriteLine(chars);
}
}
}
}
one another class named cryptology:
namespace SampleEncription
{
class Cryptology
{
private const string Salt = "603deb1015ca71be2b73aef0857d7781";
private const int SizeOfBuffer = 1024 * 8;
internal static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
{
throw new ArgumentNullException("plainText");
}
if (key == null || key.Length <= 0)
{
throw new ArgumentNullException("key");
}
if (iv == null || iv.Length <= 0)
{
throw new ArgumentNullException("key");
}
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new AesManaged())
{
rijAlg.Key = key;
rijAlg.IV = iv;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
}
}
instead of
string chars = System.Convert.ToBase64String(encryptedPassword);
do this
Encoding.UTF8.GetString(encryptedPassword, 0, encryptedPassword.Length);
I think wp8 doesn't allow you to use System.Text.Encoding.Default.GetString, you can try to default it to UTF8, which i assume that the cipher text are all in latin characters..
You forgot to flush :)
You are calling encrypted = msEncrypt.ToArray(); before closing and therefore flushing the CryptoStream. As the final block needs to be padded, not all bytes will have been written. If you use a block cipher mode of encryption or an authenticated cipher, it is always required to flush. Only stream cipher modes of encryption may not require you to flush the stream as each bit can be encryption separately.
In your implementation, you should be able to just move msEncrypt.ToArray() below the using scope of the CryptoStream, if I'm not mistaken.

Why RijndaelManaged returns junk data after encryption?

I have taken the decrypt code from http://msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream.aspx and modified it as follows below. I have an encrypted example, and it works just fine while decoding. But when using the encryption function, it returns junk string with strange symbols. below are the functions of encrypt/decrypt.
An example of encrypted string "hey" : "???U?b???z?Y???"
When decoded again: "ûc{ÁpÅ`ñ""Â"
I'm using this code to convert the byte array to string:
private string ByteArrayToString(byte[] input)
{
ASCIIEncoding dec = new ASCIIEncoding();
return dec.GetString(input);
}
here are the encrypt/decrypt functions. the decryption function is working fine.
private string DecryptStringFromBytesAes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged aesAlg = new RijndaelManaged())
{
aesAlg.Key = Key;
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Mode = CipherMode.ECB;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
private byte[] EncryptStringToBytesAes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged aesAlg = new RijndaelManaged())
{
aesAlg.Key = Key;
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Mode = CipherMode.ECB;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
What you observe is the problem of mapping arbitrary bytes (in the range 0-255) to characters. Meaningful characters are only in the range 32-255 or even only 32-127 (ASCII). Values below 32 are the so-called non-printable characters and values above 127 are dependent on the character encoding you are using. That's why the crypted text looks like junk. Mast crypto-systems therefore transform the bytes into the sensible ASCII-range. One such algorithm is BASE64. So mangling the crypted bytes through BASE64 gives characters that are all printable and that will go without problems through e-mail. Before decrypting you then have to undo the BASE64 encoding.
Another way to make the encrypted result look better is to show the hexa-decimal representation of it. For example if you have a byte value of 15 you print 0F. You may use this to represent your byte array in hex:
private string ByteArrayToHexString(byte[] data)
{
return String.Concat(data.Select(b => b.ToString("x2")));
}
In order to have your output as a hexadecimal encoding of the data, follow the methods found here. I modified them slightly to be extension methods:
public static string ToHexString(this byte[] bytes)
{
return bytes == null ? string.Empty : BitConverter.ToString(bytes).Replace("-", string.Empty);
}
public static byte[] FromHexString(this string hexString)
{
if (hexString == null)
{
return new byte[0];
}
var numberChars = hexString.Length;
var bytes = new byte[numberChars / 2];
for (var i = 0; i < numberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
}
return bytes;
}
Encrypted strings will look like garble. The way to test if the encryption is working correctly is to pass your string back through decrypt. If it works at decrypting then you know the string is correct despite looking like garbage to you.

Categories

Resources