AES/CBC/PKCS5PADDING NODEJS加密

发布于 2025-02-07 23:32:59 字数 5372 浏览 1 评论 0原文

我正在尝试将我的Java代码转换为Nodejs代码。这要复杂一些,因为自定义格式包括密码和盐。

在主要方法中,有一个示例。

这是我的Java代码:

public class App {
    private static final int DYN_SALT_LENGTH = 10;
    private static final int ITERATION_COUNT = 65556;
    private static final int KEY_LENGTH = 256;
    private static final String SECRET_KEY_ALGORITHM = "AES";
    private static final String CIPHER_TRANSFORMER = "AES/CBC/PKCS5Padding";
    private static Base64 base64Instance = new Base64();

    public static String decrypt(String data, String password, String salt) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
                    ITERATION_COUNT,
                    KEY_LENGTH);

            SecretKey secretKey = factory.generateSecret(spec);

            ByteBuffer buffer = ByteBuffer.wrap(base64Instance.decode(data));
            buffer.position(DYN_SALT_LENGTH);
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);

            // Read the IV
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            buffer.get(ivBytes, 0, ivBytes.length);

            // Read encrypted text.
            byte[] encryptedTextBytes = new byte[buffer.capacity() - DYN_SALT_LENGTH - ivBytes.length];
            buffer.get(encryptedTextBytes);

            // Initialize Cipher.
            SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));

            String result = new String(cipher.doFinal(encryptedTextBytes), StandardCharsets.UTF_8);
            return result;

        } catch (Exception e) {
            throw new RuntimeException("Failed to decrypt data", e);
        }
    }

    public static String encrypt(String data, String password, String salt) {
        // Create new salt for every new encryption request.
        byte[] saltBytes = new byte[DYN_SALT_LENGTH];
        new SecureRandom().nextBytes(saltBytes);

        try {
            // Create secret key spec.
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
                    ITERATION_COUNT,
                    KEY_LENGTH);
            SecretKey secretKey = factory.generateSecret(spec);
            SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
            byte[] ivBytes;
            byte[] encryptedTextBytes;

            // Initialize cipher
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);
            cipher.init(Cipher.ENCRYPT_MODE, secret);

            AlgorithmParameters params = cipher.getParameters();

            // Create initialization vector IV
            ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();

            // Encrypt the text.
            encryptedTextBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));

            // Response will be in the form of <salt><IV><encryptedText>
            ByteBuffer byteBuffer = ByteBuffer.allocate(saltBytes.length + ivBytes.length + encryptedTextBytes.length);
            byteBuffer.put(saltBytes);
            byteBuffer.put(ivBytes);
            byteBuffer.put(encryptedTextBytes);

            return base64Instance.encodeToString(byteBuffer.array());
        } catch (Exception e) {
            throw new RuntimeException("Failed to encrypt data", e);
        }

    }

    public static void main(String[] args) {
        String password = "password";
        String salt = "salt";
        String data = "hello world";

        String resultEncrypted = encrypt(data, password, salt);
        System.out.println(resultEncrypted);
        String resultDecrypted = decrypt(resultEncrypted, password, salt);
        System.out.println(resultDecrypted);
    }
}

我正在使用下面的JS代码尝试,但毫无疑问我在做错什么:

function getAlgorithm(keyBase64) {
  var key = Buffer.from(keyBase64, "base64");
  switch (key.length) {
    case 16:
      return "aes-128-cbc";
    case 32:
      return "aes-256-cbc";
  }

  throw new Error("Invalid key length: " + key.length);
}

