-
-
Save scottlowe/1411917 to your computer and use it in GitHub Desktop.
| require 'rubygems' | |
| require 'bundler' | |
| require 'gibberish' | |
| password = '181cc0b200124dc748c96d8fdefe2cb3f21b48212898c2e3dd705a4c8415a5abb15b3c43ccb9e9db27e6c9ad1df1b927377c8dd6bb3d07746bc3cec7c67ca016' | |
| cipher = Gibberish::AES.new(password) | |
| cypher_text = cipher.encrypt("Hello from Ruby!") | |
| plain_text = cipher.decrypt('U2FsdGVkX18GXWJnusOQi55IhmvfdVyjTLjAmmtcAmg=') | |
| print "Encrypted string is: " | |
| p cypher_text | |
| puts "Decrypted string is: '#{plain_text}'" |
| using System; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| namespace dotnet_aes | |
| { | |
| public class OpenSslAes | |
| { | |
| public static string Encrypt(string plainText, string passphrase) | |
| { | |
| byte[] key, iv; | |
| var salt = new byte[8]; | |
| new RNGCryptoServiceProvider().GetNonZeroBytes(salt); | |
| EvpBytesToKey(passphrase, salt, out key, out iv); | |
| byte[] encryptedBytes = AesEncrypt(plainText, key, iv); | |
| var encryptedBytesWithSalt = CombineSaltAndEncryptedData(encryptedBytes, salt); | |
| return Convert.ToBase64String(encryptedBytesWithSalt); | |
| } | |
| // OpenSSL prefixes the combined encrypted data and salt with "Salted__" | |
| private static byte[] CombineSaltAndEncryptedData(byte[] encryptedData, byte[] salt) | |
| { | |
| var encryptedBytesWithSalt = new byte[salt.Length + encryptedData.Length + 8]; | |
| Buffer.BlockCopy(Encoding.ASCII.GetBytes("Salted__"), 0, encryptedBytesWithSalt, 0, 8); | |
| Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 8, salt.Length); | |
| Buffer.BlockCopy(encryptedData, 0, encryptedBytesWithSalt, salt.Length + 8, encryptedData.Length); | |
| return encryptedBytesWithSalt; | |
| } | |
| public static string Decrypt(string encrypted, string passphrase) | |
| { | |
| byte[] encryptedBytesWithSalt = Convert.FromBase64String(encrypted); | |
| var salt = ExtractSalt(encryptedBytesWithSalt); | |
| var encryptedBytes = ExtractEncryptedData(salt, encryptedBytesWithSalt); | |
| byte[] key, iv; | |
| EvpBytesToKey(passphrase, salt, out key, out iv); | |
| return AesDecrypt(encryptedBytes, key, iv); | |
| } | |
| // Pull the data out from the combined salt and data | |
| private static byte[] ExtractEncryptedData(byte[] salt, byte[] encryptedBytesWithSalt) | |
| { | |
| var encryptedBytes = new byte[encryptedBytesWithSalt.Length - salt.Length - 8]; | |
| Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length + 8, encryptedBytes, 0, encryptedBytes.Length); | |
| return encryptedBytes; | |
| } | |
| // The salt is located in the first 8 bytes of the combined encrypted data and salt bytes | |
| private static byte[] ExtractSalt(byte[] encryptedBytesWithSalt) | |
| { | |
| var salt = new byte[8]; | |
| Buffer.BlockCopy(encryptedBytesWithSalt, 8, salt, 0, salt.Length); | |
| return salt; | |
| } | |
| // Key derivation algorithm used by OpenSSL | |
| // | |
| // Derives a key and IV from the passphrase and salt using a hash algorithm (in this case, MD5). | |
| // | |
| // Refer to http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM | |
| private static void EvpBytesToKey(string passphrase, byte[] salt, out byte[] key, out byte[] iv) | |
| { | |
| var concatenatedHashes = new List<byte>(48); | |
| byte[] password = Encoding.UTF8.GetBytes(passphrase); | |
| byte[] currentHash = new byte[0]; | |
| MD5 md5 = MD5.Create(); | |
| bool enoughBytesForKey = false; | |
| while (!enoughBytesForKey) | |
| { | |
| int preHashLength = currentHash.Length + password.Length + salt.Length; | |
| byte[] preHash = new byte[preHashLength]; | |
| Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length); | |
| Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length); | |
| Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length); | |
| currentHash = md5.ComputeHash(preHash); | |
| concatenatedHashes.AddRange(currentHash); | |
| if (concatenatedHashes.Count >= 48) enoughBytesForKey = true; | |
| } | |
| key = new byte[32]; | |
| iv = new byte[16]; | |
| concatenatedHashes.CopyTo(0, key, 0, 32); | |
| concatenatedHashes.CopyTo(32, iv, 0, 16); | |
| md5.Clear(); | |
| md5 = null; | |
| } | |
| static byte[] AesEncrypt(string plainText, byte[] key, byte[] iv) | |
| { | |
| MemoryStream memoryStream; | |
| RijndaelManaged aesAlgorithm = null; | |
| try | |
| { | |
| aesAlgorithm = new RijndaelManaged | |
| { | |
| Mode = CipherMode.CBC, | |
| KeySize = 256, | |
| BlockSize = 128, | |
| Key = key, | |
| IV = iv | |
| }; | |
| var cryptoTransform = aesAlgorithm.CreateEncryptor(aesAlgorithm.Key, aesAlgorithm.IV); | |
| memoryStream = new MemoryStream(); | |
| using (var cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write)) | |
| { | |
| using (var streamWriter = new StreamWriter(cryptoStream)) | |
| { | |
| streamWriter.Write(plainText); | |
| streamWriter.Flush(); | |
| streamWriter.Close(); | |
| } | |
| } | |
| } | |
| finally | |
| { | |
| if (aesAlgorithm != null) aesAlgorithm.Clear(); | |
| } | |
| return memoryStream.ToArray(); | |
| } | |
| static string AesDecrypt(byte[] cipherText, byte[] key, byte[] iv) | |
| { | |
| RijndaelManaged aesAlgorithm = null; | |
| string plaintext; | |
| try | |
| { | |
| aesAlgorithm = new RijndaelManaged | |
| { | |
| Mode = CipherMode.CBC, | |
| KeySize = 256, | |
| BlockSize = 128, | |
| Key = key, | |
| IV = iv | |
| }; | |
| ICryptoTransform decryptor = aesAlgorithm.CreateDecryptor(aesAlgorithm.Key, aesAlgorithm.IV); | |
| using (var memoryStream = new MemoryStream(cipherText)) | |
| { | |
| using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) | |
| { | |
| using (var streamReader = new StreamReader(cryptoStream)) | |
| { | |
| plaintext = streamReader.ReadToEnd(); | |
| streamReader.Close(); | |
| } | |
| } | |
| } | |
| } | |
| finally | |
| { | |
| if (aesAlgorithm != null) aesAlgorithm.Clear(); | |
| } | |
| return plaintext; | |
| } | |
| } | |
| } |
| using System; | |
| namespace dotnet_aes | |
| { | |
| class Program | |
| { | |
| private const string DotNetPlainText = "Hello from .NET"; | |
| private const string CipherBase64 = "U2FsdGVkX1+wqU8tchOuA2G2I5ceNzad86pb7p2371vKJulvMCuBPQTKo5vG\ncEF9\n"; | |
| private const string Password = "181cc0b200124dc748c96d8fdefe2cb3f21b48212898c2e3dd705a4c8415a5abb15b3c43ccb9e9db27e6c9ad1df1b927377c8dd6bb3d07746bc3cec7c67ca016"; | |
| static void Main() | |
| { | |
| DemoDecryption(); | |
| DemoEncryption(); | |
| Console.ReadLine(); | |
| } | |
| private static void DemoDecryption() | |
| { | |
| var plainText = OpenSslAes.Decrypt(CipherBase64, Password); | |
| Console.WriteLine("Decrypted string is: '{0}'", plainText); | |
| } | |
| private static void DemoEncryption() | |
| { | |
| var plainText = OpenSslAes.Encrypt(DotNetPlainText, Password); | |
| Console.WriteLine("Encrypted string is: '{0}'", plainText); | |
| } | |
| } | |
| } |
Thanks , this is very usefull to me.
@onyxraven Hi there.
I'm sorry that I've only just seen your message after all this time. You don't need to attribute me... this is public, after all. I don't remember the details concerning the writing of this, but I suspect that I patched it together from various snippets of code lying about the web, anyway!
Hi Scott, exactly what I was looking. Works on the ruby and .net sides. However, if I change the password it encrypts OK but on the .Net side I get an exception (same password of course)
password = '7B932AFA6AA07F75D2F3F6997C5809ECBB845FCD619265D52AE6F3FA29911C1E834AFAC5E06D3AA568FE8D9321CBB27FB9916620EC9B5756A20C97DE1DB2B5D7'
Private Const as String = "7B932AFA6AA07F75D2F3F6997C5809ECBB845FCD619265D52AE6F3FA29911C1E834AFAC5E06D3AA568FE8D9321CBB27FB9916620EC9B5756A20C97DE1DB2B5D7"
Any ideas ??
Scott, I'm planning on basing some code off your work here, as its super useful. Any problems using it and doing some code redistribution to private parties? I can give you attribution :-)