Java / Node PBKDF2 хэш пароля пользователя с использованием соли и количества итераций - эквивалент узла

Я пытаюсь воспроизвести этот метод здесь, в Nodejs. Я хотел бы узнать его эквивалент в Node, похожий на Репликация пароля Java код хеширования в Node.js (PBKDF2WithHmacSHA1).

Код Java:

  private final byte[] _createSaltedPassword(String password, byte[] salt, int iterationCount) {
    
    byte[] dk;    
    PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256);
    SecretKeyFactory key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    dk = key.generateSecret(keySpec).getEncoded();
    return dk; 
  }

Я сохраняю пользователя (пользователя), одноразовый номер клиента (aabbccddaabbccdd), пароль (Password123), соль и количество итераций (4096) одинаковыми для этого примера. Моя последняя попытка получить эквивалент в Node с использованием crypto.

  const crypto = require("crypto");

  const saltedPassword = crypto.pbkdf2Sync(password, salt, iterationCount, 32, 'sha256').toString('base64');
  console.log('Salted Password:', saltedPassword);

Это для дайджест-аутентификации. Журнал вывода из программы на Java:

Журнал вывода из программы Java

Соленый пароль Java: nA0hWFpZshs + iME / leUy + e2gM5mjIgo6PYJ8eNjRdhY =

Соленый пароль узла: PYgn6rcngiE0HSMwzmWhd2W5qTdaRilw4PEeuy3OyRo =

Спасибо.


person pb46    schedule 15.01.2021    source источник


Ответы (1)


Извините за то, что мне лень просмотреть связанный код, но я привожу два примера для Java и NodeJS с Crypto lib, которые генерируют секретный ключ из парольной фразы с использованием алгоритма PBKDF2. Вы можете запустить оба кода в онлайн-компиляторе - код Java и код NodeJs.

Это результат с фиксированной = небезопасной солью:

Generate a 32 byte long AES key with PBKDF2
aesKeySha256 length:  32  data:  e1ea3e4b0376c0f9bf93b94fe71719a099317297b79108aacd88c8a355d7a3d4

Предупреждение о безопасности: приведенные ниже коды не обрабатывают исключения и предназначены только для образовательных целей. Не используйте фиксированную соль в производстве, она используется только для демонстрации того, что обе платформы генерируют один и тот же секретный ключ.

Код Java:

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("Generate a 32 byte long AES key with PBKDF2");

        // get the password as char array
        char[] passwordChar = "secret password".toCharArray();
        final int PBKDF2_ITERATIONS = 15000; // number of iterations, higher is better but slower
        // ### security warning - never use a fixed salt in production, this is for compare reasons only
        byte[] salt = generateFixedSalt32Byte();
        // please use below generateSalt32Byte()
        //byte[] salt = generateSalt32Byte();
        byte[] aesKeySha256 = generateAes256KeyPbkdf2Sha256(passwordChar, PBKDF2_ITERATIONS, salt);
        System.out.println("aesKeySha256 length: " + aesKeySha256.length + " data: " + bytesToHex(aesKeySha256));
    }

    public static byte[] generateAes256KeyPbkdf2Sha256(char[] password, int iterations, byte[] salt) throws Exception {
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec keySpec = new PBEKeySpec(password, salt, iterations, 32 * 8);
        return secretKeyFactory.generateSecret(keySpec).getEncoded();
    }

    private static byte[] generateSalt32Byte() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] salt = new byte[32];
        secureRandom.nextBytes(salt);
        return salt;
    }

    private static byte[] generateFixedSalt32Byte() {
        // ### security warning - never use this in production ###
        byte[] salt = new byte[32]; // 32 x0's
        return salt;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
}

Код NodeJs

console.log("Generate a 32 byte long AES key with PBKDF2");
var crypto = require('crypto');

var password = "secret password";
var PBKDF2_ITERATIONS = 15000; // number of iterations, higher is better but slower

// ### security warning - never use a fixed salt in production, this is for compare reasons only
var salt = generateFixedSalt32Byte();
// please use below generateSalt32Byte()
//var salt = generateSalt32Byte();

var aesKeySha256 = generateAes256KeyPbkdf2Sha256(password, PBKDF2_ITERATIONS, salt);
console.log('aesKeySha256 length: ',
  aesKeySha256.length, ' data: ', bytesToHex(aesKeySha256));

function generateAes256KeyPbkdf2Sha256(password, iterations, salt) {
  return crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha256');
}

function generateSalt32Byte() {
  return crypto.randomBytes(32);
}

function generateFixedSalt32Byte() {
  return Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex');
}

function bytesToHex(input) {
  return input.toString('hex');
}
person Michael Fehr    schedule 15.01.2021
comment
Спасибо, что помогло. Думаю, я ошибся, не преобразовав свою соль: Buffer.from(salt, 'base64') Тогда это сработало! - person pb46; 15.01.2021
comment
Просто чтобы следить за этим ... он отлично работает ... мой вопрос касается последующих запросов, передающих заголовок: Authorization: Bearer authToken=abcd... Я хочу избежать аутентификации каждый раз, когда я делаю следующий запрос. Есть ли что-то, что я должен поддерживать, например, cookie, увеличивать какой-то счетчик или что-то еще? - person pb46; 23.01.2021
comment
@ pb46: Мне очень жаль, но я не специалист по таким вопросам, и вы должны задать это в другом вопросе, спасибо + хорошего дня. - person Michael Fehr; 23.01.2021