Rfc2898DeriveBytes equivalent in node js - c#

Trying to translate the below C# code to nodeJS.But the results are not same. Need some help here:
C# code:
public static string Encrypt(string clearText)
{
string EncryptionKey = "abcdef";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new
Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
Code tried in JS:
const crypto = require('crypto'),
algorithm = 'aes-256-cbc',
secret = 'abcdef',
keystring = crypto.createHash('sha256').update(String(secret)).digest('base64').substr(0, 32);
iv = crypto.createHash('sha256').update(String(secret)).digest('base64').substr(0, 16);
inputEncoding = 'utf8',
outputEncoding = 'base64';
function encrypt(text) {
let cipher = crypto.createCipheriv('aes-128-cbc', keystring, iv);
let encrypted = cipher.update(text, inputEncoding, outputEncoding);
encrypted += cipher.final(outputEncoding);
return encrypted;
}
Looking for the compatible version in node js

The parameters for PBKDF2 are:
const secret = 'abcdef';
const salt = Buffer.from([0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76]);
const iterations = 1000;
const length = 32 + 16;
const digest = 'sha1';
const keyIV = crypto.pbkdf2Sync(secret, salt, iterations, length, digest);
The first 32 bytes of keyIV are the key, the following 16 bytes the IV:
const key = keyIV.slice(0, 32);
const iv = keyIV.slice(32, 32 + 16);
In the encrypt method, UTF-16LE must be used as input encoding (utf16le) and aes-256-cbc as algorithm/mode:
const inputEncoding = 'utf16le';
const outputEncoding = 'base64';
const algorithm = 'aes-256-cbc';
function encrypt(text) {
let cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text, inputEncoding, outputEncoding);
encrypted += cipher.final(outputEncoding);
return encrypted;
}
Test:
console.log(encrypt("The quick brown fox jumps over the lazy dog")); // u9JQyr8G0FbU1D+TF1ZmXNK8cMZjhL7xbhYitmXVaWNDu1hDiBY6TNUjKcza4fX99xN0f1tT3qSNHMGWTV8o2QkDnVqNQai07qAX4R2iSW/j4E8CKGwVOCWnQ46HbfWs
in accordance with the result of the C# code.

Related

C# AES encryption algorithm to Python3

Hello I need to pass a C # encryption algorithm to python, but I can not get the same result in the final hash, would anyone know tell me what I am doing wrong?
This is the C# AES Cipher code:
using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;
public class Program
{
public static void Main()
{
string data = "leandro";
string encrypt = Encrypt(data);
Console.WriteLine(encrypt);
}
static readonly char[] padding = { '=' };
private const string EncryptionKey = "teste123";
public static string Encrypt(string clearText)
{
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
Console.WriteLine(clearBytes);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray()).TrimEnd(padding).Replace('+', '-').Replace('/', '_');
}
}
return clearText;
}
}
Output: DTyK3ABF4337NRNHPoTliQ
And this is my python version:
import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
class AESCipher(object):
def __init__(self, key, interactions=1000):
self.bs = AES.block_size
self.key = key
self.interactions = interactions
def encrypt(self, raw):
raw = self._pad(raw)
nbytes = [0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76,
0x65, 0x64, 0x65, 0x76]
salt = bytes(nbytes)
keyiv = PBKDF2(self.key, salt, 48, self.interactions)
key = keyiv[:32]
iv = keyiv[32:48]
cipher = AES.new(key, AES.MODE_CBC, iv)
enc = base64.b64encode(iv + cipher.encrypt(raw.encode('utf-16le')))
return self._base64_url_safe(str(enc, "utf-8"))
def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * \
chr(self.bs - len(s) % self.bs)
def _base64_url_safe(self, s):
return s.replace('+', '-').replace('/', '_').replace('=', '')
#staticmethod
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]
enc = AESCipher("teste123")
dec = enc.encrypt("leandro")
print(dec)
Output:
LJTFEn0vmz8IvqFZJ87k8lI8DPh8-oIOSIxmS5NE4D0
You are using Encoding.Unicode.GetBytes(clearText) which returns UTF-16LE while Python (more sensibly) defaults to UTF-8 for raw.encode(). I'd use Encoding.UTF8 for your C# code.
As already mentioned in the comments, the Python also adds the IV in front of the ciphertext, while the C# code simply performs the encryption and calculates the IV during decryption (so it doesn't need to be stored).
Here is a Python program that does the same:
import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Util.Padding import pad
class AESCipher(object):
def __init__(self, key, interactions=1000):
self.bs = AES.block_size
self.key = key
self.interactions = interactions
def encrypt(self, raw):
nbytes = [0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76,
0x65, 0x64, 0x65, 0x76]
salt = bytes(nbytes)
keyiv = PBKDF2(self.key, salt, 48, self.interactions)
key = keyiv[:32]
iv = keyiv[32:48]
cipher = AES.new(key, AES.MODE_CBC, iv)
encoded = raw.encode('utf-16le')
encodedpad = pad(encoded, self.bs)
ct = cipher.encrypt(encodedpad)
cip = base64.b64encode(ct)
return self._base64_url_safe(str(cip, "utf-8"))
def _base64_url_safe(self, s):
return s.replace('+', '-').replace('/', '_').replace('=', '')
enc = AESCipher("teste123")
dec = enc.encrypt("leandro")
print(dec)
Before you shout Eureka, please do understand that the ciphertext that the C# code is producing is not integrity protected nor authenticated. Moreover, it is vulnerable to padding oracle attacks if those are present at the receiver. Padding oracle attacks are terribly efficient and you would loose complete confidentiality of the message if they apply.
Furthermore, if the salt is non-random then the key and IV are also non-random. That in turn means that the ciphertext is as random as the plaintext. In other words, it leaks data if the same plaintext blocks are encountered. So I hope the non-random salt is just there for testing purposes.
In the end, having encryption running as expected doesn't mean that your solution is secure.