function decrypt(messagebase64, keyBase64, ivBase64) {
  const key = Buffer.from(keyBase64, "base64");
  const iv = Buffer.from(ivBase64, "base64");

  const decipher = crypto.createDecipheriv(
    getAlgorithm(keyBase64),
    key,
    iv.slice(0, 16)
  );
  let decrypted = decipher.update(messagebase64, "base64", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
}

const base64Encrypted =
  "2vSIh0J64zhrQuayUV+UIyPTpmSaN4gAv7B3CVC/a68eBfeU0bMwRm2I";

const key = crypto.scryptSync("password", "salt", 16);

const encrypted = Buffer.from(base64Encrypted, "base64");
const encryptedWOSalt = Buffer.from(base64Encrypted, "base64").slice(10);
const iv = encrypted.slice(10, 10 + 17);

const result = decrypt(
  encryptedWOSalt.toString("base64"),
  key,
  iv.toString("base64")
);
console.log(result);

这是错误的: 错误:错误:06065064:数字信封例程:EVP_DECRYPTFINAL_EX:不良解密

谢谢

I am trying to convert my java code to NodeJs code. It's a little more complicate because the customised format included the password and salt.

In main method there is one example.

Here is my java code:

public class App {
    private static final int DYN_SALT_LENGTH = 10;
    private static final int ITERATION_COUNT = 65556;
    private static final int KEY_LENGTH = 256;
    private static final String SECRET_KEY_ALGORITHM = "AES";
    private static final String CIPHER_TRANSFORMER = "AES/CBC/PKCS5Padding";
    private static Base64 base64Instance = new Base64();

    public static String decrypt(String data, String password, String salt) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
                    ITERATION_COUNT,
                    KEY_LENGTH);

            SecretKey secretKey = factory.generateSecret(spec);

            ByteBuffer buffer = ByteBuffer.wrap(base64Instance.decode(data));
            buffer.position(DYN_SALT_LENGTH);
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);

            // Read the IV
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            buffer.get(ivBytes, 0, ivBytes.length);

            // Read encrypted text.
            byte[] encryptedTextBytes = new byte[buffer.capacity() - DYN_SALT_LENGTH - ivBytes.length];
            buffer.get(encryptedTextBytes);

            // Initialize Cipher.
            SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));

            String result = new String(cipher.doFinal(encryptedTextBytes), StandardCharsets.UTF_8);
            return result;

        } catch (Exception e) {
            throw new RuntimeException("Failed to decrypt data", e);
        }
    }

    public static String encrypt(String data, String password, String salt) {
        // Create new salt for every new encryption request.
        byte[] saltBytes = new byte[DYN_SALT_LENGTH];
        new SecureRandom().nextBytes(saltBytes);

        try {
            // Create secret key spec.
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(StandardCharsets.UTF_8),
                    ITERATION_COUNT,
                    KEY_LENGTH);
            SecretKey secretKey = factory.generateSecret(spec);
            SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), SECRET_KEY_ALGORITHM);
            byte[] ivBytes;
            byte[] encryptedTextBytes;

            // Initialize cipher
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMER);
            cipher.init(Cipher.ENCRYPT_MODE, secret);

            AlgorithmParameters params = cipher.getParameters();

            // Create initialization vector IV
            ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();

            // Encrypt the text.
            encryptedTextBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));

            // Response will be in the form of <salt><IV><encryptedText>
            ByteBuffer byteBuffer = ByteBuffer.allocate(saltBytes.length + ivBytes.length + encryptedTextBytes.length);
            byteBuffer.put(saltBytes);
            byteBuffer.put(ivBytes);
            byteBuffer.put(encryptedTextBytes);

            return base64Instance.encodeToString(byteBuffer.array());
        } catch (Exception e) {
            throw new RuntimeException("Failed to encrypt data", e);
        }

    }

    public static void main(String[] args) {
        String password = "password";
        String salt = "salt";
        String data = "hello world";

        String resultEncrypted = encrypt(data, password, salt);
        System.out.println(resultEncrypted);
        String resultDecrypted = decrypt(resultEncrypted, password, salt);
        System.out.println(resultDecrypted);
    }
}

I'm trying with JS code like below but without clue about what i'm doing wrong:

function getAlgorithm(keyBase64) {
  var key = Buffer.from(keyBase64, "base64");
  switch (key.length) {
    case 16:
      return "aes-128-cbc";
    case 32:
      return "aes-256-cbc";
  }

  throw new Error("Invalid key length: " + key.length);
}

