verify js pbkdf2-sha256 hash with python passlib - javascript

I encrypt a random string in python file and hashit in javascript using pbkdf2-sha256 but some how the verification fails with python passlib . any idea why?
my python code is :
from passlib.hash import pbkdf2_sha256
import os,random,string
t = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in
range(7))
h = os.popen('node
D:/mojtaba/repos/restaurant_app/frontend/node_modules/a.js '+t).read()[:-2]
a = pbkdf2_sha256.verify(t,h)
print(a)
and js file contains :
var pbkdf2 = require('pbkdf2-sha256');
var salt = 'WitFCKH03htDKAVA6L3Xmg';
var algorithm = "pbkdf2-sha256";
var iterations = 29000;
var hashed = pbkdf2(process.argv[2], new Buffer(salt), iterations,
32).toString('base64');
var finalPass = '$'+algorithm +'$'+ iterations +'$'+ salt +'$'+ hashed;
console.log(finalPass)
and result is always false

Related

How to get raw output from SHA1 using JS as PHP does?

I'm trying to dynamically generate a security header at Postman pre-request script. To do so, I need to transform the following code snippet from PHP to JS.
$password = "SECRETPASSWORD";
$nonce = random_bytes(32);
date_default_timezone_set("UTC");
$created = date(DATE_ATOM);
$encodedNonce = base64_encode($nonce);
$passwordHash = base64_encode(sha1($nonce . $created . sha1($password, true), true));
(Note the true flag at php's sha1() function, forcing raw output).
I've coded this code snippet so far:
var uuid = require('uuid');
var CryptoJS = require('crypto-js');
var moment = require('moment');
// Generate messageId
var messageId = uuid.v4();
pm.environment.set('messageId', messageId);
// Generate nonce
var nonce = uuid.v4();
var encodedNonce = CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(nonce)
);
pm.environment.set('nonce', encodedNonce);
// Generate created
var created = moment().utc().format();
pm.environment.set('created', created);
// Generate password hash
var password = 'SECRETPASSWORD';
var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");
var passwordHash = CryptoJS.SHA1(nonce + created + rawSha1Password).toString(CryptoJS.enc.Base64);
pm.environment.set('passwordHash', passwordHash);
My JS script is almost working, the only problem seems to be the sha1 generation. Taking the following example values:
password: SECRETPASSWORD
nonce: 55d61876-f882-42f0-b390-dc662a7e7279
created: 2021-01-21T18:19:32Z
The output from PHP is:
encodedNonce: NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash: olI18mUowhmeCwjb1FJNHtTHYDA=
But, the output from JS is:
encodedNonce: NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash: tk/uYkL/3Uq0oIkYO0nlBGnV/0E=
As you can see, the encodedNonce is built correctly; however the passwordHash value is different. As I'm using Postman, I have a limited JS libraries available.
Taking this into account, how can I get the same result as the PHP one?
In the line
var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");
the password hash is read into a buffer and then UTF-8 decoded. The UTF-8 decoding generally corrupts the data, see here. A possible solution is to concatenate the WordArrays instead of the strings:
function getPasswordHash(test){
// Generate nonce
var nonceWA = !test ? CryptoJS.lib.WordArray.random(32) : CryptoJS.enc.Utf8.parse('55d61876-f882-42f0-b390-dc662a7e7279');
console.log('nonce (Base64): ' + nonceWA.toString(CryptoJS.enc.Base64));
// Generate created
var created = !test ? moment().utc().format('YYYY-MM-DDTHH:mm:ss[Z]') : '2021-01-21T18:19:32Z';
var createdWA = CryptoJS.enc.Utf8.parse(created);
console.log('created: ' + created);
// Hash password
var pwd = 'SECRETPASSWORD';
var pwdHashWA = CryptoJS.SHA1(pwd);
// Hash nonce + created + pwd
var passwordHash = CryptoJS.SHA1(nonceWA.concat(createdWA).concat(pwdHashWA)).toString(CryptoJS.enc.Base64);
console.log('passwordHash: ' + passwordHash);
}
getPasswordHash(true); // with testdata
getPasswordHash(false); // without testdata
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
When the code is executed, the first call of getPasswordHash() uses the test data for nonce and date (test=true), the second call applies a random nonce and the current date (test=false) . The call with the test data returns the same result as the PHP code:
nonce (Base64): NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
created: 2021-01-21T18:19:32Z
passwordHash: olI18mUowhmeCwjb1FJNHtTHYDA=
CryptoJS implements CryptoJS.lib.WordArray.random() as CSPRNG, which is the counterpart of the PHP method random_bytes(). The use of the uuid library is therefore actually not necessary. I have chosen CryptoJS.lib.WordArray.random() in my example because it is closest to the PHP code.

Decrypt AES-generated hex from aes-js(Javascript) to pycryptodome(Python)

So im trying to decrypt a string I cryptographed with JS in Python. I used the aes-js library. I get this: caba6777379a00d12dcd0447015cd4dbcba649857866072d. This is my JS code:
var key = aesjs.utils.utf8.toBytes("ThisKeyIs16Bytes");
console.log(`Key (bytes): ${key}`);
var text = 'psst... this is a secret';
var textBytes = aesjs.utils.utf8.toBytes(text);
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
var encryptedBytes = aesCtr.encrypt(textBytes);
var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
console.log(`Hex: ${key}`);
I've tried a few things in python, but this is what I currently have:
from Crypto.Cipher import AES
ciphered_data = bytearray.fromhex('caba6777379a00d12dcd0447015cd4dbcba649857866072d')
key = b'ThisKeyIs16Bytes'
cipher = AES.new(key, AES.MODE_CTR)
original_data = cipher.decrypt(ciphered_data)
print(original_data.decode("utf-8", errors="ignore"))
But I just recieve a mess.=*լ☻ve↕-:tQɊ#¶.
The CTR mode is used. In the Pyton code the initialization of the counter is missing, i.e. the definition of the correct start value, e.g.
...
cipher = AES.new(key, AES.MODE_CTR, nonce = b'', initial_value = 5)
...
or alternatively using a Counter object:
from Crypto.Util import Counter
...
counter = Counter.new(128, initial_value = 5)
cipher = AES.new(key, AES.MODE_CTR, counter = counter)
...
With one of these two changes the decryption works.

Porting C# AES Encryption to Javascript for Node.js

Looking for help to port below C# code into Node.js using crypto or equivalent module.
private string password="FlU4c8yQKLkYuFwsgyU4LFeIf7m3Qwy+poMBdULEMqw=";
private byte[] salt = Encoding.ASCII.GetBytes("##oEDA102ExChAnGe99#$#");
Aes encryptor = Aes.Create();
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt);
string pdbStr = Convert.ToBase64String(pdb.GetBytes(32));
Console.WriteLine(pdbStr);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
Tried porting into javascript as below, but the resulted value are not the same.
var password = "FlU4c8yQKLkYuFwsgyU4LFeIf7m3Qwy+poMBdULEMqw=";
var salt = "##oEDA102ExChAnGe99#$#";
var pdbBytes = crypto.pbkdf2Sync(Buffer.from(password, 'base64'), new Buffer(salt, 'base64'), 1000, 32);
var pdbStr = new Buffer(pdbBytes).toString('base64')
console.log("pdbStr", pdbStr);
Console output:
C# - GZlqgdLbMQ753dTmx1nlJ6HgdabTjW1CeCSoIYkLM4E=
JS - tuDsZJEEwxyXP7RvuYVxGmDy20AvMJAqkLoXX78sEU8=
Any help is much appreciated. Thanks.
these codes parts are generating same the result.
but i did not get 'GZlqgdLbMQ753dTmx1nlJ6HgdabTjW1CeCSoIYkLM4E=' result from c#.
c#
byte[] password= Convert.FromBase64String("FlU4c8yQKLkYuFwsgyU4LFeIf7m3Qwy+poMBdULEMqw=");
byte[] salt = Encoding.ASCII.GetBytes("##oEDA102ExChAnGe99#$#");
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password,salt,1000);
string pdbStr =Convert.ToBase64String(pdb.GetBytes(32));
Console.WriteLine(pdbStr);
//outpu : RMqDMSV6d8uT2NicGM212r3KMFt7ZsOI2q8+0Rr0WZQ=
JS
var crypto = require("crypto");
var password = "FlU4c8yQKLkYuFwsgyU4LFeIf7m3Qwy+poMBdULEMqw=";
var salt = "##oEDA102ExChAnGe99#$#";
crypto.DEFAULT_ENCODING = 'base64';
var pdbBytes = crypto.pbkdf2Sync(new Buffer(password,'base64'), salt, 1000, 32,'sha1');
var pdbStr = new Buffer(pdbBytes).toString()
console.log("pdbStr", pdbStr);
//outpu : RMqDMSV6d8uT2NicGM212r3KMFt7ZsOI2q8+0Rr0WZQ=

