001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.core.internal.io.dav.http;
013:
014: import java.security.MessageDigest;
015: import java.security.NoSuchAlgorithmException;
016: import java.util.StringTokenizer;
017:
018: import org.tmatesoft.svn.core.SVNErrorCode;
019: import org.tmatesoft.svn.core.SVNErrorMessage;
020: import org.tmatesoft.svn.core.SVNException;
021: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
022:
023: /**
024: * @version 1.1.1
025: * @author TMate Software Ltd.
026: */
027: class HTTPDigestAuthentication extends HTTPAuthentication {
028:
029: private static final char[] HEXADECIMAL = { '0', '1', '2', '3',
030: '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
031: private static final String NC = "00000001";
032:
033: private String myCnonce;
034: private String myQop;
035:
036: protected HTTPDigestAuthentication() {
037: }
038:
039: public void init() throws SVNException {
040: String qop = getChallengeParameter("qop");
041: String selectedQop = null;
042:
043: if (qop != null) {
044: for (StringTokenizer tok = new StringTokenizer(qop, ","); tok
045: .hasMoreTokens();) {
046: selectedQop = tok.nextToken().trim();
047: if ("auth".equals(selectedQop)) {
048: break;
049: }
050: }
051: }
052: if (selectedQop != null && !"auth".equals(selectedQop)) {
053: SVNErrorMessage err = SVNErrorMessage.create(
054: SVNErrorCode.RA_DAV_REQUEST_FAILED,
055: "Digest HTTP auth: ''(0}'' is not supported",
056: selectedQop);
057: SVNErrorManager.error(err);
058: }
059: myQop = selectedQop;
060: myCnonce = createCnonce();
061: }
062:
063: public String authenticate() throws SVNException {
064: if (getUserName() == null || getPassword() == null) {
065: return null;
066: }
067:
068: String uname = getUserName();
069: String digest = createDigest(uname, getPassword(), "US-ASCII");
070:
071: String uri = getParameter("uri");
072: String realm = getParameter("realm");
073: String nonce = getParameter("nonce");
074: String opaque = getParameter("opaque");
075: String algorithm = getParameter("algorithm", "MD5");
076:
077: StringBuffer sb = new StringBuffer();
078:
079: sb.append("Digest ");
080:
081: sb.append("username=\"" + uname + "\"").append(
082: ", realm=\"" + realm + "\"").append(
083: ", nonce=\"" + nonce + "\"").append(
084: ", uri=\"" + uri + "\"").append(
085: ", response=\"" + digest + "\"");
086: if (myQop != null) {
087: sb.append(", qop=\"" + myQop + "\"").append(", nc=" + NC)
088: .append(", cnonce=\"" + myCnonce + "\"");
089: }
090: if (algorithm != null) {
091: sb.append(", algorithm=\"" + algorithm + "\"");
092: }
093: if (opaque != null) {
094: sb.append(", opaque=\"" + opaque + "\"");
095: }
096: return sb.toString();
097: }
098:
099: public String getAuthenticationScheme() {
100: return "Digest";
101: }
102:
103: private String createDigest(String uname, String pwd, String charset)
104: throws SVNException {
105: final String digAlg = "MD5";
106:
107: String uri = getParameter("uri");
108: String realm = getParameter("realm");
109: String nonce = getParameter("nonce");
110: String method = getParameter("methodname");
111: String algorithm = getParameter("algorithm", "MD5");
112:
113: MessageDigest md5Helper;
114: try {
115: md5Helper = MessageDigest.getInstance(digAlg);
116: } catch (Exception e) {
117: SVNErrorMessage err = SVNErrorMessage
118: .create(
119: SVNErrorCode.RA_DAV_REQUEST_FAILED,
120: "Unsupported algorithm in HTTP Digest authentication: ''{0}''",
121: digAlg);
122: SVNErrorManager.error(err);
123: return null;
124: }
125: StringBuffer tmp = new StringBuffer(uname.length()
126: + realm.length() + pwd.length() + 2);
127: tmp.append(uname);
128: tmp.append(':');
129: tmp.append(realm);
130: tmp.append(':');
131: tmp.append(pwd);
132: String a1 = tmp.toString();
133: if ("MD5-sess".equals(algorithm)) {
134: String tmp2 = encode(md5Helper.digest(HTTPAuthentication
135: .getBytes(a1, charset)));
136: StringBuffer tmp3 = new StringBuffer(tmp2.length()
137: + nonce.length() + myCnonce.length() + 2);
138: tmp3.append(tmp2);
139: tmp3.append(':');
140: tmp3.append(nonce);
141: tmp3.append(':');
142: tmp3.append(myCnonce);
143: a1 = tmp3.toString();
144: }
145:
146: String md5a1 = encode(md5Helper.digest(HTTPAuthentication
147: .getBytes(a1, charset)));
148: String a2 = method + ":" + uri;
149: String md5a2 = encode(md5Helper.digest(HTTPAuthentication
150: .getASCIIBytes(a2)));
151:
152: StringBuffer tmp2;
153: if (myQop == null) {
154: tmp2 = new StringBuffer(md5a1.length() + nonce.length()
155: + md5a2.length());
156: tmp2.append(md5a1);
157: tmp2.append(':');
158: tmp2.append(nonce);
159: tmp2.append(':');
160: tmp2.append(md5a2);
161: } else {
162: String qopOption = "auth";
163: tmp2 = new StringBuffer(md5a1.length() + nonce.length()
164: + NC.length() + myCnonce.length()
165: + qopOption.length() + md5a2.length() + 5);
166: tmp2.append(md5a1);
167: tmp2.append(':');
168: tmp2.append(nonce);
169: tmp2.append(':');
170: tmp2.append(NC);
171: tmp2.append(':');
172: tmp2.append(myCnonce);
173: tmp2.append(':');
174: tmp2.append(qopOption);
175: tmp2.append(':');
176: tmp2.append(md5a2);
177: }
178:
179: return encode(md5Helper.digest(HTTPAuthentication
180: .getASCIIBytes(tmp2.toString())));
181: }
182:
183: private String getParameter(String name) {
184: return getParameter(name, null);
185: }
186:
187: private String getParameter(String name, String defaultValue) {
188: String value = getChallengeParameter(name);
189: if (value == null) {
190: value = defaultValue;
191: }
192: return value;
193: }
194:
195: private static String createCnonce() {
196: String cnonce;
197: final String digAlg = "MD5";
198: MessageDigest md5Helper;
199:
200: try {
201: md5Helper = MessageDigest.getInstance(digAlg);
202: } catch (NoSuchAlgorithmException e) {
203: return null;
204: }
205: cnonce = Long.toString(System.currentTimeMillis());
206: cnonce = encode(md5Helper.digest(HTTPAuthentication
207: .getASCIIBytes(cnonce)));
208: return cnonce;
209: }
210:
211: private static String encode(byte[] binaryData) {
212: if (binaryData.length != 16) {
213: return null;
214: }
215:
216: char[] buffer = new char[32];
217: for (int i = 0; i < 16; i++) {
218: int low = binaryData[i] & 0x0f;
219: int high = (binaryData[i] & 0xf0) >> 4;
220: buffer[i * 2] = HEXADECIMAL[high];
221: buffer[(i * 2) + 1] = HEXADECIMAL[low];
222: }
223:
224: return new String(buffer);
225: }
226:
227: }
|