package com.ruoyi.common.utils.smUtil.sm2;
  import org.bouncycastle.asn1.gm.GMNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.jce.spec.ECPrivateKeySpec; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.encoders.Hex;
  import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.ECGenParameterSpec; import java.util.Base64;
 
  public class SM2Utils {
      /**      * 生成 SM2 公私钥对      *      * @return      * @throws NoSuchAlgorithmException      * @throws InvalidAlgorithmParameterException      */     public static KeyPair geneSM2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {         final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");         // 获取一个椭圆曲线类型的密钥对生成器         final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());         // 产生随机数         SecureRandom secureRandom = new SecureRandom();         // 使用SM2参数初始化生成器         kpg.initialize(sm2Spec, secureRandom);         // 获取密钥对         KeyPair keyPair = kpg.generateKeyPair();         return keyPair;     }
      /**      * 生产hex秘钥对      */     public static void geneSM2HexKeyPair(){         try {             KeyPair keyPair = geneSM2KeyPair();             PrivateKey privateKey = keyPair.getPrivate();             PublicKey publicKey = keyPair.getPublic();             System.out.println("========  EC X Y keyPair    ========");             System.out.println(privateKey.toString());             System.out.println(publicKey.toString());             System.out.println("========  hex keyPair       ========");             System.out.println("hex priKey: " + getPriKeyHexString(privateKey));             System.out.println("hex pubKey: " + getPubKeyHexString(publicKey));             System.out.println("========  base64 keyPair    ========");             System.out.println("base64 priKey: " + new String(Base64.getEncoder().encode(privateKey.getEncoded())));             System.out.println("base64 pubKey: " + new String(Base64.getEncoder().encode(publicKey.getEncoded())));             System.out.println("========  generate finished ========");         } catch (Exception e) {             e.printStackTrace();         }     }     /**      * 获取私钥(16进制字符串,头部不带00长度共64)      *      * @param privateKey 私钥PrivateKey型      * @return      */     public static String getPriKeyHexString(PrivateKey privateKey) {         // OK //	        BCECPrivateKey s=(BCECPrivateKey)privateKey; //	        String priKeyHexString = Hex.toHexString(s.getD().toByteArray()); //	        if(null!= priKeyHexString && priKeyHexString.length()==66 && "00".equals(priKeyHexString.substring(0,2))){ //	            return priKeyHexString.substring(2); //	        }         // OK         BCECPrivateKey key = (BCECPrivateKey) privateKey;         BigInteger intPrivateKey = key.getD();         String priKeyHexString = intPrivateKey.toString(16);         return priKeyHexString;     }     /**      * 获取私钥 base64字符串      *      * @param privateKey 私钥PrivateKey型      * @return      */     public static String getPriKeyBase64String(PrivateKey privateKey) {         return new String(Base64.getEncoder().encode(privateKey.getEncoded()));     }
      /**      * 获取公钥(16进制字符串,头部带04长度共130)      *      * @param publicKey 公钥PublicKey型      * @return      */     public static String getPubKeyHexString(PublicKey publicKey) {         BCECPublicKey key = (BCECPublicKey) publicKey;         return Hex.toHexString(key.getQ().getEncoded(false));     }     /**      * 获取公钥 base64字符串      *      * @param publicKey 公钥PublicKey型      * @return      */     public static String getPubKeyBase64String(PublicKey publicKey) {         return new String(Base64.getEncoder().encode(publicKey.getEncoded()));     }
      /**      * SM2加密算法      *      * @param publicKey 公钥      * @param data      明文数据      * @return      */     public static String encrypt(String data, PublicKey publicKey) {         return encrypt(data.getBytes(StandardCharsets.UTF_8), publicKey);     }
      /**      * @param data      * @param publicKey      * @return      * @author      * @version 1.0      * 2023年4月12日下午4:41:24      */     public static String encrypt(byte[] data, PublicKey publicKey) {         BCECPublicKey key = (BCECPublicKey) publicKey;         return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));     }
      /**      * @param data      * @param pubKeyHexString      * @return      * @author      * @version 1.0      * 2023年4月12日下午4:46:37      */     public static String encrypt(String data, String pubKeyHexString) {         return encrypt(data.getBytes(StandardCharsets.UTF_8), pubKeyHexString);     }
      /**      * SM2加密算法      *      * @param pubKeyHexString 公钥(16进制字符串)      * @param data            明文数据      * @return hex字符串      */     public static String encrypt(byte[] data, String pubKeyHexString) {         // 获取一条SM2曲线参数         X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");         // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N         ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());         //提取公钥点         ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));         // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04         ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
          SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);         // 设置sm2为加密模式         sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
          byte[] arrayOfBytes = null;         try {             arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);         } catch (Exception e) {             System.out.println("SM2加密时出现异常:" + e.getMessage());         }         String cipherData = Hex.toHexString(arrayOfBytes);         return cipherData;     }
      public static String encrypt2(String dataStr, String pubKeyHexString) {         byte[] data = dataStr.getBytes(StandardCharsets.UTF_8);         // 获取一条SM2曲线参数         X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");         // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N         ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());         //提取公钥点         ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));         // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04         ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
          SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);         // 设置sm2为加密模式         sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
          byte[] arrayOfBytes = null;         try {             arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);         } catch (Exception e) {             System.out.println("SM2加密时出现异常:" + e.getMessage());         }         String cipherData = Hex.toHexString(arrayOfBytes);         if (cipherData.startsWith("04")) {             cipherData = cipherData.substring(2);         }         return cipherData;     }
      /**      * SM2解密算法      * @param cipherData    hex格式密文      * @param privateKey    密钥PrivateKey型      * @return              明文      */     public static String decrypt(String cipherData, PrivateKey privateKey) {         return decrypt(Hex.decode(cipherData), privateKey);     }
      /**      * @param cipherData      * @param privateKey      * @return      * @author      * @version 1.0      * 2023年4月12日下午4:46:50      */     public static String decrypt(byte[] cipherData, PrivateKey privateKey) {         BCECPrivateKey key = (BCECPrivateKey) privateKey;         return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));     }
      /**      * @param cipherData      * @param priKeyHexString      * @return      * @author      * @version 1.0      * 2023年4月12日下午4:46:53      */     public static String decrypt(String cipherData, String priKeyHexString) {         // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上 //        if (!cipherData.startsWith("04")) { //            cipherData = "04" + cipherData; //        }         cipherData = "04" + cipherData;         return decrypt(Hex.decode(cipherData), priKeyHexString);     }
      /**      * SM2解密算法      *      * @param cipherData      密文数据      * @param priKeyHexString 私钥(16进制字符串)      * @return      */     public static String decrypt(byte[] cipherData, String priKeyHexString) {         //获取一条SM2曲线参数         X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");         //构造domain参数         ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
          BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);         ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
          SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);         // 设置sm2为解密模式         sm2Engine.init(false, privateKeyParameters);
          String result = "";         try {             byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);             return new String(arrayOfBytes);         } catch (Exception e) {             System.out.println("SM2解密时出现异常:" + e.getMessage());         }         return result;     }
      /**      * SM2解密算法 - 解密前端传的密文      * @param decryptData      密文数据      * @param priKeyHexString 私钥(16进制字符串)      * @return      */     public static String decrypt2(String decryptData, String priKeyHexString) {         // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上         if (!decryptData.startsWith("04")) {             decryptData = "04" + decryptData;         }         byte[] cipherData = Hex.decode(decryptData);         //获取一条SM2曲线参数         X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");         //构造domain参数         ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
          BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);         ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
          SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);         // 设置sm2为解密模式         sm2Engine.init(false, privateKeyParameters);
          String result = "";         try {             byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);             //processBlock得到Base64格式,记得解码,不然前端传过来的密文无法解密             arrayOfBytes = Base64.getDecoder().decode(arrayOfBytes);             return new String(arrayOfBytes);         } catch (Exception e) {             System.out.println("SM2解密时出现异常:" + e.getMessage());         }         return result;     }
      /**      * @param data      * @param priKeyHexString hex私钥,长度64      * @return hex格式签名值      * @throws Exception      */     public static String sign(String data, String priKeyHexString) throws Exception {         return sign(data.getBytes(StandardCharsets.UTF_8), priKeyHexString);     }
      /**      * 签名      * @param data              原始数据,字节数组      * @param priKeyHexString   hex私钥,64长度      * @return                  Hex字符串      * @throws Exception      */     public static String sign(byte[] data, String priKeyHexString) throws Exception {         String signValue = null;         SM2Signer signer = new SM2Signer();         X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");         //构造domain参数         ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());         CipherParameters param = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(priKeyHexString, 16), domainParameters));         signer.init(true, param);         signer.update(data, 0, data.length);         signValue = Hex.toHexString(signer.generateSignature());         return signValue;     }
      /**      * 验签      * @param data                  数据      * @param signValue             签名值(hex型)      * @param publicKeyHexString    hex130长度公钥      * @return      */     public static boolean verify(String data, String signValue, String publicKeyHexString) throws Exception {         return verify(data.getBytes(StandardCharsets.UTF_8), Hex.decode(signValue), publicKeyHexString);     }
      /**      * 验签      * @param data                  原始数据字节数组      * @param sign                  字节数组()      * @param pKHexString    hex130长度公钥      * @return                      true or false      * @throws Exception      */     public static boolean verify(byte[] data, byte[] sign, String pKHexString) throws Exception {         SM2Signer signer = new SM2Signer();         X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");         //构造domain参数         ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());         if (pKHexString.length() == 128) {             pKHexString = "04" + pKHexString;         }         ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pKHexString));         CipherParameters param = new ECPublicKeyParameters(ecPoint, domainParameters);         signer.init(false, param);         signer.update(data, 0, data.length);         return signer.verifySignature(sign);     }
      /**      * 私钥生成公钥      * @param priKeyHexString 私钥Hex格式,必须64位      * @return 公钥Hex格式,04开头,130位      * @throws Exception 例如:      */     public static String getPubKeyByPriKey(String priKeyHexString) throws Exception {         if (priKeyHexString == null || priKeyHexString.length() != 64) {             System.err.println("priKey 必须是Hex 64位格式,例如:11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a");             return "";         }         String pubKeyHexString = null;         X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");         //构造domain参数         BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
          ECParameterSpec ecParameterSpec = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());         ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(privateKeyD, ecParameterSpec);         PrivateKey privateKey = null;         privateKey = KeyFactory.getInstance("EC", new BouncyCastleProvider()).generatePrivate(ecPrivateKeySpec);
          // 临时解决办法         String pointString = privateKey.toString(); //	        System.out.println(pointString);         String pointString_X = pointString.substring(pointString.indexOf("X: ") + "X: ".length(), pointString.indexOf("Y: ")).trim();         String pointString_Y = pointString.substring(pointString.indexOf("Y: ") + "Y: ".length()).trim(); //	        System.out.println(pointString_X); //	        System.out.println(pointString_Y);
          pubKeyHexString = "04" + pointString_X + pointString_Y;         return pubKeyHexString;
      }   }
 
   |