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.
Related
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.
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.
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.
I have created two methods in C#:
public static string Encrypt(string clearText, string encryptionKey)
{
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
var 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 (var ms = new MemoryStream())
{
using (var 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)
{
try
{
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
var 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 (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
}
catch (Exception)
{
}
return cipherText;
}
By following the steps given in This Link, I have created a CLR function in SQL Server and I am trying to call it like:
SELECT dbo.Decrypt(MyEncrypted, EncryptionKey)
FROM MyTable
The problem is, it is taking TOO MUCH TIME. Like for only 1000 rows, it took 1.5 minutes. If I call my query without the CLR function, it took less than 1 second.
Is there any thing I can do to improve the performance of the CLR functions?
I've analyzed your Decrypt method with the Performance Analyzer in VS2010 by running it 100 times:
As you can see the GetBytes method of the Rfc2898DeriveBytes instance take the most time.
I'm not sure why you have these specific encryption/decryption requirements but one way influence the time the GetBytes method takes is to instantiate the Rfc2898DeriveBytes using the constructor that takes iterations as the third parameter. Default it is on 1000, I can set it as low as 1. BUT THIS IS STRONGLY ADVISED AGAINST
var pdb = new Rfc2898DeriveBytes(encryptionKey, salt, 10);
This iterations does need to be the same for both the Encrypt and Decrypt so you'll have to Decrypt\Encrypt your current values if you want to change that.
One other option might be to cache the IV values as seems to recommended in this answer. I'm not enough of an expert if they talk there about using the same Key as well but if that is an option you might cache the call to GetBytes for the key as well.
All the described change have impact on how your data is encrypted and the strength of your encryption. Consider both impacts when testing the solutions.
I have created a simple program for encryption and decryption. I am using the AESManaged class in this program. The key and IV are derived from predefined password using Rfc2898DeriveBytes.
I tested my program as explained below:
Inputted sample text into my program and captured encrypted text.
Inputted same encrypted text into program and verified decrypted text is same as my original text.
Modified the letter before "=" in the encrypted text from step2 (into next alphabet) and decrypted using same key ad IV. and I received my original text back.
On step #3 I was expecting an error from the program but it decrypted the wrong text.
Can some one please help me to understand what is going wrong in my program and how to stop my program from decrypting wrong data.
Here is my program output:
Please put in input message
Some Text
Encrypted text is "xJzgOiMzimNOY6UsB+TNw9gUmcpdiZxQq70FxwbmkCc="
Please put in encrypted text to decrypt
xJzgOiMzimNOY6UsB+TNw9gUmcpdiZxQq70FxwbmkCc=
Decrypted text is "Some Text"
Please put in encrypted text to decrypt <<here I have modified "c=" to "d=">>
xJzgOiMzimNOY6UsB+TNw9gUmcpdiZxQq70FxwbmkCd=
Decrypted text is "Some Text"
Enter "Exit" to exit!
AesExample.cs:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Aes_Example
{
class AesExample
{
public static void Main()
{
string action, plainText, encryptedText, decryptedText;
Begin:
Console.WriteLine("Please put in input message");
plainText = Console.ReadLine();
encryptedText = Encrypt(plainText);
Console.WriteLine("Encrypted text is \"{0}\"", encryptedText);
Console.WriteLine("Please put in encrypted text to decrypt");
encryptedText = Console.ReadLine();
decryptedText = Decrypt(encryptedText);
Console.WriteLine("Decrypted text is \"{0}\"", decryptedText);
Console.WriteLine("Please put in encrypted text to decrypt");
encryptedText = Console.ReadLine();
decryptedText = Decrypt(encryptedText);
Console.WriteLine("Decrypted text is \"{0}\"", decryptedText);
Console.WriteLine("Enter \"Exit\" to exit!");
action = Console.ReadLine();
if (action.ToUpper() != "EXIT") { goto Begin; }
}
public static string Encrypt(string clearText)
{
string EncryptionKey = "TESTPWD##52";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (AesManaged encryptor = new AesManaged())
{
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 = "TESTPWD##52";
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (AesManaged encryptor = new AesManaged())
{
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;
}
}
}
That last =" part of your string is padding.
And not even cryptographic padding but Base64 padding.
The short version:
byte[] data = { 1, 2, 3, 4 };
clearText = Convert.ToBase64String(data);
cipherText = clearText.Replace("A==", "B=="); // crude test
byte[] cipherBytes = Convert.FromBase64String(cipherText);
After this, cipherBytes will still be { 1, 2, 3, 4 }
Base64 encoding uses 6 bits/char, so when you have N bytes it will need (N*8)/6 chars. Usually this means that there are some left-over bits and the last char has some room to play. But only a little and only in the last char.
Besides what is said you need to realize that low-level encryption and decryption is just a bit transformation. It doesn't know what input and output is and what it should be. I.e. if you encrypt 16 bytes, modify the 5th byte and decrypt those 16 bytes back, you will get output which doesn't correspond to the data you encrypted, but you won't get an error either.
For changes to be detected you need to employ some kind of integrity checking. This is usually done in high-level encryption schemes such as OpenPGP or CMS or protocols like SSL/TLS and SSH.