报文sm4、sm2加密,sm3加签

概念

sm2

SM2 是一种非对称加密算法。基于椭圆曲线密码(ECC)的公钥密码算法标准,可用于数字签名、密钥交换、公钥加密等,可替换RSA等算法。

sm3

SM3 是一种密码杂凑算法。通过在签名原始串后加上基金公司通信密钥的内容,进行SM3加密运算,形成的加密字符串即为签名结果。

sm4

SM4是一种对称加密算法。密钥长度和分组长度均为128bits,可替换DES/AES等算法。

背景

为了保证加解密的速度和数据安全性,通常对原始数据使用SM4加密,对密钥等数据量较小的数据使用SM2加密。然后对加密的数据做SM3签名处理。

HttpEncryptClient(发送加密请求,解密响应客户端)

package com.boot.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.digest.SM3;
import cn.hutool.crypto.symmetric.SM4;
import com.boot.config.IcbccsApiProperties;
import com.boot.enums.BusinessTypeEnum;
import com.boot.enums.SignTypeEnum;
import com.boot.enums.UriTypeEnum;
import com.boot.exception.JacksonException;
import com.boot.model.domain.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.security.KeyPair;
import java.util.Map;
import java.util.TreeMap;

/**
 * @ClassName : HttpEncryptClient
 * @Description : HttpEncryptClient
 * @Author : ChenRui
 * @Date: 2022-11-28 23:31
 */
@Slf4j
public class HttpEncryptClient {

    /** 基本参数 */
    private IcbccsApiProperties icbccsApiProperties;

    /** http客户端 */
    private RestTemplate restTemplate;

    /** sm2加密工具 */
    private SM2 encryptSm2;

    /** sm2解密工具 */
    private SM2 decryptSm2;

    /** sm3加解密工具 */
    private SM3 sm3;

    /** sm4加解密工具 */
    private SM4 sm4;

    /** sm4随机秘钥密文 */
    private String sm4SecretKeyCiphertext;

    public HttpEncryptClient(IcbccsApiProperties icbccsApiProperties, RestTemplate restTemplate) {
        Preconditions.checkNotNull(icbccsApiProperties);
        this.icbccsApiProperties = icbccsApiProperties;
        this.restTemplate = restTemplate;
        this.sm3 = SmUtil.sm3();
        this.sm4 = SmUtil.sm4();
        if(icbccsApiProperties.getSm2KeyPair() != null){
            IcbccsApiProperties.Sm2KeyPair sm2KeyPair = icbccsApiProperties.getSm2KeyPair();
            if(StringUtils.isNotBlank(sm2KeyPair.getFundPublicKey())){
                this.encryptSm2 = SmUtil.sm2(null, sm2KeyPair.getFundPublicKey());
            }
            if(StringUtils.isNotBlank(sm2KeyPair.getTrustPrivateKey())){
                this.decryptSm2 = SmUtil.sm2(sm2KeyPair.getTrustPrivateKey(), null);
            }
        }
        if(encryptSm2 != null){
            // 获取字符串格式的sm4随机秘钥
            String sm4SecretKeyStr = HexUtil.encodeHexStr(sm4.getSecretKey().getEncoded());
            // 使用sm2加密上一步得到的sm4随机秘钥
            this.sm4SecretKeyCiphertext = encryptSm2.encryptHex(sm4SecretKeyStr, KeyType.PublicKey);
        }
    }

