DigestServerAuthenticationHelper.java 10 KB

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