Encrypting a string in .net and decrypting it in python

I'm trying to encrypt password using system.Security.Cryptography which is working properly
This is the code (.Net)
if (clearText == null)
{
clearText = "";
}
string EncryptionKey = "****";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
And this is decryption code in python which is not working properly
def Decryptstr(self, text):
try:
EncryptionKey = "****"
if text is None:
return
else:
cipherbytes = base64.b64decode(text)
salt = '\0x49\0x76\0x61\0x6e\0x20\0x4d\0x65\0x64\0x76\0x65\0x64\0x65\0x76'
key_bytes = KDF.PBKDF2(EncryptionKey, salt, dkLen=32)
iv = KDF.PBKDF2(EncryptionKey, salt,dkLen=16)
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
password = cipher.decrypt(cipherbytes).decode('utf-16')
print(password)
return password
except Exception as err:
print(err)
following is the output of the above code for the encrypted string ('eet123')
䏺꧴퐄妯৞軸힡薟
Any help will be appreciated.
It seems that your PBKDF2HMAC key extraction in python side is incorrect. You need to pass correct parameters and get a 48 bytes key. Then use first 32 bytes as Key and last 16 bytes as IV (in your design).
Here is a working C#/Python code pair. First C# part:
static string encrypt(string clearText = "")
{
if (clearText == null)
{
clearText = "";
}
string EncryptionKey = "****";
byte[] clearBytes = Encoding.UTF8.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }, 100000, HashAlgorithmName.SHA1);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
encryptor.Mode = CipherMode.CBC;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
About this C# code:
By default Rfc2898DeriveBytes uses SHA1 and only 1000 rounds. My suggestion is that you should use at least 100,000. I have seen code apps that use 1,000,000 rounds. You can change hash too if you like, but number of rounds is more important.
Specify mode too. Even though it uses CBC by default, I think it is better to specify it.
Since C# uses key length to select AES algorithm, this code uses AES-256.
Now Python part:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
def Decryptstr(self, text):
try:
if text is None:
return
else:
backend = default_backend()
EncryptionKey = "****"
salt = bytes([ 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 ])
kdf = PBKDF2HMAC(algorithm=hashes.SHA1(),length=48,salt=salt,iterations=100000,backend=backend)
key_bytes = kdf.derive(bytes(EncryptionKey, 'utf-8'))
key = key_bytes[0:32]
iv = key_bytes[32:]
cipherbytes = base64.b64decode(text)
cipher = AES.new(key, AES.MODE_CBC, iv)
password = cipher.decrypt(cipherbytes).decode('utf-8')
print(password)
return password
except Exception as err:
print(err)
As you see, I used another PBKDF2HMAC library. I used it to create 48 bytes and used first 32 as Key and last 16 bytes as IV.