    /**
    * @Title: encryptExchange
    * @Param: [t, businessTypeEnum, clazz]
    * @description: 加密请求
    * @author: ChenRui
    * @date: 2022/11/29 0:24
    * @return: E
    * @throws:
    */
    public <T, E extends IcbccsBaseResponse> E encryptExchange(T t, BusinessTypeEnum businessTypeEnum, Class<E> clazz) {
        // 1.0.判断是否上传资源
        boolean isUpload = businessTypeEnum.getUriTypeEnum() == UriTypeEnum.UPLOAD;
        // 1.1.获取默认的签名类型
        SignTypeEnum signTypeEnum = SignTypeEnum.getEnumByKey(icbccsApiProperties.getSignType());
        if( signTypeEnum == null ){
            throw new RuntimeException("不支持该类型的签名、加密算法");
        }
        // 1.2.包装业务参数、生成签名
        Object reqData;
        String sign = "";
        if(isUpload){
            reqData = t;
        } else {
            switch (signTypeEnum){
                case SM3:
                    // 1.2.1.仅使用MS3进行签名
                    reqData = t;
                    sign = genSignToken(t);
                    break;
                case SM4:
                    // 1.2.2.仅使用MS4进行加密
                    reqData = generatePacketRequest(t);
                    break;
                case SM3_SM4:
                    // 1.2.3.使用MS4进行加密,将结果进行SM3签名
                    reqData = generatePacketRequest(t);
                    sign = genSignToken(reqData);
                    break;
                default:
                    throw new RuntimeException("不支持该类型的签名、加密算法");
            }
        }
        log.info("\r\n请求-业务明文: \r\n{}", t);
        log.info("\r\n请求-包裹: \r\n{}", reqData);
        log.info("\r\n请求-签名: \r\n{}", sign);

        // 1.4.构造报文
        IcbccsApiRequest icbccsApiRequest = IcbccsApiRequest.builder()
                .serviceVersion(icbccsApiProperties.getServiceVersion())
                .signType(signTypeEnum.getCode())
                .sign(sign)
                .instId(icbccsApiProperties.getInstId())
                .businessType(businessTypeEnum.getCode())
                .reqData(reqData)
                .build();
        // 1.5.提交请求
        String ciphertextResponse;
        if(isUpload){
            // 1.5.1.文件流格式提交请求
            ciphertextResponse = postFileForEntity(icbccsApiRequest);
        } else {
            // 1.5.2.json格式发送请求
            ciphertextResponse = postJosnForEntity(icbccsApiRequest);
        }
        log.info("\r\n响应-原始数据: \r\n{}", ciphertextResponse);
        // 1.6.解析响应数据
        E e;
        switch (signTypeEnum){
            case SM3:
                // 1.6.1.仅使用MS3时,直接反序列化
                try {
                    JavaType javaType = JacksonsUtil.getMapper().getTypeFactory().constructParametricType(IcbccsApiResponse.class, clazz);
                    IcbccsApiResponse<E> icbccsApiResponse = JacksonsUtil.getMapper().readValue(ciphertextResponse, javaType);
                    log.info("\r\n响应-反序列化数据:\r\n{}", icbccsApiResponse);
                    e = icbccsApiResponse.getRspData();
                } catch (Exception exception) {
                    throw new JacksonException(exception);
                }
                break;
            case SM4: case SM3_SM4:
                // 1.6.2.解密SM4
                e = extractPacketResponse(ciphertextResponse, clazz);
                break;
            default:
                throw new RuntimeException("不支持该类型的签名、加密算法");
        }
        log.info("\r\n响应-解析后业务数据: \r\n{}", e);
        return e;
    }

