DigestServerAuthenticationHelper.java 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * Conditions Of Use
  3. *
  4. * This software was developed by employees of the National Institute of
  5. * Standards and Technology (NIST), an agency of the Federal Government.
  6. * Pursuant to title 15 Untied States Code Section 105, works of NIST
  7. * employees are not subject to copyright protection in the United States
  8. * and are considered to be in the public domain. As a result, a formal
  9. * license is not needed to use the software.
  10. *
  11. * This software is provided by NIST as a service and is expressly
  12. * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
  13. * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
  14. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
  15. * AND DATA ACCURACY. NIST does not warrant or make any representations
  16. * regarding the use of the software or the results thereof, including but
  17. * not limited to the correctness, accuracy, reliability or usefulness of
  18. * the software.
  19. *
  20. * Permission to use this software is contingent upon your acceptance
  21. * of the terms of this agreement
  22. *
  23. * .
  24. *
  25. */
  26. package com.ruoyi.wvp.gb28181.auth;
  27. import gov.nist.core.InternalErrorHandler;
  28. import lombok.extern.slf4j.Slf4j;
  29. import javax.sip.address.URI;
  30. import javax.sip.header.AuthorizationHeader;
  31. import javax.sip.header.HeaderFactory;
  32. import javax.sip.header.WWWAuthenticateHeader;
  33. import javax.sip.message.Request;
  34. import javax.sip.message.Response;
  35. import java.security.MessageDigest;
  36. import java.security.NoSuchAlgorithmException;
  37. import java.time.Instant;
  38. import java.util.Random;
  39. /**
  40. * Implements the HTTP digest authentication method server side functionality.
  41. *
  42. * @author M. Ranganathan
  43. * @author Marc Bednarek
  44. */
  45. @Slf4j
  46. public class DigestServerAuthenticationHelper {
  47. private MessageDigest messageDigest;
  48. public static final String DEFAULT_ALGORITHM = "MD5";
  49. public static final String DEFAULT_SCHEME = "Digest";
  50. /** to hex converter */
  51. private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
  52. '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
  53. /**
  54. * Default constructor.
  55. * @throws NoSuchAlgorithmException
  56. */
  57. public DigestServerAuthenticationHelper()
  58. throws NoSuchAlgorithmException {
  59. messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
  60. }
  61. public static String toHexString(byte b[]) {
  62. int pos = 0;
  63. char[] c = new char[b.length * 2];
  64. for (int i = 0; i < b.length; i++) {
  65. c[pos++] = toHex[(b[i] >> 4) & 0x0F];
  66. c[pos++] = toHex[b[i] & 0x0f];
  67. }
  68. return new String(c);
  69. }
  70. /**
  71. * Generate the challenge string.
  72. *
  73. * @return a generated nonce.
  74. */
  75. private String generateNonce() {
  76. long time = Instant.now().toEpochMilli();
  77. Random rand = new Random();
  78. long pad = rand.nextLong();
  79. String nonceString = Long.valueOf(time).toString()
  80. + Long.valueOf(pad).toString();
  81. byte mdbytes[] = messageDigest.digest(nonceString.getBytes());
  82. return toHexString(mdbytes);
  83. }
  84. public Response generateChallenge(HeaderFactory headerFactory, Response response, String realm) {
  85. try {
  86. WWWAuthenticateHeader proxyAuthenticate = headerFactory
  87. .createWWWAuthenticateHeader(DEFAULT_SCHEME);
  88. proxyAuthenticate.setParameter("realm", realm);
  89. proxyAuthenticate.setParameter("qop", "auth");
  90. proxyAuthenticate.setParameter("nonce", generateNonce());
  91. proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM);
  92. response.setHeader(proxyAuthenticate);
  93. } catch (Exception ex) {
  94. InternalErrorHandler.handleException(ex);
  95. }
  96. return response;
  97. }
  98. /**
  99. * Authenticate the inbound request.
  100. *
  101. * @param request - the request to authenticate.
  102. * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
  103. *
  104. * @return true if authentication succeded and false otherwise.
  105. */
  106. public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
  107. AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
  108. if ( authHeader == null ) {
  109. return false;
  110. }
  111. String realm = authHeader.getRealm();
  112. String username = authHeader.getUsername();
  113. if ( username == null || realm == null ) {
  114. return false;
  115. }
  116. String nonce = authHeader.getNonce();
  117. URI uri = authHeader.getURI();
  118. if (uri == null) {
  119. return false;
  120. }
  121. String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
  122. String HA1 = hashedPassword;
  123. byte[] mdbytes = messageDigest.digest(A2.getBytes());
  124. String HA2 = toHexString(mdbytes);
  125. String cnonce = authHeader.getCNonce();
  126. String KD = HA1 + ":" + nonce;
  127. if (cnonce != null) {
  128. KD += ":" + cnonce;
  129. }
  130. KD += ":" + HA2;
  131. mdbytes = messageDigest.digest(KD.getBytes());
  132. String mdString = toHexString(mdbytes);
  133. String response = authHeader.getResponse();
  134. return mdString.equals(response);
  135. }
  136. /**
  137. * Authenticate the inbound request given plain text password.
  138. *
  139. * @param request - the request to authenticate.
  140. * @param pass -- the plain text password.
  141. *
  142. * @return true if authentication succeded and false otherwise.
  143. */
  144. public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
  145. AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
  146. if ( authHeader == null || authHeader.getRealm() == null) {
  147. return false;
  148. }
  149. String realm = authHeader.getRealm().trim();
  150. String username = authHeader.getUsername().trim();
  151. if ( username == null || realm == null ) {
  152. return false;
  153. }
  154. String nonce = authHeader.getNonce();
  155. URI uri = authHeader.getURI();
  156. if (uri == null) {
  157. return false;
  158. }
  159. // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
  160. String qop = authHeader.getQop();
  161. // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
  162. // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
  163. String cnonce = authHeader.getCNonce();
  164. // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量
  165. int nc = authHeader.getNonceCount();
  166. String ncStr = String.format("%08x", nc).toUpperCase();
  167. // String ncStr = new DecimalFormat("00000000").format(nc);
  168. // String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16));
  169. String A1 = username + ":" + realm + ":" + pass;
  170. String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
  171. byte mdbytes[] = messageDigest.digest(A1.getBytes());
  172. String HA1 = toHexString(mdbytes);
  173. log.debug("A1: " + A1);
  174. log.debug("A2: " + A2);
  175. mdbytes = messageDigest.digest(A2.getBytes());
  176. String HA2 = toHexString(mdbytes);
  177. log.debug("HA1: " + HA1);
  178. log.debug("HA2: " + HA2);
  179. // String cnonce = authHeader.getCNonce();
  180. log.debug("nonce: " + nonce);
  181. log.debug("nc: " + ncStr);
  182. log.debug("cnonce: " + cnonce);
  183. log.debug("qop: " + qop);
  184. String KD = HA1 + ":" + nonce;
  185. if (qop != null && qop.equalsIgnoreCase("auth") ) {
  186. if (nc != -1) {
  187. KD += ":" + ncStr;
  188. }
  189. if (cnonce != null) {
  190. KD += ":" + cnonce;
  191. }
  192. KD += ":" + qop;
  193. }
  194. KD += ":" + HA2;
  195. log.debug("KD: " + KD);
  196. mdbytes = messageDigest.digest(KD.getBytes());
  197. String mdString = toHexString(mdbytes);
  198. log.debug("mdString: " + mdString);
  199. String response = authHeader.getResponse();
  200. log.debug("response: " + response);
  201. return mdString.equals(response);
  202. }
  203. }