DigestServerAuthenticationHelper.java 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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.genersoft.iot.vmp.gb28181.auth;
  27. import java.security.MessageDigest;
  28. import java.security.NoSuchAlgorithmException;
  29. import java.text.DecimalFormat;
  30. import java.util.Date;
  31. import java.util.Random;
  32. import javax.sip.address.URI;
  33. import javax.sip.header.AuthorizationHeader;
  34. import javax.sip.header.HeaderFactory;
  35. import javax.sip.header.WWWAuthenticateHeader;
  36. import javax.sip.message.Request;
  37. import javax.sip.message.Response;
  38. import gov.nist.core.InternalErrorHandler;
  39. import org.slf4j.Logger;
  40. import org.slf4j.LoggerFactory;
  41. /**
  42. * Implements the HTTP digest authentication method server side functionality.
  43. *
  44. * @author M. Ranganathan
  45. * @author Marc Bednarek
  46. */
  47. public class DigestServerAuthenticationHelper {
  48. private Logger logger = LoggerFactory.getLogger(DigestServerAuthenticationHelper.class);
  49. private MessageDigest messageDigest;
  50. public static final String DEFAULT_ALGORITHM = "MD5";
  51. public static final String DEFAULT_SCHEME = "Digest";
  52. /** to hex converter */
  53. private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
  54. '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
  55. /**
  56. * Default constructor.
  57. * @throws NoSuchAlgorithmException
  58. */
  59. public DigestServerAuthenticationHelper()
  60. throws NoSuchAlgorithmException {
  61. messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
  62. }
  63. public static String toHexString(byte b[]) {
  64. int pos = 0;
  65. char[] c = new char[b.length * 2];
  66. for (int i = 0; i < b.length; i++) {
  67. c[pos++] = toHex[(b[i] >> 4) & 0x0F];
  68. c[pos++] = toHex[b[i] & 0x0f];
  69. }
  70. return new String(c);
  71. }
  72. /**
  73. * Generate the challenge string.
  74. *
  75. * @return a generated nonce.
  76. */
  77. private String generateNonce() {
  78. // Get the time of day and run MD5 over it.
  79. Date date = new Date();
  80. long time = date.getTime();
  81. Random rand = new Random();
  82. long pad = rand.nextLong();
  83. // String nonceString = (new Long(time)).toString()
  84. // + (new Long(pad)).toString();
  85. String nonceString = Long.valueOf(time).toString()
  86. + Long.valueOf(pad).toString();
  87. byte mdbytes[] = messageDigest.digest(nonceString.getBytes());
  88. // Convert the mdbytes array into a hex string.
  89. return toHexString(mdbytes);
  90. }
  91. public Response generateChallenge(HeaderFactory headerFactory, Response response, String realm) {
  92. try {
  93. WWWAuthenticateHeader proxyAuthenticate = headerFactory
  94. .createWWWAuthenticateHeader(DEFAULT_SCHEME);
  95. proxyAuthenticate.setParameter("realm", realm);
  96. proxyAuthenticate.setParameter("nonce", generateNonce());
  97. proxyAuthenticate.setParameter("opaque", "");
  98. proxyAuthenticate.setParameter("stale", "FALSE");
  99. proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM);
  100. // proxyAuthenticate.setParameter("qop", "auth");
  101. response.setHeader(proxyAuthenticate);
  102. } catch (Exception ex) {
  103. InternalErrorHandler.handleException(ex);
  104. }
  105. return response;
  106. }
  107. /**
  108. * Authenticate the inbound request.
  109. *
  110. * @param request - the request to authenticate.
  111. * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
  112. *
  113. * @return true if authentication succeded and false otherwise.
  114. */
  115. public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
  116. AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
  117. if ( authHeader == null ) return false;
  118. String realm = authHeader.getRealm();
  119. String username = authHeader.getUsername();
  120. if ( username == null || realm == null ) {
  121. return false;
  122. }
  123. String nonce = authHeader.getNonce();
  124. URI uri = authHeader.getURI();
  125. if (uri == null) {
  126. return false;
  127. }
  128. String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
  129. String HA1 = hashedPassword;
  130. byte[] mdbytes = messageDigest.digest(A2.getBytes());
  131. String HA2 = toHexString(mdbytes);
  132. String cnonce = authHeader.getCNonce();
  133. String KD = HA1 + ":" + nonce;
  134. if (cnonce != null) {
  135. KD += ":" + cnonce;
  136. }
  137. KD += ":" + HA2;
  138. mdbytes = messageDigest.digest(KD.getBytes());
  139. String mdString = toHexString(mdbytes);
  140. String response = authHeader.getResponse();
  141. return mdString.equals(response);
  142. }
  143. /**
  144. * Authenticate the inbound request given plain text password.
  145. *
  146. * @param request - the request to authenticate.
  147. * @param pass -- the plain text password.
  148. *
  149. * @return true if authentication succeded and false otherwise.
  150. */
  151. public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
  152. AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
  153. if ( authHeader == null ) return false;
  154. String realm = authHeader.getRealm().trim();
  155. String username = authHeader.getUsername().trim();
  156. if ( username == null || realm == null ) {
  157. return false;
  158. }
  159. String nonce = authHeader.getNonce();
  160. URI uri = authHeader.getURI();
  161. if (uri == null) {
  162. return false;
  163. }
  164. // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
  165. String qop = authHeader.getQop();
  166. // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
  167. // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
  168. //String cNonce = authHeader.getCNonce();
  169. // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量
  170. int nc = authHeader.getNonceCount();
  171. String ncStr = new DecimalFormat("00000000").format(nc);
  172. // String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16));
  173. String A1 = username + ":" + realm + ":" + pass;
  174. String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
  175. byte mdbytes[] = messageDigest.digest(A1.getBytes());
  176. String HA1 = toHexString(mdbytes);
  177. logger.debug("A1: " + A1);
  178. logger.debug("A2: " + A2);
  179. mdbytes = messageDigest.digest(A2.getBytes());
  180. String HA2 = toHexString(mdbytes);
  181. logger.debug("HA1: " + HA1);
  182. logger.debug("HA2: " + HA2);
  183. String cnonce = authHeader.getCNonce();
  184. logger.debug("nonce: " + nonce);
  185. logger.debug("nc: " + ncStr);
  186. logger.debug("cnonce: " + cnonce);
  187. logger.debug("qop: " + qop);
  188. String KD = HA1 + ":" + nonce;
  189. if (qop != null && qop.equals("auth") ) {
  190. if (nc != -1) {
  191. KD += ":" + ncStr;
  192. }
  193. if (cnonce != null) {
  194. KD += ":" + cnonce;
  195. }
  196. KD += ":" + qop;
  197. }
  198. KD += ":" + HA2;
  199. logger.debug("KD: " + KD);
  200. mdbytes = messageDigest.digest(KD.getBytes());
  201. String mdString = toHexString(mdbytes);
  202. logger.debug("mdString: " + mdString);
  203. String response = authHeader.getResponse();
  204. logger.debug("response: " + response);
  205. return mdString.equals(response);
  206. }
  207. }