    /**
    * @Title: postJosnForEntity
    * @Param: [icbccsApiRequest]
    * @description: json请求
    * @author: ChenRui
    * @date: 2022/11/29 2:25
    * @return: java.lang.String
    * @throws:
    */
    private String postJosnForEntity(IcbccsApiRequest icbccsApiRequest) {
        // 1.1.构造请求头
        HttpHeaders headers = new HttpHeaders();
        // 1.1.1.设置请求编码
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        // 1.1.2.设置预期响应编码
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        // 1.2.反序列化对象
        String jsonStr = JacksonsUtil.writeValueAsString(icbccsApiRequest);
        // 1.3.包装业务参数
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        form.add("jsonRequestData", jsonStr);
        log.info("\r\n请求-最终报文: \r\n{}", jsonStr);
        // 1.4.构造请求实体
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity(form, headers);

        // 1.5.发送请求
        String url = icbccsApiProperties.getUri().getGeneralUrl();
        log.info("\r\n请求-URL: \r\n{}", url);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, formEntity, String.class);
        return responseEntity.getBody();
    }

    /**
    * @Title: postFileForEntity
    * @Param: [icbccsApiRequest]
    * @description: 文件上传
    * @author: ChenRui
    * @date: 2022/11/30 17:28
    * @return: java.lang.String
    * @throws:
    */
    private String postFileForEntity(IcbccsApiRequest icbccsApiRequest) {
        log.info("\r\n请求-发送转换前的报文: \r\n{}", icbccsApiRequest);
        // 1.1.构造请求头
        HttpHeaders headers = new HttpHeaders();
        // 1.1.1.设置请求编码
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        // 1.1.2.设置预期响应编码
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        // 1.3.包装业务参数
        Map<String, Object> map = Maps.newHashMap();
        // 1.3.1.创建拷贝规则
        CopyOptions copyOptions = new CopyOptions();
        copyOptions.setIgnoreNullValue(false);
        copyOptions.setIgnoreCase(false);
        copyOptions.setPropertiesFilter((Field field, Object y) -> {
            if(field.getName().equals("reqData")){
                return false;
            }
            return true;
        });
        copyOptions.setConverter((Type type, Object value) -> {
            // 当为文件类型时,转成成系统文件资源对象
            if( (value instanceof File || value instanceof MultipartFile) && value != null){
                try {
                    File tempFile = null;
                    if(value instanceof File){
                        tempFile = (File) value;
                    } else if(value instanceof MultipartFile){
                        tempFile = FileUtil.multipartFileToFile((MultipartFile) value);
                    }
                    FileSystemResource resource = new FileSystemResource(tempFile);
                    tempFile.deleteOnExit();
                    return resource;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            return value;
        });
        // 1.3.2.拷贝业务参数
        BeanUtil.beanToMap(icbccsApiRequest.getReqData(), map, copyOptions);
        // 1.3.3.拷贝公共参数
        BeanUtil.beanToMap(icbccsApiRequest, map, copyOptions);
        // 1.3.4.map转换MultiValueMap
        LinkedMultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        map.entrySet().forEach(e -> form.add(e.getKey(), e.getValue()));
        log.info("\r\n请求-最终报文: \r\n{}", form);

        // 1.4.构造请求实体
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity(form, headers);

        // 1.5.发送请求
        String url = icbccsApiProperties.getUri().getUploadUrl();
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, formEntity, String.class);
        return responseEntity.getBody();
    }

    /**
    * @Title: sortMapByKey
    * @Param: [t]
    * @description: 按照ascii码升序排序
    * @author: ChenRui
    * @date: 2022/11/29 10:17
    * @return: java.util.Map<java.lang.String,java.lang.Object>
    * @throws:
    */
    private <T> Map<String, Object> sortMapByKey(T t) {
        if(t == null){
            return null;
        }
        Map<String, Object> map = BeanUtil.beanToMap(t, new TreeMap<>(), false, false);
        return map;
    }

    /**
    * @Title: genSignToken
    * @Param: [o]
    * @description: 生成签名
    * @author: ChenRui
    * @date: 2022/11/29 1:18
    * @return: java.lang.String
    * @throws:
    */
    public String genSignToken(Object o) {
        String signTarget = "";
        if(ObjectUtils.isNotEmpty(o)){
            // 1.1.按照ascii码升序排序
            Map<String, Object> map = sortMapByKey(o);
            // 1.2.按照键值对的格式构造签名参数
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Object> item : map.entrySet()) {
                if (StringUtils.isNotBlank(item.getKey()) && ObjectUtils.isNotEmpty(item.getValue())) {
                    String key = item.getKey();
                    String val = String.valueOf(item.getValue());
                    sb.append(key).append("=").append(val).append("&");
                }
            }
            // 1.3.补充基金公司密钥
            sb.append("key").append("=").append(icbccsApiProperties.getSm3Key());
            // 1.4.签名原始值
            String signSource = sb.toString();
            // 1.5.生成签名
            signTarget = sm3.digestHex(signSource).toUpperCase();
        }
        return signTarget;
    }

    /**
    * @Title: generatePacketRequest
    * @Param: [o]
    * @description: 构造加密包
    * @author: ChenRui
    * @date: 2022/11/30 13:57
    * @return: com.boot.model.domain.IcbccsCiphertextPacketRequest
    * @throws:
    */
    private IcbccsCiphertextPacketRequest generatePacketRequest(Object o){
        IcbccsCiphertextPacketRequest icbccsCiphertextPacketRequest = new IcbccsCiphertextPacketRequest();
        if(ObjectUtils.isNotEmpty(o)){
            // 1.1.序列化报文
            String plaintextStr = JacksonsUtil.writeValueAsString(o);
            log.info("\r\n请求加密-明文:\r\n{}", plaintextStr);
            // 1.2.sm4加密
            String ciphertext = sm4.encryptHex(plaintextStr);
            log.info("\r\n请求加密-密文:\r\n{}", ciphertext);
            // 1.3.构造对象
            icbccsCiphertextPacketRequest.setKey(sm4SecretKeyCiphertext);
            icbccsCiphertextPacketRequest.setEncryptData(ciphertext);
        }
        return icbccsCiphertextPacketRequest;
    }

    /**
    * @Title: extractPacketResponse
    * @Param: [icbccsApiResponseStr, clazz]
    * @description: 提取响应业务数据
    * @author: ChenRui
    * @date: 2022/11/30 15:08
    * @return: E
    * @throws:
    */
    private <E extends IcbccsBaseResponse> E extractPacketResponse(String icbccsApiResponseStr, Class<E> clazz){
        if(StringUtils.isNotBlank(icbccsApiResponseStr)){
            // 1.1.反序列化
            IcbccsApiResponse<IcbccsCiphertextPacketResponse> icbccsApiResponse = JacksonsUtil.readValue(icbccsApiResponseStr, new TypeReference<IcbccsApiResponse<IcbccsCiphertextPacketResponse>>() {});
            log.info("\r\n响应-反序列化数据:\r\n{}", icbccsApiResponse);
            // 1.2.提取响应包裹
            IcbccsCiphertextPacketResponse icbccsCiphertextPacketResponse = icbccsApiResponse.getRspData();
            if(icbccsCiphertextPacketResponse != null){
                // 1.3.提取密文秘钥、密文
                String sm2CiphertextKey = icbccsCiphertextPacketResponse.getKey();
                String encryptData = icbccsCiphertextPacketResponse.getEncryptData();
                // 1.4.解密sm2密文,得到sm4秘钥
                String sm4Key = decryptSm2.decryptStr(sm2CiphertextKey, KeyType.PrivateKey);
                // 1.5.解密sm4密文
                String icbccsBaseResponseStr = SmUtil.sm4(HexUtil.decodeHex(sm4Key)).decryptStr(encryptData);
                // 1.6.反序列化
                return JacksonsUtil.readValue(icbccsBaseResponseStr, clazz);
            }
        }
        return null;
    }

    /**
    * @Title: generateSm2KeyPair
    * @Param: []
    * @description: 生成SM2公私钥
    * @author: ChenRui
    * @date: 2022/11/30 16:17
    * @return: void
    * @throws:
    */
    private static void generateSm2KeyPair() {
        KeyPair pair = SecureUtil.generateKeyPair("SM2");
        String privateKey = Base64.encode(pair.getPrivate().getEncoded());
        String publicKey = Base64.encode(pair.getPublic().getEncoded());
        log.info("\r\nSM2私钥:\r\n" + privateKey);
        log.info("\r\nSM2公钥:\r\n" + publicKey);
    }

    public static void main(String[] args) {
        generateSm2KeyPair();
    }
}

