GenerateUserSig.java 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package com.yihu.jw.utils;
  2. import org.json.JSONException;
  3. import org.json.JSONObject;
  4. import org.springframework.util.Base64Utils;
  5. import javax.crypto.Mac;
  6. import javax.crypto.spec.SecretKeySpec;
  7. import java.nio.charset.Charset;
  8. import java.util.Arrays;
  9. import java.util.zip.Deflater;
  10. /*
  11. * Module: GenerateUserSig
  12. *
  13. * Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
  14. * 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
  15. *
  16. * Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
  17. *
  18. * 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
  19. * 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
  20. * 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
  21. *
  22. * 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
  23. * 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
  24. *
  25. * Reference:https://cloud.tencent.com/document/product/269/32688#Server
  26. */
  27. public class GenerateUserSig {
  28. /**
  29. * 生成 tls 票据
  30. *
  31. * @param sdkappid 应用的 appid
  32. * @param userId 用户 id
  33. * @param expire 有效期,单位是秒
  34. * @param userbuf 默认填写null
  35. * @param priKeyContent 生成 tls 票据使用的私钥内容
  36. * @return 如果出错,会返回为空,或者有异常打印,成功返回有效的票据
  37. */
  38. public static String GenTLSSignature(long sdkappid, String userId, long expire, byte[] userbuf, String priKeyContent) {
  39. if (StringUtil.isEmpty(priKeyContent)) {
  40. return "";
  41. }
  42. long currTime = System.currentTimeMillis() / 1000;
  43. JSONObject sigDoc = new JSONObject();
  44. try {
  45. sigDoc.put("TLS.ver", "2.0");
  46. sigDoc.put("TLS.identifier", userId);
  47. sigDoc.put("TLS.sdkappid", sdkappid);
  48. sigDoc.put("TLS.expire", expire);
  49. sigDoc.put("TLS.time", currTime);
  50. } catch (JSONException e) {
  51. e.printStackTrace();
  52. }
  53. String base64UserBuf = null;
  54. if (null != userbuf) {
  55. base64UserBuf = Base64Utils.encodeToString(userbuf);
  56. try {
  57. sigDoc.put("TLS.userbuf", base64UserBuf);
  58. } catch (JSONException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. String sig = hmacsha256(sdkappid, userId, currTime, expire, priKeyContent, base64UserBuf);
  63. if (sig.length() == 0) {
  64. return "";
  65. }
  66. try {
  67. sigDoc.put("TLS.sig", sig);
  68. } catch (JSONException e) {
  69. e.printStackTrace();
  70. }
  71. Deflater compressor = new Deflater();
  72. compressor.setInput(sigDoc.toString().getBytes(Charset.forName("UTF-8")));
  73. compressor.finish();
  74. byte[] compressedBytes = new byte[2048];
  75. int compressedBytesLength = compressor.deflate(compressedBytes);
  76. compressor.end();
  77. return new String(base64EncodeUrl(Arrays.copyOfRange(compressedBytes, 0, compressedBytesLength)));
  78. }
  79. private static String hmacsha256(long sdkappid, String userId, long currTime, long expire, String priKeyContent, String base64Userbuf) {
  80. String contentToBeSigned = "TLS.identifier:" + userId + "\n"
  81. + "TLS.sdkappid:" + sdkappid + "\n"
  82. + "TLS.time:" + currTime + "\n"
  83. + "TLS.expire:" + expire + "\n";
  84. if (null != base64Userbuf) {
  85. contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";
  86. }
  87. try {
  88. byte[] byteKey = priKeyContent.getBytes("UTF-8");
  89. Mac hmac = Mac.getInstance("HmacSHA256");
  90. SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");
  91. hmac.init(keySpec);
  92. byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes("UTF-8"));
  93. return new String(Base64Utils.encodeToString(byteSig));
  94. } catch (Exception e) {
  95. e.printStackTrace();
  96. return "";
  97. }
  98. }
  99. private static byte[] base64EncodeUrl(byte[] input) {
  100. byte[] base64 = new String(Base64Utils.encodeToString(input)).getBytes();
  101. for (int i = 0; i < base64.length; ++i)
  102. switch (base64[i]) {
  103. case '+':
  104. base64[i] = '*';
  105. break;
  106. case '/':
  107. base64[i] = '-';
  108. break;
  109. case '=':
  110. base64[i] = '_';
  111. break;
  112. default:
  113. break;
  114. }
  115. return base64;
  116. }
  117. }