暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

sm2多端加密解密,java,js,android,ios实战

IT学习道场 2022-07-29
3329

SM2非对称加密

    公钥 = 04xxxxxxxxxxxxxxxxxxxx,


    私钥 = 276xxxx


    原文:你哦哈1232154 3654 {} ,俺可接受不符点

    公钥私钥是我后台自己生成的


    java代码实现

    pom.xml

      <dependency>
          <groupId>org.bouncycastle</groupId>
          <artifactId>bcprov-jdk15to18</artifactId>
          <version>1.71</version>
      </dependency>

      国密sm2算法秘钥对

        package com.sm.mscmsm2;

        /**
        * 描述: 国密sm2算法秘钥对 - 对象 <br>
        * 时间: 2022-07-14 16:01 <br>
        * 作者:IT学习道场
        */

        public class SM2KeyPairs {
        /**
        *公钥
        */
        private String publicKey;

        /**
        * 私钥
        */
        private String privateKey;

        public SM2KeyPairs() {
        }

        public SM2KeyPairs(String publicKey, String privateKey) {
        this.publicKey = publicKey;
        this.privateKey = privateKey;
        }

        public String getPublicKey() {
        return publicKey;
        }

        public void setPublicKey(String publicKey) {
        this.publicKey = publicKey;
        }

        public String getPrivateKey() {
        return privateKey;
        }

        public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
        }

        @Override
        public String toString() {
        return "SM2KeyPairs{" +
        "publicKey='" + publicKey + '\'' +
        ", privateKey='" + privateKey + '\'' +
        '}';
        }
        }

        SM2辅助工具类

        采用数字证书类型 X9

          package com.sm.mscmsm2;

          import lombok.extern.slf4j.Slf4j;
          import org.bouncycastle.asn1.gm.GMNamedCurves;
          import org.bouncycastle.asn1.x9.X9ECParameters;
          import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
          import org.bouncycastle.crypto.InvalidCipherTextException;
          import org.bouncycastle.crypto.engines.SM2Engine;
          import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
          import org.bouncycastle.crypto.params.*;
          import org.bouncycastle.math.ec.ECPoint;
          import org.bouncycastle.util.encoders.Hex;
          import java.io.UnsupportedEncodingException;
          import java.math.BigInteger;
          import java.security.NoSuchAlgorithmException;
          import java.security.SecureRandom;
          import java.util.Base64;

          /**
          * 描述: 美术传媒sm2 <br>
          * 时间: 2022-07-25 17:32 <br>
          * 作者:王林冲
          */
          @Slf4j
          public class SM2Util {

          private static final ECDomainParameters domainParameters;

          private static final X9ECParameters sm2ECParameters;

          static {
          sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
          domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
          }

          public static SM2KeyPairs getSM2KeyPairs(){
          //生成密钥对
          ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
          try {
          keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
          AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
          //私钥,16进制格式,自己保存,格式如a2081b5b81fbea0b6b973a3ab6dbbbc65b1164488bf22d8ae2ff0b8260f64853
          BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
          String privateKeyHex = privatekey.toString(16);
          //公钥,16进制格式,发给前端,格式如04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba
          ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
          String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
          SM2KeyPairs pairs = new SM2KeyPairs(publicKeyHex, privateKeyHex);
          return pairs;
          } catch (NoSuchAlgorithmException e) {
          log.error(e.getMessage(), e);
          throw new RuntimeException(e.getMessage());
          }
          }

          public static String decrypt(String data, String privateKey) {
          byte[] cipherDataByte = Hex.decode(data);
          //刚才的私钥Hex,先还原私钥
          BigInteger privateKeyD = new BigInteger(privateKey, 16);
          ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
          //用私钥解密
          SM2Engine sm2Engine = new SM2Engine();
          sm2Engine.init(false, privateKeyParameters);
          //processBlock得到Base64格式,记得解码
          byte[] arrayOfBytes = new byte[0];
          try {
          arrayOfBytes = Base64.getDecoder().decode(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));
          } catch (InvalidCipherTextException e) {
          log.error(e.getMessage(), e);
          throw new RuntimeException(e.getMessage());
          }
          //得到明文:SM2 Encryption Test
          return new String(arrayOfBytes);
          }

          public static String encrypt(String data, String publicKey){
          //提取公钥点
          ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
          //刚才的私钥Hex,先还原私钥
          ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
          SM2Engine sm2Engine = new SM2Engine();
          sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

          byte[] arrayOfBytes = new byte[0];
          try {
          byte[] in = Base64.getEncoder().encode(data.getBytes("utf-8"));
          arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
          } catch (Exception e) {
          log.error(e.getMessage(), e);
          throw new RuntimeException(e.getMessage());
          }
          return Hex.toHexString(arrayOfBytes);

          }

          public static void main(String[] args) throws InvalidCipherTextException, UnsupportedEncodingException {
          //SM2KeyPairs sm2KeyPairs = getSM2KeyPairs();
          //System.out.println(sm2KeyPairs.toString());
          String publicKey = "0419080a9bdb6968f0ef31b2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
          String privateKey = "2766001cc5d2888xxxxxxxxxxxxxxxxxxxxxxxxx";
          String text = "你哦哈1232154 3654 {} ,俺可接受不符点";
          String encrypt = encrypt(text, publicKey);
          System.out.println("encrypt = "+ encrypt);
          //String encrypt = "041fccce91d7a35a429f449aea758364826f688577bad9d3b9c99a1ab5c390fdf63f00232e0af4fcab8fccd70c46b636e42024f260973d73c8d1b6c3d41c2b26238b8a09c994e0a912ba2022b342af049b164979018af08e75cb63a66408fe9dfd553e7a350fb0d12405b984bde185ba38ca693c9c12b06dbe445abc5b9fc754f2424bab2a766d62c12b1832c51b2cab44ba4dc0049b5f3b479fe1cc348bcfd77f65db4267";
          String jm = decrypt(encrypt, privateKey);
          System.out.println(jm);

          }
          }

          JavaScript实现

          需要引入crypto-js.js和sm2.js,加老王微信,老王私下发你,文件太大,不方便放到文章上,关注公众号后,直接输入  “ 加好友” ,可以加老王微信精彩不断

          html使用demo

            <!DOCTYPE html>
            <html lang="en">

            <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>sm2加密</title>
            <script src="./lib/crypto-js.js"></script>
            <script src="./lib/sm2.js"></script>
            </head>

            <body>

            <script type="text/javascript">
            //私钥:2766001cc5d2888553efe566781d8fb25557aecd6435e731d21ad362af8a4eaf
            //公钥:前缀04+x坐标+y坐标
            var pubkeyHex = "04190xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
            var msg='你哦哈1232154 3654 {} ,俺可接受不符点';
            //加密格式0: C1C2C3、1: C1C3C2
            var encryptData = sm2Encrypt(msg, pubkeyHex, 0);
            document.write(encryptData);
            </script>
            </body>

            </html>

            Android端和java端一样,jar包采用 bcprov-jdk15to18-1.71.jar


            java端基于hutool实现,上面不是hutool,因为个人喜欢hutool,必须要基于hutool实现一个

            pom.xml

              <dependency>
              <groupId>cn.hutool</groupId>
              <artifactId>hutool-all</artifactId>
              <version>5.7.4</version>
              </dependency>
              <dependency>
              <groupId>org.bouncycastle</groupId>
              <artifactId>bcpkix-jdk15on</artifactId>
              <version>1.57</version>
              </dependency>
              SM2KeyPairs
                package com.sm.hutoolsm2;

                /**
                * 描述: 国密sm2算法秘钥对 - 对象 <br>
                * 时间: 2022-07-14 16:01 <br>
                * 作者:IT学习道场
                */

                public class SM2KeyPairs {
                /**
                *公钥
                */
                private String publicKey;

                /**
                * 私钥
                */
                private String privateKey;

                public SM2KeyPairs() {
                }

                public SM2KeyPairs(String publicKey, String privateKey) {
                this.publicKey = publicKey;
                this.privateKey = privateKey;
                }

                public String getPublicKey() {
                return publicKey;
                }

                public void setPublicKey(String publicKey) {
                this.publicKey = publicKey;
                }

                public String getPrivateKey() {
                return privateKey;
                }

                public void setPrivateKey(String privateKey) {
                this.privateKey = privateKey;
                }

                @Override
                public String toString() {
                return "SM2KeyPairs{" +
                "publicKey='" + publicKey + '\'' +
                ", privateKey='" + privateKey + '\'' +
                '}';
                }
                }
                SM2Util
                  package com.sm.hutoolsm2;

                  import cn.hutool.core.util.HexUtil;
                  import cn.hutool.crypto.BCUtil;
                  import cn.hutool.crypto.SmUtil;
                  import cn.hutool.crypto.asymmetric.KeyType;
                  import cn.hutool.crypto.asymmetric.SM2;
                  import com.sm.sm2.SM2KeyPairs;
                  import org.bouncycastle.crypto.engines.SM2Engine;
                  import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
                  import java.nio.charset.Charset;
                  import java.util.Base64;

                  /**
                  * 描述: todo <br>
                  * 时间: 2022-07-25 17:32 <br>
                  * 作者:IT学习道场
                  */
                  public class SM2Util {


                  /**
                  * 获取公钥私钥
                  * @return SM2KeyPairs 公私钥对象
                  */
                  public static SM2KeyPairs getKeyPairs(){
                  SM2 sm2 = SmUtil.sm2();
                  // sm2的加解密时有两种方式即 C1C2C3、 C1C3C2,
                  sm2.setMode(SM2Engine.Mode.C1C2C3);
                  // 生成私钥
                  String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey()));
                  // 生成公钥
                  String publicKey = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false));

                  SM2KeyPairs keyPairs = new SM2KeyPairs(publicKey, privateKey);
                  return keyPairs;
                  }
                  /**
                  * 公钥加密
                  * @param publicKey 公钥
                  * @param text 预加密文本
                  * @return 加密后文本
                  */
                  public static String encrypt(String publicKey, String text){
                  // 通过密钥解密
                  SM2 sm2 = SmUtil.sm2(null, publicKey);
                  sm2.setMode(SM2Engine.Mode.C1C2C3);
                  //1 先把明文转成base64
                  text = Base64.getEncoder().encodeToString(text.getBytes());
                  //2 把base64的文本用公钥加密后在转成密文为16进制,否则前端解密前需要先转换格式
                  String encryptStr = sm2.encryptHex(text, Charset.forName("utf-8"), KeyType.PublicKey);
                  return encryptStr;

                  }

                  /**
                  * 私钥解密
                  * @param privateKey 私钥
                  * @param text 加密文本
                  * @return 解密后文本
                  */
                  public static String decrypt(String privateKey, String text){
                  //创建sm2 对象
                  SM2 sm2 = SmUtil.sm2(privateKey, null);
                  sm2.setMode(SM2Engine.Mode.C1C2C3);
                  // 私钥解密
                  String decrypt = sm2.decryptStr(text, KeyType.PrivateKey);
                  byte[] decode = Base64.getDecoder().decode(decrypt);
                  String decryptStr = new String(decode);
                  return decryptStr;

                  }



                  public static void main(String[] args) {
                  //SM2KeyPairs keyPairs = getKeyPairs();
                  //System.out.println(keyPairs.toString());
                  String publicKey = "0407125a6dc1d73e41dc1b57xxxxxxxxxxxxxxxxxxxxxx";
                  String privateKey = "0f3c459c2090eb5c35108xxxxxxxxxxxxx";

                  String text = "你哦哈1232154 3654 {} ,俺可接受不符点";

                  String encrypt = encrypt(publicKey, text);
                  System.out.println("encrypt = " + encrypt);
                  //String encrypt ="04ddb0f3622c51af847bbd528d9fb8cd264aa9341c817cb3077bb1464766b3b7e3a5ff523004d4f2259676267a080f66c4682844adef2e4b612e604071af16c6285fc48795e4ae1277e7d79b4bf420584dea2345eae0d5f2e12293013d25af09eaff4fabf5109a18fed74d07ed30a34b1623f161f7a70e2746accf9334b96cfc33f2aa6aae13c08b131604a9caa7d2c5453b8a021a6354ba27cd7d3f4ebcf93376efa9cf5d";
                  String decrypt = decrypt(privateKey, encrypt);
                  System.out.println("decrypt = " + decrypt);

                  }
                  }

                  hutool实现和上面的Android可用的版本一样,当然,后端也可以直接使用Android和java通用的版本


                  ios端必须和js和java和Android端一致,采用 C1C2C3模式


                  下面是ios的实现。采用国密 国密的 Objective-C 封装


                  查看具体实现过程,请至开源项目地址GitHub - muzipiao/GMObjC: SM2/SM3/SM4/ECDH library based on OpenSSL.。


                  在终端运行以下命令:

                    git clone https://github.com/muzipiao/GMObjC.git

                    cd GMObjC

                    pod install

                    open GMObjC.xcworkspace

                    向项目中引入GMObjC即可

                    使用代码如下

                    SM2 加解密

                    SM2 加解密都很简单,加密传入待加密明文和公钥,解密传入密文和私钥即可,加密逻辑代码:

                      NSString *pubKey = @"0408E3FFF9505BCFAF9307E665E9229F4E1B3936437xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
                      NSString *priKey = @"90F3A42B9FE24AB196305FD92EC82E647616C3A369xxxxxxxxxx";
                      NSString *plaintext = @"你哦哈1232154 3654 {} ,俺可接受不符点";
                      //先对明文进行base64加密,再用GMSm2Utils 对其进行sm2加密,返回asn1编码格式的密文
                      NSString *encode = [GMSm2Utils encryptText:[self base64:plaintext] publicKey:pubKey];
                      //把asn1编码格式的密文的 encode 解码成C1C3C2的密文字符串 = c1c3c2
                      NSString *c1c3c2 = [GMSm2Utils asn1DecodeToC1C3C2:encode];
                      //再把c1c3c2这个字符串转成 C1C2C3 模式的密文字符串 = c1c2c3 ,这个可以直接传给java端,用上面的java端实现的sm2Util进行解密
                      NSString *c1c2c3 = [GMSm2Utils convertC1C3C2ToC1C2C3:c1c3c2 hasPrefix:NO];
                      NSLog(@"c1c2c3 : %@",c1c2c3);

                      注意这里的密文要拼上 "04" 这个前缀,后端才能进行解密

                      这是base64加密方法

                        - (NSString *)base64:(NSString *)string{
                        NSString *target = string;
                        NSData *data = [target dataUsingEncoding:NSUTF8StringEncoding];
                        NSString *base64Str = [data base64EncodedStringWithOptions:nil];
                        // NSString *base64DecodeStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                        return base64Str;
                        }

                        我们前后端的逻辑是 先对明文进行base64加密,再sm2加密,再转16进制。

                        解密的逻辑,是先对密文进行16进制解密,再sm2解密,再base64解密

                        你们具体可根据自己的情况来处理,但是最好搞清楚逻辑,不然很容易出错,或者直接按照我们的略记处理,直接copy,省事儿


                        文章转载自IT学习道场,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论