Created
January 21, 2015 18:12
-
-
Save tiesont/bdad5a91c8055e23d8a3 to your computer and use it in GitHub Desktop.
VisualBasic.NET implementation of Scott Lowe's OpenSslAes class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Imports System.Collections.Generic | |
Imports System.IO | |
Imports System.Security.Cryptography | |
Imports System.Text | |
Namespace Security | |
''' <summary> | |
''' OpenSSL AES CBC 256 in .NET for interop with Ruby | |
''' </summary> | |
''' <remarks>Created by Scott Lowe</remarks> | |
''' <see href="https://gist.github.com/scottlowe/1411917#file-opensslaes-cs"/> | |
Public Class OpenSslAes | |
''' <summary> | |
''' Encrypts the specified plain text. | |
''' </summary> | |
''' <param name="plainText">The plain text.</param> | |
''' <param name="passphrase">The passphrase. Key and IV are derived from this value.</param> | |
''' <returns></returns> | |
Public Shared Function Encrypt(plainText As String, passphrase As String) As String | |
Dim key As Byte(), iv As Byte() | |
Dim salt = New Byte(7) {} | |
Dim rng As New RNGCryptoServiceProvider() | |
rng.GetNonZeroBytes(salt) | |
EvpBytesToKey(passphrase, salt, key, iv) | |
Dim encryptedBytes As Byte() = AesEncrypt(plainText, key, iv) | |
Dim encryptedBytesWithSalt = CombineSaltAndEncryptedData(encryptedBytes, salt) | |
Return Convert.ToBase64String(encryptedBytesWithSalt) | |
End Function | |
' OpenSSL prefixes the combined encrypted data and salt with "Salted__" | |
Private Shared Function CombineSaltAndEncryptedData(encryptedData As Byte(), salt As Byte()) As Byte() | |
Dim encryptedBytesWithSalt = New Byte(salt.Length + encryptedData.Length + 7) {} | |
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 | |
End Function | |
''' <summary> | |
''' Decrypts the specified encrypted string. | |
''' </summary> | |
''' <param name="encrypted">The encrypted string.</param> | |
''' <param name="passphrase">The passphrase. Key and IV are derived from this value.</param> | |
''' <returns></returns> | |
Public Shared Function Decrypt(encrypted As String, passphrase As String) As String | |
Dim encryptedBytesWithSalt As Byte() = Convert.FromBase64String(encrypted) | |
Dim salt = ExtractSalt(encryptedBytesWithSalt) | |
Dim encryptedBytes = ExtractEncryptedData(salt, encryptedBytesWithSalt) | |
Dim key As Byte(), iv As Byte() | |
EvpBytesToKey(passphrase, salt, key, iv) | |
Return AesDecrypt(encryptedBytes, key, iv) | |
End Function | |
' Pull the data out from the combined salt and data | |
Private Shared Function ExtractEncryptedData(salt As Byte(), encryptedBytesWithSalt As Byte()) As Byte() | |
Dim encryptedBytes = New Byte(encryptedBytesWithSalt.Length - salt.Length - 9) {} | |
Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length + 8, encryptedBytes, 0, encryptedBytes.Length) | |
Return encryptedBytes | |
End Function | |
' The salt is located in the first 8 bytes of the combined encrypted data and salt bytes | |
Private Shared Function ExtractSalt(encryptedBytesWithSalt As Byte()) As Byte() | |
Dim salt = New Byte(7) {} | |
Buffer.BlockCopy(encryptedBytesWithSalt, 8, salt, 0, salt.Length) | |
Return salt | |
End Function | |
' 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 Shared Sub EvpBytesToKey(passphrase As String, salt As Byte(), ByRef key As Byte(), ByRef iv As Byte()) | |
Dim concatenatedHashes = New List(Of Byte)(48) | |
Dim password As Byte() = Encoding.UTF8.GetBytes(passphrase) | |
Dim currentHash As Byte() = New Byte(-1) {} | |
Dim hasher = MD5.Create() | |
Dim enoughBytesForKey As Boolean = False | |
While Not enoughBytesForKey | |
Dim preHashLength As Integer = currentHash.Length + password.Length + salt.Length | |
Dim preHash As Byte() = New Byte(preHashLength - 1) {} | |
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 = hasher.ComputeHash(preHash) | |
concatenatedHashes.AddRange(currentHash) | |
If concatenatedHashes.Count >= 48 Then | |
enoughBytesForKey = True | |
End If | |
End While | |
key = New Byte(31) {} | |
iv = New Byte(15) {} | |
concatenatedHashes.CopyTo(0, key, 0, 32) | |
concatenatedHashes.CopyTo(32, iv, 0, 16) | |
hasher.Clear() | |
hasher = Nothing | |
End Sub | |
Private Shared Function AesEncrypt(plainText As String, key As Byte(), iv As Byte()) As Byte() | |
Dim memoryStream As MemoryStream | |
Dim aesAlgorithm As RijndaelManaged = Nothing | |
Try | |
aesAlgorithm = New RijndaelManaged() With { _ | |
.Mode = CipherMode.CBC, _ | |
.KeySize = 256, _ | |
.BlockSize = 128, _ | |
.Key = key, _ | |
.IV = iv _ | |
} | |
Dim cryptoTransform = aesAlgorithm.CreateEncryptor(aesAlgorithm.Key, aesAlgorithm.IV) | |
memoryStream = New MemoryStream() | |
Using cryptoStream As CryptoStream = New CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write) | |
Using streamWriter As StreamWriter = New StreamWriter(cryptoStream) | |
streamWriter.Write(plainText) | |
streamWriter.Flush() | |
streamWriter.Close() | |
End Using | |
End Using | |
Finally | |
If aesAlgorithm IsNot Nothing Then | |
aesAlgorithm.Clear() | |
End If | |
End Try | |
Return memoryStream.ToArray() | |
End Function | |
Private Shared Function AesDecrypt(cipherText As Byte(), key As Byte(), iv As Byte()) As String | |
Dim aesAlgorithm As RijndaelManaged = Nothing | |
Dim plaintext As String | |
Try | |
aesAlgorithm = New RijndaelManaged() With { _ | |
.Mode = CipherMode.CBC, _ | |
.KeySize = 256, _ | |
.BlockSize = 128, _ | |
.Key = key, _ | |
.IV = iv _ | |
} | |
Dim decryptor As ICryptoTransform = aesAlgorithm.CreateDecryptor(aesAlgorithm.Key, aesAlgorithm.IV) | |
Using memoryStream As MemoryStream = New MemoryStream(cipherText) | |
Using cryptoStream As CryptoStream = New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read) | |
Using streamReader As StreamReader = New StreamReader(cryptoStream) | |
plaintext = streamReader.ReadToEnd() | |
streamReader.Close() | |
End Using | |
End Using | |
End Using | |
Finally | |
If aesAlgorithm IsNot Nothing Then | |
aesAlgorithm.Clear() | |
End If | |
End Try | |
Return plaintext | |
End Function | |
End Class | |
End Namespace |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment