c# MCRYPT_RIJNDAEL_256 Encryption Decryption Class in php - c#

I am trying to convert c# application into php but I stuck at a place where C# provides Security class for Encryption and decryption based on RIJNDAEL algo. I am trying to convert into php.
Note: I am Using php 7.2 so mcrypt is deprecated for this version.
C# code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace pharmarackencryption
{
class Program
{
private const string initVector = "aw90rela942f65u2";
// This constant is used to determine the keysize of the encryption algorithm.
private const int keysize = 256;
public static string Encrypt(string plainText, string passPhrase = "testing")
{
byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
public static string Decrypt(string cipherText, string passPhrase = "testing")
{
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream 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);
}
static void Main(string[] args)
{
Program p = new Program();
string enc_password = Encrypt("437217");
string dec_password = Decrypt("C9xJGa03dRQx9ePm0nLnHg==");
Console.WriteLine(enc_password);
Console.WriteLine(dec_password);
}
}
}
Encryption : C9xJGa03dRQx9ePm0nLnHg==
I found some what similar code in php like
PHP code:
<?php
// key/iv in ASCII binary data, $str base64
function decrypt_stuff($key, $str, $iv) {
// $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($str), MCRYPT_MODE_CBC, $iv);
$plaintext_dec = openssl_decrypt(base64_decode($str), "aes-256-cbc", $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
return $plaintext_dec;
}
// key/iv in ascii binary data, $str ascii
function encrypt_stuff($key, $str, $iv) {
// $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_CBC, $iv));
if (($l = (strlen($str) & 15)) > 0) { $str .= str_repeat(chr(0), 16 - $l); }
$ciphertext = base64_encode(openssl_encrypt($str, "aes-256-cbc", $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv));
return $ciphertext;
}
echo encrypt_stuff("testing","437217","aw90rela942f65u2");
//Result : LTbhEHjFgfa5PDJQXJEdKQ==
Both are going same thing but still result is different

Your PHP code produces different results because you're not using the same key. In your C# code you're using PasswordDeriveBytes to create the key, which is Microsoft's implementation of PBKDF1.
PHP doesn't support PBKDF1, but we could write a function that is compatible with C#. The code below is inspired from this great answer by Maarten Bodewes, translated to PHP.
function passwordDeriveBytes($password, $salt, $iterations = 100, $len = 32) {
$key = $password . $salt;
for($i = 0; $i < $iterations; $i++) {
$key = sha1($key, true);
}
if (strlen($key) < $len) {
$hx = passwordDeriveBytes($password, $salt, $iterations - 1, 20);
$counter = 0;
while (strlen($key) < $len) {
$counter += 1;
$key .= sha1($counter . $hx, true);
}
}
return substr($key, 0, $len);
}
Also, you're bese64 encoding and padding your data manually. You're preforming zero byte padding, but in your C# code you're using PKCS7 (the default and preferred) padding. It's best to let openssl pad and encode your data.
function encrypt_stuff($key, $str, $iv) {
return openssl_encrypt($str, "aes-256-cbc", $key, 0, $iv);
}
function decrypt_stuff($key, $str, $iv) {
return openssl_decrypt($str, "aes-256-cbc", $key, 0, $iv);
}
Using the key derived from passwordDeriveBytes, this PHP code produces the same results as your C# code.
$key = passwordDeriveBytes("testing", null);
$enc = encrypt_stuff($key,"437217","aw90rela942f65u2");
echo $enc;
//C9xJGa03dRQx9ePm0nLnHg==
However, I don't recommend using this code for the following reasons.
It's best to use PBKDF2 for your key. You can use Rfc2898DeriveBytes in C#:
Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, salt, iterations);
byte[] key = kdf.GetBytes(32);
and hash_pbkdf2 in PHP:
$key = hash_pbkdf2("sha1", $password, $salt, $iterations, 32, true);
The salt should be at least 8 bytes long, and the number of iterations should be at least 10,000.
You're not using a salt. You should use a random salt for each password, it makes your keys stronger.
You're using a static IV. The IV should be unique and unpredictable. You can create a random IV with RNGCryptoServiceProvider:
byte[] iv = new byte[16];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(iv);
and openssl_random_pseudo_bytes:
$iv = openssl_random_pseudo_bytes(16);
You could use this code for the salt too.
You're not using authenticated encryption. If you're using PHP 7 you could use GCM, but unfortunately .NET doesn't provide any AEAD algorithms. However, you could use bouncycastle, if you choose to use authenticated encryption.
But your encryption code should produce different results every time you use it, because you should be using a random IV. You can decrypt the ciphertext correctly if you have the IV. The IV doesn't have to be secret; you could store it next to the ciphertext.
If you were using mcrypt with MCRYPT_RIJNDAEL_256, you could still make your PHP code compatible with C#, as RijndaelManaged supports 256 bit block size.
symmetricKey.BlockSize = 256;
However you shouldn't use mcrypt because it's not maintained, and it's deprecated in PHP 7.

