I'm porting the Following C# Encryption logic to php, i tried using OPENSSL to port the encryption but its giving me a weird error, i have no idea what im doing wrong maybe OPENSSL fault, padding? not sure
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.IO;
namespace Rextester
{
public class Program
{
public static ICryptoTransform DES;
public static DESCryptoServiceProvider Crypto = new DESCryptoServiceProvider();
public static MemoryStream memStream = new MemoryStream();
public static string encyrptedMessage;
public static void Main(string[] args)
{
Crypto.BlockSize = 64;
Crypto.FeedbackSize = 8;
Crypto.Mode = CipherMode.ECB;
Crypto.Padding = PaddingMode.PKCS7;
//Your code goes here
DES = Crypto.CreateEncryptor(Encoding.ASCII.GetBytes("L)#!&8#*"), Encoding.UTF8.GetBytes("CodeFast"));
CryptoStreamMode mode = CryptoStreamMode.Write;
// Set up streams and encrypt
byte[] messageBytes = Encoding.ASCII.GetBytes("yes");
CryptoStream cryptoStream = new CryptoStream(memStream, DES, mode);
cryptoStream.Write(messageBytes, 0, messageBytes.Length);
cryptoStream.FlushFinalBlock();
// Read the encrypted message from the memory stream
byte[] encryptedMessageBytes = new byte[memStream.Length];
memStream.Position = 0;
memStream.Read(encryptedMessageBytes, 0, encryptedMessageBytes.Length);
// Encode the encrypted message as base64 string
string encryptedMessage = Convert.ToBase64String(encryptedMessageBytes);
Console.WriteLine(encryptedMessage);
}
}
}
<?php
namespace encryption;
class DESCrypto{
private $key;
private $iv;
public function __construct(string $key, ?string $iv){
$this->key = $key;
$this->iv = utf8_encode($iv);
}
public function encrypt(string $text): string {
return openssl_encrypt($text,"DES-ECB",$this->key,OPENSSL_RAW_DATA,$this->iv);
}
public function decrypt(string $text): string {
return openssl_decrypt($text,"DES-ECB",$this->key,OPENSSL_RAW_DATA,$this->iv);
}
}
the C# Code is working fine meanwhile the php one returning the following error
Warning: openssl_decrypt(): IV passed is 4 bytes long which is longer than the 0 expected by selected cipher
Warning: openssl_encrypt(): IV passed is 4 bytes long which is longer than the 0 expected by selected cipher
Related
I am trying to decrypt a simple AES string BqvGk+lyQ+pyhSqwV3MfRg== (which translates as Hello World) upon user input with a hardcoded key but I get an error. Could the be an issue when it's trying to read the base64 string from terminal? Not sure how to fix that.
at EncryptionDecryptionUsingSymmetricKey.AesOperation.DecryptString(String key) in \\visualstudio\AES_Decryptor\AES_Decryptor\Program.cs:line 32
at EncryptionDecryptionUsingSymmetricKey.AesOperation.Main(String[] args) in \\visualstudio\AES_Decryptor\AES_Decryptor\Program.cs:line 21
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace EncryptionDecryptionUsingSymmetricKey
{
public class AesOperation
{
static void Main(string[] args)
{
var key = "b14ca5898a4e4133bbce2ea2315a1916";
Console.WriteLine("[+] Decrypt: ");
var str = Console.ReadLine();
var decryptedString = AesOperation.DecryptString(key);
Console.WriteLine($"[+] Original payload: {decryptedString}");
}
private static object DecryptString(string key)
{
throw new NotImplementedException();
}
public static string DecryptString(string key, string cipherText)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
}
}
This method is working. When I run:
void Main()
{
var key = "b14ca5898a4e4133bbce2ea2315a1916";
var decryptedString = DecryptString(key, "BqvGk+lyQ+pyhSqwV3MfRg==");
Console.WriteLine($"[+] Original payload: {decryptedString}");
}
The result is:
[+] Original payload: Hello World!
I am using your own method as well:
public static string DecryptString(string key, string cipherText)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
SO I think you need to either provide more of the error message if there is any, or we need more of the code. Cause your key decrypts the AES string just fine with your own provided method.
If I had to guess the issue is 1 of 2 things.
var str = Console.ReadLine();
I don't see you reference str anywhere. also it is going to read the entire console line, which might give you more than the aes string. Also you might just be calling your throw exception method. I am not sure why that is there. Perhaps try getting rid of that and running code. I am referring to this method.
private static object DecryptString(string key)
{
throw new NotImplementedException();
}
I used this link to test decrypting AES CBC.
Here my parameters:
This work. Then I implement with C#. My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace HHT.Module.eWallet.Helper
{
public class AESHelper
{
private AesCryptoServiceProvider _aes;
private ICryptoTransform _crypto;
public AESHelper(string key, string IV)
{
_aes = new AesCryptoServiceProvider();
_aes.BlockSize = 128;
_aes.KeySize = 256;
_aes.Key = ASCIIEncoding.ASCII.GetBytes(key);
if (!string.IsNullOrEmpty(IV)) {
_aes.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
_aes.Padding = PaddingMode.PKCS7;
_aes.Mode = CipherMode.CBC;
}
public string encrypt(string message)
{
_crypto = _aes.CreateEncryptor(_aes.Key, _aes.IV);
byte[] encrypted = _crypto.TransformFinalBlock(
ASCIIEncoding.ASCII.GetBytes(message), 0, ASCIIEncoding.ASCII.GetBytes(message).Length);
_crypto.Dispose();
return System.Convert.ToBase64String(encrypted);
}
public string decrypt(string message)
{
_crypto = _aes.CreateDecryptor(_aes.Key, _aes.IV);
byte[] decrypted = _crypto.TransformFinalBlock(
System.Convert.FromBase64String(message), 0, System.Convert.FromBase64String(message).Length);
_crypto.Dispose();
return ASCIIEncoding.ASCII.GetString(decrypted);
}
}
}
The function decrypt not work, it return wrong string. I think some parameters is not same as the picture.
Base64-encoded encrypted data:
5qG6aB4UZ6bnbbPgFhnC+qp/FJ7ZuZ+fg1cYm+OUM1uP/6PyWfLg0w5bJstmBf2W
Key:
745d88b1e8f75d320c2bd9198c08485d
You need to initialize the IV to all zeroes because otherwise it starts with a random value:
if (!string.IsNullOrEmpty(IV))
{
_aes.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
else
{
_aes.IV = new byte[_aes.BlockSize / 8];
}
With this fix in place, it gets the same result as the online site you used.
Also note that your question says ECB but your code and screenshot use CBC (and it's AES, not ACS).
I would like to limit the length of the encrypted output code like 8 or 10 or 12 character etc.
I have created the very small encrypted coed using he "Advanced Encryption Standard (AES)" with Cryptography.SymmetricAlgorithm.IV.
But the result of the Encrypted code as example below:
Input Password = "090400551"
Converted Output = "mkopj3WFb6RZMp34urFLew==" // This should be half the length
I want to reduce the length of 8 to 12 character. Any C# cryptography library or algorithm would be fine
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace AnotherEncryption
{
class Encryption
{
public static class Global
{
// set password
public const string strPassword = "090400551";
public const String strPermutation = "Secure1234";
public const Int32 bytePermutation1 = 0x78;
public const Int32 bytePermutation2 = 0x56;
public const Int32 bytePermutation3 = 0x34;
public const Int32 bytePermutation4 = 0x88;
}
static void Main(string[] args)
{
Console.Title = "Secure Password v2";
Console.WriteLine("Output---");
Console.WriteLine("");
Console.WriteLine("Password: " + Global.strPassword);
string strEncrypted = (Encrypt(Global.strPassword));
Console.WriteLine("Encrypted: " + strEncrypted);
string strDecrypted = Decrypt(strEncrypted);
Console.WriteLine("Decrypted: " + strDecrypted);
//mkopj3WFb6RZMp34urFLew==
Console.ReadKey();
}
public static string Encrypt(string strData)
{
byte[] test = Encoding.UTF8.GetBytes(strData);
return Convert.ToBase64String(Encrypt(test));
}
public static string Decrypt(string strData)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(strData)));
}
// encrypt
public static byte[] Encrypt(byte[] strData)
{
PasswordDeriveBytes passbytes =
new PasswordDeriveBytes(Global.strPermutation,
new byte[] { Global.bytePermutation1,
Global.bytePermutation2,
Global.bytePermutation3,
Global.bytePermutation4
});
MemoryStream memstream = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = passbytes.GetBytes(aes.KeySize / 8);
aes.IV = passbytes.GetBytes(aes.BlockSize / 8);
CryptoStream cryptostream = new CryptoStream(memstream, aes.CreateEncryptor(), CryptoStreamMode.Write);
cryptostream.Write(strData, 0, strData.Length);
cryptostream.Close();
return memstream.ToArray();
}
// decrypt
public static byte[] Decrypt(byte[] strData)
{
PasswordDeriveBytes passbytes =
new PasswordDeriveBytes(Global.strPermutation,
new byte[] { Global.bytePermutation1,
Global.bytePermutation2,
Global.bytePermutation3,
Global.bytePermutation4
});
MemoryStream memstream = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = passbytes.GetBytes(aes.KeySize / 8);
aes.IV = passbytes.GetBytes(aes.BlockSize / 8);
CryptoStream cryptostream = new CryptoStream(memstream,
aes.CreateDecryptor(), CryptoStreamMode.Write);
cryptostream.Write(strData, 0, strData.Length);
cryptostream.Close();
return memstream.ToArray();
}
}
}
If you put Rijndael into CFB mode with a block size of 8, then it acts as a stream cipher - for every byte you put in, you get a byte out again.
public static void Main(string[] args)
{
var algorithm = new RijndaelManaged()
{
Mode = CipherMode.CFB,
// This is the equivalent of BlockSize in CFB mode. We set it to 8 (bits) to prevent any buffering of data
// while waiting for whole blocks.
FeedbackSize = 8,
};
// Don't hard-code in real life, obviously
var key = new byte[32];
var iv = new byte[16];
var input = new byte[] { 1, 2, 3 };
byte[] result;
using (var ms = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(ms, algorithm.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
cryptoStream.Write(input, 0, input.Length);
}
result = ms.ToArray();
}
}
Note that this only appears to work on .NET Framework - .NET Core doesn't seem to support CFB (see this GitHub issue).
Note that encryption doesn't prevent tampering! People can't read your plaintext message, but they can very easily change the ciphertext to control what it gets decrypted to. Stream ciphers tend to be particularly vulnerable to this. If you need to stop someone controlling what the encrypted output decrypts into, then you need a signature.
Also note that you should not use the same IV across multiple messages. Create a random IV, and transfer it alongside your message, frequently as the first 2 bytes.
There are many answers on the internet regarding encryption, but I have been unable to find exactly what I'm looking for: simple strong encryption using the tools that c# provides to encrypt strings and text files.
My main problem is that I don't know how to save the IV into the beginning of the text file or how to create a random IV. I have an example on crypto stream and I have seen an example on DES, but they use the same IV and key and that is (by what I know) not a good thing to do.
You're right, using the same IV is a bad practice, especially if either the Key or IV are hard coded. I'd recommend using the AesManaged class. It uses the AES algorithm, the current standard. Generating an IV is fairly simple:
var aes = new AesManaged(); //Set your KeySize if you will generate a key too.
aes.GenerateIV();
var iv = aes.IV;
That's a simple way of getting a new initialization vector. If your goal is to encrypt a file, you can store the File, but what will you do with the Key? Hard coding it within your application is generally not a very good way of doing it. If your application will be password based, then you can generate the key from Rfc2898DeriveBytes to get a byte array based on a password. This way, your application never knows what the encryption key is.
Here is an example for writing the IV to a file, then the file contents.
using (AesManaged aes = new AesManaged())
{
//Set the Key here.
aes.GenerateIV();
using (var transform = aes.CreateEncryptor())
{
using (var fileStream = new FileStream("C:\\in.txt", FileMode.Open))
{
using (var saveTo = new FileStream("C:\\out.txt", FileMode.Create))
{
using (var cryptoStream = new CryptoStream(saveTo, transform,CryptoStreamMode.Write))
{
var iv = aes.IV;
cryptoStream.Write(iv, 0, iv.Length);
fileStream.CopyTo(cryptoStream);
}
}
}
}
}
see the example on following link, it will create a string encryption with hash, salt and VI key.
https://github.com/salahuddinuk/Encryption-Decryption/blob/master/EncryptDecrypt/Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace EncryptDecrypt
{
public partial class Form1 : Form
{
static readonly string PasswordHash = "P!!Sw0rd~";
static readonly string SaltKey = "Sa~LT~KEY";
static readonly string VIKey = "#1B2c3D4#e5F6<7H8<.";
public Form1()
{
InitializeComponent();
}
private void btn_Process_Click(object sender, EventArgs e)
{
try
{
lbl_Error.Text = "";
if (chb_Decrypt.Checked == true)
txt_Result.Text = Decrypt(txt_Value.Text);
else
txt_Result.Text = Encrypt(txt_Value.Text);
}
catch (Exception ex)
{
lbl_Error.Text = ex.Message;
}
}
public static string Encrypt(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
byte[] cipherTextBytes;
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
cipherTextBytes = memoryStream.ToArray();
cryptoStream.Close();
}
memoryStream.Close();
}
return Convert.ToBase64String(cipherTextBytes);
}
public static string Decrypt(string encryptedText)
{
byte[] cipherTextBytes = Convert.FromBase64String(encryptedText);
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
var memoryStream = new MemoryStream(cipherTextBytes);
var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray());
}
}
}
Nowadays, AesGcm would be an appropriate class and algorithm to use. Examples code for it is easy enough to find, and its API is fairly straightforward.
To generate the IV/nonce, use RandomNumberGenerator.Fill to populate an array of the correct size, which is 12 bytes (96 bits) for AES-GCM. RandomNumberGenerator is the cryptographically-secure one.
As for writing the IV to the file, that is up to you. Are you writing to a file stream? Then start by writing the IV, and then proceed to write the ciphertext. For AES-GCM, we would also write the tag, which will give us not just encryption, but authenticated encryption, i.e. on decryption we can confirm that the ciphertext has not been tampered with.
When reading such a file back in, we read each of the components separately - IV, ciphertext, and tag. Since you know how you wrote them, you know how to read them. For example, x bytes IV, then y bytes tag, then the remaining bytes ciphertext, if that is how you wrote the data to the file.
Pass the components to AesGcm.Decrypt and voila.
I've got this php code and I'd like to get the exact equivalent C#
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_192, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
$encryptedData = mcrypt_encrypt(MCRYPT_RIJNDAEL_192, $key, $salt . $message . $nonce, MCRYPT_MODE_CBC, $iv);
$base64Data = base64_encode($salt . $iv . $encryptedData);
$urlEncodedData = rawurlencode($base64Data);
All contributions gratefully received!
Too many variables to convert directly; I recommend that you examine what it does and rewrite it in C# to adhere to the intent of the original code.
Nay sayers and doom mongers, behold!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Web;
namespace EncryptData
{
class EncryptData
{
private static readonly Encoding ASCII_ENCODING = new System.Text.ASCIIEncoding();
private static string md5(string text)
{
return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(ASCII_ENCODING.GetBytes(text))).Replace("-", "").ToLower();
}
public abstract string nonce();
public abstract string salt();
public readonly string EncryptedData;
public EncryptData(string message)
{
// set up encrytion object
RijndaelManaged aes192 = new RijndaelManaged();
aes192.KeySize = 192;
aes192.BlockSize = 192;
aes192.Padding = PaddingMode.None;
aes192.Mode = CipherMode.CBC;
aes192.Key = ASCII_ENCODING.GetBytes(md5(SECRET_KEY));
aes192.GenerateIV();
string localSalt = salt();
string localNonce = nonce();
// form the string for encrypting
// and put into byte array
string textToEncrypt = localSalt + message+ localNonce;
byte[] plainTextBytes = ASCII_ENCODING.GetBytes(textToEncrypt);
// encrypt the data
ICryptoTransform encryptor = aes192.CreateEncryptor();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
cs.Write(plainTextBytes, 0, plainTextBytes.Length);
// convert our encrypted data from a memory stream into a byte array.
byte[] cypherTextBytes = ms.ToArray();
// close memory stream
ms.Close();
byte[] combined = null;
// combine data and convert to byte array
combined = CombinedData(localSalt, aes192.IV, cypherTextBytes);
// url encode data once converted to base64 string
EncryptedData = HttpUtility.UrlEncode(base64CombinedData(combined), ASCII_ENCODING);
}
public byte[] CombinedData(string salt, byte[] IV, byte[] cypherTextBytes)
{
// convert salt string into byte array
byte[] saltBytes = ASCII_ENCODING.GetBytes(salt);
// catenate all the byte arrays into one
// set up dest byte array with required size
byte[] rv = new byte[saltBytes.Length + IV.Length + cypherTextBytes.Length];
// copy in each byte array
Buffer.BlockCopy(saltBytes, 0, rv, 0, saltBytes.Length);
Buffer.BlockCopy(IV, 0, rv, saltBytes.Length, IV.Length);
Buffer.BlockCopy(cypherTextBytes, 0, rv, saltBytes.Length + IV.Length, cypherTextBytes.Length);
return rv;
}
public string base64CombinedData(byte[] rv)
{
return Convert.ToBase64String(rv);
}
}
}
Sorry, but to be honest - the question is rather silly.
All this code does is some nasty crypting and encoding.
PHP is full of global functions from modules that do the thing.
For C# You need to find proper libs/classes that have methods for crypting etc. It will probably ahve different logic and parameters.
I bet You'll even get some output differences due to different implementations.
I suppose this is the closest thing You'll find to an answer to Your question
In the example provided by Rob I think he should have used PaddingMode.Zero instead of PaddingMode.None, as this is the way mcrypt_encrypt() adds paddings.