function decrypt(messagebase64, keyBase64, ivBase64) {
  const key = Buffer.from(keyBase64, "base64");
  const iv = Buffer.from(ivBase64, "base64");

  const decipher = crypto.createDecipheriv(
    getAlgorithm(keyBase64),
    key,
    iv.slice(0, 16)
  );
  let decrypted = decipher.update(messagebase64, "base64", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
}

const base64Encrypted =
  "2vSIh0J64zhrQuayUV+UIyPTpmSaN4gAv7B3CVC/a68eBfeU0bMwRm2I";

const key = crypto.scryptSync("password", "salt", 16);

const encrypted = Buffer.from(base64Encrypted, "base64");
const encryptedWOSalt = Buffer.from(base64Encrypted, "base64").slice(10);
const iv = encrypted.slice(10, 10 + 17);

const result = decrypt(
  encryptedWOSalt.toString("base64"),
  key,
  iv.toString("base64")
);
console.log(result);

It's throwing an error:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

地狱即天堂 2025-02-14 23:32:59

由于您仅发布了NodeJS解密代码,因此我专注于解密。该加密应类似地实现。如果您对此有问题,请发布一个带有相应加密代码的新问题。

NODEJS代码中有几个错误:

  • 错误的键推导(在Nodejs代码中使用了Scrypt,而在Java代码PBKDF2/HMAC-SHA1中使用)。
  • 的不正确/缺失分离,
  • 盐,IV和密文编码错误以及不必要的编码/解码周期

以下NODEJS代码有效:

var crypto = require('crypto')

function getAlgorithm(key) {
   switch (key.length) {
    case 16:
      return "aes-128-cbc";
    case 32:
      return "aes-256-cbc";
  }
  throw new Error("Invalid key length: " + key.length);
}

function decrypt(message, key, iv) {
  const decipher = crypto.createDecipheriv(
    getAlgorithm(key),
    key,
    iv
  );
  let decrypted = Buffer.concat([decipher.update(message), decipher.final()]);
  return decrypted.toString("utf8");
}

const DYN_SALT_LENGTH = 10;
const IV_LENGTH = 16;
const ITERATION_COUNT = 65556;
const KEY_LENGTH = 256;

const base64Encrypted = "ossqoyCaaQINWUkTsHNGRe5Isd5s7c7U8KcLua78Ehm9jAxQNOd2tyjj";

// Separate salt, IV and ciphertext
const encrypted = Buffer.from(base64Encrypted, "base64");
const salt = encrypted.slice(0, DYN_SALT_LENGTH);
const iv = encrypted.slice(DYN_SALT_LENGTH, DYN_SALT_LENGTH + IV_LENGTH);
const ciphertext = encrypted.slice(DYN_SALT_LENGTH + IV_LENGTH);

// Derive key voa PBKDF2/HMAC-SHA1
const key = crypto.pbkdf2Sync("password", "salt", ITERATION_COUNT, KEY_LENGTH/8, "sha1");

// Decrypt
const result = decrypt(
  ciphertext,
  key,
  iv
);
console.log(result); // hello world

请注意,当前的Java代码(以及Nodejs代码)不使用随机盐来进行键衍生,但是一个静态盐,可能是用于测试目的。在最终解决方案中,出于安全原因将使用随机盐。

Since you only posted a NodeJS decryption code, I focus on decryption. The encryption is to be implemented analogously. If you have problems with this, please post a new question with the corresponding encryption code.

There are several bugs in the NodeJS code:

  • Wrong key derivation (in the NodeJS code scrypt is used, while in the Java code PBKDF2/HMAC-SHA1 is applied).
  • Incorrect/missing separation of salt, IV and ciphertext
  • Encoding bugs and unnecessary encoding/decoding cycles

The following NodeJS code works:

var crypto = require('crypto')

function getAlgorithm(key) {
   switch (key.length) {
    case 16:
      return "aes-128-cbc";
    case 32:
      return "aes-256-cbc";
  }
  throw new Error("Invalid key length: " + key.length);
}

function decrypt(message, key, iv) {
  const decipher = crypto.createDecipheriv(
    getAlgorithm(key),
    key,
    iv
  );
  let decrypted = Buffer.concat([decipher.update(message), decipher.final()]);
  return decrypted.toString("utf8");
}

const DYN_SALT_LENGTH = 10;
const IV_LENGTH = 16;
const ITERATION_COUNT = 65556;
const KEY_LENGTH = 256;

const base64Encrypted = "ossqoyCaaQINWUkTsHNGRe5Isd5s7c7U8KcLua78Ehm9jAxQNOd2tyjj";

// Separate salt, IV and ciphertext
const encrypted = Buffer.from(base64Encrypted, "base64");
const salt = encrypted.slice(0, DYN_SALT_LENGTH);
const iv = encrypted.slice(DYN_SALT_LENGTH, DYN_SALT_LENGTH + IV_LENGTH);
const ciphertext = encrypted.slice(DYN_SALT_LENGTH + IV_LENGTH);

// Derive key voa PBKDF2/HMAC-SHA1
const key = crypto.pbkdf2Sync("password", "salt", ITERATION_COUNT, KEY_LENGTH/8, "sha1");

// Decrypt
const result = decrypt(
  ciphertext,
  key,
  iv
);
console.log(result); // hello world

Note that the current Java code (and therefore also the NodeJS code) does not use the random salt for key derivation, but a static salt, possibly for testing purposes. In the final solution, the random salt is to be used for security reasons.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文