SM2.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. package com.ydtech.utils;
  2. import org.bouncycastle.crypto.params.ECDomainParameters;
  3. import org.bouncycastle.math.ec.ECCurve;
  4. import org.bouncycastle.math.ec.ECPoint;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import java.io.ByteArrayOutputStream;
  8. import java.io.IOException;
  9. import java.io.UnsupportedEncodingException;
  10. import java.math.BigInteger;
  11. import java.security.SecureRandom;
  12. import java.util.Arrays;
  13. public class SM2 {
  14. private final static Logger LOGGER = LoggerFactory.getLogger(SM2.class);
  15. public static String sm2Encrypt(String plainText, String pubKey) {
  16. byte[] data = SM2.encrypt(plainText, pubKey);
  17. String enData = Base64.encode(data);
  18. return enData;
  19. }
  20. public static String sm2Decrypt(String plainText, String priKey) {
  21. byte[] encryptData = Base64.decode(plainText);
  22. String rawData = SM2.decrypt(encryptData, priKey);
  23. return rawData;
  24. }
  25. //国密办文件中推荐的椭圆曲线相关参数
  26. private static BigInteger n = new BigInteger(
  27. "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
  28. private static BigInteger p = new BigInteger(
  29. "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
  30. private static BigInteger a = new BigInteger(
  31. "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
  32. private static BigInteger b = new BigInteger(
  33. "28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
  34. private static BigInteger gx = new BigInteger(
  35. "32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
  36. private static BigInteger gy = new BigInteger(
  37. "BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);
  38. private static final int DIGEST_LENGTH = 32;
  39. private static SecureRandom random = new SecureRandom();
  40. private static ECCurve.Fp curve = new ECCurve.Fp(p, a, b);
  41. private static ECPoint G = curve.createPoint(gx, gy);
  42. private static ECDomainParameters ecc_bc_spec = new ECDomainParameters(curve, G, n);
  43. /**
  44. * 以16进制打印字节数组
  45. *
  46. * @param b
  47. */
  48. public static void printHexString(byte[] b) {
  49. for (int i = 0; i < b.length; i++) {
  50. String hex = Integer.toHexString(b[i] & 0xFF);
  51. if (hex.length() == 1) {
  52. hex = '0' + hex;
  53. }
  54. System.out.print(hex.toUpperCase());
  55. }
  56. System.out.println();
  57. }
  58. /**
  59. * 随机数生成器
  60. *
  61. * @param max
  62. * @return
  63. */
  64. private static BigInteger random(BigInteger max) {
  65. BigInteger r = new BigInteger(256, random);
  66. while (r.compareTo(max) >= 0) {
  67. r = new BigInteger(128, random);
  68. }
  69. return r;
  70. }
  71. /**
  72. * 判断字节数组是否全0
  73. *
  74. * @param buffer
  75. * @return
  76. */
  77. private static boolean allZero(byte[] buffer) {
  78. for (int i = 0; i < buffer.length; i++) {
  79. if (buffer[i] != 0) {
  80. return false;
  81. }
  82. }
  83. return true;
  84. }
  85. /**
  86. * 公钥加密
  87. *
  88. * @param input 加密原文
  89. * @param pubKeyStr 公钥
  90. * @return
  91. */
  92. public static byte[] encrypt(String input, String pubKeyStr) {
  93. ECPoint publicKey = curve.decodePoint(hexStr2Bytes(pubKeyStr));
  94. byte[] inputBuffer = new byte[0];
  95. try {
  96. inputBuffer = input.getBytes("UTF8");
  97. } catch (UnsupportedEncodingException e) {
  98. e.printStackTrace();
  99. }
  100. byte[] C1Buffer;
  101. ECPoint kpb;
  102. byte[] t;
  103. do {
  104. /* 1 产生随机数k,k属于[1, n-1] */
  105. BigInteger k = random(n);
  106. /* 2 计算椭圆曲线点C1 = [k]G = (x1, y1) */
  107. ECPoint C1 = G.multiply(k);
  108. C1Buffer = C1.getEncoded(false);
  109. /*
  110. * 3 计算椭圆曲线点 S = [h]Pb
  111. */
  112. BigInteger h = ecc_bc_spec.getH();
  113. if (h != null) {
  114. ECPoint S = publicKey.multiply(h);
  115. if (S.isInfinity()) {
  116. throw new IllegalStateException();
  117. }
  118. }
  119. /* 4 计算 [k]PB = (x2, y2) */
  120. kpb = publicKey.multiply(k).normalize();
  121. /* 5 计算 t = KDF(x2||y2, klen) */
  122. byte[] kpbBytes = kpb.getEncoded(false);
  123. t = KDF(kpbBytes, inputBuffer.length);
  124. } while (allZero(t));
  125. /* 6 计算C2=M^t */
  126. byte[] C2 = new byte[inputBuffer.length];
  127. for (int i = 0; i < inputBuffer.length; i++) {
  128. C2[i] = (byte) (inputBuffer[i] ^ t[i]);
  129. }
  130. /* 7 计算C3 = Hash(x2 || M || y2) */
  131. byte[] C3 = sm3hash(kpb.getXCoord().toBigInteger().toByteArray(), inputBuffer,
  132. kpb.getYCoord().toBigInteger().toByteArray());
  133. /* 8 输出密文 C=C1 || C2 || C3 */
  134. byte[] encryptResult = new byte[C1Buffer.length + C2.length + C3.length];
  135. System.arraycopy(C1Buffer, 0, encryptResult, 0, C1Buffer.length);
  136. System.arraycopy(C2, 0, encryptResult, C1Buffer.length, C2.length);
  137. System.arraycopy(C3, 0, encryptResult, C1Buffer.length + C2.length, C3.length);
  138. return encryptResult;
  139. }
  140. /**
  141. * 私钥解密
  142. *
  143. * @param encryptData 密文数据字节数组
  144. * @param priKeyStr 解密私钥
  145. * @return
  146. */
  147. public static String decrypt(byte[] encryptData, String priKeyStr) {
  148. BigInteger privateKey = new BigInteger(priKeyStr, 16);
  149. byte[] C1Byte = new byte[65];
  150. System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length);
  151. ECPoint C1 = curve.decodePoint(C1Byte).normalize();
  152. /*
  153. * 计算椭圆曲线点 S = [h]C1 是否为无穷点
  154. */
  155. BigInteger h = ecc_bc_spec.getH();
  156. if (h != null) {
  157. ECPoint S = C1.multiply(h);
  158. if (S.isInfinity()) {
  159. throw new IllegalStateException();
  160. }
  161. }
  162. /* 计算[dB]C1 = (x2, y2) */
  163. ECPoint dBC1 = C1.multiply(privateKey).normalize();
  164. /* 计算t = KDF(x2 || y2, klen) */
  165. byte[] dBC1Bytes = dBC1.getEncoded(false);
  166. int klen = encryptData.length - 65 - DIGEST_LENGTH;
  167. byte[] t = KDF(dBC1Bytes, klen);
  168. if (allZero(t)) {
  169. System.err.println("all zero");
  170. throw new IllegalStateException();
  171. }
  172. /* 计算M'=C2^t */
  173. byte[] M = new byte[klen];
  174. for (int i = 0; i < M.length; i++) {
  175. M[i] = (byte) (encryptData[C1Byte.length + i] ^ t[i]);
  176. }
  177. /* 计算 u = Hash(x2 || M' || y2) 判断 u == C3是否成立 */
  178. byte[] C3 = new byte[DIGEST_LENGTH];
  179. System.arraycopy(encryptData, encryptData.length - DIGEST_LENGTH, C3, 0, DIGEST_LENGTH);
  180. byte[] u = sm3hash(dBC1.getXCoord().toBigInteger().toByteArray(), M,
  181. dBC1.getYCoord().toBigInteger().toByteArray());
  182. if (Arrays.equals(u, C3)) {
  183. try {
  184. return new String(M, "UTF8");
  185. } catch (UnsupportedEncodingException e) {
  186. e.printStackTrace();
  187. }
  188. return null;
  189. } else {
  190. return null;
  191. }
  192. }
  193. /**
  194. * 判断是否在范围内
  195. *
  196. * @param param
  197. * @param min
  198. * @param max
  199. * @return
  200. */
  201. private static boolean between(BigInteger param, BigInteger min, BigInteger max) {
  202. if (param.compareTo(min) >= 0 && param.compareTo(max) < 0) {
  203. return true;
  204. } else {
  205. return false;
  206. }
  207. }
  208. /**
  209. * 判断生成的公钥是否合法
  210. *
  211. * @param publicKey
  212. * @return
  213. */
  214. private static boolean checkPublicKey(ECPoint publicKey) {
  215. if (!publicKey.isInfinity()) {
  216. BigInteger x = publicKey.getXCoord().toBigInteger();
  217. BigInteger y = publicKey.getYCoord().toBigInteger();
  218. if (between(x, new BigInteger("0"), p) && between(y, new BigInteger("0"), p)) {
  219. BigInteger xResult = x.pow(3).add(a.multiply(x)).add(b).mod(p);
  220. BigInteger yResult = y.pow(2).mod(p);
  221. if (yResult.equals(xResult) && publicKey.multiply(n).isInfinity()) {
  222. return true;
  223. }
  224. }
  225. }
  226. return false;
  227. }
  228. /**
  229. * 字节数组拼接
  230. *
  231. * @param params
  232. * @return
  233. */
  234. private static byte[] join(byte[]... params) {
  235. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  236. byte[] res = null;
  237. try {
  238. for (int i = 0; i < params.length; i++) {
  239. baos.write(params[i]);
  240. }
  241. res = baos.toByteArray();
  242. } catch (IOException e) {
  243. e.printStackTrace();
  244. }
  245. return res;
  246. }
  247. /**
  248. * sm3摘要
  249. * @param params
  250. * @return
  251. */
  252. private static byte[] sm3hash(byte[]... params) {
  253. byte[] res = null;
  254. try {
  255. res = SM3.hash(join(params));
  256. } catch (IOException e) {
  257. e.printStackTrace();
  258. }
  259. return res;
  260. }
  261. /**
  262. * 密钥派生函数
  263. * @param Z
  264. * @param klen 生成klen字节数长度的密钥
  265. * @return
  266. */
  267. private static byte[] KDF(byte[] Z, int klen) {
  268. int ct = 1;
  269. int end = (int) Math.ceil(klen * 1.0 / 32);
  270. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  271. try {
  272. for (int i = 1; i < end; i++) {
  273. baos.write(sm3hash(Z, SM3.toByteArray(ct)));
  274. ct++;
  275. }
  276. byte[] last = sm3hash(Z, SM3.toByteArray(ct));
  277. if (klen % 32 == 0) {
  278. baos.write(last);
  279. } else {
  280. baos.write(last, 0, klen % 32);
  281. }
  282. return baos.toByteArray();
  283. } catch (Exception e) {
  284. e.printStackTrace();
  285. }
  286. return null;
  287. }
  288. public static byte[] hexStr2Bytes(String src) {
  289. int l = src.length() / 2;
  290. byte[] ret = new byte[l];
  291. for (int i = 0; i < l; ++i) {
  292. int m = i * 2 + 1;
  293. int n = m + 1;
  294. ret[i] = uniteBytes(src.substring(i * 2, m), src.substring(m, n));
  295. }
  296. return ret;
  297. }
  298. private static byte uniteBytes(String src0, String src1) {
  299. byte b0 = Byte.decode("0x" + src0);
  300. b0 = (byte) (b0 << 4);
  301. byte b1 = Byte.decode("0x" + src1);
  302. byte ret = (byte) (b0 | b1);
  303. return ret;
  304. }
  305. }