I am not familiar with the C# end of this question. However I will point out some information that may be relevant to your problem.
The descriptions in the RIJNDAEL functions refer to the block size, not the key size. For example MCRYPT_RIJNDAEL_256 states that you are using a block size of 256. It is not specifying a key size.
For the openssl AES functions you are specifying a key size. As an example aes-256-cbc specifies you are using a 256 bit key.
The block size for all the AES functions is a 128 bit. So you can use MCRYPT_RIJNDAEL_128 which matches the AES block size. You can use a 256 bit key with both the MCRYPT_RIJNDAEL_128 and the aes-256-cbc as they are both using the 128 bit block size.
So for these reasons aes-256-cbc can not be used with MCRYPT_RIJNDAEL_256. They simply are not the same thing.
This one is redundant. If you are using the aes-256-cbcyou need to make sure that you are using a 256 bit key. Obviously make sure that your are using the same key to encrypt and decrypt. Make sure your IV is the correct IV. I would make them both static for testing. Once you get it working tinker around with adding the IV to the cipher string on encrypt and separating the IV from the cipher text on decrypt
Use openssl_random_pseudo_bytes() to generate your 256 bit(32 byte)key. You can also use openssl_random_pseudo_bytes() to generate your IV.
On a side note. I would highly recommend using the LibSodium library. It is now native on the latest versions of PHP and has a C# library as well. You can find it on Github easy enough.
Once you get this going I would look at learning how to Authenticate/Verify your encryption. Here is a good starting link for that. Authentication Read
Hope that helps.

It is not recommended to store passwords at the database using an reversible algo, passwords should be stored using expensive hashes. You should consider switching your password storage to a hash like Argon2.
Check this: http://php.net/manual/en/function.password-hash.php

mcrypt has been moved. not removed. you may try installing this
sudo apt-get -y install gcc make autoconf libc-dev pkg-config
sudo apt-get -y install php7.2-dev
sudo apt-get -y install libmcrypt-dev
sudo pecl install mcrypt-1.0.1
PS: not tested

Related

How to encrypt with iOS CryptoKit and decrypt with C# in NetCore

