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无效的参数错误(实际是密钥长度不匹配)。此外代码还存在两个逻辑问题:
- 调用
encrypt后未使用返回的加密密码和IV,反而插入原始密码和硬编码的"jojo" - 硬编码的"jojo"是普通字符串,不符合AES要求的16字节Buffer格式
修复步骤
生成标准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 };修正数据库插入逻辑
使用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"); } });调整MySQL字段类型
iv存储的是16字节转换后的hex字符串(长度32),需确保MySQL中该字段类型为VARCHAR(64)或更长的字符串类型,避免数据截断。
验证修复
重启后端和前端,点击添加密码按钮,加密逻辑会正常执行,加密后的密码和IV将正确插入数据库,不再触发IV无效错误。
内容的提问来源于stack exchange,提问作者MOHIT RANA
相关产品推荐
相关产品推荐

