Based on this: http://www.superstarcoders.com/blogs/posts/symmetric-encryption-in-c-sharp.aspx
I have written encryption/decryption of byte-arrays:
public static byte[] EncryptFile(string password, byte[] bytes, string salt)
{
using (RijndaelManaged aesEncryption = new RijndaelManaged())
{
DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));
byte[] rgbKey = rgb.GetBytes(aesEncryption.KeySize >> 3);
byte[] rgbIV = rgb.GetBytes(aesEncryption.BlockSize >> 3);
aesEncryption.KeySize = 256;
aesEncryption.Mode = CipherMode.CBC;
aesEncryption.Padding = PaddingMode.PKCS7;
aesEncryption.IV = rgbIV;
aesEncryption.Key = rgbKey;
using (ICryptoTransform crypto = aesEncryption.CreateEncryptor())
{
return crypto.TransformFinalBlock(bytes, 0, bytes.Length);
}
}
}
public static byte[] DecryptFile(string password, byte[] bytes, string salt)
{
using (RijndaelManaged aesEncryption = new RijndaelManaged())
{
DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));
byte[] rgbKey = rgb.GetBytes(aesEncryption.KeySize >> 3);
byte[] rgbIV = rgb.GetBytes(aesEncryption.BlockSize >> 3);
aesEncryption.KeySize = 256;
aesEncryption.Mode = CipherMode.CBC;
aesEncryption.Padding = PaddingMode.PKCS7;
aesEncryption.IV = rgbIV;
aesEncryption.Key = rgbKey;
using (ICryptoTransform crypto = aesEncryption.CreateDecryptor())
{
return crypto.TransformFinalBlock(bytes, 0, bytes.Length);
}
}
}
But when calculating the IV and the key, should I instead use SHA256 instead of Rfc2898DeriveBytes?
No you should not use SHA256, SHA256 is a hashing function where Rfc2898DeriveBytes is used to implements password-based key derivation functionality.
A hash function can be used to verify data, where the Rfc2898DeriveBytes is used specifically to generate a key.
Via msdn Rfc2898DeriveBytes and SHA256
Related
I need to encrypt the AES key with public RSA key, but I do have a problem on return engine.ProcessBlock. It keeps giving me the error 'Index was outside the bounds of the array' and the blocksize is only 245 bytes long.
'Source array was not long enough. Check srcIndex and length, and the array's lower bounds.'
public void Main()
{
var pem = #"C:\Temp\test\publickey.pem";
AesCryptoServiceProvider AES = new AesCryptoServiceProvider();
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CBC;
AES.BlockSize = 128;
AES.KeySize = 256;
AES.GenerateKey();
AES.GenerateIV();
byte[] key = AES.Key;
byte[] iv = AES.IV;
encryptAES(AES, file, key, iv);
decryptAES(AES, file + ".encrypted", key, iv);
byte[] encryptedAes = Encrypt(key, ReadAsymmetricKeyParameter(pem));
}
public byte[] Encrypt(byte[] data, AsymmetricKeyParameter key)
{
var engine = new Pkcs1Encoding(new RsaEngine());
engine.Init(true, key);
var blockSize = engine.GetInputBlockSize();
byte[] enc = engine.ProcessBlock(data, 0, blockSize);
return enc;
}
public AsymmetricKeyParameter ReadAsymmetricKeyParameter(string pemFilename)
{
var fileStream = File.OpenText(pemFilename);
var pemReader = new PemReader(fileStream);
var KeyParameter = (AsymmetricKeyParameter)pemReader.ReadObject();
return KeyParameter;
}
I have a working solution for crypt/decrypt data in my code (below) but when I have upgraded the project to DOTNET6, RijndaelManaged becomes obsolete:
Warning SYSLIB0022 'RijndaelManaged' is obsolete: 'The Rijndael and RijndaelManaged types are obsolete. Use Aes instead.'
and
SYSLIB0023 'RNGCryptoServiceProvider' is obsolete: 'RNGCryptoServiceProvider is obsolete. To generate a random number, use one of the RandomNumberGenerator static methods instead.'
Now I want to change that to Aes/RandomNumberGenerator as stated but want to keep the output in the same way as is. Unfortunately, I am not familiar with crypt/decrypt.
Can somebody help me to rewrite the current block to work with Aes instead - or at least help me how to change that and keep the public methods works in the same way?
Whole Code I have (it works as is)
using System.Security.Cryptography;
namespace MyApp;
internal static class AES
{
private static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new())
{
using RijndaelManaged AES = new(); // This reports Warning SYSLIB0022 'RijndaelManaged' is obsolete: 'The Rijndael and RijndaelManaged types are obsolete. Use Aes instead.
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
return encryptedBytes;
}
private static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new())
{
using RijndaelManaged AES = new(); // This reports Warning SYSLIB0022 'RijndaelManaged' is obsolete: 'The Rijndael and RijndaelManaged types are obsolete. Use Aes instead.
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
return decryptedBytes;
}
public static string EncryptText(string password, string salt = "MySecretSaltWhichIWantToKeepWorking")
{
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(password);
byte[] passwordBytes = Encoding.UTF8.GetBytes(salt);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
public static string DecryptText(string hash, string salt = "MySecretSaltWhichIWantToKeepWorking")
{
try
{
byte[] bytesToBeDecrypted = Convert.FromBase64String(hash);
byte[] passwordBytes = Encoding.UTF8.GetBytes(salt);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
string result = Encoding.UTF8.GetString(bytesDecrypted);
return result;
}
catch (Exception e)
{
return e.Message;
}
}
private const int SALT_BYTE_SIZE = 24;
private const int HASH_BYTE_SIZE = 24;
private const int PBKDF2_ITERATIONS = 1000;
private const int ITERATION_INDEX = 0;
private const int SALT_INDEX = 1;
private const int PBKDF2_INDEX = 2;
public static string PBKDF2_CreateHash(string password)
{
RNGCryptoServiceProvider csprng = new(); // This reports SYSLIB0023 'RNGCryptoServiceProvider' is obsolete: 'RNGCryptoServiceProvider is obsolete. To generate a random number, use one of the RandomNumberGenerator static methods instead.'
byte[] salt = new byte[SALT_BYTE_SIZE];
csprng.GetBytes(salt);
byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
return PBKDF2_ITERATIONS + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);
}
public static bool PBKDF2_ValidatePassword(string password, string correctHash)
{
char[] delimiter = { ':' };
string[] split = correctHash.Split(delimiter);
int iterations = Int32.Parse(split[ITERATION_INDEX]);
byte[] salt = Convert.FromBase64String(split[SALT_INDEX]);
byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]);
byte[] testHash = PBKDF2(password, salt, iterations, hash.Length);
return SlowEquals(hash, testHash);
}
private static bool SlowEquals(byte[] a, byte[] b)
{
uint diff = (uint)a.Length ^ (uint)b.Length;
for (int i = 0; i < a.Length && i < b.Length; i++)
diff |= (uint)(a[i] ^ b[i]);
return diff == 0;
}
private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
{
Rfc2898DeriveBytes pbkdf2 = new(password, salt)
{
IterationCount = iterations
};
return pbkdf2.GetBytes(outputBytes);
}
}
As far as I can tell, you should be able to replace
using RijndaelManaged AES = new();
with
using var AES = Aes.Create("AesManaged");
note that you might want to change your variable name to avoid naming conflicts or confusion.
The only difference between AES and Rijndael should be that Rijndael allows more blocksizes/keysizes. But you seem to be using 256 bit key and 128 bit blocks, and this should be allowed by AES.
I encrypt password in postgres
and i want to decrypt it in c#, but two ways can not matching
.How can i do that?
private static byte[] TruncateHash(string key, int length)
{
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
// Hash the key.
byte[] keyBytes = System.Text.Encoding.Unicode.GetBytes(key);
byte[] hash = sha1.ComputeHash(keyBytes);
// Truncate or pad the hash.
Array.Resize(ref hash, length);
return hash;
}
public static string EncryptString(string plaintext, string Passphrase)
{
TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider();
// Initialize the crypto provider.
tripleDes.Key = TruncateHash(Passphrase, tripleDes.KeySize / 8);
tripleDes.IV = TruncateHash("", tripleDes.BlockSize / 8);
// Convert the plaintext string to a byte array.
byte[] plaintextBytes = System.Text.Encoding.Unicode.GetBytes(plaintext);
// Create the stream.
System.IO.MemoryStream ms = new System.IO.MemoryStream();
// Create the encoder to write to the stream.
CryptoStream encStream = new CryptoStream(ms, tripleDes.CreateEncryptor(), System.Security.Cryptography.CryptoStreamMode.Write);
// Use the crypto stream to write the byte array to the stream.
encStream.Write(plaintextBytes, 0, plaintextBytes.Length);
encStream.FlushFinalBlock();
// Convert the encrypted stream to a printable string.
return Convert.ToBase64String(ms.ToArray());
}
public static string DecryptString(string encryptedtext, string Passphrase)
{
TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider();
// Initialize the crypto provider.
tripleDes.Key = TruncateHash(Passphrase, tripleDes.KeySize / 8);
tripleDes.IV = TruncateHash("", tripleDes.BlockSize / 8);
// Convert the encrypted text string to a byte array.
byte[] encryptedBytes = Convert.FromBase64String(encryptedtext);
// Create the stream.
System.IO.MemoryStream ms = new System.IO.MemoryStream();
// Create the decoder to write to the stream.
CryptoStream decStream = new CryptoStream(ms, tripleDes.CreateDecryptor(), System.Security.Cryptography.CryptoStreamMode.Write);
// Use the crypto stream to write the byte array to the stream.
decStream.Write(encryptedBytes, 0, encryptedBytes.Length);
decStream.FlushFinalBlock();
// Convert the plaintext stream to a string.
return System.Text.Encoding.Unicode.GetString(ms.ToArray());
}
I found a way to encrypt in postgres using pgcrypto.
And below is encrypt and decrypt in postgres.
SELECT encode(encrypt_iv('ABCDE121212','Key123', '','3des'), 'base64');
select decrypt_iv(decode('jEI4V5q6h5/p12NRJm666g==','base64'),'Key123','','3des')
What's wrong in my code, c# and postgres can't not matching.
I want to keep c# code and change postgres code to matching
Source Url
Encrypt function:
public static String AES_encrypt(String input, string key, string Iv, int keyLength)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = keyLength;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key,keyLength);
aes.IV = mkey(Iv,128);
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
{
byte[] xXml = Encoding.UTF8.GetBytes(input);
cs.Write(xXml, 0, xXml.Length);
cs.FlushFinalBlock();
}
xBuff = ms.ToArray();
}
return Convert.ToBase64String(xBuff,Base64FormattingOptions.None);
}
Decrypt function:
public static String AES_decrypt(String Input, string key, string Iv, int keyLength)
{
try
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = keyLength;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key,keyLength);
aes.IV = mkey(Iv,128);
var decrypt = aes.CreateDecryptor();
byte[] encryptedStr = Convert.FromBase64String(Input);
string Plain_Text;
using (var ms = new MemoryStream(encryptedStr))
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cs))
{
Plain_Text = reader.ReadToEnd();
}
}
}
return Plain_Text;
}
catch (Exception ex)
{
return null;
}
}
Helper function:
private static byte[] mkey(string skey, int keyLength)
{
int length = keyLength / 8;
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = GenerateEmptyArray(length);
for (int i = 0; i < key.Length; i++)
{
//k[i % 16] = (byte)(k[i % 16] ^ key[i]);
k[i] = key[i];
if(i == length-1)
break;
}
return k;
}
Variables:
input = "Hello World"
key = "NBJ42RKQ2vQoYFZO"
Iv = "j1C83921vHExVhVp"
keyLength = 128
Info about variables:
input - string that is not encrypted or encrypted. If it's encrypted it will be in Base64 format
key - Any Unicode character that will match the AES key size(in this example it's 128). I have written a function that will extract the specific length of characters and add them to a byte array
Code:
public static string PasswordFixer(string skey,int keyLength)
{
int length = keyLength / 8;
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = GenerateEmptyArray(length);
for (int i = 0; i < key.Length; i++)
{
k[i] = key[i];
if(i == length-1)
break;
}
return Encoding.UTF8.GetString(k);
}
Iv - it's always 128bit long meaning 16bytes. you can ignore Iv if you want, in PostgreSQL if you planing to use `encrypt` function then you can ignore the Iv by hard coding like this `aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };`
keylength-
This is the AES key length in this example we use 128bit meaning 16 bytes. whatever the characters that you use as the Key need to match the length of 16 bytes.
PostgreSQL
The equivalent SQL statement for the encryption and decryption is this
encrypt_iv,decrypt_iv
select convert_from(decrypt_iv(decode(tbl1.encrypted,'base64')::bytea ,'NBJ42RKQ2vQoYFZO','j1C83921vHExVhVp', 'aes-cbc/pad:pkcs'), 'UTF-8') as decrypted,tbl1.encrypted from (select encode(encrypt_iv('Hello World', 'NBJ42RKQ2vQoYFZO','j1C83921vHExVhVp', 'aes-cbc/pad:pkcs'), 'base64') as encrypted) as tbl1
encrypt,decrypt
select convert_from(decrypt(decode(tbl1.encrypted,'base64')::bytea ,'NBJ42RKQ2vQoYFZO', 'aes-cbc/pad:pkcs'), 'UTF-8') as decrypted,tbl1.encrypted from (select encode(encrypt('Hello World', 'NBJ42RKQ2vQoYFZO', 'aes-cbc/pad:pkcs'), 'base64') as encrypted) as tbl1
By encrypting data using PCLCrypto following this tutorial:
http://www.c-sharpcorner.com/UploadFile/4088a7/using-cryptography-in-portable-xamarin-formswindows-phone/
The only change I made is the salt, that is set by my self:
byte[] salt = new byte[16];
salt = Encoding.UTF8.GetBytes("We##%ds51&s9$$$8");
And I am unable to decrypt the result in a Console application:
public static string Decrypt(string text, byte[] key, int keysize = 128, int blocksize = 128, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = blocksize;
aes.KeySize = keysize;
aes.Mode = cipher;
aes.Padding = padding;
byte[] iv = new byte[16];
iv = Encoding.UTF8.GetBytes("We##%ds51&s9$$$8");
byte[] src = Convert.FromBase64String(text);
using (ICryptoTransform decrypt = aes.CreateDecryptor(key, iv))
{
byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length);
decrypt.Dispose();
return Encoding.UTF8.GetString(dest);
}
}
What am I doing wrong?
Thanks!
We are using below code to encrypt in Java
public encrypt(String text) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH); //256 bit
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
this.ecipher = Cipher.getInstance("AES");
this.ecipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] bytes = encrypt.getBytes("UTF-8");
byte[] encrypted = this.ecipher.doFinal(bytes);
return Base64.encodeBase64String(encrypted);
}
Our vendor is using C# to decrypt the data
His code
string Decrypt(string textToDecrypt, string key)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.ECB;
rijndaelCipher.KeySize = 0x80;
rijndaelCipher.BlockSize = 0x80;
byte[] encryptedData = Convert.FromBase64String(textToDecrypt);
byte[] pwdBytes = Encoding.UTF8.GetBytes(key);
byte[] keyBytes = new byte[0x10];
int len = pwdBytes.Length;
if (len > keyBytes.Length) {
len = keyBytes.Length;
}
Array.Copy(pwdBytes, keyBytes, len);
rijndaelCipher.Key = keyBytes;
byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
return Encoding.UTF8.GetString(plainText);
}
But he's unable to decrypt the data. He's getting some garbage data.
Any idea how to decrypt using C# for Java Encryption part.
First off, Don't have any allusions of security with your java code. ECB mode not a good choice.
Second, the problem with the C# code is that it's using the raw bytes of the passphase for the key rather than PBKDF2WithHmacSHA1 which the java code is using. The class in C# to do the key generation is Rfc2898DeriveBytes