传输完整性要求

传输完整性是指确保信息在传输过程中不被篡改或损坏。在密评中,传输完整性的评估主要关注以下几个方面:

  1. 完整性校验机制:是否采用了消息鉴别码(MAC)或数字签名等技术对传输的信息进行完整性校验,以确保信息在传输过程中未被篡改。
  2. 错误检测与纠正:是否具备错误检测和纠正机制,以应对传输过程中可能出现的错误或损坏。
  3. 传输协议的完整性保护:传输协议是否提供了完整性保护机制,以确保信息在传输过程中的完整性。

技术方案

  • 1、使用 HMAC/SM3: 使用 HMAC(散列消息认证码)/SM3(国密摘要签名算法)来验证数据的完整性。
  • 2、数字签名: 使用数字签名来确保数据在传输过程中未被篡改。

方案-1: 前端在传输前对敏感数据进行数字签名,后端服务进行签名验证(外部加解密服务)

在方案-3、方案-4 中都是系统内部,前后端进行加密解密的操作,在一些场景中,加解密服务有专门的服务负责,前后端仅需求去调用接口即可

前端签名

在请求之前调用数字签名接口,进行签名

// TODO
后端验证

CryptoUtil


import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import okhttp3.OkHttpClient;
import okhttp3.spring.boot.OkHttp3Template;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * 外部加解密工具类
 */
public class CryptoUtil {

    private static OkHttpClient client;
    private static OkHttp3Template okHttp3Template;
    private static ObjectMapper objectMapper;

    static {

        objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.enable(MapperFeature.USE_GETTERS_AS_SETTERS);
        objectMapper.enable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS);
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        client = new OkHttpClient().newBuilder().connectTimeout(5000, TimeUnit.MILLISECONDS)
                // .hostnameVerifier(okhttpHostnameVerifier)
                // .followRedirects(properties.isFollowRedirects())
                // .followSslRedirects(properties.isFollowSslRedirects())
                .pingInterval(1, TimeUnit.MILLISECONDS).readTimeout(3000, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(true)
                // .sslSocketFactory(trustedSSLSocketFactory, trustManager)
                .writeTimeout(3, TimeUnit.SECONDS)
                // Application Interceptors、Network Interceptors :
                // https://segmentfault.com/a/1190000013164260
                // .addNetworkInterceptor(loggingInterceptor)
                // .addInterceptor(headerInterceptor)
                .build();

        okHttp3Template = new OkHttp3Template(client, objectMapper);
    }

