Reputation: 166
I have some code utilizing SubtleCrypto that encrypts a key and stores it in a database.
For another functionality i have to be able to encrypt it in Node 12. Without SubtleCrypto i have to recreate the functionality in Crypto.
I get output of the same size, but it dosen't seem to be decryptable by SubtleCrypto and i'm trying to figure out where i'm going wrong.
This is the code running with SubtleCrypto on browsers:
key = getKeyMaterial(password)];
salt = base64ToArraybuffer(optionalSalt)
aesKey = crypto.subtle.deriveKey(
{
name: constants.algorithms.pbkdf2,
salt: salt,
iterations: pbkdf2Iterations,
hash: { name: constants.hash.sha256 },
},
key,
{
name: constants.algorithms.aesGcm,
length: aesWrapKeyBitsLength,
},
true,
['wrapKey', 'unwrapKey']
),
return {
salt: arraybufferTobase64(salt),
aesKey: aesKey,
}
function getKeyMaterial(password) {
var enc = new TextEncoder();
crypto.subtle.importKey(
constants.format.raw,
enc.encode(password),
{
name: constants.algorithms.pbkdf2,
},
false,
['deriveKey']
)
Without SubtleCrypto, in Node 12, I'm forced to work with the Crypto library.. This is my, current iteration of the, code.
const ivByteLength = 12;
function wrapKey(privateKey, aesKey) {
const IV = crypto.randomBytes(ivByteLength);
const ALGORITHM = 'aes-256-gcm';
const cipher = crypto.createCipheriv(ALGORITHM, aesKey, IV);
let encrypted = cipher.update(privateKey, undefined, 'binary');
encrypted += cipher.final('binary');
const authTag = cipher.getAuthTag();
encrypted += authTag;
const output = {
wrappedKey: arraybufferTobase64(Buffer.from(encrypted, 'ascii')),
iv: arraybufferTobase64(IV),
};
return output;
}
async function deriveAesGcmKey(password, salt) {
return new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, 100000, 32, 'sha256', (err, derivedKey) => {
if (err) reject(err);
else resolve(derivedKey);
});
});
}
function arraybufferTobase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i += 1) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
function base64ToArraybuffer(base64) {
const binary = atob(base64);
const len = binary.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i += 1) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
The wrapped key have the same size in both implementations, but the one generated by Node cannot be unwrapped in the browser.
Am i assuming some defaults wrong or something?
Upvotes: 2
Views: 6875
Reputation: 12542
NodeJS has something called webcrypto
Class inside crypto
package, that has subtle crypto implementation.
Example from the Docs:
const { subtle } = require('crypto').webcrypto;
async function digest(data, algorithm = 'SHA-512') {
const ec = new TextEncoder();
const digest = await subtle.digest(algorithm, ec.encode(data));
return digest;
}
Also check out: https://nodejs.org/api/webcrypto.html#webcrypto_class_subtlecrypto
Edit:
As said in the comments for lower version of Node you can use a polyfill like https://www.npmjs.com/package/@peculiar/webcrypto its a webcrypto implementation for Nodejs version higher than Node10
Upvotes: 6