<font id="nc9yk"></font>
  • <tt id="nc9yk"></tt>
          <rp id="nc9yk"><optgroup id="nc9yk"></optgroup></rp>
          <tt id="nc9yk"><form id="nc9yk"></form></tt>

            <cite id="nc9yk"></cite>

            java實現國產sm4加密算法

             更新時間:2020年12月29日 17:23:53   作者:經典雞翅  
            這篇文章主要介紹了java實現國產sm4加密算法的步驟,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下

            前言

            今天給大家帶來一個國產SM4加密解密算法的java后端解決方案,代碼完整,可以直接使用,希望給大家帶來幫助,尤其是做政府系統的開發人員,可以直接應用到項目中進行加密解密。
            畫重點!是SM4哦,不是SM。哈哈,各位要在知識里遨游,不要想歪。正文開始~

            國產SM4加密解密算法概念介紹

            SMS4算法是在國內廣泛使用的WAPI無線網絡標準中使用的加密算法,是一種32輪的迭代非平衡Feistel結構的分組加密算法,其密鑰長度和分組長度均為128。SMS4算法的加解密過程中使用的算法是完全相同的,唯一不同點在于該算法的解密密鑰是由它的加密密鑰進行逆序變換后得到的。
            SMS4分組加密算法是中國無線標準中使用的分組加密算法,在2012年已經被國家商用密碼管理局確定為國家密碼行業標準,標準編號GM/T 0002-2012并且改名為SM4算法,與SM2橢圓曲線公鑰密碼算法,SM3密碼雜湊算法共同作為國家密碼的行業標準,在我國密碼行業中有著極其重要的位置。
            SMS4算法的分組長度為128bit,密鑰長度也是128bit。加解密算法均采用32輪非平衡Feistel迭代結構,該結構最先出現在分組密碼LOKI的密鑰擴展算法中。SMS4通過32輪非線性迭代后加上一個反序變換,這樣只需要解密密鑰是加密密鑰的逆序,就能使得解密算法與加密算法保持一致。SMS4加解密算法的結構完全相同,只是在使用輪密鑰時解密密鑰是加密密鑰的逆序。
            S盒是一種利用非線性變換構造的分組密碼的一個組件,主要是為了實現分組密碼過程中的混淆的特性和設計的。SMS4算法中的S盒在設計之初完全按照歐美分組密碼的設計標準進行,它采用的方法是能夠很好抵抗差值攻擊的仿射函數逆映射復合法。

            SM4加密算法應用場景

            SM4常用于政府系統的數據傳輸加密,比如當我們前端向后臺傳參數的時候,可以使用此算法。對參數的數據進行加密,然后后臺對加密的數據進行解密再存儲到數據庫中,保證數據傳輸過程中,不受泄露。
            本次提供的方案不僅提供sm4的加密解密,還提供了md5算法的完整性防篡改校驗。

            Java端解決方案

            對于java端,我們使用的基于spring的aop切面和自定義注解來實現。整體思路為,當后臺開啟加密解密的時候,針對于打上注解的方法,尋找實體類中打上注解的字段進行加密和解密。再從前端傳遞請求的request中取出md5的header,進行md5的完整性,防篡改校驗。

            首先我們必須說的是兩個工具類,一個是SM4Utils工具類,另一個則是md5工具類。
            下面先來說一下SM4Utils。這個工具類用于SM4算法的加密和解密及密碼校驗。我們先直接看代碼,然后后面對此進行解釋。

            SM4Utils

            public class Sm4Utils {
            
              private static final String ENCODING = "UTF-8";
              public static final String ALGORIGTHM_NAME = "SM4";
              public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
              public static final int DEFAULT_KEY_SIZE = 128;
            
              public Sm4Utils() {
              }
            
              static {
                Security.addProvider(new BouncyCastleProvider());
              }
            
              /**
               * @Description:生成ecb暗號
               */
              private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
                Cipher cipher = Cipher.getInstance(algorithmName,BouncyCastleProvider.PROVIDER_NAME);
                Key sm4Key = new SecretKeySpec(key, ALGORIGTHM_NAME);
                cipher.init(mode, sm4Key);
                return cipher;
              }
            
              /**
               * @Description:自動生成密鑰
               */
              public static byte[] generateKey() throws Exception {
                return generateKey(DEFAULT_KEY_SIZE);
              }
            
              public static byte[] generateKey(int keySize) throws Exception {
                KeyGenerator kg = KeyGenerator.getInstance(ALGORIGTHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
                kg.init(keySize, new SecureRandom());
                return kg.generateKey().getEncoded();
              }
            
            
              /**
               * @Description:加密
               */
              public static String encryptEcb(String hexKey, String paramStr, String charset) throws Exception {
                String cipherText = "";
                if (null != paramStr && !"".equals(paramStr)) {
                  byte[] keyData = ByteUtils.fromHexString(hexKey);
                  charset = charset.trim();
                  if (charset.length() <= 0) {
                    charset = ENCODING;
                  }
                  byte[] srcData = paramStr.getBytes(charset);
                  byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
                  cipherText = ByteUtils.toHexString(cipherArray);
                }
                return cipherText;
              }
            
              /**
               * @Description:加密模式之ecb
               */
              public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
                Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
                byte[] bs = cipher.doFinal(data);
                return bs;
              }
            
              /**
               * @Description:sm4解密
               */
              public static String decryptEcb(String hexKey, String cipherText, String charset) throws Exception {
                String decryptStr = "";
                byte[] keyData = ByteUtils.fromHexString(hexKey);
                byte[] cipherData = ByteUtils.fromHexString(cipherText);
                byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData);
                charset = charset.trim();
                if (charset.length() <= 0) {
                  charset = ENCODING;
                }
                decryptStr = new String(srcData, charset);
                return decryptStr;
              }
            
              /**
               * @Description:解密
               */
              public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {
                Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
                return cipher.doFinal(cipherText);
              }
            
              /**
               * @Description:密碼校驗
               */
              public static boolean verifyEcb(String hexKey,String cipherText,String paramStr) throws Exception {
                boolean flag = false;
                byte[] keyData = ByteUtils.fromHexString(hexKey);
                byte[] cipherData = ByteUtils.fromHexString(cipherText);
                byte[] decryptData = decrypt_Ecb_Padding(keyData,cipherData);
                byte[] srcData = paramStr.getBytes(ENCODING);
                flag = Arrays.equals(decryptData,srcData);
                return flag;
              }
            
              /**
               * @Description:測試類
              */
              public static void main(String[] args) {
                try {
                  String json = "{\"name\":\"color\",\"sex\":\"man\"}";
                  // 自定義的32位16進制密鑰
                  String key = "cc9368581322479ebf3e79348a2757d9";
                  String cipher = Sm4Utils.encryptEcb(key, json,ENCODING);
                  System.out.println(cipher);
                  System.out.println(Sm4Utils.verifyEcb(key, cipher, json));
                  json = Sm4Utils.decryptEcb(key, cipher,ENCODING);
                  System.out.println(json);
                } catch (Exception e) {
                  e.printStackTrace();
                }
              }
            
            }
            

            我們來仔細看一下上面的代碼,首先定義了個4個靜態不可修改都變量,用于下方方法的使用。包括編碼utf-8,密碼名稱sm4,密碼的分組方式SM4/ECB/PKCS7Padding和默認的key值長度128。
            整體的方法我們分為生成ecb暗號,自動生成密鑰,加密,解密,密碼校驗的算法。
            以上為sm4utils的核心代碼。

            MD5Utils

            /**
             * @Description:md5加密工具
             */
            public class Md5Utils {
            
              /**
               * @Description:獲得md5加密串
               */
              public static String getMD5String(String str) {
                try {
                  MessageDigest md = MessageDigest.getInstance("MD5");
                  md.update(str.getBytes());
                  return new BigInteger(1, md.digest()).toString(16);
                } catch (Exception e) {
                  e.printStackTrace();
                  return null;
                }
              }
            }

            MD5Utils主要用于對參數的完整性校驗,防止篡改。

            此時我們已經實現了主要的加密解密工具,接下來實現spring的aop自定義注解,自定義注解我們要實現三個。
            第一個是加密注解,用于方法上的,表示該方法的參數需要被加密。
            第二個是解密注解,用于方法上的,表示該方法的參數需要被解密。
            第三個是字段加密解密注解,用于標識實體類的字段是否需要被加密和解密。

            加密的自定義注解

            @Documented
            @Target({ElementType.METHOD})
            @Retention(RetentionPolicy.RUNTIME)
            @Order(-2147483648)
            public @interface EncryptMethod {
            }

            可以看到此時打了一個order注解,-2147483648用于標識優先級最高。

            解密的自定義注解

            @Target({ElementType.METHOD, ElementType.TYPE})
            @Retention(RetentionPolicy.RUNTIME)
            @Documented
            @Order(-2147483648)
            @Inherited
            public @interface DecryptMethod {
            }

            加密解密字段的自定義注解

            @Documented
            @Target({ElementType.FIELD})
            @Retention(RetentionPolicy.RUNTIME)
            @Order(-2147483648)
            public @interface EncryptField {
            }

            注解的準備工作已經做完,接下來就是實現sm4的切面方法。不了解spring的aop的實現方法,可以去先補一下spring的相關知識,這里不做贅述。我們這里采用的切面都是環繞通知,切面的切點是加密解密的注解。

            加密解密切面的實現

            說一下切面的答題實現思路。我們可以看到@ConditionalOnProperty(prefix = "sm4", value = "enable", matchIfMissing = false),這個我們做成了根據配置文件的配置進行動態的開關。
            我們在appication.yml文件中進行如下的配置。這樣切面是否生效就取決于配置。然后我們捕捉加密的注解和解密的注解,然后對加了注解的方法中的邏輯進行加密和解密。
            下方代碼的切點方法為EncryptAOPCut和DecryptAOPCut。隨后通過around對切點進行捕捉。分別調用的核心的加密算法encryptMethodAop和解密算法decryptMethodAop。

            sm4:
             enable: true

            核心的加密解密算法都是使用環繞通知的ProceedingJoinPoint類,從他的對象中我們可以取到spring的各種參數,包括request請求,請求的參數和response對象。

            /**
             * @Description:sm4加密解密切面
             */
            @Order(-2147483648)
            @Aspect
            @Component
            @ConditionalOnProperty(prefix = "sm4", value = "enable", matchIfMissing = false)
            public class Sm4Aspect {
            
              private Logger log = LoggerFactory.getLogger(Sm4Aspect.class);
              private static final String DEFAULT_KEY = "cc9368581322479ebf3e79348a2757d9";
            
              public Sm4Aspect() {
              }
            
              @Pointcut("@annotation(com.jichi.aop.sm4.EncryptMethod)")
              public void EncryptAOPCut() {
              }
            
              @Pointcut("@annotation(com.jichi.aop.sm4.DecryptMethod)")
              public void DecryptAOPCut() {
              }
            
              @Around("EncryptAOPCut()")
              public Object encryptMethodAop(ProceedingJoinPoint joinPoint) {
                Object responseObj = null;
                try {
                  responseObj = joinPoint.proceed();
                  this.handleEncrypt(responseObj);
                  //md5加密
                  String md5Data = Md5Utils.getMD5String(new Gson().toJson(responseObj));
                  SpringContextUtil.getHttpServletResponse().setHeader("md5",md5Data);
                } catch (Throwable throwable) {
                  throwable.printStackTrace();
                  this.log.error("encryptMethodAop處理出現異常{}", throwable);
                }
                return responseObj;
              }
            
              @Around("DecryptAOPCut()")
              public Object decryptMethodAop(ProceedingJoinPoint joinPoint) {
                Object responseObj = null;
                try {
                  responseObj = joinPoint.getArgs()[0];
                  //throw new RuntimeException("md5校驗失敗");
                  this.handleDecrypt(responseObj);
                  String md5 = "";
                  md5 = Md5Utils.getMD5String(new Gson().toJson(responseObj));
                  System.out.println(md5);
                  String origianlMd5 = "";
                  origianlMd5 = SpringContextUtil.getHttpServletRequest().getHeader("md5");
                  if(origianlMd5.equals(md5)){
                    responseObj = joinPoint.proceed();
                  }else{
                    this.log.error("參數的md5校驗不同,可能存在篡改行為,請檢查!");
                    throw new Exception("參數的md5校驗不同,可能存在篡改行為,請檢查!");
                  }
                } catch (Throwable throwable) {
                  throwable.printStackTrace();
                  this.log.error("decryptMethodAop處理出現異常{}", throwable);
                }
                return responseObj;
              }
            
              private void handleEncrypt(Object requestObj) throws Exception {
                if (!Objects.isNull(requestObj)) {
                  Field[] fields = requestObj.getClass().getDeclaredFields();
                  Field[] fieldsCopy = fields;
                  int fieldLength = fields.length;
                  for(int i = 0; i < fieldLength; ++i) {
                    Field field = fieldsCopy[i];
                    boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
                    if (hasSecureField) {
                      field.setAccessible(true);
                      String plaintextValue = (String)field.get(requestObj);
                      String encryptValue = Sm4Utils.encryptEcb(DEFAULT_KEY, plaintextValue, "");
                      field.set(requestObj, encryptValue);
                    }
                  }
                }
              }
            
              private Object handleDecrypt(Object responseObj) throws Exception {
                if (Objects.isNull(responseObj)) {
                  return null;
                } else {
                  Field[] fields = responseObj.getClass().getDeclaredFields();
                  Field[] fieldsCopy = fields;
                  int fieldLength = fields.length;
                  for(int i = 0; i < fieldLength; ++i) {
                    Field field = fieldsCopy[i];
                    boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
                    if (hasSecureField) {
                      field.setAccessible(true);
                      String encryptValue = (String)field.get(responseObj);
                      String plaintextValue = Sm4Utils.decryptEcb(DEFAULT_KEY, encryptValue, "");
                      field.set(responseObj, plaintextValue);
                    }
                  }
                  return responseObj;
                }
              }
            }
            

            代碼實際應用

            首先我們可以定義一個實體類,對實體類的字段進行加密或解密的標識。我們這里建立了一個Info實體類,對于其中的name屬性,我們加了注解加密解密字段,對于sex屬性我們不做任何處理。

            @Data
            public class Info {
            
              @EncryptField
              private String name;
            
              private String sex;
            
            }

            然后我們對于controller方法打上加密的方法或解密的方法。

            @RestController
            @RequestMapping("/demo/test")
            public class TestController {
            
            
              @PostMapping("/saveInfo")
              @DecryptMethod
              public HashMap<String,String> saveInfo(@RequestBody Info info) {
                HashMap<String,String> result = new HashMap<String,String>();
                String name = info.getName();
                System.out.println(name);
                String sex= info.getSex();
                System.out.println(sex);
                result.put("flag","1");
                result.put("msg","操作成功");
                return result;
              }
            
            }

            注意到方法上的注解@DecryptMethod,以為這著我們的這個方法將會進行解密。如果是@EncryptMethod,則代表對方法進行加密。

            總結

            到此為止,涉及到java后端的代碼解決方案已經完畢。示例代碼已經給出,大家可以直接使用,本人親測有效。文中難免有不足,歡迎大家批評指正。

            以上就是java實現國產sm4加密算法的詳細內容,更多關于Java 國產sm4加密算法的資料請關注腳本之家其它相關文章!

            相關文章

            • Spring boot 連接多數據源過程詳解

              Spring boot 連接多數據源過程詳解

              這篇文章主要介紹了Spring boot 連接多數據源過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
              2019-08-08
            • 解決Eclipse的Servers視圖中無法添加Tomcat6/Tomcat7的方法

              解決Eclipse的Servers視圖中無法添加Tomcat6/Tomcat7的方法

              這篇文章主要介紹了解決Eclipse的Servers視圖中無法添加Tomcat6/Tomcat7的方法的相關資料,需要的朋友可以參考下
              2017-02-02
            • Java 分析并解決內存泄漏的實例

              Java 分析并解決內存泄漏的實例

              這篇文章主要介紹了Java 分析并解決內存泄漏的實例,幫助大家更好的理解和學習Java,感興趣的朋友可以了解下
              2020-08-08
            • SpringMVC和Swagger整合方法

              SpringMVC和Swagger整合方法

              Swagger 是一個規范和完整的框架,用于生成、描述、調用和可視化 RESTful 風格的 Web 服務。下面通過本文給大家分享SpringMVC和Swagger整合方法,感興趣的朋友一起看看吧
              2017-08-08
            • mybatisplus解決駝峰命名映射問題詳解

              mybatisplus解決駝峰命名映射問題詳解

              這篇文章主要介紹了mybatisplus解決駝峰命名映射問題詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
              2020-09-09
            • Java Hibernate對象(瞬時態,持久態,脫管態)詳解

              Java Hibernate對象(瞬時態,持久態,脫管態)詳解

              這篇文章主要介紹了Java Hibernate對象(瞬時態,持久態,脫管態)詳解的相關資料,這里對Java Hibernate對象進行了介紹及總結,需要的朋友可以參考下
              2016-11-11
            • Java編程一道多線程問題實例代碼

              Java編程一道多線程問題實例代碼

              這篇文章主要介紹了Java編程一道多線程問題實例代碼,分享了相關代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
              2018-02-02
            • 使用Spring事件機制實現異步的方法

              使用Spring事件機制實現異步的方法

              這篇文章主要介紹了使用Spring事件機制實現異步的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
              2018-06-06
            • 淺談java 面對對象(抽象 繼承 接口 多態)

              淺談java 面對對象(抽象 繼承 接口 多態)

              下面小編就為大家帶來一篇淺談java 面對對象(抽象 繼承 接口 多態)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
              2017-02-02
            • Spring security BCryptPasswordEncoder密碼驗證原理詳解

              Spring security BCryptPasswordEncoder密碼驗證原理詳解

              這篇文章主要介紹了Spring security BCryptPasswordEncoder密碼驗證原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
              2020-03-03

            最新評論

            hao500彩票 www.globalryb.com:军事| www.051b.com:新化县| www.utahsbusinessdirectory.com:巴中市| www.golobd.com:友谊县| www.haohanghuanbao.com:葫芦岛市| www.thehelvetia.com:肇东市| www.koreanista.net:玛纳斯县| www.m2992.com:会宁县| www.purefitnessoc.com:桂林市| www.s59uk.com:桓台县| www.xinhuigroup.com.cn:吉林省| www.modasaatler.com:手游| www.cp6779.com:桓台县| www.mushroompipe.com:宁夏| www.zxwire.com:北流市| www.stonedz.com:南充市| www.playmarket-androids.net:贡山| www.gbbsrh.com:白城市| www.mcmhonmono.com:桂平市| www.m7y3.com:新化县| www.mlshgs.com:洪江市| www.uncanventional.com:华容县| www.mai0565.com:高要市| www.clsiouxlookout.com:绵阳市| www.cognaso.com:乐陵市| www.bslhw.com:赤城县| www.rq6.net:陆川县| www.906765.com:如皋市| www.xhjsw.cn:雷山县| www.zone416.com:五台县| www.hairbook.org:玉林市| www.competitorurl.com:南宫市| www.gaobaoit.com:安阳县| www.mugua668.com:黔东| www.genoad.com:花莲市| www.chengsekeji.com:扶余县| www.wugongjie.com:新田县| www.siquanlvzhi.com:东丽区| www.cuidighlinn.com:稷山县| www.gandjarexe.com:南陵县| www.bestbridalevent.com:丰顺县| www.tiantaojiaosu.com:峨眉山市| www.conceptflame.com:晴隆县| www.g9838.com:分宜县| www.hongshunpuyi.com:苏尼特右旗| www.v88v99.com:SHOW| www.chen0370.com:古丈县| www.suhang-cn.com:山东省| www.ahtydzs.com:讷河市| www.rdkfw.cn:崇明县| www.zainvista.com:班戈县| www.xffrw.cn:广汉市| www.zhugangfamen.com:江永县| www.zgspbw.com:侯马市| www.gtparking.com:丰顺县| www.wwwhg5844.com:安新县| www.zgjxcf.com:咸丰县| www.bluefairyus.com:保德县| www.cheap-uggboots4u.com:宝兴县| www.assa7777.com:礼泉县| www.rubinsteintaybi.org:河东区| www.laorenke.com:甘泉县| www.ahlikartu.com:资中县| www.hdalsdq.com:汶川县| www.taobaokk8.com:罗源县| www.n7989.com:临安市| www.w-wha.org:五峰| www.kidizzle.com:永顺县| www.1shoupifa.com:嘉荫县| www.zendiko.com:三穗县| www.golobd.com:北票市| www.kzftp.com:靖州| www.wwwbc250.com:濮阳市| www.cinplatcorporate.com:邳州市| www.stmgqhw.com:怀集县| www.ganzaojijs.com:阆中市| www.mj95988.com:吉林市| www.eldukedegreaser.com:兖州市| www.cp6783.com:正安县| www.freemovieswatch.org:奉贤区| www.hz-xp.com:阳城县| www.bwpha.com:大同市| www.impobol.com:梁河县| www.qxnxf.com:达孜县|