加密参数类

package com.boot.config;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName : DhccPlatformProperties
 * @Description : DhccPlatformProperties
 * @Author : ChenRui
 * @Date: 2020-05-14 14:02
 */
@Data
@Configuration
@Slf4j
@ConfigurationProperties(prefix = "ms.icbccs-api")
public class IcbccsApiProperties {

    @Schema(description = "版本号")
    private String serviceVersion;

    @Schema(description = "签名/加密算法类型")
    private String signType;

    @Schema(description = "机构标识")
    private String instId;

    @Schema(description = "sm3加解密key-基金公司密钥-签名")
    private String sm3Key;

    @Schema(description = "请求地址")
    private Uri uri = new Uri();

    @Schema(description = "sm2密钥对")
    private Sm2KeyPair sm2KeyPair = new Sm2KeyPair();

    @Data
    @Schema(description = "地址资源")
    public static class Uri {

        @Schema(description = "基础地址")
        private String baseUri;

        @Schema(description = "通用地址")
        private String generalUrl;

        @Schema(description = "文件上传地址")
        private String uploadUrl;
    }

    @Data
    @Schema(description = "sm2密钥集合")
    public static class Sm2KeyPair {

        @Schema(description = "信托公司sm2私钥")
        private String trustPrivateKey;

        @Schema(description = "基金公司sm2公钥")
        private String fundPublicKey;
    }
}