Sending Form URL in the mail link using asp.net

I have a requirement where I want to send the User the link of the form via Email. The link for every user will be different. Currently I am just passing hardcode value in the mail.
Here is my code.
sb.AppendLine("Please click on the link to view the details.");
sb.AppendLine("</br></br>");
sb.AppendLine("<a href='" + ConfigurationManager.AppSettings["ClickHereLink"].ToString() + "' >Click here to open request</a>. ");
sb.AppendLine("</br></br>");
sb.AppendLine("<b>This is a system generated mail. Please do not reply to the sender.</b>");
And the ClickHereLink is been defined in Web.config like below
<add key="ClickHereLink" value="https://www.test.com"/>
How can I send dynamic link for every user based on the details
Encryption Method
public static class EncryptDecrypt
{
public static string Encrypt(string clearText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
public static string Decrypt(string cipherText)
{
string EncryptionKey = "MAKV2SPBNI99212";
cipherText = cipherText.Replace(" ", "+");
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
}
I am assuming that you are using .Net 4.x, and if so Try this,
if you are not using 4.x then where I have used string interoperlation then you should change that for string.Format("...", ...);
void Main()
{
var urlStr = new StringBuilder();
var url = "http://www.yoursite.com";
urlStr.Append($"{url}?{BuildQs(Request.QueryString)}");
}
private string BuildQs(System.Collections.Specialized.NameValueCollection collection)
{
var sb = new StringBuilder();
foreach (var key in collection.Keys)
{
var value = collection[key.ToString()].ToString();
sb.Append($"{key}={value}");
}
var enc = new EncryptDecrypt();
return enc.Encrypt(sb.ToString());
}
public class EncryptDecrypt
{
public string Encrypt(string clearText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
public static string Decrypt(string cipherText)
{
string EncryptionKey = "MAKV2SPBNI99212";
cipherText = cipherText.Replace(" ", "+");
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
}

Invalid length for a Base-64 char array error

As the title says, I am getting:
Invalid length for a Base-64 char array.
So what I am doing is that i construct the email for delivery using the below method:
smsg += "<br><b>To complete your registration, verify your email</b>" + "<br>HERE!";
The Encrypt method looks like this:
private static string Encrypt(string clearText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
Here is what the HTML looks like in hotmail:
http://localhost:30850/Activation.aspx?username=9mQkc5vEebsl2Qg6hbxL+r3KVNhOEsig32oP6eb6rd0=
On the receiving end, the Activation.aspx.cs page has the line:
String username = Request.QueryString["username"].ToString();
Label1.Text = Decrypt(username);
And also the Decryption Method:
private string Decrypt(string cipherText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
So basically when I input username like foe example Jonathan it have no problem displaying Jonathan on the receiving end label. However, if i input anything that is not a multiple of 4 i get error. I understand that Base64 takes in multiple of 4, if it has 7 characters it will add space to it. Is there any solution to this? Thank you very much.
The base64 username string is padded with an '=' at the end; this is ignored when the URL query string is being interpreted. ie:
username => "9mQkc5vEebsl2Qg6hbxL+r3KVNhOEsig32oP6eb6rd0="
becomes:
username => 9mQkc5vEebsl2Qg6hbxL+r3KVNhOEsig32oP6eb6rd0
in the request handler.
You need to URL encode the string before using it in a URL.
Default set of Base64 characters is not safe to use in Url, especially + (space) and = (name/value separator for query params).
You can use modified set (manually replace some characters with other once) or carefully encode your data to make sure each character is preserved (+ in particular as ASP.Net treats it as space).
See Base64 on Wikipedia for links to specifications and Passing base64 encoded strings in URL for similar question in PHP.

Corresponding T-SQL to perform same encryption/decryption as C# code

Could anyone provide an example of performing exactly the same encryption/decryption as the C# code below, but with T-SQL?
The below functions are used to encrypt/decrypt given strings for later storage in a database. But I'm just curious to know whether there is a T-SQL-way of doing the exact same thing.
Note: The value of the string variable "EncryptionKey" is made up for this specific purpose
private string Encrypt(string clearText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
private string Decrypt(string cipherText)
{
string EncryptionKey = "MAKV2SPBNI99212";
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}

Categories

Resources