• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package gov.nist.javax.sip.clientauthutils;
2 
3 import gov.nist.core.StackLogger;
4 
5 import java.security.MessageDigest;
6 import java.security.NoSuchAlgorithmException;
7 
8 /**
9  * The class takes standard Http Authentication details and returns a response according to the
10  * MD5 algorithm
11  *
12  * @author Emil Ivov
13  */
14 
15 public class MessageDigestAlgorithm {
16     /**
17      * Calculates an http authentication response in accordance with rfc2617.
18      * <p>
19      *
20      * @param algorithm a string indicating a pair of algorithms (MD5 (default), or MD5-sess) used
21      *        to produce the digest and a checksum.
22      * @param hashUserNameRealmPasswd MD5 hash of (username:realm:password)
23      * @param nonce_value A server-specified data string provided in the challenge.
24      * @param cnonce_value an optional client-chosen value whose purpose is to foil chosen
25      *        plaintext attacks.
26      * @param method the SIP method of the request being challenged.
27      * @param digest_uri_value the value of the "uri" directive on the Authorization header in the
28      *        request.
29      * @param entity_body the entity-body
30      * @param qop_value Indicates what "quality of protection" the client has applied to the
31      *        message.
32      * @param nc_value the hexadecimal count of the number of requests (including the current
33      *        request) that the client has sent with the nonce value in this request.
34      * @return a digest response as defined in rfc2617
35      * @throws NullPointerException in case of incorrectly null parameters.
36      */
37 
calculateResponse(String algorithm, String hashUserNameRealmPasswd, String nonce_value, String nc_value, String cnonce_value, String method, String digest_uri_value, String entity_body, String qop_value, StackLogger stackLogger)38     static String calculateResponse(String algorithm, String hashUserNameRealmPasswd,
39             String nonce_value, String nc_value, String cnonce_value,
40             String method, String digest_uri_value, String entity_body, String qop_value,
41             StackLogger stackLogger)  {
42         if (stackLogger.isLoggingEnabled()) {
43             stackLogger.logDebug("trying to authenticate using : " + algorithm + ", "+
44                     hashUserNameRealmPasswd + ", " + nonce_value + ", "
45                     + nc_value + ", " + cnonce_value + ", " + method + ", " + digest_uri_value
46                     + ", " + entity_body + ", " + qop_value);
47         }
48 
49         if (hashUserNameRealmPasswd == null || method == null
50                 || digest_uri_value == null || nonce_value == null)
51             throw new NullPointerException(
52                     "Null parameter to MessageDigestAlgorithm.calculateResponse()");
53 
54         // The following follows closely the algorithm for generating a response
55         // digest as specified by rfc2617
56 
57         if (cnonce_value == null || cnonce_value.length() == 0)
58                 throw new NullPointerException(
59                         "cnonce_value may not be absent for MD5-Sess algorithm.");
60 
61 
62         String A2 = null;
63         if (qop_value == null || qop_value.trim().length() == 0
64                 || qop_value.trim().equalsIgnoreCase("auth")) {
65             A2 = method + ":" + digest_uri_value;
66         } else {
67             if (entity_body == null)
68                 entity_body = "";
69             A2 = method + ":" + digest_uri_value + ":" + H(entity_body);
70         }
71 
72         String request_digest = null;
73 
74         if (cnonce_value != null && qop_value != null && nc_value != null
75                 && (qop_value.equalsIgnoreCase("auth") || qop_value.equalsIgnoreCase("auth-int")))
76 
77         {
78             request_digest = KD(hashUserNameRealmPasswd, nonce_value + ":" + nc_value + ":" + cnonce_value + ":"
79                     + qop_value + ":" + H(A2));
80 
81         } else {
82             request_digest = KD(hashUserNameRealmPasswd, nonce_value + ":" + H(A2));
83         }
84 
85         return request_digest;
86 
87 
88     }
89 
90     /**
91      * Calculates an http authentication response in accordance with rfc2617.
92      * <p>
93      *
94      * @param algorithm a string indicating a pair of algorithms (MD5 (default), or MD5-sess) used
95      *        to produce the digest and a checksum.
96      * @param username_value username_value (see rfc2617)
97      * @param realm_value A string that has been displayed to the user in order to determine the
98      *        context of the username and password to use.
99      * @param passwd the password to encode in the challenge response.
100      * @param nonce_value A server-specified data string provided in the challenge.
101      * @param cnonce_value an optional client-chosen value whose purpose is to foil chosen
102      *        plaintext attacks.
103      * @param method the SIP method of the request being challenged.
104      * @param digest_uri_value the value of the "uri" directive on the Authorization header in the
105      *        request.
106      * @param entity_body the entity-body
107      * @param qop_value Indicates what "quality of protection" the client has applied to the
108      *        message.
109      * @param nc_value the hexadecimal count of the number of requests (including the current
110      *        request) that the client has sent with the nonce value in this request.
111      * @return a digest response as defined in rfc2617
112      * @throws NullPointerException in case of incorrectly null parameters.
113      */
calculateResponse(String algorithm, String username_value, String realm_value, String passwd, String nonce_value, String nc_value, String cnonce_value, String method, String digest_uri_value, String entity_body, String qop_value, StackLogger stackLogger)114     static String calculateResponse(String algorithm, String username_value, String realm_value,
115             String passwd, String nonce_value, String nc_value, String cnonce_value,
116             String method, String digest_uri_value, String entity_body, String qop_value,
117             StackLogger stackLogger) {
118         if (stackLogger.isLoggingEnabled()) {
119             stackLogger.logDebug("trying to authenticate using : " + algorithm + ", "
120                     + username_value + ", " + realm_value + ", "
121                     + (passwd != null && passwd.trim().length() > 0) + ", " + nonce_value + ", "
122                     + nc_value + ", " + cnonce_value + ", " + method + ", " + digest_uri_value
123                     + ", " + entity_body + ", " + qop_value);
124         }
125 
126         if (username_value == null || realm_value == null || passwd == null || method == null
127                 || digest_uri_value == null || nonce_value == null)
128             throw new NullPointerException(
129                     "Null parameter to MessageDigestAlgorithm.calculateResponse()");
130 
131         // The following follows closely the algorithm for generating a response
132         // digest as specified by rfc2617
133         String A1 = null;
134 
135         if (algorithm == null || algorithm.trim().length() == 0
136                 || algorithm.trim().equalsIgnoreCase("MD5")) {
137             A1 = username_value + ":" + realm_value + ":" + passwd;
138         } else {
139             if (cnonce_value == null || cnonce_value.length() == 0)
140                 throw new NullPointerException(
141                         "cnonce_value may not be absent for MD5-Sess algorithm.");
142 
143             A1 = H(username_value + ":" + realm_value + ":" + passwd) + ":" + nonce_value + ":"
144                     + cnonce_value;
145         }
146 
147         String A2 = null;
148         if (qop_value == null || qop_value.trim().length() == 0
149                 || qop_value.trim().equalsIgnoreCase("auth")) {
150             A2 = method + ":" + digest_uri_value;
151         } else {
152             if (entity_body == null)
153                 entity_body = "";
154             A2 = method + ":" + digest_uri_value + ":" + H(entity_body);
155         }
156 
157         String request_digest = null;
158 
159         if (cnonce_value != null && qop_value != null && nc_value != null
160                 && (qop_value.equalsIgnoreCase("auth") || qop_value.equalsIgnoreCase("auth-int")))
161 
162         {
163             request_digest = KD(H(A1), nonce_value + ":" + nc_value + ":" + cnonce_value + ":"
164                     + qop_value + ":" + H(A2));
165 
166         } else {
167             request_digest = KD(H(A1), nonce_value + ":" + H(A2));
168         }
169 
170         return request_digest;
171     }
172 
173     /**
174      * Defined in rfc 2617 as H(data) = MD5(data);
175      *
176      * @param data data
177      * @return MD5(data)
178      */
H(String data)179     private static String H(String data) {
180         try {
181             MessageDigest digest = MessageDigest.getInstance("MD5");
182 
183             return toHexString(digest.digest(data.getBytes()));
184         } catch (NoSuchAlgorithmException ex) {
185             // shouldn't happen
186             throw new RuntimeException("Failed to instantiate an MD5 algorithm", ex);
187         }
188     }
189 
190     /**
191      * Defined in rfc 2617 as KD(secret, data) = H(concat(secret, ":", data))
192      *
193      * @param data data
194      * @param secret secret
195      * @return H(concat(secret, ":", data));
196      */
KD(String secret, String data)197     private static String KD(String secret, String data) {
198         return H(secret + ":" + data);
199     }
200 
201     // the following code was copied from the NIST-SIP instant
202     // messenger (its author is Olivier Deruelle). Thanks for making it public!
203     /**
204      * to hex converter
205      */
206     private static final char[] toHex = {
207         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
208     };
209 
210     /**
211      * Converts b[] to hex string.
212      *
213      * @param b the bte array to convert
214      * @return a Hex representation of b.
215      */
toHexString(byte b[])216     private static String toHexString(byte b[]) {
217         int pos = 0;
218         char[] c = new char[b.length * 2];
219         for (int i = 0; i < b.length; i++) {
220             c[pos++] = toHex[(b[i] >> 4) & 0x0F];
221             c[pos++] = toHex[b[i] & 0x0f];
222         }
223         return new String(c);
224     }
225 }
226