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
Related
I created an encryption app using node.js crypto. But the encryption/decryption seems to only work on .txt files. I encrypted an image file, but when i decrypted it, it doesnt return the original image.
const crypto = require("crypto");
const fs = require("fs");
//
function encrypt(text, password) {
let salt = crypto.randomBytes(16).toString("hex");
let key = crypto.scryptSync(password, salt, 16, { N: 16384 });
key = key.toString("hex");
const cipher = crypto.createCipheriv("aes-256-gcm", key, salt);
const encrypted = cipher.update(text, "utf8", "hex") + cipher.final("hex");
const tag = cipher.getAuthTag().toString("hex");
return salt + tag + encrypted;
}
function decrypt(text, password) {
const salt = text.substr(0, 32);
const tag = Buffer.from(text.substr(32, 32), "hex");
const ciphertext = text.substr(64, text.length);
let key = crypto.scryptSync(password, salt, 16, { N: 16384 });
key = key.toString("hex");
const cipher = crypto.createDecipheriv("aes-256-gcm", key, salt);
cipher.setAuthTag(tag);
try {
let decrypted = cipher.update(ciphertext, "hex", "utf8") + cipher.final("utf8");
return decrypted;
} catch (e) {
return true;
}
}
//
function encryptFile(fileLocation, password) {
fs.readFile(fileLocation, "utf8", (err, data) => {
if (err) return console.log(err);
if (data) {
const encText = encrypt(data, password);
fs.writeFileSync(fileLocation, encText);
console.log("File Encrypted");
}
});
}
function decryptFile(fileLocation, password) {
fs.readFile(fileLocation, "utf8", (err, data) => {
if (err) {
throw err;
} else {
const decText = decrypt(data, password);
if (decText === true) {
console.log("Wrong Password/Salt");
} else {
fs.writeFileSync(fileLocation, decText);
console.log("File Decrypted");
}
}
});
}
//
const fileLocation = "./sample.txt";
// encryptFile(fileLocation, "password");
decryptFile(fileLocation, "password");
I did it on repl so you can have a look - https://replit.com/#sampauls8520/file-enc
I have provided an image file as well so that you can encrypt/decrypt that.
You can't read binary data (this includes images) using the UTF-8 text encoding, nor write them back.
You will need to rework your code so that it doesn't operate on text strings, but with binary buffers.
IOW:
don't specify an encoding for fs.readFile/fs.writeFile ("If no encoding is specified, then the raw buffer is returned." say the docs)
don't specify either input or output encoding for cipher.update()/cipher.final()
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'),
};
};
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);
I am trying to decrypt and encrypt strings with openpgp.js.
Encryption works fine, but I can't decrypt. I really can't get it to work.
Here is a working example of the encryption: https://jsfiddle.net/d4vL8ueh/1/
var message = "secret message";
const encryptMessage = async() => {
if(window.crypto.getRandomValues){
if(message != ""){
const publicKeyString = document.getElementById("pubkey").innerHTML;
var options = {
message: openpgp.message.fromText(message),
publicKeys: (await openpgp.key.readArmored(publicKeyString)).keys
};
openpgp.encrypt(options).then(ciphertext => {
alert(ciphertext.data);
})
}
}
else{
window.alert("This browser does not support basic cryptography!");
}
}
encryptMessage();
But the decryption doesn't work at all: https://jsfiddle.net/pceswg0t/2/
const decryptMessage = async() => {
encrypted = document.getElementById("encrypted").innerHTML;
if(window.crypto.getRandomValues){
if(encrypted != ""){
const privateKeyString = document.getElementById("privkey").innerHTML;
var options = {
message: await openpgp.message.readArmored(message),
privateKeys: (await openpgp.key.readArmored(privateKeyString)).keys,
passphrase: "dfgjk23jkfdklfsdds232334fddf"
};
openpgp.decrypt(options).then(plaintext => {
decrypted = plaintext.data
alert(decrypted);
})
}
}
else{
window.alert("This browser does not support basic cryptography!");
}
}
decryptMessage();
There are two problems:
Firstly, a non-existent message variable is being passed into readArmored when setting options.message. This should be using the encrypted variable instead:
message: await openpgp.message.readArmored(encrypted),
Secondly, the private key is encrypted, so it needs to be decrypted before it can be used. This can be done by calling decrypt() on the key before it is used:
var privateKeys = await openpgp.key.readArmored(privateKeyString);
privateKeys.keys[0].decrypt("dfgjk23jkfdklfsdds232334fddf")
var options = {
message: await openpgp.message.readArmored(encrypted),
privateKeys: privateKeys.keys
};
Here's an updated JSFiddle: https://jsfiddle.net/gfkqbsoz/
I am trying to decrypt a string but test keeps failing and I cannot seem to figure out the issue.
Here are the files involved:
Encryptor.js
import * as crypto from 'crypto'
const hashAlgorithm = 'sha256';
const encryptAlgorithm = 'aes192';
const textEncoding = 'hex';
const encryptionKey = "some secret key";
export const getHash = text => {
try {
const encryptedData = crypto.createHmac(hashAlgorithm, encryptionKey);
encryptedData.setEncoding(textEncoding)
.end(text, () => encryptedData.read())
return encryptedData;
} catch (error) {
console.error(error);
}
}
export const encrypt = text => {
if (!text)
throw Error('Text may not be blank');
try {
const cipher = crypto.createCipher(encryptAlgorithm, encryptionKey);
let cipherText = '';
cipher.on('readable', () => {
let data = cipher.read();
if (data)
cipherText += data.toString(textEncoding);
});
cipher.on('end', () => cipherText);
cipher.write(text);
cipher.end();
}
catch (error) {
return console.error(error);
}
}
export const decrypt = text => {
if (!text)
throw Error('Decrypt: Text may not be blank');
try {
const decipher = crypto.createDecipher(encryptAlgorithm, encryptionKey);
let decrypted = '';
decipher.on('readable', () => {
const data = decipher.read();
if (data)
return decrypted += data.toString('utf8');
});
decipher.on('end', () => decrypted);
decipher.write(text, textEncoding);
decipher.end();
}
catch (error) {
console.error(error);
}
}
Encryptor.test.js
import {getHash, encrypt, decrypt } from './Encryptor.js';
const dataString = "Important information must always be encypted"
it('Should create a hashed message FROM STRING', () => {
expect(getHash(dataString)).not.toEqual(dataString)
})
it('Should encrypt the message FROM STRING', () => {
expect(encrypt(dataString)).not.toEqual(dataString)
})
it('Should decrypt the message FROM ENCRYPTED STRING', () => {
expect(decrypt(encrypt(dataString))).toEqual(dataString)
})
As far as I understand, this code is fine. However, when I run the test, the last one fails with this trace:
If I pass a literal string to the decrypt function like so:
it('Should decrypt the message FROM ENCRYPTED STRING', () => {
expect(decrypt("encypted")).toEqual(dataString)
})
the returned results are undefined and I also get the error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length error
I followed almost exactly the examples here with no success.