Node.js, использующий hex, получил неверную длину конечного блока - PullRequest
0 голосов
/ 09 ноября 2019

Я пытался зашифровать / расшифровать файл, используя node.js. Я написал 4 пары функций для шифрования и дешифрования. Если я не установил никакой кодировки, они работают. Когда я пробовал какую-то кодировку, такую ​​как «hex», «base64», они терпели неудачу. Все ошибки произошли при расшифровке зашифрованного файла.

Особенно с использованием pipe, я не знаю, как установить входную и выходную кодировку.

Ошибка

  const ret = this._handle.final();
                           ^

Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
    at Decipheriv.final (internal/crypto/cipher.js:164:28)
    at ReadStream.readStream.on.on (C:\test\tutorial\scrypt\node\index.js:111:37)
    at ReadStream.emit (events.js:194:15)
    at endReadableNT (_stream_readable.js:1125:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! scrypt@1.0.0 start: `node index.js`
npm ERR! Exit status 1

Код (index.js)

const crypto = require( 'crypto' )
const fs = require( 'fs' )

const algorithm = 'aes-192-cbc'
const password = 'testing'
const salt = 'salt'

const key = crypto.scryptSync( password, salt, 24 )
const iv = Buffer.alloc( 16, 0 )

function encrypt_pnull() {
    let inf = 'copyright.txt'
    let outf = 'encrypted_pnull.enc'
    cipher = crypto.createCipheriv( algorithm, key, iv )
    input = fs.createReadStream( inf )
    encrypted = fs.createWriteStream( outf )
    input.pipe( cipher ).pipe( encrypted )
}

function decrypt_pnull() {
    let inf = 'encrypted_pnull.enc'
    let outf = 'decrypted_pnull.txt'
    decipher = crypto.createDecipheriv( algorithm, key, iv )
    inp_enc = fs.createReadStream( inf )
    decrypted = fs.createWriteStream( outf )
    inp_enc.pipe( decipher ).pipe( decrypted )
}

function encrypt_phex() {
    let inf = 'copyright.txt'
    let outf = 'encrypted_phex.enc'
    cipher = crypto.createCipheriv( algorithm, key, iv )
    input = fs.createReadStream( inf )
    encrypted = fs.createWriteStream( outf )
    // cipher.setEncoding( 'hex' )
    cipher.setEncoding( 'base64' )
    input.pipe( cipher ).pipe( encrypted )
}

function decrypt_phex() {
    let inf = 'encrypted_phex.enc'
    let outf = 'decrypted_phex.txt'
    decipher = crypto.createDecipheriv( algorithm, key, iv )

    /// None of the following setting work
    // decipher.setEncoding( 'hex' )
    // decipher.setEncoding( 'utf8' )
    decipher.setEncoding( 'base64' )

    inp_enc = fs.createReadStream( inf )
    decrypted = fs.createWriteStream( outf )
    inp_enc.pipe( decipher ).pipe( decrypted )
}


function encrypt_snull() {
    let inf = 'copyright.txt'
    let outf = 'encrypted_snull.enc'
    cipher = crypto.createCipheriv( algorithm, key, iv )
    let writeStream = fs.createWriteStream( outf )

    let readStream = fs.createReadStream( inf )
    readStream.on( 'data', ( chunk ) => {
        writeStream.write( cipher.update( chunk ) )
    } ).on( 'end', () => {
        writeStream.write( cipher.final() )
        writeStream.end()
    } )
}

function decrypt_snull() {
    let inf = 'encrypted_snull.enc'
    let outf = 'decrypted_snull.txt'
    decipher = crypto.createDecipheriv( algorithm, key, iv )
    writeStream = fs.createWriteStream( outf )

    readStream = fs.createReadStream( inf )
    readStream.on( 'data', ( chunk ) => {
        writeStream.write( decipher.update( chunk ) )
    } ).on( 'end', () => {
        writeStream.write( decipher.final() )
        writeStream.end()
    } )
}

function encrypt_shex() {
    let inf = 'copyright.txt'
    let outf = 'encrypted_shex.enc'
    cipher = crypto.createCipheriv( algorithm, key, iv )
    let writeStream = fs.createWriteStream( outf )

    let readStream = fs.createReadStream( inf )
    readStream.on( 'data', ( chunk ) => {
        writeStream.write( cipher.update( chunk, 'utf8', 'hex' ) )
    } ).on( 'end', () => {
        writeStream.write( cipher.final( 'hex' ) )
        writeStream.end()
    } )
}

function decrypt_shex() {
    let inf = 'encrypted_shex.enc'
    let outf = 'decrypted_shex.txt'
    decipher = crypto.createDecipheriv( algorithm, key, iv )
    writeStream = fs.createWriteStream( outf )

    readStream = fs.createReadStream( inf )
    readStream.on( 'data', ( chunk ) => {
        writeStream.write( decipher.update( chunk, 'hex', 'utf8' ) )
    } ).on( 'end', () => {
        writeStream.write( decipher.final( 'utf8' ) )
        writeStream.end()
    } )
}


/// work
// encrypt_pnull()
// decrypt_pnull()

/// fail
// encrypt_phex()
// decrypt_phex()

/// work
// encrypt_snull()
// decrypt_snull()

/// fail
// encrypt_shex()
// decrypt_shex()

1 Ответ

0 голосов
/ 10 ноября 2019

Проблема на стороне расшифровки. encrypted_phex.enc имеет строку в кодировке base64 (потому что в вашем примере используется «base64» вместо «hex»). Однако decipher.setEncoding('base64') указывает поток преобразования decipher для приема некодированных буферов и вывода строк в кодировке base64. Чтобы это работало, нам нужно сначала декодировать кодировку base64 перед расшифровкой, и нам не нужно снова кодировать вывод с помощью base64.

Вот наивная реализация декодирования base64 перед расшифровкой. Это может быть неэффективно для больших данных, потому что он буферизует все чанки и декодирует сразу. Функцию decode() можно заменить на более эффективную реализацию [1] , но это должно работать как минимум (при условии, что вы вызываете decrypt_phex() после того, как encrypt_phex() закончит запись файла).

function decode(encoding) {
  const chunks = [];
  return new stream.Transform({
    transform(chunk, _encoding, callback) {
      chunks.push(chunk);
      callback();
    },
    flush(callback) {
      const str = Buffer.concat(chunks).toString();
      const decoded = Buffer.from(str, encoding);
      callback(null, decoded);
    }
  });
}

function decrypt_phex() {
  let inf = "encrypted_phex.enc";
  let outf = "decrypted_phex.txt";
  let decipher = crypto.createDecipheriv(algorithm, key, iv);

  inp_enc = fs.createReadStream(inf);
  let decrypted = fs.createWriteStream(outf);
  inp_enc
    .pipe(decode("base64"))
    .pipe(decipher)
    .pipe(decrypted);
}

РЕДАКТИРОВАТЬ

Версия .on("data").on("end") имеет аналогичную проблему при кодировании. Параметры encoding (utf8, hex, base64 и т. Д.) действительны только для строк. Они нам не нужны при получении или выводе Buffer с.

Документация Cipher гласит:

Обновляет шифр данными. Если задан аргумент inputEncoding, аргумент data представляет собой строку, использующую указанную кодировку. Если аргумент inputEncoding не указан, данные должны быть Buffer, TypedArray или DataView. Если data - это Buffer, TypedArray или DataView, то inputEncoding игнорируется.

И чтобы компоненты Node.js понимали «шестнадцатеричное» кодирование, нам необходимо преобразовать Buffer s в строки,Вот пример использования функции decode() сверху.

function encrypt_shex() {
  let inf = "copyright.txt";
  let outf = "encrypted_shex.enc";
  let cipher = crypto.createCipheriv(algorithm, key, iv);
  let writeStream = fs.createWriteStream(outf);

  let readStream = fs.createReadStream(inf);
  readStream
    .on("data", chunk => {
      // The second argument is not necessary because `chunk` is a Buffer.
      writeStream.write(cipher.update(chunk, null, "hex"));
    })
    .on("end", () => {
      writeStream.write(cipher.final("hex"));
      writeStream.end();
    });
}

function decrypt_shex() {
  let inf = "encrypted_shex.enc";
  let outf = "decrypted_shex.txt";
  let decipher = crypto.createDecipheriv(algorithm, key, iv);
  let writeStream = fs.createWriteStream(outf);

  let readStream = fs.createReadStream(inf);
  readStream
    // Decode hex string into a Buffer
    .pipe(decode("hex"))
    .on("data", chunk => {
      writeStream.write(decipher.update(chunk));
    })
    .on("end", () => {
      writeStream.write(decipher.final());
      writeStream.end();
    });
}

Мы могли бы подумать о декодировании шестнадцатеричной строки в обработчике data, как показано ниже. Однако этот подход имеет проблему, потому что шестнадцатеричное кодирование представляет один байт с двумя символами (два байта). Хотя он работает до тех пор, пока chunk имеет четное число байтов, когда chunk имеет нечетное число байтов, decipher не может его понять и выдает ошибку TypeError: Bad input string.

function decrypt_shex() {
  let inf = "encrypted_shex.enc";
  let outf = "decrypted_shex.txt";
  let decipher = crypto.createDecipheriv(algorithm, key, iv);
  let writeStream = fs.createWriteStream(outf);

  let readStream = fs.createReadStream(inf);
  readStream
    .on("data", chunk => {
      // `chunk` is a Buffer, so convert it into a string before giving it as a hex string to `decipher`.
      writeStream.write(decipher.update(chunk.toString("ascii"), "hex"));
    })
    .on("end", () => {
      writeStream.write(decipher.final());
      writeStream.end();
    });
}

[1] Потоковые реализации должны быть более эффективными для больших данных. Например: b64 . Я не проверял это все же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...