Compatibility between AesJS and C# System.Security.Cryptography - c#

I'm developing an application in NodeJS that consums a C# API. And I want to implement securized calls with AES. I'm using AesJS in NodeJS and System.Security.Cryptography in C#.
The C# part is used in other parts of the application and works correctly, so I'm guessing my error is in the NodeJS part.
The error is:
Invalid length for a Base-64 char array or string.
My code:
NodeJS
encryptData = function(data) {
var key = pbkdf2.pbkdf2Sync(config.aes.pass, config.aes.salt, 1, 128 / 8, null);
var dataBytes = aesjs.utils.utf8.toBytes(data);
var ivBytes = aesjs.utils.utf8.toBytes(config.aes.iv);
var aesOfb = new aesjs.ModeOfOperation.ofb(key, ivBytes);
var encryptedBytes = aesOfb.encrypt(dataBytes);
var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
return encryptedHex;
}
C#
public static string DecodeAndDecrypt(string encrypted)
{
using (var csp = Aes.Create())
{
var d = GetCryptoTransform(csp, false);
byte[] output = Convert.FromBase64String(encrypted);
byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length);
string decypted = Encoding.UTF8.GetString(decryptedOutput);
return decypted;
}
}
The error happens in this line:
byte[] output = Convert.FromBase64String(encrypted);
UPDATE
In order to test what's commented, I added parse to base64 in NodeJS:
return Buffer.from(encryptedHex).toString('base64');
Now I'm getting one step ahead into:
byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length);
And it returns error:
The input data is not a complete block.

Related

Postgresql decrypting data encrypted on C#

So, I'm trying to decrypt a data on Postgres that was encrypted on C#.
On both sides I'm trying to have the same specification:
Algorithm: DES
Mode: ECB
Padding: PKCS
Key: md5 hashed bytes from string
Well, I couldn't find on Postgres docs anything about DES algorithm and "Postgres PKCS" and "C# PKCS7", while reading pgcrypto
So, for my tests I wrote an small sample code in C#, here:
using System;
public class Program
{
public static void Main()
{
System.Text.StringBuilder sbHash = new System.Text.StringBuilder();
System.Security.Cryptography.MD5CryptoServiceProvider md5provider = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] keyBytes = md5provider.ComputeHash(new System.Text.UTF8Encoding().GetBytes("myCriptoKey"));
string myText = "testing texts";
for (int i = 0; i < keyBytes.Length; i++)
{
sbHash.Append(keyBytes[i].ToString("x2")); //X2 for uppercase
}
Console.WriteLine("MD5 1: " + sbHash.ToString());
Console.WriteLine("MD5 2: " + BitConverter.ToString(keyBytes).Replace("-","").ToLower());
var criptoProvider = new System.Security.Cryptography.TripleDESCryptoServiceProvider
{
Mode = System.Security.Cryptography.CipherMode.ECB,
Key = keyBytes,
Padding = System.Security.Cryptography.PaddingMode.PKCS7
};
byte[] bytesFroMyText = System.Text.Encoding.UTF8.GetBytes(myText);
byte[] bytesEncrypted = criptoProvider.CreateEncryptor().TransformFinalBlock(bytesFroMyText, 0, bytesFroMyText.Length);
string result = Convert.ToBase64String(bytesEncrypted);
Console.WriteLine("To B64: " + result);
bytesFroMyText = Convert.FromBase64String(result);
byte[] bytesDecrypted = criptoProvider.CreateDecryptor().TransformFinalBlock(bytesFroMyText, 0, bytesFroMyText.Length);
result = System.Text.Encoding.UTF8.GetString(bytesDecrypted);
Console.WriteLine("From B64: " + result);
}
}
Generating the following output:
MD5 1: 0a76defbd66108b4d01405901f752604
MD5 2: 0a76defbd66108b4d01405901f752604
To B64: +hib+YR+/a0JuUhVm0TiJQ==
From B64: testing texts
So, as you can notice, my encrypted result after convert to base 64 is "+hib+YR+/a0JuUhVm0TiJQ==".
But I couldn't generate the same result on Postgres, trying the following:
SELECT ENCODE(ENCRYPT('testing texts', 'myCriptoKey', 'des-ecb/pad:pkcs')::bytea, 'base64');
SELECT ENCODE(ENCRYPT('testing texts', 'myCriptoKey'::bytea, 'des-ecb/pad:pkcs')::bytea, 'base64');
SELECT ENCODE(ENCRYPT('testing texts', MD5('myCriptoKey')::bytea, 'des-ecb/pad:pkcs')::bytea, 'base64');
SELECT ENCODE(ENCRYPT('testing texts', DIGEST('myCriptoKey', 'md5')::bytea, 'des-ecb/pad:pkcs')::bytea, 'base64');
Results are:
B5/rqJsOUBdMFQDgqYT9YA==
B5/rqJsOUBdMFQDgqYT9YA==
XQMiNJStZx/+xLvrLH8U7A==
0F/aYK60eaiYkI2T+IPaMA==
So if I use C# generated base64 string on any sql DECODE matching all tried encryption above, like:
SELECT CONVERT_FROM(DECRYPT(DECODE('+hib+YR+/a0JuUhVm0TiJQ==','base64')::bytea, DIGEST('myCriptoKey', 'md5')::bytea, 'des-ecb/pad:pkcs'), 'UTF-8')
Result is:
ERROR: invalid byte sequence for encoding "UTF8": 0xff
SQL state: 22021
So, my question is. How to decrypt on Postgres the generated data on C# script above.