sm工具类,用来生成对应的sm对象

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package cn.hutool.crypto;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.crypto.digest.SM3;
import cn.hutool.crypto.digest.mac.BCHMacEngine;
import cn.hutool.crypto.digest.mac.MacEngine;
import cn.hutool.crypto.symmetric.SM4;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;

public class SmUtil {
    private static final int RS_LEN = 32;
    public static final String SM2_CURVE_NAME = "sm2p256v1";
    public static final ECDomainParameters SM2_DOMAIN_PARAMS = BCUtil.toDomainParams(GMNamedCurves.getByName("sm2p256v1"));
    public static final ASN1ObjectIdentifier ID_SM2_PUBLIC_KEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301");

    public SmUtil() {
    }

    public static SM2 sm2() {
        return new SM2();
    }

    public static SM2 sm2(String privateKeyStr, String publicKeyStr) {
        return new SM2(privateKeyStr, publicKeyStr);
    }

    public static SM2 sm2(byte[] privateKey, byte[] publicKey) {
        return new SM2(privateKey, publicKey);
    }

    public static SM2 sm2(PrivateKey privateKey, PublicKey publicKey) {
        return new SM2(privateKey, publicKey);
    }

    public static SM2 sm2(ECPrivateKeyParameters privateKeyParams, ECPublicKeyParameters publicKeyParams) {
        return new SM2(privateKeyParams, publicKeyParams);
    }

    public static SM3 sm3() {
        return new SM3();
    }

    public static SM3 sm3WithSalt(byte[] salt) {
        return new SM3(salt);
    }

    public static String sm3(String data) {
        return sm3().digestHex(data);
    }

    public static String sm3(InputStream data) {
        return sm3().digestHex(data);
    }

    public static String sm3(File dataFile) {
        return sm3().digestHex(dataFile);
    }

    public static SM4 sm4() {
        return new SM4();
    }

    public static SM4 sm4(byte[] key) {
        return new SM4(key);
    }

    public static byte[] changeC1C2C3ToC1C3C2(byte[] c1c2c3, ECDomainParameters ecDomainParameters) {
        int c1Len = (ecDomainParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1;
        int c3Len = true;
        byte[] result = new byte[c1c2c3.length];
        System.arraycopy(c1c2c3, 0, result, 0, c1Len);
        System.arraycopy(c1c2c3, c1c2c3.length - 32, result, c1Len, 32);
        System.arraycopy(c1c2c3, c1Len, result, c1Len + 32, c1c2c3.length - c1Len - 32);
        return result;
    }

    public static byte[] changeC1C3C2ToC1C2C3(byte[] c1c3c2, ECDomainParameters ecDomainParameters) {
        int c1Len = (ecDomainParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1;
        int c3Len = true;
        byte[] result = new byte[c1c3c2.length];
        System.arraycopy(c1c3c2, 0, result, 0, c1Len);
        System.arraycopy(c1c3c2, c1Len + 32, result, c1Len, c1c3c2.length - c1Len - 32);
        System.arraycopy(c1c3c2, c1Len, result, c1c3c2.length - 32, 32);
        return result;
    }

    public static byte[] rsAsn1ToPlain(byte[] rsDer) {
        BigInteger[] decode;
        try {
            decode = StandardDSAEncoding.INSTANCE.decode(SM2_DOMAIN_PARAMS.getN(), rsDer);
        } catch (IOException var4) {
            throw new IORuntimeException(var4);
        }

        byte[] r = bigIntToFixedLengthBytes(decode[0]);
        byte[] s = bigIntToFixedLengthBytes(decode[1]);
        return ArrayUtil.addAll(new byte[][]{r, s});
    }

    public static byte[] rsPlainToAsn1(byte[] sign) {
        if (sign.length != 64) {
            throw new CryptoException("err rs. ");
        } else {
            BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, 32));
            BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, 32, 64));