I would like to encrypt data in iOS app with a SymetricKey and the CryptoKit and decrypt on server side with C# in Net Core.
iOS code:
class Security {
static let keyStr = "d5a423f64b607ea7c65b311d855dc48f" //32
static let iv="31348c0987c7" //12
class func encode(_ text:String)->String {
let key=SymmetricKey(data: Security.keyStr.data(using: .utf8)!)
let nonce=try! AES.GCM.Nonce(data: iv.data(using: .utf8)!)
let encrypted=try! AES.GCM.seal(text.data(using: .utf8)!, using: key, nonce: nonce)
return encrypted.combined!.base64EncodedString()
}
}
I pass the result of the encryption to my backend and I would like to decrypt
C# Code:
public string decrypt(string encryptedText)
{
string keyStr = "d5a423f64b607ea7c65b311d855dc48f";
string iv = "31348c0987c7";
string plaintext = "";
Debug.WriteLine(encryptedText);
using (Aes aesAlg = Aes.Create())
{
Debug.WriteLine(AesGcm.IsSupported);
var key = System.Text.Encoding.UTF8.GetBytes(keyStr);
var iV = System.Text.Encoding.UTF8.GetBytes(iv);
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(Convert.FromBase64String(request.pswd)))
{
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();
}
}
}
}
Debug.WriteLine(plaintext);
}
So for example word: Test gets encrypted as: MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=
When I get to line:
aesAlg.IV = iV;
I get an error "Specified initialization vector (IV) does not match the block size for this algorithm."
It seems as if C# needs byte[16], but in iOS I seem to be stuck with 12.
I got stuck at this point. Any idea greately appreciated.
Thank you.
The posted Swift code applies AES in GCM mode, s. AES.GCM. The posted C# code also uses AES, however not the GCM mode, but the default CBC mode (s. Aes, Mode).
The CBC mode applies a 16 bytes IV, while the GCM mode uses a 12 bytes nonce. That is what the error message is pointing to.
For successful decryption, AES in GCM mode must also be used on the C# side. In .NET AES in GCM mode is supported with the AesGcm class (as of .NET Core 3.0).
Note also that the data given by the Swift code is the Base64 encoding of the concatenation of 12 bytes nonce, ciphertext and 16 bytes tag (in that order), which must be separated in the C# code, where the portions are processed individually.
A possible C# implementation that decrypts the ciphertext generated by the posted Swift code is:
byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");
Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
Span<byte> nonce = nonceCiphertextTagSpan[..12];
Span<byte> ciphertext = nonceCiphertextTagSpan[12..^16];
Span<byte> tag = nonceCiphertextTagSpan[^16..];
byte[] plaintext = new byte[ciphertext.Length];
using AesGcm aesGcm = new AesGcm(key);
aesGcm.Decrypt(nonce, ciphertext, tag, plaintext); // throws an 'CryptographicException: The computed authentication tag did not match the input authentication tag' if authentication fails
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test
Edit: An alternative to the native .NET class AesGcm is C#/BouncyCastle. Maybe this is supported in your environment:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
...
byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");
Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
byte[] nonce = nonceCiphertextTagSpan[..12].ToArray();
byte[] ciphertextTag = nonceCiphertextTagSpan[12..].ToArray();
GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
AeadParameters aeadParameters = new AeadParameters(new KeyParameter(key), 128, nonce);
gcmBlockCipher.Init(false, aeadParameters);
byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(ciphertextTag.Length)];
int length = gcmBlockCipher.ProcessBytes(ciphertextTag, 0, ciphertextTag.Length, plaintext, 0);
gcmBlockCipher.DoFinal(plaintext, length); // throws an 'InvalidCipherTextException: mac check in GCM failed' if authentication fails
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test
Note that unlike the native AesGcm class, C#/BouncyCastle requires the concatenation of ciphertext and tag, so only the nonce needs to be separated.

AES-256-CBC in .NET Core (C#)

I am searching for C# Code to reproduce the following openssl command.
openssl enc -d -aes-256-cbc -in my_encrypted_file.csv.enc -out my_decrypted_file.csv -pass file:key.bin
Additional information:
The encrypted file in present as byte[]
The key.bin is a byte[] with length of 256 (the key is obtained by a more simple decryption of yet another file, which i managed to realize in C#).
I have been trying out various examples found by searching the web.
The problem is, that all of these examples require an IV (initialization vector). Unfortunately, I don't have an IV and no one on the team knows what this is or how it could be defined.
The openssl command does not seem to need one, so I am a bit confused about this.
Currently, the code, I am trying with, looks as follows:
public static string DecryptAesCbc(byte[] cipheredData, byte[] key)
{
string decrypted;
System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
aes.KeySize = 256;
aes.Key = key;
byte[] iv = new byte[aes.BlockSize / 8];
aes.IV = iv;
aes.Mode = CipherMode.CBC;
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(cipheredData))
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decrypted = sr.ReadToEnd();
}
}
return decrypted;
}
}
The code fails saying that my byte[256] key has the wrong length for this kind of algorithm.
Thanks for any help with this!
Cheers, Mike
The posted OpenSSL statement uses the -pass file: option and thus a passphrase (which is read from a file), see openssl enc. This causes the encryption process to first generate a random 8 bytes salt and then, together with the passphrase, derive a 32 bytes key and 16 bytes IV using the (not very secure) proprietary OpenSSL function EVP_BytesToKey. This function uses several parameters, e.g. a digest and an iteration count. The default digest for key derivation is MD5 and the iteration count is 1. Note that OpenSSL version 1.1.0 and later uses SHA256 as default digest, i.e. depending on the OpenSSL version used to generate the ciphertext, the appropriate digest must be used for decryption. Preceding the ciphertext is a block whose first 8 bytes is the ASCII encoding of Salted__, followed by the 8 bytes salt.
Therefore, the decryption must first determine the salt. Based on the salt, together with the passphrase, key and IV must be derived and then the rest of the encrypted data can be decrypted. Thus, first of all an implementation of EVP_BytesToKey in C# is required, e.g. here. Then a possible implementation could be (using MD5 as digest):
public static string DecryptAesCbc(byte[] cipheredData, string passphrase)
{
string decrypted = null;
using (MemoryStream ms = new MemoryStream(cipheredData))
{
// Get salt
byte[] salt = new byte[8];
ms.Seek(8, SeekOrigin.Begin);
ms.Read(salt, 0, 8);
// Derive key and IV
OpenSslCompat.OpenSslCompatDeriveBytes db = new OpenSslCompat.OpenSslCompatDeriveBytes(passphrase, salt, "MD5", 1);
byte[] key = db.GetBytes(32);
byte[] iv = db.GetBytes(16);
using (Aes aes = Aes.Create())
{
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.IV = iv;
// Decrypt
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs, Encoding.UTF8))
{
decrypted = sr.ReadToEnd();
}
}
}
}
return decrypted;
}
Note that the 2nd parameter of DecryptAesCbc is the passphrase (as string) and not the key (as byte[]). Also note that StreamReader uses an encoding (UTF-8 by default), which requires compatible data (i.e. text data, but this should be met for csv files). Otherwise (i.e. for binary data as opposed to text data) StreamReader must not be used.

