You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js使用AES-256-CTR加密密码时遇无效IV错误求助

解决AES-256-CTR加密时的「TypeError: Invalid initialization vector」错误

问题描述

开发基于Node.js和React的密码管理器应用,使用AES-256-CTR算法实现加解密。后端通过Node.js的crypto模块加密密码时,触发「TypeError: Invalid initialization vector」错误。注释掉加密相关代码后,数据可正常插入MySQL(手动设置iv为"jojo"),给React的addPassword函数添加async/await后仍无法解决问题。

问题相关代码

index.js

const express = require("express");
const app = express();
const mysql = require("mysql2");
const cors = require("cors");
const { encrypt } = require("./EncyrptionHandler");

app.use(cors());
app.use(express.json());

const PORT = 3001;
const db = mysql.createConnection({
  user: "root",
  host: "localhost",
  password: "root",
  database: "passwordmanager",
});

db.connect((err) => {
  if (err) {
    console.log(err);
  }
  console.log("Connected");
});
app.post("/addpassword", (req, res) => {
  const { password, website } = req.body;
  // const { iv, password: encryptedPassword } = encrypt(password);
  // console.log(iv, password);
  const iv = "jojo";
  const encryptedData = encrypt(password);
  console.log(encryptedData);
  const query = "INSERT INTO password (password, website, iv) VALUES (?, ?, ?)";
  db.query(query, [password, website, iv], (err, result) => {
    if (err) {
      console.error("Error inserting data:", err);
      res.status(500).send("Error inserting data");
      return;
    }
    res.send("Success");
  });
});

app.get("/", (req, res) => {
  res.send("<h1>hello world</h1>");
});

app.listen(PORT, () => {
  console.log("listening on port");
});

EncryptionHandler.js

const crypto = require("crypto");

const secret = "Pneumonoultramicroscopicsilicovolcanoconiosis";

const encrypt = (password) => {
  const iv = crypto.randomBytes(16); // Generate a 16-byte IV for aes-256-ctr
  const cipher = crypto.createCipheriv("aes-256-ctr", Buffer.from(secret), iv);
  let encryptedPassword = cipher.update(password, "utf8", "hex");
  encryptedPassword += cipher.final("hex");

  return {
    iv: iv.toString("hex"),
    password: encryptedPassword,
  };
};

module.exports = { encrypt };

App.js(含修改后的async/await版本)

import { useState } from "react";
import "./App.css";
import axios from "axios";

function App() {
  const [password, setPassword] = useState("");
  const [website, setWebsite] = useState("");

  // 修改后的async/await版本
  const addPassword = async () => {
    try {
      const response = await axios.post("http://localhost:3001/addpassword", {
        password: password,
        website: website,
      });
      console.log("Password added successfully:", response.data);
      setPassword("");
      setWebsite("");
    } catch (error) {
      console.error("Error adding password:", error);
    }
  };

  return (
    <div className="App">
      <div className="AdddingPassword">
        <input
          type="text"
          placeholder="Ex. 123456"
          onChange={(e) => setPassword(e.target.value)}
        />
        <input
          type="text"
          placeholder="facebook.com"
          onChange={(e) => setWebsite(e.target.value)}
        />
        <button onClick={addPassword}>Add Password</button>
      </div>
    </div>
  );
}

export default App;

问题原因与修复方案

核心问题

错误的本质是AES-256-CTR要求密钥长度必须为32字节(256位),你使用的secret字符串长度超过32字节,直接用Buffer.from(secret)会截断密钥,不符合加密规范,导致crypto模块抛出IV无效的参数错误(实际是密钥长度不匹配)。此外代码还存在两个逻辑问题:

  1. 调用encrypt后未使用返回的加密密码和IV,反而插入原始密码和硬编码的"jojo"
  2. 硬编码的"jojo"是普通字符串,不符合AES要求的16字节Buffer格式

修复步骤

  1. 生成标准32字节密钥
    用crypto.scryptSync从原始字符串生成符合要求的32字节AES密钥:

    // EncryptionHandler.js 修改后
    const crypto = require("crypto");
    const secret = "Pneumonoultramicroscopicsilicovolcanoconiosis";
    // 生成32字节AES-256密钥,salt建议用环境变量存储,不要硬编码
    const key = crypto.scryptSync(secret, "your-custom-salt", 32);
    
    const encrypt = (password) => {
      const iv = crypto.randomBytes(16); // 16字节IV符合AES-CTR要求
      const cipher = crypto.createCipheriv("aes-256-ctr", key, iv);
      let encryptedPassword = cipher.update(password, "utf8", "hex");
      encryptedPassword += cipher.final("hex");
    
      return {
        iv: iv.toString("hex"),
        password: encryptedPassword,
      };
    };
    
    module.exports = { encrypt };
    
  2. 修正数据库插入逻辑
    使用encrypt返回的加密密码和IV插入数据库,替换原始密码和硬编码IV:

    // index.js 修改/addpassword接口
    app.post("/addpassword", (req, res) => {
      const { password, website } = req.body;
      try {
        const { iv, password: encryptedPassword } = encrypt(password);
        const query = "INSERT INTO password (password, website, iv) VALUES (?, ?, ?)";
        db.query(query, [encryptedPassword, website, iv], (err, result) => {
          if (err) {
            console.error("Error inserting data:", err);
            res.status(500).send("Error inserting data");
            return;
          }
          res.send("Success");
        });
      } catch (err) {
        console.error("Encryption error:", err);
        res.status(500).send("Encryption failed");
      }
    });
    
  3. 调整MySQL字段类型
    iv存储的是16字节转换后的hex字符串(长度32),需确保MySQL中该字段类型为VARCHAR(64)或更长的字符串类型,避免数据截断。

验证修复

重启后端和前端,点击添加密码按钮,加密逻辑会正常执行,加密后的密码和IV将正确插入数据库,不再触发IV无效错误。

内容的提问来源于stack exchange,提问作者MOHIT RANA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 00:22:19