Encrypt string with Blowfish in NodeJS

I need to encrypt a string but I almost get the output I desire, I read online that it has something to do with padding and iv_vector at the end to complete for the remaining 8 bytes to be same length as txtToEncrypt.
I'm using this library https://github.com/agorlov/javascript-blowfish
// function in Java that I need
// javax.crypto.Cipher.getInstance("Blowfish/CBC/NoPadding").doFinal("spamshog")
var iv_vector = "2278dc9wf_178703";
var txtToEncrypt = "spamshog";
var bf = new Blowfish("spamshog", "cbc");
var encrypted = bf.encrypt(txtToEncrypt, iv_vector);
console.log(bf.base64Encode(encrypted));
Actual output: /z9/n0FzBJQ=
What I need: /z9/n0FzBJRGS6nPXso5TQ==
If anyone has any clue please let me know. I searched all over Google all day.
Finally, here is how to encrypt a string in NodeJS with Blowfish
// Module crypto already included in NodeJS
var crypto = require('crypto');
var iv = "spamshog";
var key = "spamshog";
var text = "2278dc9wf_178703";
var decipher = crypto.createCipheriv('bf-cbc', key, iv);
decipher.setAutoPadding(false);
var encrypted = decipher.update(text, 'utf-8', "base64");
encrypted += decipher.final('base64');
console.log(encrypted);
Returns: /z9/n0FzBJRGS6nPXso5TQ==

