如何使用 SM4 加密算法保护敏感数据并实现数据解密

目录

  1. 简介
  2. 什么是 SM4 加密算法
  3. SM4 与其他对称加密算法的区别
  4. 项目背景与需求分析
  5. 项目的技术栈与依赖
  6. 实现步骤
      1. 设置 Axios 实例
      1. 编写加密函数 loginTo
      1. 编写解密函数 decrypt
      1. 配置 Axios 请求和响应拦截器
  7. 前端与后端的交互与安全性设计
  8. 常见问题与解决方法
  9. 项目完整代码示例
  10. 总结与未来发展方向

1. 简介

在前端开发中,保护用户的敏感信息尤为重要,特别是在处理用户的认证、交易等敏感操作时。在日常开发中,我们常使用 HTTPS 保证传输的安全性,但对于部分数据还需进一步加密,确保在传输过程中的安全。本文将详细介绍如何在前端项目中使用 SM4 加密算法保护敏感数据,并结合 Axios 实现数据在传输中的加密与解密。


2. 什么是 SM4 加密算法

SM4 是中国国家密码管理局设计的一种对称加密算法。作为国密算法之一,它具有较高的安全性,主要用于保障敏感数据的传输安全。SM4 加密算法采用 128 位的密钥长度和分组加密模式,其核心特点如下:

  • 对称加密:即加密和解密使用相同的密钥,密钥需要在通信双方之间安全地共享。
  • 分组加密:SM4 使用 128 位的分组大小,即每次加密 128 位的数据。
  • 支持多种模式:SM4 支持 ECB、CBC、CFB 等多种加密模式,允许在不同应用场景下选择适合的加密模式。

3. SM4 与其他对称加密算法的区别

SM4 类似于 AES、DES 等对称加密算法,但在具体实现和应用上存在差异:

  • 密钥长度:AES 可支持 128、192、256 位的密钥长度,而 SM4 只支持 128 位密钥。
  • 加密模式:SM4 也支持 ECB、CBC 等分组加密模式,符合对称加密的标准。
  • 安全性:SM4 是中国国家密码管理局定义的加密标准,在国内多种应用中有广泛使用,其安全性符合国家级应用需求。

4. 项目背景与需求分析

在本项目中,我们的目标是加密一个字符串 ticket,并将加密数据通过 Axios 传输给服务器。服务器收到加密数据后解密并进行相应处理。我们的需求包括以下几点:

  1. 数据加密:前端需要对特定敏感数据进行加密,确保传输过程中的数据安全。
  2. 加密模式选择:为了简化实现,我们采用 ECB(Electronic Codebook)模式,该模式适合小数据的加密。
  3. 加密算法选择:SM4 作为对称加密算法,为我们提供了符合国密标准的加密方案。
  4. 与后端交互:在前端和后端之间传输加密数据,并在响应拦截器中对返回的数据进行解密。

5. 项目的技术栈与依赖

本项目主要使用了以下技术和依赖:

  1. Axios:用于处理 HTTP 请求和响应。
  2. sm-crypto:一个 JavaScript 实现的 SM 加密库,支持 SM2、SM3 和 SM4 加密算法。
  3. React:作为前端 UI 框架。
  4. TypeScript:增强代码的类型安全性。

6. 实现步骤

1. 设置 Axios 实例

首先,我们创建 Axios 实例,并在实例上设置请求和响应拦截器,以便在每次请求前加密数据,接收到响应后解密数据。

import axios from "axios";
import * as sm from 'sm-crypto';

// 创建 Axios 实例
const instance = axios.create({
    
    
    baseURL: "/",
    timeout: 20000
});

2. 编写加密函数 loginTo

loginTo 函数用于将字符串 ticket 使用 SM4 算法加密,并返回加密后的 Base64 字符串。我们选择 ECB 模式,并应用 PKCS7 填充。

const loginTo = () => {
    
    
    const ticket = "gly"; // 要加密的字符串
    const key = '5e3gggggg1yd2h'; // SM4 密钥,16 字节
    const keyBytes = Array.from(new TextEncoder().encode(key));

    try {
    
    
        const encryptedData = sm.sm4.encrypt(ticket, keyBytes, {
    
     mode: 'ecb', padding: 'pkcs7' });
        const base64Data = btoa(String.fromCharCode(...new Uint8Array(encryptedData)));
        console.log("Encrypted Base64 Data:", base64Data);
        return base64Data;
    } catch (error) {
    
    
        console.error("Encryption error:", error);
        throw new Error("Encryption process failed.");
    }
};

3. 编写解密函数 decrypt

解密函数 decrypt 用于对加密的 Base64 数据进行解密,还原为原始的字符串 ticket