    /**
     * 调用加密接口
     */
    public static String encrypt(String plainTxt) {
        try {
            return okHttp3Template.get("http://localhost:8080/encrypt?plainTxt=" + plainTxt, String.class);
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 调用解密接口
     */
    public static String decrypt(String cipherTxt) {
        try {
            return okHttp3Template.get("http://localhost:8080/decrypt?plainTxt=" + cipherTxt, String.class);
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 调用签名接口
     */
    public static String digest(String plainTxt) {
        try {
            return okHttp3Template.get("http://localhost:8080/digest?plainTxt=" + plainTxt, String.class);
        } catch (IOException e) {
            return null;
        }
    }

}

方案-2: 前端在传输前对敏感数据进行数字签名,后端服务进行签名验证(SM3 算法)

考虑到国内的情况,优先使用国密 SM3 算法,主要思路是前端在传输前对 Query 参数 和 Body 进行SM3 摘要,后端服务在 Gateway 统一进行SM3 摘要 和 对比

前端
  • 1、安装 gm-crypt 包
npm install gm-crypt
  • 2、封装js

sm3Util.js

import { SM4, SM3, SM2 } from 'gm-crypto';
const slat = 'xxx'; // 密钥 前后端一致即可,后端提供
/*
 * 摘要签名函数
 * @param {String} text 待加密文本
 * @param key string 加密key(16位)
 * @param iv string 偏移向量(16位)
 */
export function SM3Digest(text, slat = pwdKey, iv = pwdKey) {
  return SM3.digest(text, 'utf8', 'base64')
}
  • 3、使用
import { SM3Digest } from '@/util/sm3Util.js';

const slat = 'xxx'; // 盐,由后端定义,可以从接口获取
const enPw = SM3Digest(this.form.password, slat); // 加密
  • 2、前端在请求发送之前使用 国密SM3杂凑(摘要)算法 计算请求参数的哈希值。

要在项目中使用 crypto-js 进行 HMAC-SHA256 加密,首先需要安装 crypto-js 包。

import CryptoJS from 'crypto-js';

// 示例数据和密钥
const data = 'message';
const key = 'secret-key';

// 生成 HMAC-SHA256
const hash = CryptoJS.HmacSHA256(data, key);

// 将哈希转换为字符串
const hashString = hash.toString(CryptoJS.enc.Hex);

console.log(hashString);
  • 3、发送者使用自己的私钥和哈希值,通过数字签名算法(如ECDSA或DSA)生成数字签名。

使用 CryptoJS (crypto.js) 進行 HmacSHA256

验证签名
  • 1、接收者使用相同的哈希算法计算消息的哈希值。
  • 2、接收者使用发送者的公钥对数字签名进行解密。
  • 3、如果解密后的哈希值与消息的哈希值匹配,则证明消息未被篡改,且确实来自发送者。这是因为只有发送者知道私钥,能够生成正确的签名。
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.digest.SM3;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SM3 摘要签名算法工具类
 */
public class SM3Util {

    private static Map<String, SM3> sm3Map = new ConcurrentHashMap<>();

    /**
     * 获取SM3
     * @param salt,加盐
     * @return SM3
     */
    public static SM3 getSm3( String salt) {
        return sm3Map.computeIfAbsent(salt, k -> new SM3(salt.getBytes(CharsetUtil.CHARSET_UTF_8)));
    }

    /**
     * SM3-摘要
     */
    public static String digest(String salt, String plainTxt) {
        SM3 sm3 = getSm3(salt);
        return sm3.digestHex(plainTxt);
    }

}

方案-3: 前端在传输前对敏感数据进行数字签名,后端服务进行签名验证(HMAC 摘要算法)

与请求加解密一样,除了使用国密SM3杂凑(摘要)算法,还可以使用 HMAC 摘要算法,进行摘要操作。

  • 前端在传输前对 Query 参数 和 Body 进行HMAC 摘要,并作为参数传递给后端
  • 后端服务在 Gateway 再次进行HMAC 摘要,并与前端的摘要进行对比
签名过程
  • 1、安装 crypto-js 包
npm install crypto-js
  • 2、前端在请求发送之前使用哈希算法(如 SHA-256)计算请求参数的哈希值。
import CryptoJS from 'crypto-js';

// 示例数据和密钥
const data = 'message';
const key = 'secret-key';

// 生成 HMAC-SHA256
const hash = CryptoJS.HmacSHA256(data, key);

// 将哈希转换为字符串
const hashString = hash.toString(CryptoJS.enc.Hex);

console.log(hashString);
  • 3、前端使用私钥和哈希值,通过数字签名算法(如ECDSA或DSA)生成数字签名。

  • RSA (Rivest-Shamir-Adleman)

  • DSA (Digital Signature Algorithm)

  • ECDSA (Elliptic Curve Digital Signature Algorithm)

  • EdDSA (Edwards-curve Digital Signature Algorithm)

  • HMAC (Hash-based Message Authentication Code)

验证签名
  • 1、接收者使用相同的哈希算法计算消息的哈希值。
  • 2、接收者使用发送者的公钥对数字签名进行解密。
  • 3、如果解密后的哈希值与消息的哈希值匹配,则证明消息未被篡改,且确实来自发送者。这是因为只有发送者知道私钥,能够生成正确的签名。
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.digest.HMac;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * HMAC 摘要签名算法工具类
 */
public class HMACUtil {

    private static Map<String, HMac> hMacMap = new ConcurrentHashMap<>();

    /**
     * 获取 HMac
     *
     * @param algorithm,摘要算法 HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384、HmacSHA512
     * @param salt,加盐
     * @return HMac
     */
    public static HMac getHMac( String algorithm, String salt) {
        return hMacMap.computeIfAbsent(salt, k -> new HMac(algorithm, salt.getBytes(CharsetUtil.CHARSET_UTF_8)));
    }

    /**
     * HMac-摘要
     */
    public static String digest(String algorithm, String salt, String plainTxt) {
        HMac hMac = getHMac(algorithm, salt);
        return hMac.digestHex(plainTxt);
    }

}
作者:Jeebiz  创建时间:2024-11-05 19:30
最后编辑:Jeebiz  更新时间:2024-11-14 21:58