SHA3 CryptoJS equivalent in C#

I have data encrypted with CryptoJS using SHA3 like this example. I try to write in C# using SHA3.NET, a BouncyCastle wrapper.
using (var shaAlg = SHA3.Net.Sha3.Sha3512())
{
var hash = shaAlg.ComputeHash(Encoding.UTF8.GetBytes(data));
var hashString = BitConverter.ToString(result).Replace("-", "").ToLowerInvariant();
}
Using the BouncyCastle directly generates the same result as above (not matched CryptoJS).
var hashAlgorithm = new Org.BouncyCastle.Crypto.Digests.Sha3Digest(512);
byte[] input = Encoding.ASCII.GetBytes(data);
hashAlgorithm.BlockUpdate(input, 0, input.Length);
byte[] result = new byte[64];
hashAlgorithm.DoFinal(result, 0);
string hashString = BitConverter.ToString(result).Replace("-", "").ToLowerInvariant();
When passing data parameter abc the result should be 18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96 but the result is b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0

Encrypt from C# just like SQL Server EncryptByPassPhrase?

After following the answer of this question, I was able to recreate the DecryptByPassPhrase function of SQL. Now I am trying to do the EncryptByPassPhrase function.
We have different SQL version on different server which cannot be upgrade to the same version at the same time, so we want to create our c# function (with SQL CLR).
I need to encode with SHA1 (like a SQL 2012) since the SQL on the other server might not be upgraded to 2017 yet
Here is my c#, the function compile, but I can't decrypt it with my other c# function (which is exactly the function of the answer of the other question)
int keySize = 16;
var cryptoAlgo = TripleDES.Create();
cryptoAlgo.Padding = PaddingMode.PKCS7;
cryptoAlgo.Mode = CipherMode.CBC;
cryptoAlgo.GenerateIV(); //cryptoAlgo.IV = StringToByteArray(value.Substring(8, 16));
//cryptoAlgo.IV = StringToByteArray("7854E155CEE338D5");
var valueInByte = Encoding.Unicode.GetBytes(value); //UTF8Encoding.UTF8.GetBytes(value); //encrypted = StringToByteArray(value.Substring(24));
byte[] passwordBytes = Encoding.Unicode.GetBytes(password);
var hashAlgo = SHA1.Create();
hashAlgo.TransformFinalBlock(passwordBytes, 0, passwordBytes.Length);
cryptoAlgo.Key = hashAlgo.Hash.Take(keySize).ToArray();
byte[] encrypted = cryptoAlgo.CreateEncryptor().TransformFinalBlock(valueInByte, 0, valueInByte.Length);
//byte[] encryptedData = encrypted.Skip(8).ToArray();
//bool isUtf16 = (Array.IndexOf(encryptedData, (byte)0) != -1);
//string encryptedText = (isUtf16 ? Encoding.Unicode.GetString(encryptedData) : Encoding.UTF8.GetString(encryptedData));
return new SqlString(encryptedText);
//return new SqlString(encryptedText);
//return Convert.ToBase64String(encrypted, 0, encrypted.Length);
//return Convert.ToBase64String(encryptedData, 0, encryptedData.Length);
//return "0x01000000" + Convert.ToBase64String(cryptoAlgo.IV, 0, cryptoAlgo.IV.Length) + Convert.ToBase64String(encrypted, 0, encrypted.Length);
//return "0x01000000" + Convert.ToBase64String(encryptedData, 0, encryptedData.Length);
there a lot of different return that i've tried (they are in comment). I noticed that SQL return "0x01000000" and I've guess that the next 16 character are the IV so i've tried to add them, with no luck
Had a similar requirement, note that simply encrypting the data is not enough, your final byte array should be structured as follows:
First four bytes are the version: 2 0 0 0
Next 16 bytes are the
initialization vector: cryptoAlgo.IV
The remainder is the encrypted data, structured (prior to encrypting) as follows:
First
four bytes are the Magic value: 13, 240, 173, 186 (0xbaadf00d)
Next 2
bytes are 0
Next 2 bytes are the length of the value bytes
The remainder is the value bytes
Code:
public static class SqlCryptographyExtensions
{
const uint MagicPrefix = 0xbaadf00d;
public static byte[] EncryptByPassPhrase(this string value, string passPhrase)
{
var keyBytes = Encoding.Unicode.GetBytes(passPhrase);
// Depending on whether you're working with NVARCHAR/VARCHAR on SQL Server, use Unicode/UTF encoding here
var valueBytes = Encoding.UTF8.GetBytes(value);
var payload = new List<byte>();
payload.AddRange(BitConverter.GetBytes(MagicPrefix));
payload.AddRange(BitConverter.GetBytes((UInt16)0));
payload.AddRange(BitConverter.GetBytes((UInt16)valueBytes.Length));
payload.AddRange(valueBytes);
var payloadBytes = payload.ToArray();
HashAlgorithm hash = SHA256.Create();
SymmetricAlgorithm encryption = Aes.Create();
encryption.GenerateIV();
encryption.Padding = PaddingMode.PKCS7;
encryption.Mode = CipherMode.CBC;
hash.TransformFinalBlock(keyBytes, 0, keyBytes.Length);
encryption.Key = hash.Hash.Take(32).ToArray();
byte[] encryptedPayload = encryption.CreateEncryptor().TransformFinalBlock(payloadBytes, 0, payloadBytes.Length);
byte[] version = new byte[] { 2, 0, 0, 0 };
var encryptedBytes = new List<byte>();
encryptedBytes.AddRange(version);
encryptedBytes.AddRange(encryption.IV);
encryptedBytes.AddRange(encryptedPayload);
return encryptedBytes.ToArray();
}
}

