Bladeren bron

新增后端往前端签名,前端验签

xusl 2 jaren geleden
bovenliggende
commit
018dfcd529

+ 56 - 0
backend/src/main/java/com/jiayue/ssi/config/ResponseAdvice.java

@@ -0,0 +1,56 @@
+package com.jiayue.ssi.config;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.jiayue.ssi.constant.SecretKeyConstants;
+import com.jiayue.ssi.util.SM2CryptUtils;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+/**
+* 返回值统一处理
+*
+* @author xsl
+* @since 2023/03/03
+*/
+@ControllerAdvice(annotations = RestController.class)
+public class ResponseAdvice implements ResponseBodyAdvice<Object> {
+    @Override
+    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
+        return true;
+    }
+
+    @Override
+    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
+                                  ServerHttpResponse response) {
+        ObjectMapper objectMapper = new ObjectMapper();
+        String secretResult = "";
+        try {
+            String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
+            // 加密处理
+            String encrypt = SM2CryptUtils.encrypt(result, SecretKeyConstants.CLIENT_PUBLIC_KEY);
+            // 签名
+            String signStr = SM2CryptUtils.sign(SecretKeyConstants.SERVER_PRIVATE_KEY,result);
+
+//            String encrypt = SM2CryptUtils.encrypt("1122", SecretKeyConstants.CLIENT_PUBLIC_KEY);
+//            System.out.println("加密:"+encrypt);
+//            String signStr = SM2CryptUtils.sign(SecretKeyConstants.SERVER_PRIVATE_KEY,"1122");
+//            System.out.println("签名:"+signStr);
+            // 解密
+//            String text = SM2CryptUtils.decrypt(encrypt,SecretKeyConstants.CLIENT_PRIVATE_KEY);
+//            System.out.println("解密:"+text);
+//            boolean bo = SM2CryptUtils.verifySign(SecretKeyConstants.SERVER_PUBLIC_KEY,text,signStr);
+//            System.out.println("验签:"+bo);
+            secretResult = "returnData="+encrypt+"&returnSign="+signStr;
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+        }
+        return secretResult;
+    }
+}

+ 23 - 0
backend/src/main/java/com/jiayue/ssi/util/ResponseInfo.java

