加密与安全_前后端通过AES-CBC模式安全传输数据

文章目录

  • Pre
  • 概述
  • 前端加密是否有意义?
  • 环境准备
  • 加密方法、MODE和PADDING的选择
  • 前端
  • 后端
  • 应用:从传输到解密的全过程
  • 安全性增强
    • 动态生成密钥和初始向量
      • 1. 前端:动态生成密钥和IV
      • 2. 后端:解密动态密钥和IV
  • 结语

在这里插入图片描述


Pre

加密与安全_解密AES加密中的IV和Seed

加密与安全_双向RSA+AES加密及Code实现

加密与安全_常见的分组密码 ECB、CBC、CFB、OFB模式介绍


概述

当我们在前端和后端之间传输敏感信息时,安全性变得越来越重要。今天,我们将一起探索如何在前端加密密码,并在后端安全解密的过程。

使用Vue.js作为前端框架,并通过Java来处理后端解密。


前端加密是否有意义?

前端加密是否真的有意义?其实,这个问题的答案并不绝对。

  • 一方面,前端加密能够一定程度上防止中间人攻击,从而保护用户的隐私。
  • 但另一方面,如果攻击者能够篡改前端代码,前端加密就形同虚设。

因此,前端加密到底值不值得,还是见仁见智。


环境准备

前端使用的是Vue.js,也可以选择纯JS。同时,我们引入crypto-js库。至于后端,Java将担任这次任务的重任。

为了让前端和后端能够顺利交流,我们必须确保前后端使用相同的加密方式、模式(MODE)和填充方式(PADDING)


加密方法、MODE和PADDING的选择

加密方法、MODE和PADDING之间也必须配合得天衣无缝。前后端的这些参数必须一致,才能确保传输的信息能够正确加密和解密。

必须选择前后端都支持的加密方法、模式和填充方式。

https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html

https://cryptojs.gitbook.io/docs/#pbkdf2


以下是\选择的参数:

  • 加密方法: AES(推荐加密方法,参考:FIPS 197)
  • 模式: CBC(默认模式,参考:FIPS 81)
  • 填充方式: Pkcs7(与Java的PKCS5Padding基本等价)

在这里,我们选择AES/CBC/Pkcs7作为加密组合,而Java端则使用AES/CBC/PKCS5Padding。这样一来,我们的前后端就可以在同一个频道上交流了。


前端

需要引入crypto-js库,并设置好密钥(key)和初始向量(iv)。这些设置必须与后端保持一致,才能确保加密与解密的正确性。

// aesutils.js
import CryptoJs from 'crypto-js'

// 把key、iv设置成固定值,前后端的值要一致
let key = CryptoJs.enc.Utf8.parse("xxxxxx");
let iv = CryptoJs.enc.Utf8.parse("yyyyyy");

export function Encrypt(word) {
    let srcs = CryptoJs.enc.Utf8.parse(word);
    var encrypted = CryptoJs.AES.encrypt(srcs, key, {
        iv: iv,
        mode: CryptoJs.mode.CBC,
        padding: CryptoJs.pad.Pkcs7
    });
    return CryptoJs.enc.Base64.stringify(encrypted.ciphertext);
}

在这个加密函数中,我们将原始文本转换为Base64编码后的密文,然后将其发送到后端。如此一来,前端的密码在传输过程中便不会以明文形式暴露。


后端

后端的任务是接收到前端传来的密文,并将其解密为原始的明文密码。这一步同样至关重要,因为后端需要将这些密码存储或用于后续操作。


import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class AESUtils {

    private static final String key = "xxxxxx";
    private static final String iv = "yyyyyy";

    public static String deocdeStr(String text) {
        try {
            byte[] encrypted = new Base64().decode(text);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding ");
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
            IvParameterSpec ivParameter = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));

            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameter);

            byte[] decrypted = cipher.doFinal(encrypted);
            String originalString = new String(decrypted, StandardCharsets.UTF_8);
            return originalString;
        } catch (Exception e) {
            return e.toString();
        }
    }
}

