001: // DigestAuthPrincipal.java
002: // $Id: DigestAuthPrincipal.java,v 1.8 2005/02/18 17:32:40 ylafon Exp $
003: // (c) COPYRIGHT MIT, INRIA and Keio, 1999.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigsaw.acl;
007:
008: import java.io.UnsupportedEncodingException;
009: import java.security.Principal;
010: import java.security.MessageDigest;
011: import java.security.NoSuchAlgorithmException;
012:
013: import org.w3c.jigsaw.http.Request;
014: import org.w3c.www.http.HttpCredential;
015: import org.w3c.util.StringUtils;
016:
017: /**
018: * @version $Revision: 1.8 $
019: * @author Benoît Mahé (bmahe@w3.org)
020: * This algorithm used is based on RFC 2069
021: */
022: public class DigestAuthPrincipal extends HTTPPrincipal {
023:
024: protected String dac_user = null;
025: String dac_realm = null;
026: String dac_nonce = null;
027: String dac_uri = null;
028: String dac_response = null;
029: String dac_algorithm = null;
030: String dac_method = null;
031: String nonce = null;
032: String old_nonce = null;
033: String algo = null;
034: boolean stale = false;
035: boolean no_user = false;
036:
037: public boolean isStale() {
038: return stale;
039: }
040:
041: /**
042: * Check that the challenge matches with the provided nonce
043: * @return true if it matches
044: */
045: private boolean checkDigest2069(String username, String realm,
046: String password, String nonce) {
047: // check if the user knows the right passwd
048: String a1, a2, ha1, ha2;
049: StringBuffer sb = new StringBuffer(256);
050: // a1 = unq(username-value) ":" unq(realm-value) ":" passwd
051: sb.append(username).append(':').append(realm);
052: sb.append(':').append(password);
053: a1 = sb.toString();
054: // A2 = Method ":" digest-uri-value
055: sb = new StringBuffer(256);
056: sb.append(dac_method).append(':').append(dac_uri);
057: a2 = sb.toString();
058: MessageDigest md = null;
059: try {
060: md = MessageDigest.getInstance(this .algo);
061: } catch (NoSuchAlgorithmException algex) {
062: // fatal error, can't authenticate
063: return false;
064: }
065: try {
066: md.update(a1.getBytes("ISO-8859-1"));
067: ha1 = StringUtils.toHexString(md.digest());
068: md.reset();
069: md.update(a2.getBytes("ISO-8859-1"));
070: ha2 = StringUtils.toHexString(md.digest());
071: md.reset();
072: String kd, hkd;
073: // KD( H(A1), unq(nonce-value) ":" H(A2)
074: sb = new StringBuffer(256);
075: sb.append(ha1).append(':').append(nonce).append(':')
076: .append(ha2);
077: kd = sb.toString();
078: md.update(kd.getBytes("ISO-8859-1"));
079: hkd = StringUtils.toHexString(md.digest());
080: return hkd.equals(dac_response);
081: } catch (UnsupportedEncodingException ex) {
082: // in case iso-8859-1 is not known...
083: }
084: return false;
085: }
086:
087: public boolean equals(Object another) {
088: if (no_user)
089: return false;
090: if (another instanceof AclPrincipal) {
091: AclPrincipal aclp = (AclPrincipal) another;
092: String username = aclp.getName();
093: String realm = aclp.getRealm();
094: String passwd = aclp.getPassword();
095:
096: if (!dac_user.equals(username))
097: return false;
098: if (!dac_realm.equals(realm))
099: return false;
100: if (dac_algorithm != null
101: && !dac_algorithm.equals(this .algo))
102: return false;
103: // are we using the current nonce?
104: if (!dac_nonce.equals(this .nonce)) {
105: // no, is it the old one?
106: if (dac_nonce.equals(this .old_nonce)) {
107: // yes, does it matches?
108: if (checkDigest2069(username, realm, passwd,
109: old_nonce)) {
110: // it doesn't mean that we are validating an old nonce
111: // but it is a trick, allowing two nonces at the same
112: // time to populate "AuthenticationInfo" with the
113: // next nonce.
114: stale = true;
115: return true;
116: }
117: } else {
118: // reject but mark as atale if auth is ok with nonce.
119: if (checkDigest2069(username, realm, passwd,
120: dac_nonce)) {
121: stale = true;
122: }
123: }
124: return false;
125: }
126: return checkDigest2069(username, realm, passwd, nonce);
127: } else if (another instanceof DigestAuthPrincipal) {
128: return false;
129: }
130: return false;
131: }
132:
133: public String toString() {
134: if (dac_user != null)
135: return dac_user;
136: return "Digest";
137: }
138:
139: public int hashCode() {
140: if (dac_nonce != null)
141: return dac_nonce.hashCode();
142: else
143: return -1;
144: }
145:
146: public String getName() {
147: return dac_user;
148: }
149:
150: public DigestAuthPrincipal(Request request, String nonce,
151: String old_nonce, String algo) throws InvalidAuthException {
152: super (request);
153: HttpCredential credential = (request.isProxy() ? request
154: .getProxyAuthorization() : request.getAuthorization());
155: if ((credential == null)
156: || (!credential.getScheme().equalsIgnoreCase("Digest"))) {
157: no_user = true;
158: } else {
159: no_user = false;
160: dac_user = credential.getAuthParameter("username");
161: dac_uri = credential.getAuthParameter("uri");
162: dac_response = credential.getAuthParameter("response");
163: dac_realm = credential.getAuthParameter("realm");
164: dac_method = request.getMethod();
165: dac_nonce = credential.getAuthParameter("nonce");
166: this .nonce = nonce;
167: this .old_nonce = old_nonce;
168: this .algo = algo;
169: if (dac_user == null || dac_uri == null
170: || dac_response == null || dac_realm == null) {
171: String msg = ("Invalid authentication header");
172: throw new InvalidAuthException(msg);
173: }
174: }
175: }
176:
177: public DigestAuthPrincipal(Request request)
178: throws InvalidAuthException {
179: super (request);
180: throw new InvalidAuthException("Bad call for authentification");
181: }
182:
183: protected DigestAuthPrincipal(Request request, String internal)
184: throws InvalidAuthException {
185: super(request);
186: }
187: }
|