crypto加密-实战篇之非对称和对称结合加密
前言
在Web应用中,为了安全地传输用户密码,前端使用AES加密密码,然后使用RSA加密AES密钥。这样可以确保密码在传输过程中不被泄露,同时保证了整个过程的安全性。
步骤
-
后端生成RSA密钥对:
- 后端生成RSA密钥对(公钥和私钥)。
- 后端将RSA公钥发送给前端。
-
前端生成AES密钥:
- 前端生成一个随机的AES密钥。
- 前端使用AES密钥加密用户密码。
-
前端使用RSA公钥加密AES密钥:
- 前端使用后端提供的RSA公钥加密AES密钥。
-
前端将加密后的数据发送到后端:
- 前端将加密后的密码、IV(初始化向量)、AuthTag(认证标签)和加密后的AES密钥一起发送到后端。
-
后端解密AES密钥:
- 后端使用RSA私钥解密AES密钥。
-
后端使用AES密钥解密密码:
- 后端使用解密后的AES密钥解密用户密码。
详细步骤说明
-
后端生成RSA密钥对:
- 后端生成RSA密钥对,并将公钥发送给前端。
- 示例代码:
const crypto = require('crypto'); const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); console.log('Public Key:', publicKey);
-
前端生成AES密钥:
- 前端生成一个随机的AES密钥。
- 示例代码:
const crypto = require('crypto'); const aesKey = crypto.randomBytes(32); // 256位AES密钥
-
前端使用AES密钥加密密码:
- 前端使用AES密钥加密用户密码。
- 示例代码:
const crypto = require('crypto'); function encryptData(data, aesKey) { const iv = crypto.randomBytes(12); // 12字节IV const cipher = crypto.createCipheriv('aes-256-gcm', aesKey, iv); let encrypted = cipher.update(data, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag().toString('hex'); return { iv: iv.toString('hex'), encrypted, authTag }; } const data = 'my_secret_password'; const { iv, encrypted, authTag } = encryptData(data, aesKey);
-
前端使用RSA公钥加密AES密钥:
- 前端使用RSA公钥加密AES密钥。
- 示例代码:
const crypto = require('crypto'); const encryptedAESKey = crypto.publicEncrypt( { key: publicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, aesKey ); console.log('Encrypted AES Key:', encryptedAESKey.toString('base64'));
-
前端将加密后的数据发送到后端:
- 前端将加密后的密码、IV、AuthTag和加密后的AES密钥一起发送到后端。
- 示例代码:
const encryptedData = { encryptedPassword: encrypted, iv: iv, authTag: authTag, encryptedAESKey: encryptedAESKey.toString('base64') }; // 发送到后端 console.log(encryptedData);
-
后端解密AES密钥:
- 后端使用RSA私钥解密AES密钥。
- 示例代码:
const crypto = require('crypto'); const decryptedAESKey = crypto.privateDecrypt( { key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING }, Buffer.from(encryptedAESKey, 'base64') ); console.log('Decrypted AES Key:', decryptedAESKey.toString('hex'));
-
后端使用AES密钥解密密码:
- 后端使用解密后的AES密钥解密用户密码。
- 示例代码:
const crypto = require('crypto'); function decryptData(encryptedData, iv, authTag, aesKey) { const decipher = crypto.createDecipheriv('aes-256-gcm', aesKey, Buffer.from(iv, 'hex')); decipher.setAuthTag(Buffer.from(authTag, 'hex')); let decrypted = decipher.update(encryptedData, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } const decryptedPassword = decryptData(encrypted, iv, authTag, decryptedAESKey); console.log('Decrypted Password:', decryptedPassword);
入库
若还需反解,那就后端在解析完前端的加密后,再次使用后端自己的对称密钥文件进行加密存储!
否则直接 hash 化即可!
针对密码,hash 化处理是最常见的。所以后续都是再讲 hash 化!
将用户设置的密码入库:hash 化的字符串,以及加入的随机字符salt
import crypto from "crypto";
// 使用PBKDF2对密码进行哈希处理
const hashPassword = ( password: string, saltParam?: string ): { hash: string; salt: string } => {
const saltValue = saltParam || crypto.randomBytes(4).toString("hex"); // 生成随机盐值(也就是随机字符串),建议 6 位
const iterations = 100000; // 迭代次数
const keyLength = 6; // 哈希长度(建议 64 为)
const digest = "sha512"; // 哈希算法
const hash = crypto
.pbkdf2Sync(password, saltValue, iterations, keyLength, digest)
.toString("hex");
return {
hash,
salt: saltValue,
};
};
// 将明文密码哈希处理,并入库(注册的时候)
const { hash, salt } = hashPassword("hello2015");
console.log(hash, salt); // 0aafb73d2d7f, d49e7a0a
const record = { // 入库数据
pwd: 'c50b44be34b6',
salt:'b0b9f633'
}
校验使用
const inputPwd = 'hello2015'; // 前端传递过的需要校验(是否存在)的数据
const { hash: newHash } = hashPassword(inputPwd, record.salt);
if(newHash === record.pwd){
console.log("密码验证成功");
}
hash
哈希化(Hashing)是一种单向过程,设计上是不可逆的。这意味着你不能从哈希值直接还原出原始数据。,所以即便不小心泄露了也没事。
salt
中文为盐
,目的是在给字符串 hash 化的时候,加上随机字符。
这样即便密码相同,hash 也会不同,从而防止防止彩虹表攻击,即为每个用户生成一个独特的盐值。
盐值本身无意义,所以泄露了也没事!
总结
这是一个典型的非对称加密结合对称加密的流程,常用于安全传输敏感数据(如密码)
1、后端或前端 产出非对称密钥(找个安全的地方存储起来,私钥千万不能暴漏)
2、前端每次加密都需临时生成对称密钥-->去加密数据
3、前端还需要拿非对称的公钥-->去加密第二步产出的对称密钥
4、前端把第2 和第 3 步骤,产出都数据都提交给后端
5、后端拿非对称的私钥 去解密对称密钥,再拿对称密钥解密出密码
6、后端拿解密出的密码去 hash 化,注意保 hash 用到的留盐值
7、最后后端将 hash 化的密码入库,并把盐值也入库
上诉描述的内容完全属于计算机密码学(Cryptography)的知识范畴,而且是现代密码学中非常经典和实用的技术组合。
您提出的方案是一个典型的混合加密系统(Hybrid Cryptosystem),它结合了两种密码学体系的优点