1
votes

For some reason I can't rely on SSL encryption. And I don't want to use any third-party libraries. So I tried to use native browser's crypto.subtle and node's crypto modules, aes-256-cbc, but with no luck:

Creating the key and encrypting test message in browser:

(async function() {
  const {key, keyStr} = await generateKey()
  console.log(`string key for the node server: ${keyStr}`)
  const {iv, encrypted} = await encrypt(key, 'test message')
  console.log(`base64 iv: ${iv}, base64 encrypted message: ${encrypted}`)
})()

async function generateKey() {
  const key = await crypto.subtle.generateKey(
    {name: 'AES-CBC', length: 256},
    true,
    ['encrypt', 'decrypt']
  )
  const jwk = await crypto.subtle.exportKey('jwk', key)
  return {key, keyStr: jwk.k}
}

async function encrypt(key, text) {
  const iv = crypto.getRandomValues(new Uint8Array(16))
  const encrypted = await crypto.subtle.encrypt(
    {name: 'AES-CBC', iv},
    key,
    str2buf(text),
  )
  return {
    iv: buf2base64(iv),
    encrypted: buf2base64(encrypted),
  }
}

// helpers
function str2buf(str) {
  const bytes = new Uint8Array(str.length)
  for (let i = 0; i < str.length; i++) bytes[i] = str.charCodeAt(i)
  return bytes
}
function buf2base64(buf) {
  let binary = ''
  let bytes = new Uint8Array(buf)
  let len = bytes.byteLength
  for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i])
  return btoa(binary)
}

Output: string key for node server: 9ffC8m6BhFFf0mYTPrf5SAzDVCAGg1ce59LP5dqGnVc base64 iv: XTRis0eBYEl+NAt8adZN+w==, encrypted message: uReCH7g3p8FNKpwo6E+kfw==

How can I decrypt the message on the node server? I tried to crypto.createDecipheriv and following gives the 'Invalid key length' error:

const crypto = require('crypto')

const key = '9ffC8m6BhFFf0mYTPrf5SAzDVCAGg1ce59LP5dqGnVc'
const iv = 'XTRis0eBYEl+NAt8adZN+w=='
const encryptedMessage = 'uReCH7g3p8FNKpwo6E+kfw=='

const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'base64'))
let decrypted = decipher.update(Buffer.from(encryptedMessage, 'base64'))
decrypted += decipher.final('utf8')
console.log(decrypted)
1
why can't you rely on TLS? - Remi
Because I need to store messages on the server and don't want to expose unencrypted content to the hosting company. I will request messages later by another node server. And there I need them to decrypt. - Kosteg
createDecipheriv works fine and decrypts the ciphertext from your posted example as test message. Post your most recent NodeJS code. - Topaco
Updated with NodeJS code - Kosteg

1 Answers

1
votes

The key is also Base64 encoded and must therefore be decoded like the IV, e.g.:

const key = Buffer.from('9ffC8m6BhFFf0mYTPrf5SAzDVCAGg1ce59LP5dqGnVc', 'base64') 

With this change the ciphertext is decrypted to test message.