DigestServerAuthenticationHelper.java 11 KB

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