c# Bouncy Castle Blowfish Decryption - Pad block corrupted

I am trying to decrypt a blowfish encrypted string with Bouncycastle in C#.
I am able to easily encrypt and decrypt my own string but, unfortunately, I have to decrypt a string that is generated by another system.
I AM able to recreate that same string with C# / Bouncycastle using the following but I have yet to decrypt it successfully.
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
...
static readonly Encoding Encoding = Encoding.UTF8;
public string BlowfishEncrypt(string strValue, string key)
{
try
{
BlowfishEngine engine = new BlowfishEngine();
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(engine);
KeyParameter keyBytes = new KeyParameter(Encoding.GetBytes(key));
cipher.Init(true, keyBytes);
byte[] inB = Encoding.GetBytes(strValue);
byte[] outB = new byte[cipher.GetOutputSize(inB.Length)];
int len1 = cipher.ProcessBytes(inB, 0, inB.Length, outB, 0);
cipher.DoFinal(outB, len1);
return BitConverter.ToString(outB).Replace("-", "");
}
catch (Exception)
{
return "";
}
}
Below is what I have for decryption at the moment. The line that fails with error "pad block corrupted" is cipher.DoFinal(out2, len2);
public string BlowfishDecrypt(string name, string keyString)
{
BlowfishEngine engine = new BlowfishEngine();
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(engine);
StringBuilder result = new StringBuilder();
cipher.Init(false, new KeyParameter(Encoding.GetBytes(keyString)));
byte[] out1 = Convert.FromBase64String(name);
byte[] out2 = new byte[cipher.GetOutputSize(out1.Length)];
int len2 = cipher.ProcessBytes(out1, 0, out1.Length, out2, 0);
cipher.DoFinal(out2, len2); //Pad block corrupted error happens here
String s2 = BitConverter.ToString(out2);
for (int i = 0; i < s2.Length; i++) {
char c = s2[i];
if (c != 0) {
result.Append(c.ToString());
}
}
return result.ToString();
}
Any idea what I might be doing wrong in BlowfishDecrypt()?
Note:
I converted the above (encrypt and decrypt) from a bouncycastle Java example I found somewhere; the encrypt works. The only difference I can see is that the Java example uses a StringBuffer where I use a StringBuilder.
Thank you, Artjom B!
byte[] out1 = Convert.FromBase64String(name);
Should have been
byte[] out1 = Hex.Decode(name);
From there, all I had to do was convert the Hex to a string.