后端解密的核心步骤与前端加密紧密关联。首先,我们将Base64编码的密文解码为字节数组,然后利用AES/CBC/PKCS5Padding的组合将其解密为原始字符串。


应用:从传输到解密的全过程

在前端,我们使用工具类加密密码,然后通过axios等方法将加密后的密文传输到后端:

import { Encrypt } from "@/utils/aesutils";

Encrypt(this.$data.formData.password);

在后端,接收到加密的密文后,我们调用工具类解密并还原密码,然后将其用于后续的数据库操作:

 
AESUtils.deocdeStr(user.getPassword());

安全性增强

尽管我们使用了固定的密钥和初始向量(IV),但在实际的生产环境中,这种方式的安全性可能还不够强。接下来,我们将进一步提升安全性,探索如何使用动态生成的密钥和初始向量,并研究在生产环境中如何更好地管理这些关键要素。

动态生成密钥和初始向量

使用固定的密钥和IV,虽然简化了前后端的协调工作,但也带来了风险。一旦密钥和IV被泄露,攻击者就可以轻松解密传输的数据。为了解决这个问题,我们可以采取动态生成密钥和IV的方法。

1. 前端:动态生成密钥和IV

在前端,我们可以在每次需要加密时,动态生成密钥和IV。然后,将密钥和IV与加密后的数据一起发送到后端。为了避免密钥和IV直接暴露在网络传输中,我们可以将它们与加密数据一起加密或使用安全的密钥交换算法。

import CryptoJs from 'crypto-js';

// 生成随机密钥和IV
function generateKeyAndIV() {
    let key = CryptoJs.lib.WordArray.random(128 / 8); // 128-bit key
    let iv = CryptoJs.lib.WordArray.random(128 / 8);  // 128-bit IV
    return { key, iv };
}

// 加密函数
export function encryptWithDynamicKey(word) {
    const { key, iv } = generateKeyAndIV();
    let srcs = CryptoJs.enc.Utf8.parse(word);
    let encrypted = CryptoJs.AES.encrypt(srcs, key, {
        iv: iv,
        mode: CryptoJs.mode.CBC,
        padding: CryptoJs.pad.Pkcs7
    });

    // 将密钥和IV与加密后的数据一起编码
    return {
        ciphertext: CryptoJs.enc.Base64.stringify(encrypted.ciphertext),
        key: CryptoJs.enc.Base64.stringify(key),
        iv: CryptoJs.enc.Base64.stringify(iv)
    };
}

generateKeyAndIV函数生成了一个随机的密钥和IV。然后,我们将这些随机生成的密钥和IV与加密后的数据一起编码并发送给后端。

2. 后端:解密动态密钥和IV

在后端接收到加密数据和动态生成的密钥、IV后,我们需要解码并使用它们进行解密操作。

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class AESUtils {

    public static String decryptWithDynamicKey(String ciphertext, String keyBase64, String ivBase64) {
        try {
            byte[] encrypted = new Base64().decode(ciphertext);
            byte[] key = new Base64().decode(keyBase64);
            byte[] iv = new Base64().decode(ivBase64);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
            IvParameterSpec ivParameter = new IvParameterSpec(iv);

            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameter);

            byte[] decrypted = cipher.doFinal(encrypted);
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            return e.toString();
        }
    }
}

这个解密函数接受Base64编码的密文、密钥和IV。我们先将它们解码为字节数组,然后使用它们来解密密文,恢复原始数据。


结语

通过动态生成密钥和初始向量,并使用安全的密钥管理服务,我们可以大幅提升系统的安全性。这不仅能防止密钥泄露带来的风险,还能通过安全的密钥交换和管理,确保前后端通信的绝对安全。

在实际的生产环境中,密钥的管理和轮换至关重要,建议使用专业的KMS来简化并加强密钥管理工作。通过这些措施,可以构建一个更加安全和稳健的系统 。

加密与安全_前后端通过AES-CBC模式安全传输数据_第1张图片

你可能感兴趣的:(【加密与安全】,安全,AES-CBC)