Translating Java password hashing method to JS implementation - javascript
I would like to translate Java code password hashing used in Blynk IoT software that I can use in Express.js app. Thanks for any help!
Java code:
https://www.onlinegdb.com/HJe19lyFB
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Main {
public static void main(String[] args){
System.out.println(makeHash("password","mail#gmail.com"));
}
private static final String SHA_256 = "SHA-256";
private Main() {
try {
MessageDigest.getInstance(SHA_256);
} catch (NoSuchAlgorithmException e) {
}
}
public static String makeHash(String password, String salt) {
try {
MessageDigest md = MessageDigest.getInstance(SHA_256);
md.update(password.getBytes(StandardCharsets.UTF_8));
byte[] byteData = md.digest(makeHash(salt.toLowerCase()));
return Base64.getEncoder().encodeToString(byteData);
} catch (Exception e) {
//ignore, will never happen.
}
return password;
}
private static byte[] makeHash(String val) throws NoSuchAlgorithmException {
return MessageDigest.getInstance(SHA_256).digest(val.getBytes(StandardCharsets.UTF_8));
}
}
Currently not working solution:
https://repl.it/#patryk0493/blynk-password-hashing
const btoa = require('btoa');
var util = require('util');
const KJUR = require('jsrsasign');
const password = 'password';
const email = 'mail#gmail.com';
const options = {"alg": "sha256", "prov": "cryptojs"}
makeHash = (str) => {
const md = new KJUR.crypto.MessageDigest(options);
return md.digestString(new util.TextEncoder().encode(str.toLowerCase()))
}
const md = new KJUR.crypto.MessageDigest(options);
md.updateString(new util.TextEncoder().encode(password));
const byteData = md.digest(makeHash(email.toLowerCase()));
const base64 = btoa(byteData)
console.log(base64);
You can re-implement the Java password hashing using the standard Node.js crypto module:
const crypto = require('crypto');
const makeHash = (data) => {
const hash = crypto.createHash('sha256');
return hash.update(data, 'utf8');
}
const password = "password";
const salt = "mail#gmail.com";
const result = makeHash(password)
.update(makeHash(salt).digest())
.digest('base64')
console.log(result);
Related
Is it possible to use Web Crypto API as a replacement to JSEncrypt?
I'm trying to use the Web Crypto API as a replacement of JSEncrypt, mainly because it's a heavy library and if there's a native solution, it should be better. The current implementation using JSEncrypt is the following: const pubKey = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq8SZ9OOd3GaNcGDHyiXR U8rbWSQRMhMTM97ADN3BG18rUtVJb7+lS2eJkZ+IfQSxk4rpiRq1xS7oyP4gop4t Z5X3s7PDNMYBSwq/lDBz8NdPjjhOeTXzvC2+I7Y8gTyRUN8EtZwSOc4mez5BfMww gnl51U+ainouHiBteYYb7Stk14wMDglwtASTQuIk8KHi6SSoz+iBhAreuXFODWnm UzAONT3925DB8BXQTJAVyWn93OuNYf/ryKt/lULx6+1MV5SsAPLurTD6rRm5Vcpe p8lMU7MYi/LmCwDYagkr4EV1FnHn5aQnxVbVBERs32M7coFunuyKsP8bv5DkOjUm hQIDAQAB -----END PUBLIC KEY-----`; const value = 'The quick brown fox jumps over the lazy dog'; const crypt = new JSEncrypt({default_key_size: 2048}); crypt.setPublicKey(pubKey); const encrypted = crypt.encrypt(value); And what I'm trying to do with the Web Crypto API is to make hashed string that can be decrypted later with JSEncrypt (It will be on another application): const pubKey = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq8SZ9OOd3GaNcGDHyiXR U8rbWSQRMhMTM97ADN3BG18rUtVJb7+lS2eJkZ+IfQSxk4rpiRq1xS7oyP4gop4t Z5X3s7PDNMYBSwq/lDBz8NdPjjhOeTXzvC2+I7Y8gTyRUN8EtZwSOc4mez5BfMww gnl51U+ainouHiBteYYb7Stk14wMDglwtASTQuIk8KHi6SSoz+iBhAreuXFODWnm UzAONT3925DB8BXQTJAVyWn93OuNYf/ryKt/lULx6+1MV5SsAPLurTD6rRm5Vcpe p8lMU7MYi/LmCwDYagkr4EV1FnHn5aQnxVbVBERs32M7coFunuyKsP8bv5DkOjUm hQIDAQAB -----END PUBLIC KEY-----`; const privKey = `-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAq8SZ9OOd3GaNcGDHyiXRU8rbWSQRMhMTM97ADN3BG18rUtVJ b7+lS2eJkZ+IfQSxk4rpiRq1xS7oyP4gop4tZ5X3s7PDNMYBSwq/lDBz8NdPjjhO eTXzvC2+I7Y8gTyRUN8EtZwSOc4mez5BfMwwgnl51U+ainouHiBteYYb7Stk14wM DglwtASTQuIk8KHi6SSoz+iBhAreuXFODWnmUzAONT3925DB8BXQTJAVyWn93OuN Yf/ryKt/lULx6+1MV5SsAPLurTD6rRm5Vcpep8lMU7MYi/LmCwDYagkr4EV1FnHn 5aQnxVbVBERs32M7coFunuyKsP8bv5DkOjUmhQIDAQABAoIBAG2zoqBEvw2EcdDT /sWcYZSienT0FAdnJ1x5Zy7QcgSJQ6IjlkNIoskF11aqDQHLfZ49mel2paDJ1OKJ a05wt47wuMKMjJ+n1iuXMcPzLhkjaivqyjOfwYrtrAIi4YGorSZ5SQi0JO/GOc18 dVa/EjxCWQ1jSdAnOE0FaOW0QSG3Bu8eZ7j1u1Zd+lCuzqoG2TURsQR3ymKfuYiO +43zV5HyQ7o2tbL6EcfxKPNX/QLrYd7OUU63UOgU0Ca6aHr5bcJPjPmqYdClWhM7 NGIjMgUASzU175aVW9Rrx4nIPMNm2VPTqwWGmJlxP/yuG1A6JMdTPOSLWqpGvbLa 7NfuugECgYEA77gkhzrJKXER85vd3BC27cA30vz8fj0kY4HEPA1vQz1Jx1mwMFoT hQ+6S4md8PoId7nOA0lbYwxX+ymoSI2XCDVp680OZ1GUWxZQpOc+Tt8RTbeue+cQ puquM6iPY0Qcsz03Y91VM/zw+n7pFP67w9U5nZrAowzJUuYJ3HyQYycCgYEAt28H eh0Ndo0qf0Tn6NIV/viEFqq+6Bsxdw6F5sPNZGxY1doFrlPKUmSlM4K+kdtEuVpn nkX/J3eYEPk9aBA7ymwqRA+KuLWZOwj0e5eA+maNOpBElSFUoR8aGwie7h3mCYOe uxIuq4/4fK+91rajnr/GncnMAe5g/Vdrb41UBHMCgYBCY3cKVId9iKj2FIae+aoP shpDvniW5Yv8WuXj07yqnICgSwB2g5vAUBjB7rzlDfnmXnjlhB1hEvwSvPi0DzJy vYg2G7YPexJhH475/GjPIwwBwAr8mYONFpVkDRyY2zYvlZaPe66rFTQPy7H1dI6/ r3+jXEZZYaMn6Dl27LB4JQKBgEKTT5lodfxzG9t5PNppdYZG2FoJuqQvSnK1mK+6 nQ83q75iFPHCg4oH8VWSvGvfsz+6qiTtrr+6TjzK2Rit26WKQITXRzfm4ShmBaXd xPgcxGFe/yVJJZgc27K4HEa8H0OQu8mVvc6SwFMwzaviOdxiKkoWda61wAny8pFY MAg1AoGAb1SNma8NXEZUr4gWbWekGKzkbRD7oQ8KGzSprQwVdrGr1DXcXiv0DZQX sJWYSywTy2Pgh4ePH6SqEP+0JyPo4QpOCLs6+Tw+h4QlYruWncLuBp8M06XqZQVj +n1I025MZnTWMpibyyfq18TROzqzk2OHm/QH3OizkxlYSk5uwc0= -----END RSA PRIVATE KEY-----`; const value = 'The quick brown fox jumps over the lazy dog'; async function importPublicKeyAndEncrypt( publicKey, plaintext, ) { const pub = await importPublicKey(publicKey); const encrypted = await encryptRSA(pub, new TextEncoder().encode(plaintext)); const encryptedBase64 = window.btoa(ab2str(encrypted)); return encryptedBase64; } function importPublicKey(spkiPem) { return window.crypto.subtle.importKey( 'spki', getSpkiDer(spkiPem), { name: 'RSA-OAEP', hash: 'SHA-256', }, false, ['encrypt'], ); } async function encryptRSA(key, plaintext) { const encrypted = await window.crypto.subtle.encrypt( { name: 'RSA-OAEP', length: 2048, }, key, plaintext, ); return encrypted; } function getSpkiDer(spkiPem) { const pemHeader = '-----BEGIN PUBLIC KEY-----'; const pemFooter = '-----END PUBLIC KEY-----'; const pemContents = spkiPem.substring( pemHeader.length, spkiPem.length - pemFooter.length, ); const binaryDerString = window.atob(pemContents); return str2ab(binaryDerString); } function str2ab(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i += 1) { bufView[i] = str.charCodeAt(i); } return buf; } function ab2str(buf) { return String.fromCharCode.apply(null, new Uint8Array(buf)); } importPublicKeyAndEncrypt(pubKey, value) .then(hash => { const crypt = new JSEncrypt({default_key_size: 2048}); crypt.setKey(privKey); return crypt.decrypt(hash); }) .then(decrypted => console.log('decrypt2', decrypted)); But unfortunately the decrypted result is always null. So I guess there is something related to the algorithm or hashing. Is it possible to make this work without JSEncrypt?
How to zlib deflate a string in browser
I have in my backend a simple system for encrypting and compressing data. Backend NodeJS export const aesEncrypt = (text: string, key: string = ENCRYPTION_KEY) => { let iv = randomBytes(IV_LENGTH); let cipher = createCipheriv('aes-256-cbc', Buffer.from(key), iv); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return iv.toString('hex') + ':' + encrypted.toString('hex'); } export const textCompressToBase64Url = (text: string) => { return deflateSync(text).toString('base64url') } export const packDataForUrl = (data: string, key: string = ENCRYPTION_KEY) => { return textCompressToBase64Url(aesEncrypt(data, key)) } Result string: eJwVU8cRxDAIbIkowXUjUv8lHP55BoM2Bp_W59hmGWH0DrxKpMlumMlfgAKD2DBKEx0gJs9j2ll-D7Gfl2PAgn1dPN696eDor3dWdR4iHH3hGQRKqKVy790Dneo7VZMEelNVag-n4rKAjhn4NNCiMmqwQ7QHMjgfkk0Rd3GlyviJuxz2zJWh6-_oAd4z9WHQPMhXgQ7P3LkegZHMt9-8h_IA-qSfm5LVIxn3PMJ0DEZSefcNW4CVKMluHkfhqjdIyXSPPWdeJGeJUWjInVq4E3tCClEQq2NSDFfA1QjSi71twSRRoMMk7GtceDIzTCHm9iqjFcB3D0U-N6tTIdnnkpnf_VpHDBJ5VnVZas7DQMVUrUu5TWXZV81j378DjVsYsGhdI7j7my6mFT6HH5dxxusT6_CiKpISF-u3A8U4O-aguJbnMapTBa0R8qEf6iK_GsZoZ89ZuTkBNoCazYWwUR8nQ0jBq9xu6Vr70nMKwAHeXWSNhI4qOS6QjSvtDg_iLoQVRmfAy_RVv3yN7buUFS6MiKCf0yVsgqr8KqYYXf0zR56utsdzXLFGauMhJ7QggHSphe0bwH1DyuQJT-DGAyfgUltmP4JcPUJ0akPPB0NTW6bV-61OIGcSV7N1zcik1_67QIH1c6i3An5X41WgZZfugnkWRxYF-2LxMuskueL8dNV_z7Ub5StKPVmZ2e52Y5M3I905SlaVdG2rF9X5dFg307uxiak2vgYSruvggU07bhP5wV273c6-Z65Zd_mjb0HXXs7pe-mrA-lsHzQZNvp8z2jt4rrID89spGGlyLdsagOyWd-eX13TX1DmNhnRdkMzt0NRHivqORe3nH_9zDtg I then try to use Pako (port of zlib for browser) in the frontend to deflate before doing a AES decrypt however i keep getting various errors: export const textDecompressFromBase64Url = (text: string) => { //#ts-ignore const sanitizedbase64 = text.replace(/_/g, '/').replace(/-/g, '+') const testData = convertDataURIToBinary(sanitizedbase64) const inflated = inflate(testData).toLocaleString() return inflated } const convertDataURIToBinary = (base64Data: string) => { const raw = window.atob(base64Data); const rawLength = raw.length; let array = new Uint8Array(new ArrayBuffer(rawLength)); for (let i = 0; i < rawLength; i++) { array[i] = raw.charCodeAt(i); } return array; } If I don't use { raw: true } I get the following which is not usable in my aes function in the inflate method: 98,51,54,101,53,97,57,49,101,56,56,99,98,98,56,50,97,54,48,97,100,99,49,50,102,99,101,101,48,102,102,99,58,98,48,53,48,51,48,52,56,102,51,49,52,101,50,50,54,48,50,51,50,57,99,54,56,53,101,99,100,57,55,54,50,51,57,54,97,99,102,56,48,51,52,49,101,55,57,52,57,98,97,55,55,99,57,48,57,49,57,97,101,51,57,54,100,100,54,97,49,49,48,54,53,97,98,57,99,98,50,48,53,50,49,53,100,53,52,55,55,55,53,101,99,101,99,53,57,97,49,49,53,56,52,99,48,50,97,102,100,100,100,53,56,97,49,102,100,98,55,51,52,48,53,102,56,56,48,57,102,101,48,50,56,50,97,56,50,101,48,56,54,50,50,50,48,53,99,98,51,99,97,49,50,56,102,100,50,51,101,100,51,100,99,53,52,102,57,54,98,55,57,49,101,52,99,48,55,52,102,50,55,57,97,54,53,54,48,51,98,55,51,100,101,51,57,54,53,99,54,49,51,55,53,48,50,54,51,102,102,55,102,55,57,98,98,49,98,99,51,51,55,101,97,102,97,97,49,52,97,48,48,101,54,99,57,54,55,99,52,99,100,101,102,52,99,98,55,54,97,50,49,99,57,49,98,51,49,50,53,52,97,55,97,102,51,56,98,48,56,100,52,53,50,52,98,99,51,54,57,49,52,51,100,100,97,102,49,50,99,51,50,55,54,56,97,57,51,51,51,100,99,54,53,52,55,50,98,53,98,52,55,102,100,48,56,54,102,98,99,57,49,52,100,49,49,52,49,49,100,101,98,102,99,52,56,49,50,54,48,52,57,98,48,99,57,100,51,57,101,56,55,102,55,99,50,50,98,49,57,48,102,99,48,49,98,51,51,100,49,54,99,99,99,98,56,53,48,98,102,55,101,49,53,56,53,100,98,48,51,55,98,99,57,98,99,97,57,56,56,100,54,100,98,52,99,101,54,55,50,56,56,57,55,52,99,101,50,50,54,56,48,99,49,51,102,55,55,99,52,102,55,57,57,51,102,51,48,50,100,51,50,100,101,53,55,53,48,101,56,53,52,99,54,49,100,100,102,97,51,57,99,101,50,98,49,56,51,101,52,51,48,49,100,50,97,99,102,50,48,55,100,101,53,53,50,54,48,100,53,56,99,102,51,97,51,100,56,51,99,98,97,101,54,98,99,54,56,49,57,48,100,50,52,100,52,57,52,56,101,97,100,56,51,53,49,98,54,56,51,99,51,98,50,98,55,56,99,54,97,51,49,53,57,50,100,98,50,102,100,50,52,99,48,49,98,102,50,101,100,50,57,55,53,98,56,51,49,56,54,99,102,51,56,100,57,56,57,50,48,49,101,48,48,53,56,56,102,55,48,98,56,102,53,57,102,57,50,56,49,48,99,52,49,55,53,51,101,57,56,99,57,53,100,49,57,48,97,57,50,98,48,49,102,48,51,51,49,56,49,51,53,98,99,48,101,98,100,100,52,54,57,52,48,99,101,49,100,99,54,48,49,102,54,49,49,101,57,56,98,56,100,49,98,101,99,98,48,97,99,99,57,51,50,55,100,57,48,50,54,101,55,100,51,50,53,48,55,48,102,52,52,52,49,57,54,54,101,100,52,51,56,52,49,53,53,51,97,100,98,102,100,51,49,57,53,57,52,55,50,98,52,97,53,53,48,98,54,57,99,102,57,53,49,100,102,52,100,57,54,53,52,54,98,53,100,48,98,48,50,53,98,50,98,98,56,101,57,56,48,51,101,55,98,52,100,56,52,97,52,51,102,98,49,48,101,54,49,102,98,48,55,50,101,56,99,99,101,97,50,48,99,97,100,56,98,52,53,102,100,52,49,101,51,54,49,98,53,99,53,101,52,102,101,53,57,101,97,56,51,53,48,52,54,102,99,49,101,97,100,99,57,98,56,50,56,52,101,49,49,100,55,50,55,100,48,51,53,102,55,57,57,101,50,49,53,57,55,101,54,98,49,55,53,101,52,101,53,57,55,99,102,57,97,56,98,54,52,53,100,48,51,57,98,53,100,57,100,56,56,101,99,50,52,55,52,57,51,97,53,56,101,97,97,97,57,53,101,101,49,52,56,52,99,48,100,97,52,100,50,57,51,56,55,57,102,101,54,56,97,102,102,52,101,101,99,102,53,50,56,100,100,99,50,55,56,57,48,57,98,100,101,99,97,53,102,51,53,99,57,54,56,52,99,100,98,52,100,101,56,51,55,56,48,52,98,57,53,56,51,99,54,48,54,99,57,49,53,50,49,51,97,48,55,99,98,97,57,56,54,56,56,101,56,57,53,99,100,55,98,98,56,49,57,100,53,52,54,97,51,51,99,102,101,55,55,50,52,99,98,55,50,53,102,50,49,99,53,99,51,48,54,57,49,51,55,54,102,53,100,55,99,98,49,48,99,51,97,49,54,102,54,48,52,48,102,100,52,99,97,101,52,101,100,51,99,51,100,101,98,48,53,50,55,53,53,56,56,97,98,50,99,99,53,102,56,49,49,56,100,55,99,53,99,99,56,100,52,98,100,57,98,102,98,49,54,54,55,49,51,57,54 I have try to convert this to hex as I do in the backend but it is not working If I pass the {raw: true} I get the following: invalid code lengths set In the backend however i'm able to inflate and decrypt with zlib with no issue: export const textDecompressFromBase64Url = (text: string) => { return inflateSync(Buffer.from(text, 'base64')).toString() } export const aesDecrypt = (text: string, key: string = ENCRYPTION_KEY) => { let textParts = text.split(':'); let iv = Buffer.from(textParts.shift(), 'hex'); let encryptedText = Buffer.from(textParts.join(':'), 'hex'); let decipher = createDecipheriv('aes-256-cbc', Buffer.from(key), iv); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString(); } From the comments it is said that this is impossible to make it work but here is a working example in nodejs of the full circle: https://jdoodle.com/ia/s5Y TIMELINE: Stringify object AES encrypt the string + add the IV in front in format of hex string Deflate the string with zlib and output as base64url Inflate the string with zlib Decrypt the string with AES The issue is using pako I'm not able to execute step 4
Decrypting message form backend with error: BadPaddingException
Hello I have a simple RSA private and public key, I send the public key to backend to encrypt a text and return the encrypted text back to be decrypted. Here is client side code: SWIFT FRONT END func createPK(uid: String) { do { let keyPair = try SwiftyRSA.generateRSAKeyPair(sizeInBits: 2048) let privateKey = keyPair.privateKey let publicKey = keyPair.publicKey let aa = try publicKey.pemString() print(aa) AF.request("http://localhost:5001/cakes-delight/us-central1/encryption", method: .post, parameters: ["publicKey" : aa]).responseJSON { (result) in if let err = result.error { print("error req \(err)") return } if let statusCode = result.response?.statusCode { if statusCode != 200 { print("err: \(statusCode)") return } } if let result = result.value { if let JSON = result as? NSDictionary { if let id = JSON["res"] as? String { do { print("\n\nENCRYPTED?: \(id)\n\n") var k = id let encrypted = try EncryptedMessage(base64Encoded: k.toBase64()) print("aa") let clear = try encrypted.decrypted(with: privateKey, padding: .PKCS1) print("bb") let string = try clear.string(encoding: .utf8) print("\n\nDECRYPTED : \(string)\n\n") return } catch let error{ print("failed \(error)") return } } } } } } catch { print("err") } } TYPESCRIPT BACKEND Code: export const encryption = functions.https.onRequest(async(request, response) => { try { console.log(request.body["publicKey"]) // let key = await new NodeRSA('-----BEGIN RSA PUBLIC KEY-----\n'+ // request.body["publicKey"] + // '-----END RSA PUBLIC KEY-----'); let key = await new NodeRSA( request.body["publicKey"] ); let v = key.encrypt("i am coco!", "base64") console.log(v) let responseJSON = { res: v } response.status(200).send(responseJSON); } catch (err) { console.log(err) } }); SwiftyRSA gives me this error: "chunkDecryptFailed(index: 0)". I tested my key on a RSA Encryption website 8gwifi. I used the encrypted message coming from my backend and try to decrypt it to get a better error. I get: SYSTEM Error Error Performing RSA Encryption javax.crypto.BadPaddingException: Decryption error
Posted an array of string in ReactJS but got one string in WebAPI
I tried to post an array of string from ReactJS to WebApi but I got just one string [0]. Here is my ReactJS code: import React, { useState } from "react"; import axios from "axios"; export const FileUpload = () => { const [files, setFiles] = useState(); const [fileNames, setFileNames] = useState(); const saveFile = (e) => { var tempfiles = files; if (tempfiles == null) { tempfiles = []; } tempfiles.push(e.target.files[0]); setFiles(tempfiles); var tempFileNames = fileNames; if (tempFileNames == null) { tempFileNames = []; } tempFileNames.push(e.target.files[0].name) setFileNames(tempFileNames); }; const uploadFile = async (e) => { debugger const formData = new FormData(); //upload many for (let i = 0; i < files.length; i++) { formData.append("Files[${i}]", files[i]); // formData.append('FileNames[${i}]', fileNames[i]); } //upload 1 // formData.append("FormFile", file); //add test data formData.append('TestField', 'abcxyz'); formData.append('FileNames', fileNames); formData.append('ProjectId', 123); formData.append('NameToDisclose', false); // try { //upload many const res = await axios.post("https://localhost:44376/api/test/UploadMany", formData); ////upload 1 // const res = await axios.post("https://localhost:44376/api/test/Upload", formData); console.log(res); } catch (ex) { console.log(ex); } }; return ( <> <input type="file" onChange={saveFile} /> <input type="button" value="upload" onClick={uploadFile} /> </> ); }; Here is my Controller: using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using UploadFileToWebApiBE.Model; namespace UploadFileToWebApiBE.Controllers { [Route("api/[controller]")] [ApiController] public class TestController : ControllerBase { [HttpPost] [Route("UploadMany")] public ActionResult Post([FromForm]UploadFileMany files) { try { files.Files = Request.Form.Files; return StatusCode(StatusCodes.Status201Created); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError); } } } } Here is my UploadFileMany: using Microsoft.AspNetCore.Http; namespace UploadFileToWebApiBE.Model { public class UploadFileMany { public string TestField { get; set; } public IFormFileCollection Files { get; set; } public string[] FileNames { get; set; } public int ProjectId { get; set; } public bool NameToDisclose { get; set; } = false; } } This is the data from ReactJS: This is the data from WebApi: I want to have 3 items for FileNames, not one item seperated by a comma. Any help will be much appreciated.
Try to pass filenames as a JSON string by using: formData.append('FileNames', JSON.stringify(fileNames); and then parse the JSON string in your back-end codes. because i remember that formData in JavaScript doesn't accept arrays and objects as input. You can stringify them and pass your arrays and objects in a standard format. I hope this works for you
php openssl_seal equivalent in Node.js
I have a code snippet in php which I would like to move into node.js but I cannot seem to find the right way to do it. class EncryptService { const PUBLIC_CERT_PATH = 'cert/public.cer'; const PRIVATE_CERT_PATH = 'cert/private.key'; const ERROR_LOAD_X509_CERTIFICATE = 0x10000001; const ERROR_ENCRYPT_DATA = 0x10000002; public $outEncData = null; public $outEnvKey = null; public $srcData; public function encrypt() { $publicKey = openssl_pkey_get_public(self::PUBLIC_CERT_PATH); if ($publicKey === false) { $publicKey = openssl_pkey_get_public("file://".self::PUBLIC_CERT_PATH); } if ($publicKey === false) { $errorMessage = "Error while loading X509 public key certificate! Reason:"; while (($errorString = openssl_error_string())) { $errorMessage .= $errorString . "\n"; } throw new Exception($errorMessage, self::ERROR_LOAD_X509_CERTIFICATE); } $publicKeys = array($publicKey); $encData = null; $envKeys = null; $result = openssl_seal($this->srcData, $encData, $envKeys, $publicKeys); if ($result === false) { $this->outEncData = null; $this->outEnvKey = null; $errorMessage = "Error while encrypting data! Reason:"; while (($errorString = openssl_error_string())) { $errorMessage .= $errorString . "\n"; } throw new Exception($errorMessage, self::ERROR_ENCRYPT_DATA); } $this->outEncData = base64_encode($encData); $this->outEnvKey = base64_encode($envKeys[0]); } }; The problem is that I cannot find an implementation of the openssl_sign in Javascript anywhere. I do need to keep this structure because I use both outEncData and outEnvKey. I managed to find the equivalent implementation of openssl_sign with the crypto package but nothing for openssl_seal. LE added working solution as an answer
OK I've spent some time to figure this out, in short it is now in the repo: ivarprudnikov/node-crypto-rc4-encrypt-decrypt. But we want to follow SO rules here. Below assumes that you have public key for signing the generated key and private key for testing if all is great. Randomly generated secret key used for encryption: const crypto = require('crypto'); const generateRandomKeyAsync = async () => { return new Promise((resolve, reject) => { crypto.scrypt("password", "salt", 24, (err, derivedKey) => { if (err) reject(err); resolve(derivedKey.toString('hex')); }); }); } Encrypt data with the generated key and then encrypt that key with a given public key. We want to send back both encrypted details and encrypted key as we expect the user on another side to have private key. const crypto = require('crypto'); const path = require('path'); const fs = require('fs'); const encryptKeyWithPubAsync = async (text) => { return new Promise((resolve) => { fs.readFile(path.resolve('./public_key.pem'), 'utf8', (err, publicKey) => { if (err) throw err; const buffer = Buffer.from(text, 'utf8'); const encrypted = crypto.publicEncrypt(publicKey, buffer); resolve(encrypted.toString('base64')); }); }); } const encryptStringAsync = async (clearText) => { const encryptionKey = await generateRandomKeyAsync(); const cipher = await crypto.createCipheriv("RC4", encryptionKey, null); const encryptedKey = await encryptKeyWithPubAsync(encryptionKey); return new Promise((resolve, reject) => { let encryptedData = ''; cipher.on('readable', () => { let chunk; while (null !== (chunk = cipher.read())) { encryptedData += chunk.toString('hex'); } }); cipher.on('end', () => { resolve([encryptedKey, encryptedData]); // return value }); cipher.write(clearText); cipher.end(); }); } So now we can encrypt the details: encryptStringAsync("foo bar baz") .then(details => { console.log(`encrypted val ${details[1]}, encrypted key ${details[0]}`); }) Will print something like: encrypting foo bar baz encrypted val b4c6c7a79712244fbe35d4, encrypted key bRnxH+/pMEKmYyvJuFeNWvK3u4g7X4cBaSMnhDgCI9iii186Eo9myfK4gOtHkjoDKbkhJ3YIErNBHpzBNc0rmZ9hy8Kur8uiHG6ai9K3ylr7sznDB/yvNLszKXsZxBYZL994wBo2fI7yfpi0B7y0QtHENiwE2t55MC71lCFmYtilth8oR4UjDNUOSrIu5QHJquYd7hF5TUtUnDtwpux6OnJ+go6sFQOTvX8YaezZ4Rmrjpj0Jzg+1xNGIIsWGnoZZhJPefc5uQU5tdtBtXEWdBa9LARpaXxlYGwutFk3KsBxM4Y5Rt2FkQ0Pca9ZZQPIVxLgwIy9EL9pDHtm5JtsVw== To test above assumptions it is necessary first to decrypt the key with the private one: const decryptKeyWithPrivateAsync = async (encryptedKey) => { return new Promise((resolve) => { fs.readFile(path.resolve('./private_key.pem'), 'utf8', (err, privateKey) => { if (err) throw err; const buffer = Buffer.from(encryptedKey, 'base64') const decrypted = crypto.privateDecrypt({ key: privateKey.toString(), passphrase: '', }, buffer); resolve(decrypted.toString('utf8')); }); }); } After key is decrypted it is possible to decrypt the message: const decryptWithEncryptedKey = async (encKey, encVal) => { const k = await decryptKeyWithPrivateAsync(encKey); const decipher = await crypto.createDecipheriv("RC4", k, null); return new Promise((resolve, reject) => { let decrypted = ''; decipher.on('readable', () => { while (null !== (chunk = decipher.read())) { decrypted += chunk.toString('utf8'); } }); decipher.on('end', () => { resolve(decrypted); // return value }); decipher.write(encVal, 'hex'); decipher.end(); }); } Hope this answers the question.
The final and working version that worked for me. My problem was that I used an 128bit random key encrypt the data, instead 256bit worked in the end. The encryption works in JS and it can be decrypted in php with the openssl_open using your private key, which was what I asked in the original question. const crypto = require('crypto'); const path = require('path'); const fs = require('fs'); const encryptMessage = (message) => { const public_key = fs.readFileSync(`${appDir}/certs/sandbox.public.cer`, 'utf8'); const rc4Key = Buffer.from(crypto.randomBytes(32), 'binary'); const cipher = crypto.createCipheriv('RC4', rc4Key, null); let data = cipher.update(message, 'utf8', 'base64'); cipher.final(); const encryptedKey = crypto.publicEncrypt({ key: public_key, padding: constants.RSA_PKCS1_PADDING }, rc4Key); return { 'data': data, 'env_key': encryptedKey.toString('base64'), }; };