1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.proxy.handlers.http.digest;
21
22 import java.io.UnsupportedEncodingException;
23 import java.security.NoSuchAlgorithmException;
24 import java.security.SecureRandom;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.StringTokenizer;
30
31 import org.apache.mina.core.filterchain.IoFilter.NextFilter;
32 import org.apache.mina.proxy.ProxyAuthException;
33 import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
34 import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
35 import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
36 import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
37 import org.apache.mina.proxy.session.ProxyIoSession;
38 import org.apache.mina.proxy.utils.StringUtilities;
39 import org.apache.mina.util.Base64;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43
44
45
46
47
48
49 public class HttpDigestAuthLogicHandler extends AbstractAuthLogicHandler {
50
51 private static final Logger LOGGER = LoggerFactory.getLogger(HttpDigestAuthLogicHandler.class);
52
53
54
55
56 private Map<String, String> directives = null;
57
58
59
60
61 private HttpProxyResponse response;
62
63 private static SecureRandom rnd;
64
65 static {
66
67 try {
68 rnd = SecureRandom.getInstance("SHA1PRNG");
69 } catch (NoSuchAlgorithmException e) {
70 throw new RuntimeException(e);
71 }
72 }
73
74
75
76
77
78
79
80 public HttpDigestAuthLogicHandler(ProxyIoSession proxyIoSession) throws ProxyAuthException {
81 super(proxyIoSession);
82
83 ((HttpProxyRequest) request).checkRequiredProperties(HttpProxyConstants.USER_PROPERTY,
84 HttpProxyConstants.PWD_PROPERTY);
85 }
86
87
88
89
90 @Override
91 public void doHandshake(NextFilter nextFilter) throws ProxyAuthException {
92 if (LOGGER.isDebugEnabled()) {
93 LOGGER.debug(" doHandshake()");
94 }
95
96 if (step > 0 && directives == null) {
97 throw new ProxyAuthException("Authentication challenge not received");
98 }
99
100 HttpProxyRequest./../../../../org/apache/mina/proxy/handlers/http/HttpProxyRequest.html#HttpProxyRequest">HttpProxyRequest req = (HttpProxyRequest) request;
101 Map<String, List<String>> headers = req.getHeaders() != null ? req.getHeaders()
102 : new HashMap<String, List<String>>();
103
104 if (step > 0) {
105 if (LOGGER.isDebugEnabled()) {
106 LOGGER.debug(" sending DIGEST challenge response");
107 }
108
109
110 HashMap<String, String> map = new HashMap<>();
111 map.put("username", req.getProperties().get(HttpProxyConstants.USER_PROPERTY));
112 StringUtilities.copyDirective(directives, map, "realm");
113 StringUtilities.copyDirective(directives, map, "uri");
114 StringUtilities.copyDirective(directives, map, "opaque");
115 StringUtilities.copyDirective(directives, map, "nonce");
116 String algorithm = StringUtilities.copyDirective(directives, map, "algorithm");
117
118
119 if (algorithm != null && !"md5".equalsIgnoreCase(algorithm) && !"md5-sess".equalsIgnoreCase(algorithm)) {
120 throw new ProxyAuthException("Unknown algorithm required by server");
121 }
122
123
124 String qop = directives.get("qop");
125
126 if (qop != null) {
127 StringTokenizer st = new StringTokenizer(qop, ",");
128 String token = null;
129
130 while (st.hasMoreTokens()) {
131 String tk = st.nextToken();
132
133 if ("auth".equalsIgnoreCase(token)) {
134 break;
135 }
136
137 int pos = Arrays.binarySearch(DigestUtilities.SUPPORTED_QOPS, tk);
138
139 if (pos > -1) {
140 token = tk;
141 }
142 }
143
144 if (token != null) {
145 map.put("qop", token);
146
147 byte[] nonce = new byte[8];
148 rnd.nextBytes(nonce);
149
150 try {
151 String cnonce = new String(Base64.encodeBase64(nonce), proxyIoSession.getCharsetName());
152 map.put("cnonce", cnonce);
153 } catch (UnsupportedEncodingException e) {
154 throw new ProxyAuthException("Unable to encode cnonce", e);
155 }
156 } else {
157 throw new ProxyAuthException("No supported qop option available");
158 }
159 }
160
161 map.put("nc", "00000001");
162 map.put("uri", req.getHttpURI());
163
164
165 try {
166 map.put("response", DigestUtilities.computeResponseValue(proxyIoSession.getSession(), map, req
167 .getHttpVerb().toUpperCase(), req.getProperties().get(HttpProxyConstants.PWD_PROPERTY),
168 proxyIoSession.getCharsetName(), response.getBody()));
169
170 } catch (Exception e) {
171 throw new ProxyAuthException("Digest response computing failed", e);
172 }
173
174
175
176 StringBuilder sb = new StringBuilder("Digest ");
177 boolean addSeparator = false;
178
179 for ( Map.Entry<String, String> entry : map.entrySet()) {
180 String key = entry.getKey();
181
182 if (addSeparator) {
183 sb.append(", ");
184 } else {
185 addSeparator = true;
186 }
187
188 boolean quotedValue = !"qop".equals(key) && !"nc".equals(key);
189 sb.append(key);
190
191 if (quotedValue) {
192 sb.append("=\"").append(entry.getValue()).append('\"');
193 } else {
194 sb.append('=').append(entry.getValue());
195 }
196 }
197
198 StringUtilities.addValueToHeader(headers, "Proxy-Authorization", sb.toString(), true);
199 }
200
201 addKeepAliveHeaders(headers);
202 req.setHeaders(headers);
203
204 writeRequest(nextFilter, req);
205 step++;
206 }
207
208 @Override
209 public void handleResponse(final HttpProxyResponse response) throws ProxyAuthException {
210 this.response = response;
211
212 if (step == 0) {
213 if (response.getStatusCode() != 401 && response.getStatusCode() != 407) {
214 throw new ProxyAuthException("Received unexpected response code (" + response.getStatusLine() + ").");
215 }
216
217
218
219 List<String> values = response.getHeaders().get("Proxy-Authenticate");
220 String challengeResponse = null;
221
222 for (String s : values) {
223 if (s.startsWith("Digest")) {
224 challengeResponse = s;
225 break;
226 }
227 }
228
229 if (challengeResponse == null) {
230 throw new ProxyAuthException("Server doesn't support digest authentication method !");
231 }
232
233 try {
234 directives = StringUtilities.parseDirectives(challengeResponse.substring(7).getBytes(
235 proxyIoSession.getCharsetName()));
236 } catch (Exception e) {
237 throw new ProxyAuthException("Parsing of server digest directives failed", e);
238 }
239 step = 1;
240 } else {
241 throw new ProxyAuthException("Received unexpected response code (" + response.getStatusLine() + ").");
242 }
243 }
244 }