const decrypt = (base64Data) => {
    
    
    if (!base64Data) {
    
    
        throw new Error("Base64 data is null or undefined.");
    }

    const data = Uint8Array.from(atob(base64Data), c => c.charCodeAt(0));
    const key = '5e3gggggg1yd2h'; // SM4 密钥,16 字节
    const keyBytes = Array.from(new TextEncoder().encode(key));

    try {
    
    
        const decryptedData = sm.sm4.decrypt(data, keyBytes, {
    
     mode: 'ecb', padding: 'pkcs7' });
        const result = new TextDecoder().decode(decryptedData);
        return result;
    } catch (error) {
    
    
        console.error("Decryption error:", error);
        throw new Error("Decryption process failed.");
    }
};

4. 配置 Axios 请求和响应拦截器

在 Axios 实例上配置请求和响应拦截器,以便在每次请求前加密 ticket,并在接收到响应后对返回的数据解密。

instance.interceptors.request.use(
    config => {
    
    
        const encryptedTicket = loginTo(); // 获取加密后的 `ticket`
        config.headers['ticket'] = encryptedTicket; // 将加密后的数据放入请求头
        return config;
    },
    err => {
    
    
        return Promise.reject(err);
    }
);

instance.interceptors.response.use(
    res => {
    
    
        if (res.data && res.data.encryptedData) {
    
    
            const decryptedData = decrypt(res.data.encryptedData); // 解密返回的数据
            console.log("Decrypted Response Data:", decryptedData);
        }
        return res.data;
    },
    err => {
    
    
        return Promise.reject(err);
    }
);

7. 前端与后端的交互与安全性设计

在实际项目中,前端和后端的安全性设计尤为重要。通过 SM4 加密,我们可以在前端将敏感数据加密传输,同时在后端使用相同密钥解密。在生产环境中,前后端需要通过 HTTPS 保证传输的安全性,同时在密钥管理方面,应确保密钥不会被暴露。

8. 常见问题与解决方法

  1. 加密后的数据为空:可能是因为加密参数不正确,或密钥长度不符合 SM4 标准。请确保密钥长度为 16 字节。
  2. 解密失败:检查解密模式和填充方式,确保与加密时一致。

9. 项目完整代码示例

// index.ts

import axios from "axios";
import * as sm from 'sm-crypto';

const instance = axios.create({
    
    
    baseURL: "/",
    timeout: 20000
});

const loginTo = () => {
    
    
    const ticket = "gly";
    const key = '5e3gggggg1yd2h';
    const keyBytes = Array.from(new TextEncoder().encode(key));
    try {
    
    
        const encryptedData = sm.sm4.encrypt(ticket, keyBytes, {
    
     mode: 'ecb', padding: 'pkcs7' });
        const base64Data = btoa(String.fromCharCode(...new Uint8Array(encryptedData)));
        return base64Data;
    } catch (error) {
    
    
        console.error("Encryption error:", error);
        throw new Error("Encryption process failed.");
    }
};

const decrypt = (base64Data) => {
    
    
    if (!base64Data) throw new Error("Base64 data is null or undefined.");
    const data = Uint8

Array.from(atob(base64Data), c => c.charCodeAt(0));
    const key = '5e3gggggg1yd2h';
    const keyBytes = Array.from(new TextEncoder().encode(key));
    try {
    
    
        const decryptedData = sm.sm4.decrypt(data, keyBytes, {
    
     mode: 'ecb', padding: 'pkcs7' });
        const result = new TextDecoder().decode(decryptedData);
        return result;
    } catch (error) {
    
    
        console.error("Decryption error:", error);
        throw new Error("Decryption process failed.");
    }
};

instance.interceptors.request.use(
    config => {
    
    
        const encryptedTicket = loginTo();
        config.headers['ticket'] = encryptedTicket;
        return config;
    },
    err => {
    
    
        return Promise.reject(err);
    }
);

instance.interceptors.response.use(
    res => {
    
    
        if (res.data && res.data.encryptedData) {
    
    
            const decryptedData = decrypt(res.data.encryptedData);
            console.log("Decrypted Response Data:", decryptedData);
        }
        return res.data;
    },
    err => {
    
    
        return Promise.reject(err);
    }
);

export default instance;

10. 总结与未来发展方向

通过本文的介绍,我们实现了在前端使用 SM4 加密算法对敏感数据的加密传输。通过合理配置 Axios 拦截器,我们实现了加密数据的传输与解密。在实际项目中,还可以进一步完善,比如使用 HTTPS、实现更复杂的数据安全策略以及合理的密钥管理等。希望这篇文章为你在前端安全性设计方面提供了参考。