convert C# code to php for encrypt and decrypt [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I try to convert this code to php, but i can't and always i get different result in C# and PHP
Here is my C# code for encrypt and decrypt :
private static readonly byte[] initVectorBytes = Encoding.ASCII.GetBytes("1234567812345678");
private const int keysize = 256;
private string pass = "sample";
public static string Encrypt(string plainText, string passPhrase)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
public static string Decrypt(string cipherText, string passPhrase)
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
and this is my php code :
$iv = "1234567812345678";
$out = null;
$key = "sample";
foreach ($iv as $i) { $out .= chr(ord(substr($i,0,1))); }
$res = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_CBC, implode($out));
The keys are not the same. C# is extending the key with PasswordDeriveBytes which is a good method. PHP mcrypt is extending they key with nulls. You need the extended (256-bit) keys to be the same.
The padding is not the same. Unencrypted data needs to be a multiple of the block size (128-bits for AES) and if it isn't always that padding must to be added. C# is not specifying any padding and will expect the data to be a multiple of the block size (128-bits). PHP will by default add null padding which is non-standard and will not work for binary data. You need to add common padding, the standard is PKCS#7 (aka PKCS#5), See PKCS#7 padding. C# supports PKCS#5 but for mcrypt you will have to do it in your code (the mcrypt developers were Bozos and did not provide standard padding).
Rijndael supports multiple bock sizes, it is not clear what the C# default block size is. If what you want is AES (it should be) the block size needs to be 128-bits.
Given that the MSDN documentation does not specify defaults it is best to explicitly set the block size, key size, mode and padding.
Your PHP code will not run on PHP 5.6 as the key size is wrong, it must be 32 bytes.
Said that, on previous versions PHP was padding the key with \0's to reach the correct key length, but in C# you're creating derived bytes (what indeed is correct) to get enough bytes for your key, which ends in different keys used on C# and PHP.
As a proof, create a key with 32 bytes (32 chars) and use directly those 32 bytes as key, both in PHP and C#, in that way it should work.
But at the end you will need a common way to derive the bytes both on PHP and C# to finally have a consistent keying code, an example can be to use a SHA-256 hash to generate the key.

AES Decryption Using C#

