当前位置: 首页 > 知识库问答 >
问题:

AES/CBC/PKCS5填充节点加密

弘柏
2023-03-14

我正在尝试将java代码转换为NodeJs代码。这有点复杂,因为定制的格式包括密码和salt。

在main方法中有一个例子。

以下是我的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:错误解密

谢谢

共有1个答案

毛淳
2023-03-14

因为您只发布了一个NodeJS解密代码,所以我将重点放在解密上。加密将以类似方式实现。如果您对此有问题,请使用相应的加密代码发布新问题。

NodeJS代码中有几个bug:

  • 错误的密钥推导(在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
 类似资料:
  • 我目前使用< code > AES/CBC/PKCS 5 Padding 对Java文件进行加密,密钥大小为256字节,但在搜索时,我在stack exchange PKCS # 5-PKCS # 7 Padding上发现了它, PKCS#5填充是8字节块大小的PKCS#7填充的子集 所以我想知道 > < li >对于上述配置,< code > AES/CBC/pkcs 7 padding 的性能

  • 我一直在尝试用Python实现AES CBC解密。由于加密文本不是16字节的倍数,因此需要填充。没有填充,这个错误浮出水面 "TypeError:奇数长度字符串" 但我找不到在PyCrypto Python中实现PKCS5的适当参考。是否有任何命令来实现这一点?谢谢 在研究了马库斯的建议后,我这样做了。 我的目标实际上是使用此代码解密十六进制消息(128字节)。但是,输出 " ?:" 非常小,un

  • 我阅读了一些有关使用Java密码加密和解密数据的示例。例如: 我有2个关于解密过程的问题。 虽然IV是必需的,但我们可以使用将其保留为隐式。将自动对其应用随机IV。但是,在解密模式下,必须使用相同的IV。它是否只表示<code>密码。init(int opmode,Key Key,AlgorithmParameters params)应该使用,IV应该从加密中获取,存储并传递到这里 除了“KeyG

  • 这应该是一个简单的问题,但我无法从openssl文档中找到任何示例或答案。 我想加密128位,应该适合一个加密块。 所以我调用,然后呢? 我是否调用(加密 128 位块)和(即使没有更多要加密的内容)? 还是只有?还是只有?

  • 我希望有一个用C编写的程序,可以在没有openssl这样的大型库的帮助下,用AES-CBC对字符串进行编码/解码。 目标: 使用密码短语对字符串进行编码/解码: 因此,应用程序需要接受3个输入参数。。。 输入字符串(待编码)/或已编码字符串(待解码) 用于编码/解码字符串的密码 编码或解码指示器 我对C语言不熟悉(我可以用C#编码)。 我已经找到了https://github.com/kokke/

  • 我只想用这3种模式测试openSSL中的AES:128192和256密钥长度,但我解密的文本与我的输入不同,我不知道为什么。此外,当我传递一个巨大的输入长度(比如1024字节)时,我的程序显示。。。我的意见总是一样的,但这并不重要,至少现在是这样。代码如下: 编辑: 当我将输出大小更改为而不是时,我得到了结果: 那么,是否有可能存在Outpus大小和IV大小的问题?它们应该有什么尺寸(AES-CB