Sm2Util.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. package com.doc.common.utils.encrypt;
  2. import org.bouncycastle.asn1.gm.GMNamedCurves;
  3. import org.bouncycastle.asn1.x9.X9ECParameters;
  4. import org.bouncycastle.crypto.CipherParameters;
  5. import org.bouncycastle.crypto.engines.SM2Engine;
  6. import org.bouncycastle.crypto.params.ECDomainParameters;
  7. import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
  8. import org.bouncycastle.crypto.params.ECPublicKeyParameters;
  9. import org.bouncycastle.crypto.params.ParametersWithRandom;
  10. import org.bouncycastle.crypto.signers.SM2Signer;
  11. import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
  12. import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
  13. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  14. import org.bouncycastle.jce.spec.ECParameterSpec;
  15. import org.bouncycastle.jce.spec.ECPrivateKeySpec;
  16. import org.bouncycastle.math.ec.ECPoint;
  17. import org.bouncycastle.util.encoders.Hex;
  18. import java.math.BigInteger;
  19. import java.nio.charset.StandardCharsets;
  20. import java.security.*;
  21. import java.security.spec.ECGenParameterSpec;
  22. import java.util.Base64;
  23. /**
  24. * SM2工具类
  25. *
  26. * @author van
  27. */
  28. public class Sm2Util {
  29. /**
  30. * 私有key
  31. */
  32. private static final String PRIVATE_KEY = "ebaedbccd3f730ec1985b945f10429b468c7f3f70e66da19b8657f023e6cded4";
  33. /**
  34. * 公有key
  35. */
  36. private static final String PUBLIC_KEY = "04404c634de4deda80486b4a331adf03448a449d980fe040540abe242ba275cc815aada6a63c63e24d4672e14360c72b1819914f49708f25498ededc2217384960";
  37. /**
  38. * 生成 SM2 公私钥对
  39. *
  40. * @return
  41. * @throws NoSuchAlgorithmException
  42. * @throws InvalidAlgorithmParameterException
  43. */
  44. public static KeyPair geneSM2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
  45. final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
  46. // 获取一个椭圆曲线类型的密钥对生成器
  47. final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
  48. // 产生随机数
  49. SecureRandom secureRandom = new SecureRandom();
  50. // 使用SM2参数初始化生成器
  51. kpg.initialize(sm2Spec, secureRandom);
  52. // 获取密钥对
  53. KeyPair keyPair = kpg.generateKeyPair();
  54. return keyPair;
  55. }
  56. /**
  57. * 生产hex秘钥对
  58. */
  59. public static void geneSM2HexKeyPair() {
  60. try {
  61. KeyPair keyPair = geneSM2KeyPair();
  62. PrivateKey privateKey = keyPair.getPrivate();
  63. PublicKey publicKey = keyPair.getPublic();
  64. System.out.println("======== EC X Y keyPair ========");
  65. System.out.println(privateKey);
  66. System.out.println(publicKey);
  67. System.out.println("======== hex keyPair ========");
  68. System.out.println("hex priKey: " + getPriKeyHexString(privateKey));
  69. System.out.println("hex pubKey: " + getPubKeyHexString(publicKey));
  70. System.out.println("======== base64 keyPair ========");
  71. System.out.println("base64 priKey: " + new String(Base64.getEncoder().encode(privateKey.getEncoded())));
  72. System.out.println("base64 pubKey: " + new String(Base64.getEncoder().encode(publicKey.getEncoded())));
  73. System.out.println("======== generate finished ========");
  74. } catch (Exception e) {
  75. e.printStackTrace();
  76. }
  77. }
  78. /**
  79. * 获取私钥(16进制字符串,头部不带00长度共64)
  80. *
  81. * @param privateKey 私钥PrivateKey型
  82. * @return
  83. */
  84. public static String getPriKeyHexString(PrivateKey privateKey) {
  85. BCECPrivateKey key = (BCECPrivateKey) privateKey;
  86. BigInteger intPrivateKey = key.getD();
  87. String priKeyHexString = intPrivateKey.toString(16);
  88. return priKeyHexString;
  89. }
  90. /**
  91. * 获取私钥 base64字符串
  92. *
  93. * @param privateKey 私钥PrivateKey型
  94. * @return
  95. */
  96. public static String getPriKeyBase64String(PrivateKey privateKey) {
  97. return new String(Base64.getEncoder().encode(privateKey.getEncoded()));
  98. }
  99. /**
  100. * 获取公钥(16进制字符串,头部带04长度共130)
  101. *
  102. * @param publicKey 公钥PublicKey型
  103. * @return
  104. */
  105. public static String getPubKeyHexString(PublicKey publicKey) {
  106. BCECPublicKey key = (BCECPublicKey) publicKey;
  107. return Hex.toHexString(key.getQ().getEncoded(false));
  108. }
  109. /**
  110. * 获取公钥 base64字符串
  111. *
  112. * @param publicKey 公钥PublicKey型
  113. * @return
  114. */
  115. public static String getPubKeyBase64String(PublicKey publicKey) {
  116. return new String(Base64.getEncoder().encode(publicKey.getEncoded()));
  117. }
  118. /**
  119. * SM2加密算法
  120. *
  121. * @param publicKey 公钥
  122. * @param data 明文数据
  123. * @return
  124. */
  125. public static String encrypt(String data, PublicKey publicKey) {
  126. return encrypt(data.getBytes(StandardCharsets.UTF_8), publicKey);
  127. }
  128. /**
  129. * SM2加密算法
  130. *
  131. * @param data 明文数据
  132. * @return
  133. */
  134. public static String encrypt(String data) {
  135. return encrypt(data.getBytes(StandardCharsets.UTF_8), PUBLIC_KEY);
  136. }
  137. public static String encrypt(byte[] data, PublicKey publicKey) {
  138. BCECPublicKey key = (BCECPublicKey) publicKey;
  139. return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));
  140. }
  141. public static String encrypt(String data, String pubKeyHexString) {
  142. return encrypt(data.getBytes(StandardCharsets.UTF_8), pubKeyHexString);
  143. }
  144. /**
  145. * SM2加密算法
  146. *
  147. * @param pubKeyHexString 公钥(16进制字符串)
  148. * @param data 明文数据
  149. * @return hex字符串
  150. */
  151. public static String encrypt(byte[] data, String pubKeyHexString) {
  152. // 获取一条SM2曲线参数
  153. X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
  154. // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
  155. ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
  156. //提取公钥点
  157. ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));
  158. // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
  159. ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
  160. SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
  161. // 设置sm2为加密模式
  162. sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
  163. byte[] arrayOfBytes = null;
  164. try {
  165. arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);
  166. } catch (Exception e) {
  167. System.out.println("SM2加密时出现异常:" + e.getMessage());
  168. }
  169. return Hex.toHexString(arrayOfBytes);
  170. }
  171. /**
  172. * SM2解密算法
  173. *
  174. * @param cipherData hex格式密文
  175. * @return 明文
  176. */
  177. public static String decrypt(String cipherData) {
  178. return decrypt(Hex.decode(cipherData), PRIVATE_KEY);
  179. }
  180. /**
  181. * SM2解密算法
  182. *
  183. * @param cipherData hex格式密文
  184. * @param privateKey 密钥PrivateKey型
  185. * @return 明文
  186. */
  187. public static String decrypt(String cipherData, PrivateKey privateKey) {
  188. return decrypt(Hex.decode(cipherData), privateKey);
  189. }
  190. public static String decrypt(byte[] cipherData, PrivateKey privateKey) {
  191. BCECPrivateKey key = (BCECPrivateKey) privateKey;
  192. return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));
  193. }
  194. public static String decrypt(String cipherData, String priKeyHexString) {
  195. // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
  196. if (!cipherData.startsWith("04")) {
  197. cipherData = "04" + cipherData;
  198. }
  199. return decrypt(Hex.decode(cipherData), priKeyHexString);
  200. }
  201. /**
  202. * SM2解密算法
  203. *
  204. * @param cipherData 密文数据
  205. * @param priKeyHexString 私钥(16进制字符串)
  206. * @return
  207. */
  208. public static String decrypt(byte[] cipherData, String priKeyHexString) {
  209. //获取一条SM2曲线参数
  210. X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
  211. //构造domain参数
  212. ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
  213. BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
  214. ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
  215. SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
  216. // 设置sm2为解密模式
  217. sm2Engine.init(false, privateKeyParameters);
  218. String result = "";
  219. try {
  220. byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);
  221. return new String(arrayOfBytes);
  222. } catch (Exception e) {
  223. System.out.println("SM2解密时出现异常:" + e.getMessage());
  224. }
  225. return result;
  226. }
  227. /**
  228. * @param data
  229. * @param priKeyHexString hex私钥,长度64
  230. * @return hex格式签名值
  231. * @throws Exception
  232. */
  233. public static String sign(String data, String priKeyHexString) throws Exception {
  234. return sign(data.getBytes(StandardCharsets.UTF_8), priKeyHexString);
  235. }
  236. /**
  237. * 签名
  238. *
  239. * @param data 原始数据,字节数组
  240. * @param priKeyHexString hex私钥,64长度
  241. * @return Hex字符串
  242. * @throws Exception
  243. */
  244. public static String sign(byte[] data, String priKeyHexString) throws Exception {
  245. String signValue = null;
  246. SM2Signer signer = new SM2Signer();
  247. X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
  248. //构造domain参数
  249. ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
  250. CipherParameters param = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(priKeyHexString, 16), domainParameters));
  251. signer.init(true, param);
  252. signer.update(data, 0, data.length);
  253. signValue = Hex.toHexString(signer.generateSignature());
  254. return signValue;
  255. }
  256. /**
  257. * 验签
  258. *
  259. * @param data 原始数据
  260. * @param signValue 原始签名值(hex型)
  261. * @param publicKeyHexString hex130长度公钥
  262. * @return ture or false
  263. * @throws Exception
  264. */
  265. public static boolean verify(String data, String signValue, String publicKeyHexString) throws Exception {
  266. return verify(data.getBytes(StandardCharsets.UTF_8), Hex.decode(signValue), publicKeyHexString);
  267. }
  268. /**
  269. * 验签
  270. *
  271. * @param data 原始数据字节数组
  272. * @param sign 字节数组()
  273. * @param publicKeyHexString hex130长度公钥
  274. * @return true or false
  275. * @throws Exception
  276. */
  277. public static boolean verify(byte[] data, byte[] sign, String publicKeyHexString) throws Exception {
  278. SM2Signer signer = new SM2Signer();
  279. X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
  280. //构造domain参数
  281. ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
  282. if (publicKeyHexString.length() == 128) {
  283. publicKeyHexString = "04" + publicKeyHexString;
  284. }
  285. ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKeyHexString));
  286. CipherParameters param = new ECPublicKeyParameters(ecPoint, domainParameters);
  287. signer.init(false, param);
  288. signer.update(data, 0, data.length);
  289. return signer.verifySignature(sign);
  290. }
  291. /**
  292. * 私钥生成公钥
  293. *
  294. * @param priKeyHexString 私钥Hex格式,必须64位
  295. * @return 公钥Hex格式,04开头,130位
  296. * @throws Exception 例如:
  297. * 04181db7fe400641115c0dec08e23d8ddb94c5999f2fb6efd03030780142e077a63eb4d47947ef5baee7f40fec2c29181d2a714d9c6cba87b582f252a4e3e9a9f8
  298. * 11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a
  299. */
  300. public static String getPubKeyByPriKey(String priKeyHexString) throws Exception {
  301. if (priKeyHexString == null || priKeyHexString.length() != 64) {
  302. System.err.println("priKey 必须是Hex 64位格式,例如:11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a");
  303. return "";
  304. }
  305. String pubKeyHexString = null;
  306. X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
  307. //构造domain参数
  308. BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
  309. ECParameterSpec ecParameterSpec = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
  310. ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(privateKeyD, ecParameterSpec);
  311. PrivateKey privateKey = null;
  312. privateKey = KeyFactory.getInstance("EC", new BouncyCastleProvider()).generatePrivate(ecPrivateKeySpec);
  313. // 临时解决办法
  314. String pointString = privateKey.toString();
  315. // System.out.println(pointString);
  316. String pointString_X = pointString.substring(pointString.indexOf("X: ") + "X: ".length(), pointString.indexOf("Y: ")).trim();
  317. String pointString_Y = pointString.substring(pointString.indexOf("Y: ") + "Y: ".length()).trim();
  318. // System.out.println(pointString_X);
  319. // System.out.println(pointString_Y);
  320. pubKeyHexString = "04" + pointString_X + pointString_Y;
  321. return pubKeyHexString;
  322. }
  323. public static void main(String[] args) throws Exception {
  324. String p = "04195ac8a9295ac7224cba6fc2820b700b78985ecc0ce196ae2ebb6543a0b500937536b61c5334689a6dddac9009f535df2cf91e90189ebe36f6d0dbeb0860d3e6df53ebba1e8311a298485daeda73358b7dd7c9e188a2f9f706254504cfdf2096";
  325. System.err.println(Sm2Util.decrypt(p));
  326. System.out.println("====== sm2utils test ======");
  327. String M = "123456";
  328. System.out.println("明文:\t" + M);
  329. System.out.println("begin 开始生成密钥对>>>");
  330. KeyPair keyPair = geneSM2KeyPair();
  331. PublicKey publicKey = keyPair.getPublic();
  332. String pubKeyHexString = getPubKeyHexString(publicKey);
  333. System.out.println("publicKey\t" + pubKeyHexString);
  334. PrivateKey privateKey = keyPair.getPrivate();
  335. String priKeyHexString = getPriKeyHexString(privateKey);
  336. System.out.println("privateKey\t" + priKeyHexString);
  337. System.out.println("end 结束生成密钥对>>>");
  338. priKeyHexString = "ebaedbccd3f730ec1985b945f10429b468c7f3f70e66da19b8657f023e6cded4"; //1
  339. pubKeyHexString = "04404c634de4deda80486b4a331adf03448a449d980fe040540abe242ba275cc815aada6a63c63e24d4672e14360c72b1819914f49708f25498ededc2217384960"; //1
  340. String cipherData = encrypt(M, pubKeyHexString);
  341. System.out.println("加密:\t" + cipherData);
  342. String text = decrypt(cipherData, priKeyHexString);
  343. System.out.println("解密:\t" + text);
  344. text = decrypt("04294f7750ff7ed05e620923b23a9fe9477146983d981f9de135909f66265ec440b110b7ec46568148a011358050671e52af6a69585fb94d44f47d2176827151a7ae1ac1a7f5263dcbabb3eb45b5898901703a533ead697c49e222ca8717c97e6f3269727924fe", priKeyHexString);
  345. System.out.println("解密:\t" + text);
  346. String sign = sign(M, priKeyHexString);
  347. System.out.println("signvalue\t" + sign);
  348. sign = "3045022100d69b69421985160ac2a116a006525b0a414090d8d4b815dd98f97c74c1de68bd02201bfbddc24b3ee159faf13948f1ba1ef3368a5d019ea4137d2f69d4d5aaf2a05a";
  349. boolean verifyResult = verify(M, sign, pubKeyHexString);
  350. System.out.println("verifyResult\t" + verifyResult);
  351. }
  352. }