I am using a Java based configuration management tool called Zuul which supports encrypting sensitive configuration information using various encryption schemes.
I have configured it to use below scheme for my data
AES (Bouncy Castle)
Name: PBEWITHSHA256AND128BITAES-CBC-BC
Requirements: Bouncy Castle API and JCE Unlimited Strength Policy Files
Hashing Algorithm: SHA256
Hashing Iterations: 1000
Now when reading my configuration data back, I need to decrypt the information before I can use it and the documentation provides below information around this topic.
The encrypted values produced by Jasypt (and thus Zuul) are are prefixed with the salt (usually 8 or 16 bytes depending on the algorithm requirements). They are then Base64 encoded. Decrypting the results goes something like this:
Convert the Base64 string to bytes
Strip off the first 8 or 16 bytes as the salt
Keep the remaining bytes for the encrypted payload
Invoke the KDF function with the salt, iteration count and the password to create the secret key.
Use the secret key to decrypt the encrypted payload
More details here: Zull Encryption wiki
Based on above details, I have written below code (and my knowledge around security is very limited)
public static string Decrypt(string cipher, string password)
{
const int saltLength = 16;
const int iterations = 1000;
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = cipherBytes.Take(saltLength).ToArray();
byte[] encryptedBytes = cipherBytes.Skip(saltLength).ToArray();
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, saltBytes, iterations);
byte[] keyBytes = key.GetBytes(16);
AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider();
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
MemoryStream msDecrypt = new MemoryStream(encryptedBytes);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
StreamReader srDecrypt = new StreamReader(csDecrypt);
return srDecrypt.ReadToEnd();
}
I configured Zuul to use below password for the encryption
SimplePassword
And now I have an encrypted string given to me by Zuul and I need to decrypt it
p8C9hAHaoo0F25rMueT0+u0O6xYVpGIkjHmWqFJmTOvpV8+cipoDFIUnaOFF5ElQ
When I try to decrypt this string using above code, I get below exception
System.Security.Cryptography.CryptographicException : Padding is invalid and cannot be removed.
As I mentioned earlier, my knowledge around this topic is limited and I am not able to figure out if the information provided in the documentation is not enough, if I am doing something wrong while writing the decryption routine or should I be using bouncy castle for decryption as well.
Any help with this will be much appreciated.
According to Zuul documentation they are deriving both key and iv from the password/salt.
So you should derive 256+128 bits (i.e. 48 bytes), and use first 32 bytes as the key, and next 16 bytes as IV.
And this should be done in one operation, not as consequent calls to key.DeriveBytes.
I resorted to Bouncy Castle for decryption instead since that is used by Zuul as well.
Here is the code that works
public static string Decrypt(string cipher, string password)
{
const int saltLength = 16;
const int iterations = 1000;
const string algSpec = "AES/CBC/NoPadding";
const string algName = "PBEWITHSHA256AND128BITAES-CBC-BC";
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = cipherBytes.Take(saltLength).ToArray();
byte[] encryptedBytes = cipherBytes.Skip(saltLength).ToArray();
char[] passwordChars = password.ToCharArray();
Asn1Encodable defParams = PbeUtilities.GenerateAlgorithmParameters(algName, saltBytes, iterations);
IWrapper wrapper = WrapperUtilities.GetWrapper(algSpec);
ICipherParameters parameters = PbeUtilities.GenerateCipherParameters(algName, passwordChars, defParams);
wrapper.Init(false, parameters);
byte[] keyText = wrapper.Unwrap(encryptedBytes, 0, encryptedBytes.Length);
return Encoding.Default.GetString(keyText);
}

C# AES Function not returning expected results

I'm using this function to Encrypt/Decrypt data using AES because it looked simple and clean (googl'ed code)
public static string Encrypt(string toEncrypt)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes("3a8114db34d5623d4fd1ee0fb0ga7a73"); // 256-AES key
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.CBC;
rDel.Padding = PaddingMode.PKCS7; // better lang support
ICryptoTransform cTransform = rDel.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
public static string Decrypt(string toDecrypt)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes("3a8114db34d5623d4fd1ee0fb0ga7a73"); // AES-256 key
byte[] toEncryptArray = Convert.FromBase64String(toDecrypt);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.CBC;
rDel.Padding = PaddingMode.PKCS7; // better lang support
ICryptoTransform cTransform = rDel.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return UTF8Encoding.UTF8.GetString(resultArray);
}
I'm trying to encrypt the data "test garbage" and thats what i receive back:
YfhyS3GE/liPCaXR0cMHfQ==
However, I tried the same key/phrase on a lot of online-aes encrypt/decrypt and all of them are returning
U2FsdGVkX184u0/vPgA/B0rxofp5Iuqm7hfn4+QZAhg=
Can anyone actually tell me whats wrong?
"3a8114db34d5623d4fd1ee0fb0ga7a73" is hex encoded 128 bit key not a utf8 encoded 256 bit key.
That said simple and clean doesn't necessarily mean correct. For example, the code your using does use a random IV, but doesn't include it in the wire format, you'll never be able to decrypt what you encrypt.
I have a cut and paste style simple code sample that I try to keep up to date and reviewed that uses authenticated encryption using AES:
Modern Examples of Symmetric Authenticated Encryption of a string. C#
First a few issues with your code. Apparently Google doesn't always return the best code on top.
You are getting a key through the UTF8 encoding, which is silly. This produces a very weak key:
// 256-AES key
byte[] keyArray = UTF8Encoding.UTF8.GetBytes("3a8114db34d5623d4fd1ee0fb0ga7a73");
You are using CBC mode but the IV is not (explicitly) set.
Then you compare to some online-aes encrypt/decrypt services and you see a difference. That's because they probably (hopefully) work different.
The main thing here is that your 2 methods are a match and you can round-trip your data. But a good encryption would use a different way to get Key and IV.
I'm not exactly sure why you see a different (smaller) length encrypted data but that's up to a whole list of settings : Key length, Padding mode etc.

Categories