python decrypt a text encrypted in jsencrypt

In a web form the answers (packed in a jsonstring) are encrypted in several steps. First a random key is generated. Second the random key is used for AES encryption of the jsonstring. The random key is encrypted as well. Both are send in the body of a mail.
// Generate Random key
var rand_key = ('0000' + Math.random().toString(36).replace('.', '')).substr(-10);
console.log('rand_key', rand_key)
//var pubkey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALDjeFwFNhMCjMwcRVVKG1VvfsntEVPR3lNTujJnNk1+iSqZ4Tl5Lwq9GbwO+qlYVwXHNmeqG7rkEhL9uyDIZVECAwEAAQ=="
// rsa_key_public07012016.bin
//var pubkey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCv8FVei4Q2ehmYsSCv/uODSojIOGHwfQe686S1cEH5i/1mGME5ZzNqyy0d+lhMRD0tr7Sje7JoCEC/XRIZaiKJjpl1+3RXotf/Cx3bd9H7WtitshZB1m38ZZFsrX4oigMpUPFbCefMeBS4hvvNnmtl08lQGhfIXdXeflZsgWRHtQIDAQAB";
// my_pub_key.pem
var pubkey ="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA38gtENP9/hpirSCIsPh6CAVm0UmME4XBlPyK8yhwk079EUJpNzlEhu9HKcA/B7Fxo2lNoY9Tb9e+PYtJ6+VOB4+Y6zgGMX7cchYmumKRTbbQ6FNfBE5Q8XnOAUlgC7gNrs0e5lW7JH1kWlK+eTT4TANT7F3US09aXmym+fZaRInbXmJujGnDIbRIIbzr5FE82EeMpw2TqRWV466wz5EeFWSSQ8EqV1pSox8B1ywb6cnB/Vofs2qR9Zf2efi9TMcSGm/ij/p9IZcbLeep9qfGsv29lbLNMfwNwQyH0JU27eAM4tPdirceZPxfD6iiILmKzN253BMoAeQCp6us53CnGQIDAQAB"
// Make form_data a JSON string
var jsonstring = JSON.stringify(form_data);
// Create AES encrypted object
var aes_encrypted_json = CryptoJS.AES.encrypt(jsonstring, rand_key);
// Encrypt rand_key
var encrypt = new JSEncrypt();
//console.log('encrypt obj', encrypt);
encrypt.setPublicKey(pubkey);
var encrypted_rand_key = encrypt.encrypt(rand_key);
//var encrypted = encrypt.encrypt(jsonstring);
console.log('encypted', encrypted_rand_key);
var mail_body = encrypted_rand_key + aes_encrypted_json
console.log('body', mail_body)
var mailto_string = "mailto:info#xyz.com?subject=FORM&body=" + encodeURIComponent(mail_body);
$('#mailtosend').attr('href', mailto_string);
At the recipient mail server side I want to decrypt the random generated key and the jsonstring using a private key using the pycryptodome package.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base64 import *
def decrypt(key, text):
if type(key) == str:
key = key.encode()
if type(text) == str:
text = text.encode()
rsakey = RSA.importKey(key)
rsakey = PKCS1_v1_5.new(rsakey)
d = rsakey.decrypt(text, 'bolloux')
return d
# rand_key am2mhiwwmi
text = "ZvcrluUmZLY3lRRw01W9mQnhMn7zzpIWn1Bo3csM/ZZ0pWY/8H2dCB9fZDi9/cmp0UtIqDXhLd7SIwyxqrFgPcHUuEHlZl0WQcjSty8PjadG2Abulk1XqEQV4u0Gb/bFGDBMcf5tV1G0d4FFcBPE8r8inrxUjSj2CSffVL8gIGq3ZfY5g7t5FOZV8npBCEONgOLKYnzIiHrHUuXWsOaMAqxMFOLd5DTDLKAkyMybDClsLW9ka+CvWd5fnZBCvO2ziehFp7b9PG4QPSnQpdC8jNLGZB2h0FI8YQD6IyUwmVluUbAlPMqwd6A2CBdGCbfbMChaA5R7bJgKkYhPOQTjaQ=="
text = b64decode(text.encode())
with open('my_priv_key.pem', 'rb') as f:
key = f.read()
decrypt(key, text)
I run into a encoding problem. "UnicodeDecodeError: 'ascii' codec can't decode byte 0xf7 in position 1: ordinal not in range(128)" The encoding is complicating the issue beyond my capabilities.
My questions:
1. How can I resolve the encoding problem ?
2. How can I make the decryption work ?
Thanks
The issue is more than likely caused by b64decode(text) returning a str that contains values such as \xf7 and then attempting to .encode() those values within your decrypt function. encode will use the default encoding which in this case is ascii. I would personally remove the calls to encode unless you specifically have a reason you are doing so.

Categories

Resources