项目数据接口国密支持

10 篇文章 0 订阅
9 篇文章 0 订阅

项目数据接口国密支持

说明

国密即国家密码局认定的国产密码算法,即商用密码。

国密主要有SM1,SM2, SM3, SM4。密钥长度和分组长度均为128位。

1、SM1为对称加密。其加密强度与AES(高级加密标准, Advanced Encryption Standard)相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
2、SM2为非对称加密,基于ECC。 该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
3、SM3为消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
4、SM4为无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。
由于SM1、SM4加解密的分组大小为128bit,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。

国家在大力推进商用密码应用,并大力推广国产化

尤其在甲方为国企客户情况,需要针对产品上行到平台的数据以及对平台下行到设备的数据进行国密加解密!!!


项目使用

对SM2/SM4支持

项目为spingboot的Java常规maven管理的,jdk1.8+;

添加Maven依赖
        <!--SM2加密算法依赖-->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.68</version>
        </dependency>
        <!-- SM4加密算法依赖-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId >hutool-crypto</artifactId>
            <version>5.7.1</version>
        </dependency>
编写工具类
@Component
public class EncryptionUtils {
    // SM2非对称加密
    public static String sm2Encrypt(String publicKey,String plainText) {
        SM2 sm2 = new SM2(null, publicKey);
        String res = sm2.encryptBcd(plainText, KeyType.PublicKey);
        return res;
    }

    // SM2非对称解密
    public static String sm2Decrypt(String privatekey, String cipherText) {
        SM2 sm2 = new SM2(privatekey, null);
        return sm2.decryptStr(cipherText, KeyType.PrivateKey ,CharsetUtil.CHARSET_UTF_8);
    }

    // SM4对称加密
    public static String sm4Encrypt(String secretKey, String plainText) {
        SM4 sm4 = SmUtil.sm4(secretKey.getBytes());
        return sm4.encryptHex(plainText);
    }

    // SM4对称解密
    public static String sm4Decrypt(String secretKey, String cipherText) {
        SM4 sm4 = SmUtil.sm4(secretKey.getBytes());
        return sm4.decryptStr(cipherText, CharsetUtil.CHARSET_UTF_8);
    }

    public static void main(String[] args) {
        // 生成SM2秘钥
//        Map<String, String> smKeyMap = EncryptionUtils.generateSm2Key();
//        String publicKey = smKeyMap.get("KEY_PUBLIC_KEY");
//        String privateKey = smKeyMap.get("KEY_PRIVATE_KEY");
//        System.out.println("公钥:" + publicKey);
//        System.out.println("私钥:" + privateKey);

        // 生成SM4秘钥
        System.out.println("生成SM4秘钥:"+EncryptionUtils.generateKey());
    }
    /**
     * 生成SM2公私钥
     *
     * @return
     */
    public static Map<String, String> generateSm2Key() {
        SM2 sm2 = new SM2();
        ECPublicKey publicKey = (ECPublicKey) sm2.getPublicKey();
        ECPrivateKey privateKey = (ECPrivateKey) sm2.getPrivateKey();
        // 获取公钥
        byte[] publicKeyBytes = publicKey.getQ().getEncoded(false);
        String publicKeyHex = HexUtil.encodeHexStr(publicKeyBytes);

        // 获取64位私钥
        String privateKeyHex = privateKey.getD().toString(16);
        // BigInteger转成16进制时,不一定长度为64,如果私钥长度小于64,则在前方补0
        StringBuilder privateKey64 = new StringBuilder(privateKeyHex);
        while (privateKey64.length() < 64) {
            privateKey64.insert(0, "0");
        }

        Map<String, String> result = new HashMap<>();
        result.put("KEY_PUBLIC_KEY", publicKeyHex);
        result.put("KEY_PRIVATE_KEY", privateKey64.toString());
        return result;
    }

    public static String generateKey()  {
        // 生成SM4秘钥
        String key = RandomUtil.randomString(16);
        System.out.println("生成1个128bit的加密key:"+key);
        return key;
    }
}
项目切面编程

需要解密则需要使用拦截器等进行请求前置处理

定义注解:

/**
 * 加密生成类 注解,对返回数据进行加密
 * 响应结果加密
 * 条件:
 *  1.添加注解:@EncryptionAnnotation
 *  2.返回类型为 R / AjaxResult
 * 加密内容为:R.data -> R.encryptData
 * 加密算法支持:国密-SM2/SM4
 * @author xiaohang
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptionAnnotation {
}

切面编程:

对返回类需要根据项目定义进行统一配置修改

/**
 * 响应结果加密
 * 条件:
 *  1.添加注解:@EncryptionAnnotation
 *  2.返回类型为 AjaxResult
 * 加密内容为:AjaxResult.data -> R.encryptData
 * 加密算法支持:国密-SM2/SM4
 * @author xiaohang
 */
@ControllerAdvice
public class EncryptAjaxResultResponseBodyAdvice implements ResponseBodyAdvice<AjaxResult> {

    @Value("${data.encrypt.publicKey}")
    private String publicKey;
    @Value("${data.encrypt.privateKey}")
    private String privatekey;
    @Value("${data.encrypt.open}")
    private boolean open;
    @Value("${data.encrypt.showLog}")
    private boolean showLog;

    @Value("${data.encrypt.type}")
    private String encryptType;

    @Value("${data.encrypt.secretKey}")
    private String secretKey;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        if (!returnType.hasMethodAnnotation(EncryptionAnnotation.class)){
            return false;
        }
        String returnName = returnType.getParameterType().getName();

        if (returnName.equals(AjaxResult.class.getName()) ) {
            return true;
        }

        return false;
    }

    @SneakyThrows
    @Override
    public AjaxResult beforeBodyWrite(AjaxResult body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (!open) {
            return body;
        }
        // 真实数据
        Object data = body.get(AjaxResult.DATA_TAG);

        // 如果data为空,直接返回
        if (data == null) {
            return body;
        }

        String dataText = JSONUtil.toJsonStr(data);

        // 如果data为空,直接返回
        if (StringUtils.isBlank(dataText)) {
            return body;
        }

        String encryptText;
        try {
            if ("SM2".equals(encryptType)){
                encryptText = EncryptionUtils.sm2Encrypt(publicKey,dataText);
            }else if ("SM4".equals(encryptType)){
                encryptText = EncryptionUtils.sm4Encrypt(secretKey,dataText);
            }else{
                encryptText = null;
            }
            if (showLog){
                System.out.println("加密前明文:"+dataText);
                 System.out.println("加密后密文:"+encryptText);
            }
        }catch (Exception e){
            e.printStackTrace();
            encryptText="数据加密异常,请联系管理人员!";
        }

        body.put(AjaxResult.ENCRYPT_DATA,encryptText);
        body.put(AjaxResult.DATA_TAG,null);
        return body;
    }
}
参数配置
# 数据加密
data:
  encrypt:
    open: true
    showLog: false
    type: SM2
    publicKey: 【SM2的公钥】
    privateKey: 【SM2的私钥】
    secretKey: 【SM4的密钥】

测试使用
@Slf4j
@RestController
@RequestMapping("test")
public class TestController extends BaseController{
    @GetMapping("encryption")
    @EncryptionAnnotation
    public AjaxResult encryption2(String data){
        CommercialTenant t = new CommercialTenant();
        t.setBrandName("加密测试,返回AjaxResult的数据");
        return AjaxResult.success(t);
    }
}

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小_杭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值