@@ -0,0 +1,23 @@
+package com.jiayue.ssi.util;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+*
+*
+* @author xsl
+* @since 2023/03/01
+*/
+public class ResponseInfo {
+    public static void doResponse(HttpServletResponse response,String message,Integer code){
+        response.addHeader("Access-Control-Allow-Origin", "*");
+        response.setContentType("text/html;charset=UTF-8");
+        response.setStatus(code);
+        try {
+            response.getWriter().write(message);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 25 - 6
backend/src/main/java/com/jiayue/ssi/util/SM2CryptUtils.java

@@ -6,13 +6,11 @@ import cn.hutool.crypto.ECKeyUtil;
 import cn.hutool.crypto.asymmetric.KeyType;
 import cn.hutool.crypto.asymmetric.SM2;
 import cn.hutool.crypto.SmUtil;
-import com.jiayue.ssi.constant.SecretKeyConstants;
 import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.signers.PlainDSAEncoding;
 import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
-
-import java.security.PublicKey;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -41,7 +39,12 @@ public class SM2CryptUtils {
         return keys;
     }
 
-    // 加密
+    /**
+     * 加密
+     * @param data          数据
+     * @param publicKey     公钥
+     * @return
+     */
     public static String encrypt(String data,String publicKey) {
         String publicKeyTmp = publicKey;
         if (publicKey.length() == 130) {
@@ -60,9 +63,13 @@ public class SM2CryptUtils {
 
     }
 
-    // 解密
+    /**
+     * 解密
+     * @param data          解密数据
+     * @param privateKey    私钥
+     * @return
+     */
     public static String decrypt(String data,String privateKey) {
-        // ECKeyUtil.toSm2PrivateParams()
         SM2 sm2 = new SM2(ECKeyUtil.toSm2PrivateParams(privateKey), null);
         sm2.setMode(SM2Engine.Mode.C1C3C2);
         sm2.setEncoding(new PlainDSAEncoding());
@@ -83,6 +90,18 @@ public class SM2CryptUtils {
         return verify;
     }
 
+    /**
+     * 签名
+     * @param privateKey    私钥
+     * @param content       签名原内容
+     * @return
+     */
+    public static String sign(String privateKey,String content) {
+        SM2 sm2 = new SM2(privateKey, null);
+        String sign = sm2.signHex(HexUtil.encodeHexStr(content));
+        return sign;
+    }
+
     public static void main(String[] args) {
         Map<String,String> map = createSM2Key();
         for(Map.Entry<String, String> entry:map.entrySet()){

+ 55 - 14
ui/src/main.js

@@ -142,23 +142,36 @@ Vue.prototype.$axios.interceptors.response.use(
                 URL.revokeObjectURL(elink.href) // 释放URL 对象
                 document.body.removeChild(elink)
             }
-
             response.data = ''
             response.headers['content-type'] = 'text/json'
             return response
         } else {
             const res = response.data
+            let returnStr = res.split("&")
+            let returnData = returnStr[0].split("=")[1]
+            let returnSign = returnStr[1].split("=")[1]
+            // 解密
+            let decData = doDecryptStr(returnData)
+            // 验签
+            let verifyResult = doVerifySignature(decData,returnSign)
+
+          if (!verifyResult){
+            return Promise.reject(new Error('返回数据验签失败' || 'Error'))
+          }
+
+          let data = JSON.parse(decData)
+          alert(data)
             // if the custom code is not 20000, it is judged as an error.
             //console.log(res.code)
-            if (res.code !== 0) {
+            if (data.code !== 0) {
                 Message({
-                    message: res.message || 'Error',
+                    message: data.message || 'Error',
                     type: 'error',
                     duration: 5 * 1000
                 })
 
                 // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
-                if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
+                if (data.code === 50008 || data.code === 50012 || data.code === 50014) {
                     // to re-login
                     MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
                         confirmButtonText: 'Re-Login',
@@ -170,9 +183,9 @@ Vue.prototype.$axios.interceptors.response.use(
                         })
                     })
                 }
-                return Promise.reject(new Error(res.message || 'Error'))
+                return Promise.reject(new Error(data.message || 'Error'))
             } else {
-                return res
+                return data
             }
         }
     },
@@ -235,6 +248,24 @@ new Vue({
     render: h => h(App)
 })
 
+export function test(){
+  let sm2 = require('sm-crypto').sm2;
+  let cipherMode = 1
+  // 加密
+  let encryptData = sm2.doEncrypt('1122加密', '0460ff8c8c306fe62f6f9d11c5c82c30d10bbbc703da094e423072cac7dc663c97fad52eccb34f311f47a07f280de157ba4f2aa659cabe749121384b9376ea2ed2', cipherMode);
+  let sm3 = require('sm-crypto').sm2;
+  // 签名
+  let sign = sm3.doSignature('1122加密','6155d63ee27cbeca07f3e40c4f8856f1be8119fcbda1aadc7e0e595e52bad7bd')
+  // 解密
+  let sm4 = require('sm-crypto').sm2;
+  let doDecrypt = sm4.doDecrypt(encryptData, privateKey1, cipherMode);
+  alert('解密:'+doDecrypt)
+  // 验签
+  let sm5 = require('sm-crypto').sm2;
+  let verifyResult = sm5.doVerifySignature(doDecrypt, sign, publicKey2) // 验签结果
+  alert('验签:'+verifyResult)
+}
+
 // 加密:
 export function doEncrypt(msgString) {
   // let msg = msgString;
@@ -254,25 +285,25 @@ export function doEncrypt(msgString) {
 
 // 解密
 export function doDecryptStr(enStr) {
-  let msg = enStr;
-  if (typeof (enStr) !== 'string') {
-    msg = JSON.stringify(enStr);
-  }
+  // let msg = enStr;
+  // if (typeof (enStr) !== 'string') {
+  //   msg = JSON.stringify(enStr);
+  // }
 
   let sm2 = require('sm-crypto').sm2;
   // 1 - C1C3C2;	0 - C1C2C3;	默认为1
   let cipherMode = 1
 
   // 加密后的密文,需要前去掉04。因为doDecrypt中自行添加了04,后端加密代码也自行添加了04
-  let en = enStr.data.substr(2)
+  // let en = enStr.data.substr(2)
   // 解密结果
-  let doDecrypt = sm2.doDecrypt(en, privateKey1, cipherMode);
+  let doDecrypt = sm2.doDecrypt(enStr, privateKey1, cipherMode);
   // 解密后类型转换
   let objData = JSON.parse(doDecrypt)
-  return objData;
+  return doDecrypt;
 }
 
-// 签名
+// 签名
 export function doSign(msgString) {
   let sm2 = require('sm-crypto').sm2;
   // 1 - C1C3C2;	0 - C1C2C3;	默认为1
@@ -281,3 +312,13 @@ export function doSign(msgString) {
   let sign = sm2.doSignature(msgString,privateKey1, { hash:true, der:true })
   return sign;
 }
+
+// 验签
+export function doVerifySignature(msgString,sigValueHex) {
+  let sm2 = require('sm-crypto').sm2;
+  // 1 - C1C3C2;	0 - C1C2C3;	默认为1
+  let cipherMode = 1
+  // 签名
+  let verifyResult = sm2.doVerifySignature(msgString, sigValueHex, publicKey2,{ hash:true, der:true }) // 验签结果
+  return verifyResult;
+}