            try {
                return StandardDSAEncoding.INSTANCE.encode(SM2_DOMAIN_PARAMS.getN(), r, s);
            } catch (IOException var4) {
                throw new IORuntimeException(var4);
            }
        }
    }

    public static MacEngine createHmacSm3Engine(byte[] key) {
        return new BCHMacEngine(new SM3Digest(), key);
    }

    public static HMac hmacSm3(byte[] key) {
        return new HMac(HmacAlgorithm.HmacSM3, key);
    }

    private static byte[] bigIntToFixedLengthBytes(BigInteger rOrS) {
        byte[] rs = rOrS.toByteArray();
        if (rs.length == 32) {
            return rs;
        } else if (rs.length == 33 && rs[0] == 0) {
            return Arrays.copyOfRange(rs, 1, 33);
        } else if (rs.length < 32) {
            byte[] result = new byte[32];
            Arrays.fill(result, (byte)0);
            System.arraycopy(rs, 0, result, 32 - rs.length, rs.length);
            return result;
        } else {
            throw new CryptoException("Error rs: {}", new Object[]{Hex.toHexString(rs)});
        }
    }
}

报文类

package com.boot.model.domain;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**
 * @ClassName : IcbccsPublicParameters
 * @Description : IcbccsPublicParameters
 * @Author : ChenRui
 * @Date: 2022-11-28 23:53
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
@Schema(description = "工银瑞信对外请求参数")
public class IcbccsApiRequest<T> implements Serializable {

    @Schema(description = "版本号")
    private String serviceVersion;

    @Schema(description = "机构标识")
    private String instId;

    @Schema(description = "签名类型")
    private String signType;

    @Schema(description = "签名结果")
    private String sign;

    @Schema(description = "交易类型")
    private String businessType;

    @Schema(description = "业务参数")
    private T reqData;
}

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SM3SM2是中国密码算法标准中的两个重要算法,用于加密和签名验证。 首先,SM3是一种哈希算法,用于将任意长度的数据生成一个固定长度的哈希值。它采用了置换、移位、异或等操作,能够保证散列值的唯一性和抗碰撞性,被广泛应用于数字证书、数据完整性验证等场景。 其次,SM2是一种椭圆曲线密码算法,包括加密和签名验证两个部分。在加密过程中,SM2利用椭圆曲线离散对数问题实现了高强度的加密能力,能够保证数据的机密性。而在签名验证过程中,SM2使用私钥对签名内容进行加签,然后使用公钥对签名进行验证,从而确保签名的真实性和完整性。 加签验签是指使用SM2算法进行数字签名和验证的过程。在加签过程中,首先需要计算待签名消息的哈希值,即将消息输入到SM3算法中进行哈希计算。然后使用私钥对计算得到的哈希值进行签名,生成一段签名信息。在验签过程中,通过对收到的签名信息、原始消息以及公钥进行计算和验证,来验证签名的真实性和完整性。 总结来说,SM3SM2是中国密码算法标准中的两个重要算法,SM3用于生成哈希值,SM2用于加密和签名验证。加签验签是指使用SM2算法对数据进行签名和验证的过程,通过计算哈希值、使用私钥进行签名和使用公钥进行验证,保证签名的真实性和完整性。 ### 回答2: SM3SM2是中国密码学领域推出的一套密码算法,用于数据的加签和验签操作。 SM3是一种哈希算法,可以将任意长度的数据转换为固定长度的哈希值。加签操作是指对要发送的数据进行哈希运算,并使用私钥对哈希值进行加密生成签名。具体步骤如下: 1. 对要发送的数据进行哈希运算,得到哈希值。 2. 使用私钥对哈希值进行加密,生成签名。 3. 将签名和原始数据一起发送给接收方。 验签操作是指接收方使用公钥对接收到的签名和原始数据进行验证,确保数据的完整性和真实性。具体步骤如下: 1. 对接收到的数据进行哈希运算,得到哈希值。 2. 使用公钥对签名进行解密,得到解密后的哈希值。 3. 将解密后的哈希值与步骤1中计算得到的哈希值进行比对,如果相等,则验证通过,否则验证失败。 SM2是一种非对称加密算法,用于数字签名和密钥交换。在加签和验签中,SM2算法可以结合SM3算法来实现数据的加密和验证。加签操作过程与SM3相同,而验签操作过程需要使用公钥对签名进行解密,并进行比对验证。 SM3SM2加签验签操作,可以确保数据的完整性、真实性和安全性,广泛应用于电子商务、电子支付、网络通信等领域。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值