Advertisement
ZeekoSec

Bouncy Castle AES-GCM

Mar 27th, 2015
749
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.82 KB | None | 0 0
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using Org.BouncyCastle.Crypto;
  5. using Org.BouncyCastle.Crypto.Engines;
  6. using Org.BouncyCastle.Crypto.Generators;
  7. using Org.BouncyCastle.Crypto.Modes;
  8. using Org.BouncyCastle.Crypto.Parameters;
  9. using Org.BouncyCastle.Security;
  10. namespace Encryption
  11. {
  12.  
  13.   public static class AESGCM
  14.   {
  15.     private static readonly SecureRandom Random = new SecureRandom();
  16.  
  17.     //Preconfigured Encryption Parameters
  18.     public static readonly int NonceBitSize = 128;
  19.     public static readonly int MacBitSize = 128;
  20.     public static readonly int KeyBitSize = 256;
  21.  
  22.     //Preconfigured Password Key Derivation Parameters
  23.     public static readonly int SaltBitSize = 128;
  24.     public static readonly int Iterations = 10000;
  25.     public static readonly int MinPasswordLength = 12;
  26.  
  27.  
  28.     /// <summary>
  29.     /// Helper that generates a random new key on each call.
  30.     /// </summary>
  31.     /// <returns></returns>
  32.     public static byte[] NewKey()
  33.     {
  34.       var key = new byte[KeyBitSize / 8];
  35.       Random.NextBytes(key);
  36.       return key;
  37.     }
  38.  
  39.     /// <summary>
  40.     /// Simple Encryption And Authentication (AES-GCM) of a UTF8 string.
  41.     /// </summary>
  42.     /// <param name="secretMessage">The secret message.</param>
  43.     /// <param name="key">The key.</param>
  44.     /// <param name="nonSecretPayload">Optional non-secret payload.</param>
  45.     /// <returns>
  46.     /// Encrypted Message
  47.     /// </returns>
  48.     /// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception>
  49.     /// <remarks>
  50.     /// Adds overhead of (Optional-Payload + BlockSize(16) + Message +  HMac-Tag(16)) * 1.33 Base64
  51.     /// </remarks>
  52.     public static string SimpleEncrypt(string secretMessage, byte[] key, byte[] nonSecretPayload = null)
  53.     {
  54.       if (string.IsNullOrEmpty(secretMessage))
  55.         throw new ArgumentException("Secret Message Required!", "secretMessage");
  56.  
  57.       var plainText = Encoding.UTF8.GetBytes(secretMessage);
  58.       var cipherText = SimpleEncrypt(plainText, key, nonSecretPayload);
  59.       return Convert.ToBase64String(cipherText);
  60.     }
  61.  
  62.  
  63.     /// <summary>
  64.     /// Simple Decryption & Authentication (AES-GCM) of a UTF8 Message
  65.     /// </summary>
  66.     /// <param name="encryptedMessage">The encrypted message.</param>
  67.     /// <param name="key">The key.</param>
  68.     /// <param name="nonSecretPayloadLength">Length of the optional non-secret payload.</param>
  69.     /// <returns>Decrypted Message</returns>
  70.     public static string SimpleDecrypt(string encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
  71.     {
  72.       if (string.IsNullOrEmpty(encryptedMessage))
  73.         throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
  74.  
  75.       var cipherText = Convert.FromBase64String(encryptedMessage);
  76.       var plaintext = SimpleDecrypt(cipherText, key, nonSecretPayloadLength);
  77.       return plaintext == null ? null : Encoding.UTF8.GetString(plaintext);
  78.     }
  79.  
  80.     /// <summary>
  81.     /// Simple Encryption And Authentication (AES-GCM) of a UTF8 String
  82.     /// using key derived from a password (PBKDF2).
  83.     /// </summary>
  84.     /// <param name="secretMessage">The secret message.</param>
  85.     /// <param name="password">The password.</param>
  86.     /// <param name="nonSecretPayload">The non secret payload.</param>
  87.     /// <returns>
  88.     /// Encrypted Message
  89.     /// </returns>
  90.     /// <remarks>
  91.     /// Significantly less secure than using random binary keys.
  92.     /// Adds additional non secret payload for key generation parameters.
  93.     /// </remarks>
  94.     public static string SimpleEncryptWithPassword(string secretMessage, string password,
  95.                              byte[] nonSecretPayload = null)
  96.     {
  97.       if (string.IsNullOrEmpty(secretMessage))
  98.         throw new ArgumentException("Secret Message Required!", "secretMessage");
  99.  
  100.       var plainText = Encoding.UTF8.GetBytes(secretMessage);
  101.       var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload);
  102.       return Convert.ToBase64String(cipherText);
  103.     }
  104.  
  105.  
  106.     /// <summary>
  107.     /// Simple Decryption and Authentication (AES-GCM) of a UTF8 message
  108.     /// using a key derived from a password (PBKDF2)
  109.     /// </summary>
  110.     /// <param name="encryptedMessage">The encrypted message.</param>
  111.     /// <param name="password">The password.</param>
  112.     /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
  113.     /// <returns>
  114.     /// Decrypted Message
  115.     /// </returns>
  116.     /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
  117.     /// <remarks>
  118.     /// Significantly less secure than using random binary keys.
  119.     /// </remarks>
  120.     public static string SimpleDecryptWithPassword(string encryptedMessage, string password,
  121.                              int nonSecretPayloadLength = 0)
  122.     {
  123.       if (string.IsNullOrWhiteSpace(encryptedMessage))
  124.         throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
  125.  
  126.       var cipherText = Convert.FromBase64String(encryptedMessage);
  127.       var plaintext = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength);
  128.       return plaintext == null ? null : Encoding.UTF8.GetString(plaintext);
  129.     }
  130.  
  131.     public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key, byte[] nonSecretPayload = null)
  132.     {
  133.       //User Error Checks
  134.       if (key == null || key.Length != KeyBitSize / 8)
  135.         throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");
  136.  
  137.       if (secretMessage == null || secretMessage.Length == 0)
  138.         throw new ArgumentException("Secret Message Required!", "secretMessage");
  139.  
  140.       //Non-secret Payload Optional
  141.       nonSecretPayload = nonSecretPayload ?? new byte[] { };
  142.  
  143.       //Using random nonce large enough not to repeat
  144.       var nonce = new byte[NonceBitSize / 8];
  145.       Random.NextBytes(nonce, 0, nonce.Length);
  146.  
  147.       var cipher = new GcmBlockCipher(new AesFastEngine());
  148.       var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
  149.       cipher.Init(true, parameters);
  150.  
  151.       //Generate Cipher Text With Auth Tag
  152.       var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
  153.       var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
  154.       cipher.DoFinal(cipherText, len);
  155.  
  156.       //Assemble Message
  157.       using (var combinedStream = new MemoryStream())
  158.       {
  159.         using (var binaryWriter = new BinaryWriter(combinedStream))
  160.         {
  161.           //Prepend Authenticated Payload
  162.           binaryWriter.Write(nonSecretPayload);
  163.           //Prepend Nonce
  164.           binaryWriter.Write(nonce);
  165.           //Write Cipher Text
  166.           binaryWriter.Write(cipherText);
  167.         }
  168.         return combinedStream.ToArray();
  169.       }
  170.     }
  171.  
  172.     public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
  173.     {
  174.       //User Error Checks
  175.       if (key == null || key.Length != KeyBitSize / 8)
  176.         throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");
  177.  
  178.       if (encryptedMessage == null || encryptedMessage.Length == 0)
  179.         throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
  180.  
  181.       using (var cipherStream = new MemoryStream(encryptedMessage))
  182.       using (var cipherReader = new BinaryReader(cipherStream))
  183.       {
  184.         //Grab Payload
  185.         var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);
  186.  
  187.         //Grab Nonce
  188.         var nonce = cipherReader.ReadBytes(NonceBitSize / 8);
  189.  
  190.         var cipher = new GcmBlockCipher(new AesFastEngine());
  191.         var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
  192.         cipher.Init(false, parameters);
  193.  
  194.         //Decrypt Cipher Text
  195.         var cipherText = cipherReader.ReadBytes(encryptedMessage.Length - nonSecretPayloadLength - nonce.Length);
  196.         var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];  
  197.  
  198.         try
  199.         {
  200.           var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
  201.           cipher.DoFinal(plainText, len);
  202.  
  203.         }
  204.         catch (InvalidCipherTextException)
  205.         {
  206.           //Return null if it doesn't authenticate
  207.           return null;
  208.         }
  209.  
  210.         return plainText;
  211.       }
  212.  
  213.     }
  214.  
  215.     public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
  216.     {
  217.       nonSecretPayload = nonSecretPayload ?? new byte[] {};
  218.  
  219.       //User Error Checks
  220.       if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
  221.         throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");
  222.  
  223.       if (secretMessage == null || secretMessage.Length == 0)
  224.         throw new ArgumentException("Secret Message Required!", "secretMessage");
  225.  
  226.       var generator = new Pkcs5S2ParametersGenerator();
  227.  
  228.       //Use Random Salt to minimize pre-generated weak password attacks.
  229.       var salt = new byte[SaltBitSize / 8];
  230.       Random.NextBytes(salt);
  231.  
  232.       generator.Init(
  233.         PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
  234.         salt,
  235.         Iterations);
  236.  
  237.       //Generate Key
  238.       var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);
  239.  
  240.       //Create Full Non Secret Payload
  241.       var payload = new byte[salt.Length + nonSecretPayload.Length];
  242.       Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length);
  243.       Array.Copy(salt,0, payload,nonSecretPayload.Length, salt.Length);
  244.  
  245.       return SimpleEncrypt(secretMessage, key.GetKey(), payload);
  246.     }
  247.  
  248.     public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
  249.     {
  250.       //User Error Checks
  251.       if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
  252.         throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");
  253.  
  254.       if (encryptedMessage == null || encryptedMessage.Length == 0)
  255.         throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
  256.  
  257.       var generator = new Pkcs5S2ParametersGenerator();
  258.  
  259.       //Grab Salt from Payload
  260.       var salt = new byte[SaltBitSize / 8];
  261.       Array.Copy(encryptedMessage, nonSecretPayloadLength, salt, 0, salt.Length);
  262.  
  263.       generator.Init(
  264.         PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
  265.         salt,
  266.         Iterations);
  267.  
  268.       //Generate Key
  269.       var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);
  270.  
  271.       return SimpleDecrypt(encryptedMessage, key.GetKey(), salt.Length + nonSecretPayloadLength);
  272.     }
  273.   }
  274. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement