I'm trying to encrypt and decrypt data from and to Laravel using C#
I've tried using this code. I've modified it to work correctly after Laravel's updates.
however, when I try to decrypt the C# string in Laravel I recieve "The MAC is invalid." Exception.
I have no idea about what's the problem with the computation of the MAC on C# side.
Any ideas on how to solve this issue?
C# Code:
using System;
using System.Text;
using System.Security.Cryptography;
using System.Web.Script.Serialization;
using System.Collections.Generic;
namespace Aes256CbcEncrypterApp
{
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, world!");
// The sample encryption key.
byte[] Key = Convert.FromBase64String("My44CharKeyBase64");
// The sample text to encrypt and decrypt.
string Text = "Here is some text to encrypt!";
// Encrypt and decrypt the sample text via the Aes256CbcEncrypter class.
string Encrypted = Aes256CbcEncrypter.Encrypt(Text, Key);
string Decrypted = Aes256CbcEncrypter.Decrypt(Encrypted, Key);
// Show the encrypted and decrypted data and the key used.
Console.WriteLine("Original: {0}", Text);
Console.WriteLine("Key: {0}", Convert.ToBase64String(Key));
Console.WriteLine("Encrypted: {0}", Encrypted);
Console.WriteLine("Decrypted: {0}", Decrypted);
Console.ReadKey();
}
}
/**
* A class to encrypt and decrypt strings using the cipher AES-256-CBC.
*/
class Aes256CbcEncrypter
{
private static readonly Encoding encoding = Encoding.UTF8;
public static string Encrypt(string plainText, byte[] key)
{
try
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.GenerateIV();
ICryptoTransform AESEncrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] buffer = encoding.GetBytes(plainText);
string encryptedText = Convert.ToBase64String(AESEncrypt.TransformFinalBlock(buffer, 0, buffer.Length));
String mac = "";
mac = BitConverter.ToString(HmacSHA256(Convert.ToBase64String(aes.IV) + encryptedText, key)).Replace("-", "").ToLower();
var keyValues = new Dictionary<string, object>
{
{ "iv", Convert.ToBase64String(aes.IV) },
{ "value", encryptedText },
{ "mac", mac },
};
JavaScriptSerializer serializer = new JavaScriptSerializer();
return Convert.ToBase64String(encoding.GetBytes(serializer.Serialize(keyValues)));
}
catch (Exception e)
{
throw new Exception("Error encrypting: " + e.Message);
}
}
public static string Decrypt(string plainText, byte[] key)
{
try
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key;
// Base 64 decode
byte[] base64Decoded = Convert.FromBase64String(plainText);
string base64DecodedStr = encoding.GetString(base64Decoded);
// JSON Decode base64Str
JavaScriptSerializer serializer = new JavaScriptSerializer();
var payload = serializer.Deserialize<Dictionary<string, string>>(base64DecodedStr);
aes.IV = Convert.FromBase64String(payload["iv"]);
ICryptoTransform AESDecrypt = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] buffer = Convert.FromBase64String(payload["value"]);
return encoding.GetString(AESDecrypt.TransformFinalBlock(buffer, 0, buffer.Length));
}
catch (Exception e)
{
throw new Exception("Error decrypting: " + e.Message);
}
}
static byte[] HmacSHA256(String data, byte[] key)
{
using (HMACSHA256 hmac = new HMACSHA256(key))
{
return hmac.ComputeHash(encoding.GetBytes(data));
}
}
}
}
After digging, debugging, looking for any updates Laravel made and compared it to the C# code above, there was not any major deference, however the problem was at Laravel side and solved by entering these commands.
php artisan config:clear
php artisan cache:clear
php artisan view:clear
php artisan route:clear
composer dump-autoload
Remember to disable payload serialization while encrypting and decrypting to get the original payload and not a serialized one since serialization is enabled by default.
echo encrypt("test", false);
echo decrypt("base64EncryptedString", false)
In case more exceptions thrown from encrypt() or decrypt() try to open the browser in incognito mode or delete browser cache.
Related
I have a couple of projects in different languages that encrypt/decrypt string, for example, a php project can encrypt a string and store in a database and a c# project should be able to pull it out of the database and decrypt it.
I have a c# .net core project which I've successfully made the decryption method to decrypt string that are encrypted from other projects, but I now need a method that also does the encryption.
I have implemented a method which isn't throwing any errors but the encryption string it returns is completely wrong.
Below is my implementation of the encryption
public string encrypt(string decryptedString)
{
string encrypted;
byte[] key = Encoding.UTF8.GetBytes(CipherKey);
System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key;
aes.IV = Encoding.UTF8.GetBytes(CipherIv);
aes.Mode = CipherMode.CBC;
ICryptoTransform decipher = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Write))
{
using (StreamWriter sr = new StreamWriter(cs))
{
sr.Write(decryptedString);
var encryptedString = ms.ToArray();
var result = new byte[aes.IV.Length + encryptedString.Length];
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
Buffer.BlockCopy(encryptedString, 0, result, aes.IV.Length, encryptedString.Length);
return Convert.ToBase64String(result);
}
}
}
}
And the equivalent method to decrypt (the decryption works fine) is below
public string decrypt(string encryptedPassword, bool throwException = false)
{
try
{
string decrypted;
byte[] key = Encoding.UTF8.GetBytes(CipherKey);
System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key;
aes.IV = Encoding.UTF8.GetBytes(CipherIv);
aes.Mode = CipherMode.CBC;
byte[] base64decodeBytes = System.Convert.FromBase64String(encryptedPassword);
string base64Decode = Encoding.UTF8.GetString(base64decodeBytes);
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(base64decodeBytes))
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decrypted = sr.ReadToEnd();
}
}
return decrypted;
}
catch (Exception ex)
{
return encryptedPassword;
}
}
UPDATE
From the advice in the comment by 500 - Internal Server Error I have made a change which seems to have resolved the issue somewhat but I am now getting junk at the start of the string in the decryption.
For example I am doing the following to test:
string encryptionString = encryption.encrypt("Hello World");
Console.WriteLine("Encrypted String: {0}", encryptionString);
string decodedString = encryption.decrypt(encryptionString);
Console.WriteLine("Decrypted String: {0}", decodedString);
Encrypted String: Z3BmenhibnNmZ1tqa2xhZgs/GYMu49dNPNGoRROTWz4=
Decrypted String: �u�D'���);�
SKHHello World
Ok so for a school project I am making an app that has an encryption function.
Here is my code
public static string EncryptString(string stringToEncrypt, string hash)
{
byte[] data = UTF8Encoding.UTF8.GetBytes(stringToEncrypt);
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
byte[] keys = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(hash));
using (TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider() {
Key = keys, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 })
{
ICryptoTransform transform = tripleDES.CreateEncryptor();
byte[] results = transform.TransformFinalBlock(data, 0, data.Length);
return Convert.ToBase64String(results, 0, results.Length);
}
}
}
public static string DecryptString(string stringToDecrypt, string hash)
{
byte[] data = UTF8Encoding.UTF8.GetBytes(stringToDecrypt);
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
byte[] keys = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(hash));
using (TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider()
{
Key = keys,
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
})
{
ICryptoTransform transform = tripleDES.CreateDecryptor();
byte[] results = transform.TransformFinalBlock(data, 0, data.Length);
return Convert.ToBase64String(results, 0, results.Length);
}
}
}
Currently the encryption method works, however the decryption method crashes the entire app. The error message is as follows:
System.Security.Cryptography.CryptographicException: 'Length of the data to decrypt is invalid.'
It it important for the app that the user can set their own encryption/decryption key.
What exactly is causing the app to not work? I can provide more information if needed.
Thanks.
This should resolve your problem, you just need to decode the base64 string in the decrypt function, ie byte[] data = Convert.FromBase64String(stringToDecrypt);.
Also we want to return a string, rather than base64 data, so we'll do UTF8Encoding.UTF8.GetString() at then end of DecryptString.
I would take note of the fact that this would not be considered secure as the comments above indicate, but this is a learning exercise, so this will get you up and running!
I've also created a DotNetFiddle here: https://dotnetfiddle.net/PPzXKo
using System;
using System.Security;
using System.Text;
using System.Security.Cryptography;
namespace triple_des_test
{
public class Program
{
public void Main()
{
string key = "my key";
string encrypted = EncryptString("Hello world", key);
Console.WriteLine("Encrypted: " + encrypted);
Console.WriteLine("Decrypted: " + DecryptString(encrypted, key));
}
public static string EncryptString(string stringToEncrypt, string key)
{
byte[] data = UTF8Encoding.UTF8.GetBytes(stringToEncrypt);
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
byte[] keys = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
using (TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider() {
Key = keys, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 })
{
ICryptoTransform transform = tripleDES.CreateEncryptor();
byte[] results = transform.TransformFinalBlock(data, 0, data.Length);
return Convert.ToBase64String(results, 0, results.Length);
}
}
}
public static string DecryptString(string stringToDecrypt, string key)
{
byte[] data = Convert.FromBase64String(stringToDecrypt);
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
byte[] keys = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
using (TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider()
{
Key = keys,
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
})
{
ICryptoTransform transform = tripleDES.CreateDecryptor();
byte[] results = transform.TransformFinalBlock(data, 0, data.Length);
return UTF8Encoding.UTF8.GetString(results, 0, results.Length);
}
}
}
}
}
I am trying to implement image steganography with LSB and everything works except decrypting.
There is my class responsible for encryption and decryption of strings below. Encrypting works fine but Decrypt method always returns null:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace WindowsFormsApp1
{
class Encryptor {
//text to encrypt or already decrypted
private String decryptedText = "";
//text to decrypt or already encrypted
private String encryptedText = "";
private String key = "";
public Encryptor setDecryptedText(String text)
{
decryptedText = text;
return this;
}
public Encryptor setEncryptedText(String text)
{
encryptedText = text;
return this;
}
public Encryptor setKey(String text)
{
key = text;
return this;
}
Byte[] getHash(Byte[] hash)
{
Byte[] newHash = new Byte[32];
for (int i = 0; i < 32; i++)
{
newHash[i] = hash[i];
}
return newHash;
}
Byte[] getIV(Byte[] hash)
{
Byte[] newHash = new Byte[16];
int j = 0;
for (int i = 32; i < 48; i++)
{
newHash[j++] = hash[i];
}
return newHash;
}
String EncryptAesManaged()
{
SHA512 shaM = new SHA512Managed();
Byte[] data = Encoding.UTF8.GetBytes(key);
Byte[] hash = shaM.ComputeHash(data);
try
{
return Encrypt(decryptedText, getHash(hash), getIV(hash));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
String DecryptAesManaged()
{
SHA512 shaM = new SHA512Managed();
var data = Encoding.UTF8.GetBytes(key);
Byte[] hash = shaM.ComputeHash(data);
try
{
return Decrypt(Convert.FromBase64String(encryptedText), getHash(hash), getIV(hash));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return "";
}
String Encrypt(string plainText, byte[] Key, byte[] IV)
{
Byte[] encrypted;
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform encryptor = aes.CreateEncryptor(Key, IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs)) {
sw.Write(Encoding.UTF8.GetBytes(plainText));
cs.FlushFinalBlock();
encrypted = ms.ToArray();
}
}
}
aes.Clear();
}
return Convert.ToBase64String(encrypted);
}
string Decrypt(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform decryptor = aes.CreateDecryptor(Key, IV);
try
{
using (MemoryStream ms = new MemoryStream(cipherText))
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
using (StreamReader reader = new StreamReader(cs))
{
plaintext = reader.ReadToEnd(); //Here get null
}
aes.Clear();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return plaintext;
}
public String getEncrypted()
{
return EncryptAesManaged();
}
public String getDecrypted()
{
return DecryptAesManaged();
}
}
}
Why is Decrypt() returning null rather than the originally encrypted string?
You don't show how you use your Encryptor class, so your question doesn't quite include a Minimal, Complete, and Verifiable example. I was able to reproduce the problem with the following test harness:
public static void Test()
{
var key = "my key";
var plainText = "hello";
var encryptor = new Encryptor();
encryptor.setDecryptedText(plainText);
encryptor.setKey(key);
var encrypted = encryptor.getEncrypted();
Console.WriteLine(encrypted);
var deecryptor = new Encryptor();
deecryptor.setEncryptedText(encrypted);
deecryptor.setKey(key);
var decrypted = deecryptor.getDecrypted();
Console.WriteLine(decrypted);
Assert.IsTrue(plainText == decrypted);
}
Demo fiddle #1 here.
Given that, your code has 2 problems, both of which are actually in encryption rather than decryption.
Firstly, in Encrypt(string plainText, byte[] Key, byte[] IV), you are writing to the StreamWriter sw, then flushing the CryptoStream and returning the MemoryStream contents -- but you never flush or dispose sw, so its buffered contents are never forwarded to the underlying stream(s).
To fix this, your code should looks something like:
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(Encoding.UTF8.GetBytes(plainText));
}
}
encrypted = ms.ToArray();
}
Now getDecrypted() no longer returns a null result -- but instead returns a wrong result of "System.Byte[]", as shown in demo fiddle #2 here.
Secondly, again in Encrypt(...), you are effectively encoding your plainText twice at this line:
sw.Write(Encoding.UTF8.GetBytes(plainText));
Encoding.UTF8.GetBytes(plainText) converts the plain text to a byte array, but the StreamWriter is also intended to do this job, converting strings to bytes and passing them to the underlying stream. So, since you are not passing a string to Write(), the overload that gets called is StreamWriter.Write(Object):
Writes the text representation of an object to the text string or stream by calling the ToString() method on that object.
Thus what actually gets encrypted is the ToString() value of a byte array, which is "System.Byte[]".
To fix this, simply remove the call to Encoding.UTF8.GetBytes(plainText) and write the string directly. Thus your Encrypt() method should now look like:
static String Encrypt(string plainText, byte[] Key, byte[] IV)
{
string encrypted;
using (var aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform encryptor = aes.CreateEncryptor(Key, IV);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write, true))
{
using (var sw = new StreamWriter(cs))
{
sw.Write(plainText);
}
}
// Calling GetBuffer() avoids the extra allocation of ToArray().
encrypted = Convert.ToBase64String(ms.GetBuffer(), 0, checked((int)ms.Length));
}
aes.Clear();
}
return encrypted;
}
Demo fiddle #3 here that now passes successfully.
Disclaimer: this answer does not attempt to to review your code for security best practices such as secure setup of salt and IV.
I'm currently working on a class that encrypts large amounts of text with a randomly generated encryption key encrypted by a X509 certificate from a smart card, using a RSACryptoServiceProvider to perform the master key encryption and decryption operations. However, when I have the fOEAP padding option set to true, I have the "Error while decoding OAEP padding" error on decryption every time. I've checked the key size and it is within acceptable limits. And I've gone through breakpoints to make sure that the Base64 string that gets returned from the encryption function is the exact same as the encrypted Base64 string that gets passed back to the decryption function when the file gets loaded again.
The key pair is definitely correct, since it works fine without OAEP. And I've checked the text encoding too.
EDIT: It turns out this could be a smart card specific issue, when I tried decryption with a local X509 certificate the decryption succeeded.
EDIT: This is the decryption code that fails:
string TestString = "Hello World!";
X509Certificate2 cert = DRXEncrypter.GetCertificate("Select a test certificate", "Select a certificate to use for this test from the local store.");
string key = DRXEncrypter.GenerateEncryptionKey(214);
Console.WriteLine("Encryption Key: " + key);
string encrypted = DRXEncrypter.EncryptBody(TestString, key);
Console.WriteLine("Encrypted Body: " + encrypted);
string cryptokey = DRXEncrypter.EncryptWithCert(cert, key);
Console.WriteLine("Encrypted Decryption Key: " + cryptokey);
string decrypted = DRXEncrypter.DecryptBody(encrypted, cryptokey, cert);
Console.WriteLine("Decrypted Body: " + decrypted);
Console.WriteLine("Output String: " + decrypted + ".");
Here is the code from the crypto provider class I've written. I've been stuck on this issue for hours, so it would be great if someone could help me out.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;
namespace CoreDRXEditor
{
public class DRXEncrypter
{
private byte[] Salt = Encoding.ASCII.GetBytes("81PO9j8I1a94j");
private string EncryptionKey;
private const bool UseOAEP = true;
public DRXEncrypter(string EncryptionKey)
{
this.EncryptionKey = EncryptionKey;
}
public static string EncryptBody(string body, string encryptionkey)
{
// Use the plaintext master key to encrypt the body.
DRXEncrypter enc = new DRXEncrypter(encryptionkey);
// Encrypt the body.
return enc.Encrypt(body);
}
public static int GetMaxKeySize(X509Certificate2 cert)
{
RSACryptoServiceProvider csp = cert.PublicKey.Key as RSACryptoServiceProvider;
return csp.KeySize;
}
public static string DecryptBody(string body, string encryptionkey, X509Certificate2 cert)
{
// Decrypt the encrypted encryption key with the certificate.
string DecryptedKey = Convert.ToBase64String(DecryptWithCert(cert, encryptionkey));
// Create a new DRXEncrypter using the decrypted encryption key to decrypt the body.
DRXEncrypter enc = new DRXEncrypter(DecryptedKey);
// Return the decrypted body.
return enc.Decrypt(body);
}
public static string GenerateEncryptionKey(int KeyLength)
{
using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
byte[] CryptoBytes = new byte[KeyLength];
rng.GetBytes(CryptoBytes);
return Convert.ToBase64String(CryptoBytes);
}
}
public static X509Certificate2 GetCertificate(string title, string message)
{
X509Store cstore = new X509Store(StoreLocation.CurrentUser);
cstore.Open(OpenFlags.ReadOnly);
X509CertificateCollection certs = X509Certificate2UI.SelectFromCollection(cstore.Certificates, title, message, X509SelectionFlag.SingleSelection);
if (certs.Count == 1)
{
X509Certificate2 mcert = certs[0] as X509Certificate2;
return mcert;
}
else
{
return null;
}
}
public static string EncryptWithCert(X509Certificate2 cert, string PlainText)
{
RSACryptoServiceProvider csp = cert.PublicKey.Key as RSACryptoServiceProvider;
byte[] PlainBytes = Convert.FromBase64String(PlainText);
// This converts the plain text into a byte array and then encrypts the raw bytes.
byte[] CryptoBytes = csp.Encrypt(PlainBytes, UseOAEP);
// This converts the encrypted bytes into a Base64 string.
string ReturnString = Convert.ToBase64String(CryptoBytes);
return ReturnString;
}
public static byte[] DecryptWithCert(X509Certificate2 cert, string EncryptedText)
{
RSACryptoServiceProvider csp = cert.PrivateKey as RSACryptoServiceProvider;
//CspParameters csps = new CspParameters();
byte[] EncryptedBytes = Convert.FromBase64String(EncryptedText);
// This converts the encrypted, Base64 encoded byte array from EncryptWithCert() to a byte[] and decrypts it.
byte[] CryptoBytes = csp.Decrypt(EncryptedBytes, UseOAEP);
return CryptoBytes;
}
public string Encrypt(string PlainText)
{
RijndaelManaged Algorithm = null;
string Output = null;
try
{
Rfc2898DeriveBytes PrivateKey = new Rfc2898DeriveBytes(this.EncryptionKey, this.Salt);
Algorithm = new RijndaelManaged();
Algorithm.Key = PrivateKey.GetBytes(Algorithm.KeySize / 8);
Algorithm.Padding = PaddingMode.PKCS7;
ICryptoTransform Encryption = Algorithm.CreateEncryptor(Algorithm.Key, Algorithm.IV);
using (MemoryStream msa = new MemoryStream())
{
msa.Write(BitConverter.GetBytes(Algorithm.IV.Length), 0, sizeof(int));
msa.Write(Algorithm.IV, 0, Algorithm.IV.Length);
using (CryptoStream csa = new CryptoStream(msa, Encryption, CryptoStreamMode.Write))
{
using (StreamWriter swa = new StreamWriter(csa))
{
swa.Write(PlainText);
}
}
Output = Convert.ToBase64String(msa.ToArray());
}
}
finally
{
if (Algorithm != null)
{
Algorithm.Clear();
}
}
return Output;
}
public string Decrypt(string EncryptedText)
{
RijndaelManaged Algorithm = null;
string Output = null;
try
{
Rfc2898DeriveBytes PrivateKey = new Rfc2898DeriveBytes(this.EncryptionKey, this.Salt);
byte[] KeyBytes = Convert.FromBase64String(EncryptedText);
using (MemoryStream msb = new MemoryStream(KeyBytes))
{
Algorithm = new RijndaelManaged();
Algorithm.Key = PrivateKey.GetBytes(Algorithm.KeySize / 8);
Algorithm.IV = ReadByteArray(msb);
Algorithm.Padding = PaddingMode.PKCS7;
ICryptoTransform Decryption = Algorithm.CreateDecryptor(Algorithm.Key, Algorithm.IV);
using (CryptoStream csb = new CryptoStream(msb, Decryption, CryptoStreamMode.Read))
{
using (StreamReader srb = new StreamReader(csb))
{
Output = srb.ReadToEnd();
}
}
}
}
finally
{
if (Algorithm != null)
{
Algorithm.Clear();
}
}
return Output;
}
public static string Sha512(string ToHash)
{
using (SHA512 SHA = new SHA512Managed())
{
byte[] HashByte = Encoding.UTF8.GetBytes(ToHash);
byte[] HashBytes = SHA.ComputeHash(HashByte);
string Hash = System.Text.Encoding.UTF8.GetString(HashBytes, 0, HashBytes.Length);
return Hash;
}
}
public static string Base64Encode(string data)
{
byte[] str = Encoding.UTF8.GetBytes(data);
return Convert.ToBase64String(str);
}
public static string Base64Decode(string data)
{
byte[] str = Convert.FromBase64String(data);
return Encoding.UTF8.GetString(str);
}
private byte[] ReadByteArray(Stream st)
{
byte[] Length = new byte[sizeof(int)];
st.Read(Length, 0, Length.Length);
byte[] Buffer = new byte[BitConverter.ToInt32(Length, 0)];
st.Read(Buffer, 0, Buffer.Length);
return Buffer;
}
}
}
I've been arguing with this today with smartcards (or more accurately, a Yubikey Neo with the smartcard PIV applet enabled); using this code:
var encryptor = (RSACryptoServiceProvider)c.PublicKey.Key;
var decryptor = (RSACryptoServiceProvider)c.PrivateKey;
var encrypt = encryptor.Encrypt(bytes, RSAEncryptionPadding.Pkcs1);
var decrypt = decryptor.Decrypt(encrypt, RSAEncryptionPadding.Pkcs1);
I've found that it matters what padding algo I use. If I use PKCS1 padding, everything works. If I use OaepSHA1, I get the Error while decoding [...] error. If I use anything else (e.g., OaepSHA256) I get a Not supported error.
I can only conclude that my smartcard doesn't properly support OAEP SHA1, but padding with PKCS#1 everything is good.
Even if this answers what you already know, it may be useful as another datapoint for anyone else coming along using smartcards.
Make sure your key size is not too small or too large.
See comments from MSDN
The RSACryptoServiceProvider supports key sizes from 384 bits to 16384
bits in increments of 8 bits if you have the Microsoft Enhanced
Cryptographic Provider installed. It supports key sizes from 384 bits
to 512 bits in increments of 8 bits if you have the Microsoft Base
Cryptographic Provider installed.
So you might need to pad short key strings with some bytes to get the minimum key length
Ok, I managed to check this and from what I can see, I have problems with some certificates. I am not sure why some certificates work while others don't. It would be good to know why some certificates fail in this case?
Anyway, I created a new self signed certificate using windows "Manage File Encryption Certificates" and used this certificate, and all seems to work.
The out put from your code.
Encryption Key: aUc/GXWDoh2LktaEGeCJfju1dHP118yD/fzfT0iJLuhOq2QeyGpG6m3aBHaxvdH0ufeXRHbMjmlmPgIL/bhABzkT2C5Oa6ZhY3IFXb5t7JXZ3AtUunvtNAnRyFJ7MzklrSZGgQ
vF67DSNfIVE17doKt6j6mkCpSco56ooZCrOs2Mp3vSXqNjvjiwMEfQbk41aYUNVNVNlBGhdNQCIZIAKezQCUpWqzn2II27FIDfqDIEW4ieyzpXC05GzUlGXDxFOiFUPk3n0Y94vgeF8AlCD74eyZtz
WQ==
Encrypted Body: EAAAANS/W7+GGRbT1q5NCYvZlDZYtxaA8g55HzUqP5qxhenn
Encrypted Decryption Key: vc/tcsApmY1503BFi7oSu/RDvZivA1Ed58KJuLoEC6eE8q0BIa6ye2JvtXyxkVbzzL0MA51pZ2ZhMIsfCnBsEDjCgy+JLTZTGM1Mv+em9frFUKb0zHbICnPUa/3H
yd1yOWsdn5ws19QN2dzC6eau+ExhT2T/vyZO4Nf9NdHKnB8n2yB1rrQ/T+N2EYCNH/AVPDAsme6JG7k9Od2XIipBXMyCgXgWYZmQusq+JQjA9d3c4CrQYcg/ERF+K3oZv/gPicBkAR5taxwSxAajGg
bpkJNsbhTMHTN9bOn333qZ6ojlo5e882baZXuZWPr9qtj1b7ONoOyuSx/OvGKjt93BQg==
Decrypted Body: Hello World!
Output String: Hello World!.
Hope that helps
The below AES algorithm in C# returns an encrypted value that is different from what node js and CryptoJS returns. NodeJS Crypto library and CryptoJS return the same values but .NET's AesCryptoServiceProvider returns a different value. Any ideas?
C# example
private const string AesIV = #"!QAZ2WSX#EDC4RFV";
private const string AesKey = #"5TGB&YHN7UJM(IK<";
public static void Main()
{
try
{
string original = "HelloWorld";
Console.WriteLine(Encrypt(original));
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
/// <summary>
/// AES Encryption
/// </summary>
private static string Encrypt(string text)
{
// AesCryptoServiceProvider
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.IV = Encoding.UTF8.GetBytes(AesIV);
aes.Key = Encoding.UTF8.GetBytes(AesKey);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
// Convert string to byte array
byte[] src = Encoding.Unicode.GetBytes(text);
// encryption
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
return Convert.ToBase64String(dest);
}
}
NodeJS example:
crypto = require "crypto"
algo = 'aes-128-cbc'
keyBuffer = new Buffer("!QAZ2WSX#EDC4RFV")
ivBuffer = new Buffer("5TGB&YHN7UJM(IK<")
cipher = crypto.createCipheriv(algo, keyBuffer, ivBuffer)
textBuffer = new Buffer('HelloWorld')
encrypted = cipher.update(textBuffer)
encryptedFinal = cipher.final()
encryptedText = encrypted.toString('base64') + encryptedFinal.toString('base64')
console.log encryptedText
CryptoJS example:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var key = CryptoJS.enc.Utf8.parse('!QAZ2WSX#EDC4RFV');
var iv = CryptoJS.enc.Utf8.parse('5TGB&YHN7UJM(IK<');
var encrypted = CryptoJS.AES.encrypt("HelloWorld", key, { iv: iv });
alert(encrypted);
</script>
Your C# version uses UTF-16LE to convert HelloWorld to plaintext bytes. The NodeJS one (and presumably hence the CryptoJS one) use UTF-8 bytes. Use Encoding.UTF8.GetBytes() in the C# one.
Default padding in NodeJS is PKCS5 (assuming CryptoJS is the same since you said they produce the same result). Your C# code is setting padding to PKCS7.