I have To encrypt data using aes-128-ecb in nodejs my code is
I am using Crypto to encrypt data
const crypto = require('crypto');
const secret = '1000060000000000';
const cipher = crypto.createCipher('aes-128-ecb', secret);
const ciphertext = cipher.update('9', 'utf8', 'base64')+cipher.final('base64');
console.log("Cipher text is: " + ciphertext);
the output should be EtgITaHs6lEvEHBipj08Kg==
but the output is coming as nNzqejauQBnfiDqznGhZ0Q==
The problem here is the use of crypto.createCipher, it doesn't use the key directly, but rather a digest.
To quote the documentation:
The implementation of crypto.createCipher() derives keys using the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt.
If we use cipher.createCipheriv on the other hand, we can specify the key directly and it will give us the expected output.
Here's an example:
const crypto = require("crypto");
function encrypt(plainText, key, outputEncoding = "base64") {
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(plainText), cipher.final()]).toString(outputEncoding);
}
function decrypt(cipherText, key, outputEncoding = "utf8") {
const cipher = crypto.createDecipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(cipherText), cipher.final()]).toString(outputEncoding);
}
const key = "1000060000000000";
const plainText = "9";
const encrypted = encrypt(plainText, key, "base64");
console.log("Encrypted string (base64):", encrypted);
const decrypted = decrypt(Buffer.from(encrypted, "base64"), key, "utf8")
console.log("Decrypted string:", decrypted);
The output will be
EtgITaHs6lEvEHBipj08Kg==
It is late but will help others
You can pass any Algorithm i.e. aes-128-cbc , aes-128-ecb
Create a new file and name it as aes-service.js in service folder or anywhere in Node.js application
aes-service.js
const crypto = require('crypto');
const cryptkey = 'C51GH00SE8499727';
const iv = 'BDA30EGDH1578F81';
async function encrypt(text){
try {
var cipher = crypto.createCipheriv('aes-128-cbc',cryptkey,iv);
var crypted = cipher.update(text,'utf8','base64'); //base64 , hex
crypted += cipher.final('base64');
return crypted;
} catch (err) {
console.error('encrypt error',err);
return null;
}
}
async function decrypt(encryptdata){
//Check all Algorithms
console.log(crypto.getCiphers()); // ['aes-128-cbc', 'aes-128-ccm', ...]
try {
let decipher = crypto.createDecipheriv('aes-128-cbc',cryptkey,iv)
decipher.setAutoPadding(false)
let decoded = decipher.update(encryptdata,'base64','utf8') //base64 , hex
decoded += decipher.final('utf8')
return decoded
} catch (err) {
console.error('decrypt error',err)
return null
}
}
const AesService = {
encrypt:encrypt,
decrypt:decrypt,
}
module.exports = AesService
Node.js contorller i.e abc.controller.js
//Get aes encrypted data from node.js request
const AesService = require("./services/aes-service")
exports.getAesEncryptedDatafromReq= async (req, res) => {
try{
let decryptData = ''
try{
const buffers = [];
for await (const chunk of req) {
buffers.push(chunk);
}
const dataBuffer = Buffer.concat(buffers).toString();
const jsonParsedData = JSON.parse(dataBuffer)
decryptData = jsonParsedData.data
}catch(err){}
let decryptedData = await AesService.decrypt(decryptData)
console.log('decrypted data',decryptedData)
let sendbackdata = {
"status": 0,
"anotehr_key":[
{ "dec":"0", "asc":"1"}
]
}
sendbackdata = JSON.stringify(sendbackdata)
let encryptedData = await AesService.encrypt(sendbackdata)
//Check if encrypted performed well
// let decryptedDataAgain = await AesService.decrypt(encryptedData)
//console.log('decryptedDataAgain ',decryptedDataAgain)
return res.status(201).send({"data":encryptedData})
}catch(err){
return res.status(500)
}
}
Add route
router.post("/get/aes/encrypted/data/from/req", controller.getAesEncryptedDatafromReq)
Related
I am trying to rewrite old NodeJs encryption algorithm from
crypto.createDecipheriv(algorithm, key, iv[, options])
into webcrypto
subtle.decrypt(algorithm, key, data)
This code work good enough with AES-128-CTR algorithm
const algorithm = 'aes-128-ctr';
const iv = '0123456789ABCDEF0123456789ABCDEF';
const privateKey = '16Random_Letters';
const hexBufferFromIv = Buffer.from(iv, 'hex');
const utfBufferFromPrivateKey = Buffer.from(privateKey, 'utf8');
function oldEncryptData(data: string): string {
const cipher = createCipheriv(
algorithm,
utfBufferFromPrivateKey,
hexBufferFromIv,
);
let crypted = cipher.update(data, 'utf8', 'base64');
crypted += cipher.final('base64');
return crypted;
}
function oldDecryptData(data: string): string {
const decipher = createDecipheriv(
algorithm,
utfBufferFromPrivateKey,
hexBufferFromIv,
);
let dec = decipher.update(data, 'base64', 'utf8');
dec += decipher.final('utf8');
return dec;
}
async function testDecrypt() {
const sourceText = `any text to encrypt!`;
const encryptedText = oldEncryptData(sourceText);
const decryptedText = oldDecryptData(encryptedText);
return sourceText === decryptedText;
}
testDecrypt().then(console.log);
Right now I test this code and WebCrypto examples in nodejs, but as a final result I wont to move webCrypto.subtle.decrypt functionality into NGINX njs and as I know, njs doesn't support other options for decryption except for WebCrypto.
Interface for WebCrypto decrypt for AES-CTR in general looks like
const data = await crypto.subtle.decrypt(
{
name: "AES-CTR",
counter, // BufferSource
length: 128, // 1-128
},
key, // AES key
encData, // BufferSource
);
And I don't undersatnd.
counter is the same thing as the Initialization vector in createDecipheriv method?
How I should generate key for subtle.decrypt method from the same passphrase?
Do I need to do any additional transformation from or to base64 or utf8 encoding to reproduce input and output encoding in cipher.update(data, 'utf8', 'base64'); and in decipher.update(data, 'base64', 'utf8'); methods?
Thanks Topaco for hints. I'll write a more complete answer. Maybe it will be useful for someone.
Yes, Initialization vector and counter can be treated as the same thing.
For generating a key from the same passphrase you should use importKey method. And you should sent the same ArrayBuffer from the passphrase as in createCipheriv method.
Yes, if your old method used some specific encoding and decoding, you should repeat the same encoding/decoding logic after Webcrypto.SubtleCrypto.encrypt() and decrypt() methods.
Full workable example may looks something like
import { webcrypto } from 'crypto';
const iv = '0123456789ABCDEF0123456789ABCDEF';
const privateKey = '16Random_Letters';
const hexBufferFromIv = Buffer.from(iv, 'hex');
const utfBufferFromPrivateKey = Buffer.from(privateKey, 'utf8');
async function generateKeyFromPassPhrase(): Promise<CryptoKey> {
return webcrypto.subtle.importKey(
'raw',
utfBufferFromPrivateKey,
{
name: 'AES-CTR',
},
true,
['decrypt', 'encrypt'],
);
}
async function newEncryptData(data: string): Promise<string> {
const key = await generateKeyFromPassPhrase();
const encryptResult = await webcrypto.subtle.encrypt(
{
name: 'AES-CTR',
length: 128,
counter: hexBufferFromIv,
},
key,
Buffer.from(data),
);
return Buffer.from(encryptResult).toString('base64');
}
async function newDecryptData(data: string): Promise<string> {
const key = await generateKeyFromPassPhrase();
const decryptResult = await webcrypto.subtle.decrypt(
{
name: 'AES-CTR',
length: 128,
counter: hexBufferFromIv,
},
key,
Buffer.from(data, 'base64'),
);
return Buffer.from(decryptResult).toString();
}
async function testDecrypt() {
const sourceText = `any text to encrypt!`;
const encrypted2 = await newEncryptData(sourceText);
const decrypted2 = await newDecryptData(encrypted2);
return sourceText === decrypted2;
}
testDecrypt().then(console.log);
I generate a pair public/private key on Client and send the publicKey to the Server and the backend will generate a sharedKey on its side and respond me a publicKey which help me to generate a sharedKey on the client too for encryption/decryption. So I encrypt a message by AES-256-GCM on Nodejs and decrypted the message on the Client.
Backend-Side:
export function encrypt(sharedKey: string, message: string) {
const firstIv = getRandomIV();
const cipher = crypto.createCipheriv(
'aes-256-gcm',
Buffer.from(sharedKey, 'base64'),
firstIv
);
const encrypted = cipher.update(message, 'utf8');
return Buffer.from(encrypted + cipher.final()).toString('base64');
}
function getRandomIV() {
return crypto.randomBytes(12);
}
Client-Side:
async function decrypt(encryptedData: Uint8Array) {
const aesKey = await generateAesKey();
const nonce = encryptedData.subarray(0, SERVER_ENCRYPTION_IV_LENGTH);
const data = encryptedData.subarray(SERVER_ENCRYPTION_IV_LENGTH);
const decrypted = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: nonce,
},
aesKey,
data
);
return {
decrypted: new Uint8Array(decrypted),
decryptedString: new TextDecoder().decode(decrypted),
};
}
async function generateAesKey() {
const publicKey = await getServerPublicKey();
const privateKey = await getPrivateKey();
const sharedSecret = await crypto.subtle.deriveBits(
{
name: 'ECDH',
public: publicKey!,
},
privateKey,
256
);
const aesSecret = await crypto.subtle.digest('SHA-256', sharedSecret);
return crypto.subtle.importKey('raw', aesSecret, 'AES-GCM', true, [
'encrypt',
'decrypt',
]);
}
Now, I can't decrypt the server encrypted response in the client and I encounter to DOMException error and I don't know why?
GCM uses an authentication tag that is handled separately by NodeJS/Crypto, while WebCrypto automatically concatenates it with the ciphertext.
Therefore, in the NodeJS code, the tag must be explicitly determined and appended to the ciphertext. This is missing in the current NodeJS code and can be taken into account as follows. Note the determination of the tag with cipher.getAuthTag() and its concatenation:
var crypto = require('crypto');
function encrypt(key, plaintext) {
var nonce = getRandomIV();
var cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);
var nonceCiphertextTag = Buffer.concat([
nonce,
cipher.update(plaintext),
cipher.final(),
cipher.getAuthTag() // Fix: Get tag with cipher.getAuthTag() and concatenate: nonce|ciphertext|tag
]);
return nonceCiphertextTag.toString('base64');
}
function getRandomIV() {
return crypto.randomBytes(12);
}
var message = Buffer.from('The quick brown fox jumps over the lazy dog', 'utf8');
var sharedKey = Buffer.from('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=', 'base64');
var ciphertext = encrypt(sharedKey, message);
console.log(ciphertext); // wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k=
A possible output is
wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k=
The following code for decryption on the WebCrypto side is essentially based on your code (without the derivation of the key from a shared secret, which is irrelevant to the current problem):
(async () => {
var nonceCiphertextTag = base64ToArrayBuffer('wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k=');
var nonceCiphertextTag = new Uint8Array(nonceCiphertextTag);
var decrypted = await decrypt(nonceCiphertextTag);
console.log(decrypted); // The quick brown fox jumps over the lazy dog
})();
async function decrypt(nonceCiphertextTag) {
const SERVER_ENCRYPTION_IV_LENGTH = 12; // For GCM a nonce length of 12 bytes is recommended!
var nonce = nonceCiphertextTag.subarray(0, SERVER_ENCRYPTION_IV_LENGTH);
var ciphertextTag = nonceCiphertextTag.subarray(SERVER_ENCRYPTION_IV_LENGTH);
var aesKey = base64ToArrayBuffer('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=');
aesKey = await window.crypto.subtle.importKey('raw', aesKey, 'AES-GCM', true, ['encrypt', 'decrypt']);
var decrypted = await crypto.subtle.decrypt({name: 'AES-GCM', iv: nonce}, aesKey, ciphertextTag);
return new TextDecoder().decode(decrypted);
}
// Helper
// https://stackoverflow.com/a/21797381/9014097
function base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
which successfully decrypts the ciphertext of the NodeJS side:
The quick brown fox jumps over the lazy dog
i am trying to decrypt the data stored in my database before sending it to the client side.
I am using the builtin crypto module with AES-256-GCM encryption.
I have successfully implemented the encryption and it is working properly my problem is i am trying to decrypt the data in a different file but i keep getting an error.
this is the error:
(node:35798) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The "iv" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined
1.js
router.post(
"/",
async(req, res) => {
function getFullAddress({housenumber, address1, address2, city, postcode, country}) {
return [housenumber, address1, ...(address2 ? [address2]: []), city, postcode, country].join(", ");
}
const aes256gcm = (key) => {
const encrypt = (str) => {
const iv = new crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let enc = cipher.update(str, 'utf8', 'base64');
enc += cipher.final('base64');
return Buffer.concat([Buffer.from(enc), iv, cipher.getAuthTag()]).toString("base64");
};
return {
encrypt,
};
};
const aesCipher = aes256gcm(key);
const hashedPasscode = await bcrypt.hash(req.body.passcode, 12);
await User.create({
email: req.body.email,
mobilenumber: aesCipher.encrypt(req.body.mobilenumber),
passcode: hashedPasscode,
address: aesCipher.encrypt(getFullAddress(req.body))
})
2.js
router.get(
"/",
async(req, res) => {
const aes256gcm = (key) => {
const decrypt = (enc, iv, authTag) => {
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(authTag);
let str = decipher.update(enc, 'base64', 'utf8');
str += decipher.final('utf8');
return str;
};
return {
decrypt,
};
};
const aesCipher = aes256gcm(key);
const decrypted_MobileNumber = aesCipher.decrypt(user.mobilenumber);
const decrypted_address = aesCipher.decrypt(user.address);
console.log('decrypted_MobileNumber',decrypted_MobileNumber)
console.log('decrypted_address',decrypted_address)
here is an example of the data stored in my database
mobilenumber: 'Sm4xQjA2bmUwUUdEdW4zQkZ3PT3QEq5fBbTJ9ht4TgpQXTLmPYBSoQA836977j0rr3GYwg==',
This is what you do during encryption:
Buffer.concat([Buffer.from(enc), iv, cipher.getAuthTag()]).toString("base64");
Now, you need to reverse this during decryption:
enc = Buffer.from(enc, "base64");
const iv = enc.slice(enc.length-32, enc.length-16);
const tag = enc.slice(enc.length-16);
enc = enc.slice(0, enc.length-32);
The second issue is that a nonce/iv for GCM mode should be 12 bytes long. I've changed that and so some of the indices from the previous issue should change too.
The third issue is that you cannot concatenate encrypted and Base64-encoded chunks. You have to concatenate them before Base64 encoding so that there is no Base64 padding in the middle of the string. This shouldn't be much of an issue for GCM because the call to cipher.final('base64'); should return an empty string.
The fourth and clear issue is that during encryption you're encoding twice, but you only need to encode once.
And together this would look like this:
const crypto = require('crypto');
const aes256gcm = (key) => {
const encrypt = (str) => {
const iv = new crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let enc1 = cipher.update(str, 'utf8');
let enc2 = cipher.final();
return Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString("base64");
};
const decrypt = (enc) => {
enc = Buffer.from(enc, "base64");
const iv = enc.slice(enc.length - 28, enc.length - 16);
const tag = enc.slice(enc.length - 16);
enc = enc.slice(0, enc.length - 28);
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
let str = decipher.update(enc, null, 'utf8');
str += decipher.final('utf8');
return str;
};
return {
encrypt,
decrypt,
};
};
const cipher = aes256gcm(Buffer.alloc(32)); // just a test key
const ct = cipher.encrypt('test');
const pt = cipher.decrypt(ct);
console.log(pt);
I'm having troubles verifying signatures created by the Web Crypto API.
Here is the code I'm using to generate RSA keys in the browser:
let keys;
const generateKeys = async () => {
const options = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: { name: 'SHA-256' },
};
keys = await window.crypto.subtle.generateKey(
options,
false, // non-exportable (public key still exportable)
['sign', 'verify'],
);
};
And to export the public key:
const exportPublicKey = async () => {
const publicKey = await window.crypto.subtle.exportKey('spki', keys.publicKey);
let body = window.btoa(String.fromCharCode(...new Uint8Array(publicKey)));
body = body.match(/.{1,64}/g).join('\n');
return `-----BEGIN PUBLIC KEY-----\n${body}\n-----END PUBLIC KEY-----`;
// Output:
//
// -----BEGIN PUBLIC KEY-----
// MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx7J3SUG4sq/HSGIaGZWY
// 8b26cfEpVFYHoDUDUORIJzA/fLE9aj+uOKpGUTSfW69rMm7DAOLDz05KaEJJSI5+
// YbDPr2S82A2ByHHQt+Vu168sGz4noXTTSX2HIdVutaR/IJ0a5pNOa1vRR4MUW/ZO
// YaRir3yC5YXgcFLwwQaifNZ3lZ7WndbYEjTGOcieQQ81IUP2221PZCJI52S95nYm
// VfslsLiPhOFH7XhGSqelGYDi0cKyl0p6dKvYxFswfKKLTuWnu2BEFLjVq4S5Y9Ob
// SGm0KL/8g7pAqjac2sMzzhHtxZ+7k8tynzAf4slJJhHMm5U4DcSelTe5zOkprCJg
// muyv0H1Acb3tfXsBwfURjiE0cvSMhfum5I5epF+f139tsr1zNF24F2WgvEZZbXcG
// g1LveGCJ/0BY0pzE71DU2SYiUhl+HGDv2u32vJO80jCDf2lu7izEt544a+XE+2X0
// zVpwjNQGa2Nd4ApGosa1fbcS5MsEdbyrjMf80SAmOeb9g3y5Zt2MY7M0Njxbvmmd
// mF20PkklpH0L01lhg2AGma4o4ojolYHzDoM5a531xTw1fZIdgbSTowz0SlAHAKD3
// c2KCCsKlBbFcqy4q7yNX63SqmI3sNA3kTH9CQJdBloRvV103Le9C0iY8CAWQmow5
// N/sDJUabgOMqe9yopSjb7LUCAwEAAQ==
// -----END PUBLIC KEY-----
};
To sign a message:
const generateHash = async (message) => {
const encoder = new TextEncoder();
const buffer = encoder.encode(message);
const digest = await window.crypto.subtle.digest('SHA-256', buffer);
return digest;
};
const signMessage = async (message) => {
const { privateKey } = keys;
const digest = await generateHash(message);
const signature = await window.crypto.subtle.sign('RSASSA-PKCS1-v1_5', privateKey, digest);
return signature;
};
To verify the message in browser:
const verifyMessage = async (signature, message) => {
const { publicKey } = keys;
const digest = await generateHash(message);
const result = await window.crypto.subtle.verify('RSASSA-PKCS1-v1_5', publicKey, signature, digest);
return result;
};
When the keys are created, the public key is exported and sent to the server. Later:
const message = 'test';
const signature = await signMessage(message);
await verifyMessage(signature, message); // true
sendToServer(message, bufferToHex(signature));
Since the signature is an ArrayBuffer, I convert it to hex with the following code:
const bufferToHex = input => [...new Uint8Array(input)]
.map(v => v.toString(16).padStart(2, '0')).join('');
On the server (NodeJS 8.11.0):
const publicKey = getPublicKey(userId);
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(message, 'utf-8');
const sigBuf = Buffer.from(signature, 'hex');
verifier.verify(publicKey, sigBuf); // false
I've been chasing down this issue for days and just cannot seem to figure it out. I've tried both RSA-SHA256 and sha256WithRSAEncryption for verification to no avail. Furthermore, no errors are being thrown. Any help would be enormously appreciated!
So I don't fully understand why this is the case, but to solve the issue I needed to convert the SHA hash from an ArrayBuffer into a hex string, then read back into an array buffer using TextEncoder.
const generateHash = async (message) => {
const encoder = new TextEncoder();
const buffer = encoder.encode(message);
const digest = await window.crypto.subtle.digest('SHA-256', buffer);
// Convert to hex string
return [...new Uint8Array(digest)]
.map(v => v.toString(16).padStart(2, '0')).join('');;
};
Then when signing:
const signMessage = async (message) => {
const encoder = new TextEncoder();
const { privateKey } = keys;
const digest = await generateHash(message);
const signature = await window.crypto.subtle.sign('RSASSA-PKCS1-v1_5', privateKey, encoder.encode(digest));
return signature;
};
The signature no longer verifies on the client but it verifies in node. 🤷♂️
The issue is that you are signing the hash of hash of your input when you should actually be signing hash of your input. SubtleCrypto internally hashes the input. There is no need for you to provide hashed input. Since you provided hashed input as an argument, SubtleCrypto hashed it again and then signed it which led to mismatch of signatures.
It may be useful to note that both crypto and crypto.subtle hash the message before signing, so hashing separately is not necessary.
Assuming you were able to create and load your keys properly, your code should look like this.
Browser
const message = 'hello'
const encoder = new TextEncoder()
const algorithmParameters = { name: 'RSASSA-PKCS1-v1_5' }
const signatureBytes = await window.crypto.subtle.sign(
algorithmParameters,
privateKey,
encoder.encode(message)
)
const base64Signature = window.btoa(
String.fromCharCode.apply(null, new Uint8Array(signatureBytes))
)
console.log(base64Signature)
// TiJZTTihhUYAIlOm2PpnvJa/+15WOX2U0iKJ2LXsLecvohhRIWnwFfdHy4ci10mcv/UQgf2+bFf9lfFZUlPPdzckBNfXIqAjafM8XquJiw/t1v+pEGtJpaGASlzuWuL37gp3k8ux3l6zBKKbBVPPASkHVhz37uY1AXeMblfRbFE=
Node
This implementation is using crypto, but you could use the crypto.subtle to be more similar to the browser javascript syntax
const crypto = require('crypto')
const message = 'hello'
const base64Signature = 'TiJZTTihhUYAIlOm2PpnvJa/+15WOX2U0iKJ2LXsLecvohhRIWnwFfdHy4ci10mcv/UQgf2+bFf9lfFZUlPPdzckBNfXIqAjafM8XquJiw/t1v+pEGtJpaGASlzuWuL37gp3k8ux3l6zBKKbBVPPASkHVhz37uY1AXeMblfRbFE='
const hashingAlgorithm = 'rsa-sha256'
const doesVerify = crypto.verify(
hashingAlgorithm,
Buffer.from(message),
{ key: publicKey },
Buffer.from(base64Signature, 'base64')
);
console.log(doesVerify)
// true
Not a direct answer but it might just be easier to use this: https://www.npmjs.com/package/#peculiar/webcrypto so your code on client and server is consistent while addressing this problem at the same time.
I would like to encrypt an object then decrypt it. The encryption works very well but the decryption fails. Below my code :
crypto_ext.js
const crypto = require("crypto")
const password = "shared_key"
const algorithm = "aes256"
export const encrypt = (text) => {
if(!text) return ''
const cipher = crypto.createCipher(algorithm, password);
let crypted = cipher.update(text, 'utf-8', 'base64');
crypted += cipher.final('base64');
return crypted;
}
export const decrypt = (text) => {
if(!text) return ''
const decipher = crypto.createDecipher(algorithm, password);
let decrypted = decipher.update(text, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');
return decrypted;
}
and in my test.js, I have :
import {encrypt, decrypt} from './crypto_ext.js'
let test = {key1: val1, key2: val2}
test = encrypt(JSON.stringify(test)) || test
console.log("Encrypt : ", test)
console.log("Decrypt : ", decrypt(test)) // I should have my object as string here
And this is what I'm getting as error :
Uncaught Error: unable to decrypt data
at unpad (decrypter.js:83)
at Decipher.webpackJsonp../node_modules/browserify-aes/decrypter.js.Decipher._final (decrypter.js:38)
at Decipher.webpackJsonp../node_modules/cipher-base/index.js.CipherBase._finalOrDigest (index.js:76)
at decrypt (crypto_ext.js:17)
...
Can you please tell me what I'm doing wrong ?
Method createCipher deprecated. Use createCipheriv instead.
Try to use aes192 algorithm.
Dependencies: crypto package
You can achieve encoding and decoding with below codes:-
const crypto = require('crypto');
var password = 'ojisdasjdsjabdjs';
var iv = 'kiamdksndn';
function sha1(input) {
return crypto.createHash('sha1').update(input).digest();
}
function password_derive_bytes(password, salt, iterations, len) {
var key = Buffer.from(password + salt);
for (var i = 0; i < iterations; i++) {
key = sha1(key);
}
if (key.length < len) {
var hx = password_derive_bytes(password, salt, iterations - 1, 20);
for (var counter = 1; key.length < len; ++counter) {
key = Buffer.concat([key, sha1(Buffer.concat([Buffer.from(counter.toString()), hx]))]);
}
}
return Buffer.alloc(len, key);
}
async function encode(string) {
var key = password_derive_bytes(password, '', 100, 32);
var cipher = crypto.createCipheriv('aes-256-cbc', key, Buffer.from(iv));
var part1 = cipher.update(string, 'utf8');
var part2 = cipher.final();
const encrypted = Buffer.concat([part1, part2]).toString('base64');
return encrypted;
}
async function decode(string) {
var key = password_derive_bytes(password, '', 100, 32);
var decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv));
var decrypted = decipher.update(string, 'base64', 'utf8');
decrypted += decipher.final();
return decrypted;
}
And then call below functions for encode and decode
For encode
await encode(JSON.stringify({'title': 'some text'}));
For decode
await decode('encoded_string_to_decode');
Try to use 'bcrypt' package it will help you in the encryption for passwords.
If you want to encryption for Data. Then use crypto or node-rsa
Link npm bcrypt package
Node-RSA