PHP & C# compatible Rijndael managed CBC mode, 256 bit encryption/decryption

This is my very first attempt at cryptography and I am having trouble with porting the encryption from PHP to C#.
I had searched the internet for a working solution to my problem but everything I have tried does not work. I am getting different results between the two languages.
In PHP I have the following code:
function encrypt($Key, $strToEncrypt){
$md5Key = md5(pack("H*", $Key));
$md5Iv = md5($Key);
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$padding = $block - (strlen($strToEncrypt) % $block);
$strToEncrypt .= str_repeat(chr($padding), $padding);
$enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $md5Key, $strToEncrypt, MCRYPT_MODE_CBC, $md5Iv);
$enc2 = base64_encode($enc);
return $enc2;
}
and in C# the following code:
public string Encrypt(string strToEncrypt)
{
string ret;
var pKey = PackH(_appkey);
var md5Key = CalcMd5(pKey);
var iv = CalcMd5(_appkey);
var enc =Encoding.UTF8;
var eIv = enc.GetBytes(iv);
var eKey = enc.GetBytes(md5Key);
using (var rij = new RijndaelManaged { BlockSize = 256, KeySize = 256, IV = eIv, Key = eKey, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros})
using (var memoryStream = new MemoryStream())
using (var cryptoStream = new CryptoStream(memoryStream, rij.CreateEncryptor(eKey, eIv), CryptoStreamMode.Write))
{
using (var sw = new StreamWriter(cryptoStream))
{
sw.Write(strToEncrypt);
}
ret = Convert.ToBase64String(memoryStream.ToArray());
}
return ret;
}
The C# Pack function:
protected byte[] PackH(string hex)
{
if ((hex.Length % 2) == 1) hex += '0';
var bytes = new byte[hex.Length / 2];
for (var i = 0; i < hex.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
And the C# CalcMd5 function:
protected string CalcMd5(string textToEnc)
{
var sB = new StringBuilder();
using (var mdHash = MD5.Create())
{
var cHash = mdHash.ComputeHash(Encoding.UTF8.GetBytes(textToEnc));
foreach (byte t in cHash)
{
sB.Append(t.ToString("x2"));
}
}
return sB.ToString();
}
I have another CalcMd5 function that takes in a byte[] (it is like the one above but does not have the GetBytes part).
The keys and the string that needs encrypting are the same both in PHP and C#:
The Key: "24acd2fcc7b20b8bd33ff45176f03061a09b729487e10d2dd38ab917" and
The string that I want to encode: "110114135AB96637711100"
In C# the result of the function is:"LHTqpxCJrONmbDdUFHyUZZUVf94z1RmSXWo85/wyEew=" while in PHP is: "5MkCjfs0vp2HSKdY5XPUAuV68YsrP31Q+ddZsd5p7Sc=".
I have tried modifying the padding mode in C#, also tried different methods found on the stackoverflow site but none of them works.
I have checked and the final key and Iv that are passed to the mcrypt function and RijndaelManaged function are the same and both have 32 byte size.
The oddly part is that the decryption functions are working very well (it is working to decrypt the PHP encrypted string with C# function and the other war around C# encrypted string is decrypted with the PHP function).
Could it be a problem with the encoding? Or maybe the padding? Or is there something else that I have overlooked?
The problem seems to be your padding, on PHP-side you are manually doing PKCS7-Padding:
$padding = $block - (strlen($strToEncrypt) % $block);
$strToEncrypt .= str_repeat(chr($padding), $padding);
And on C#-side you are using:
Padding = PaddingMode.Zeros
To fix this you could either modify the PHP-code by removing the above mentioned two lines since mcrypt() does automatically do ZeroBytePadding for you.
Or you could change the padding in C# to:
Padding = PaddingMode.PKCS7

Categories