001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.security.srp;
023:
024: import java.io.IOException;
025: import java.rmi.RemoteException;
026: import java.rmi.server.RMIClientSocketFactory;
027: import java.rmi.server.RMIServerSocketFactory;
028: import java.rmi.server.UnicastRemoteObject;
029: import java.security.GeneralSecurityException;
030: import java.security.KeyException;
031: import java.security.NoSuchAlgorithmException;
032: import java.util.Collections;
033: import java.util.HashMap;
034: import java.util.Map;
035: import javax.crypto.SealedObject;
036:
037: import org.jboss.logging.Logger;
038: import org.jboss.security.Util;
039: import org.jboss.security.srp.SRPVerifierStore.VerifierInfo;
040:
041: /** An implementation of the RMI SRPRemoteServerInterface interface.
042:
043: @author Scott.Stark@jboss.org
044: @version $Revision: 57210 $
045: */
046: public class SRPRemoteServer extends UnicastRemoteObject implements
047: SRPRemoteServerInterface {
048: private static Logger log = Logger.getLogger(SRPRemoteServer.class);
049: /** The counter used to unique user sessions */
050: private static int userSessionCount = 0;
051: /** A map of <SRPSessionKey, SRPServerSession> for the active sessions */
052: private Map sessionMap = Collections.synchronizedMap(new HashMap());
053:
054: /** The store implementation providing the SRP VerifierInfo */
055: private SRPVerifierStore verifierStore;
056: /** The callback interface for SRP session events. */
057: private SRPServerListener listener;
058: /** A flag indicating if the client must supply an aux challenge */
059: private boolean requireAuxChallenge;
060:
061: public SRPRemoteServer(SRPVerifierStore verifierStore)
062: throws RemoteException {
063: setVerifierStore(verifierStore);
064: }
065:
066: public SRPRemoteServer(SRPVerifierStore verifierStore, int port)
067: throws RemoteException {
068: super (port);
069: setVerifierStore(verifierStore);
070: }
071:
072: public SRPRemoteServer(SRPVerifierStore verifierStore, int port,
073: RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
074: throws RemoteException {
075: super (port, csf, ssf);
076: setVerifierStore(verifierStore);
077: }
078:
079: /**
080: */
081: public void setVerifierStore(SRPVerifierStore verifierStore) {
082: this .verifierStore = verifierStore;
083: log.info("setVerifierStore, " + verifierStore);
084: }
085:
086: public void addSRPServerListener(SRPServerListener listener) {
087: this .listener = listener;
088: }
089:
090: public void removeSRPServerListener(SRPServerListener listener) {
091: if (this .listener == listener)
092: this .listener = null;
093: }
094:
095: public boolean getRequireAuxChallenge() {
096: return this .requireAuxChallenge;
097: }
098:
099: public void setRequireAuxChallenge(boolean flag) {
100: this .requireAuxChallenge = flag;
101: }
102:
103: /** The start of a new client session.
104: */
105: public SRPParameters getSRPParameters(String username)
106: throws KeyException, RemoteException {
107: Object[] params = this .getSRPParameters(username, false);
108: SRPParameters srpParams = (SRPParameters) params[0];
109: return srpParams;
110: }
111:
112: public Object[] getSRPParameters(String username,
113: boolean multipleSessions) throws KeyException,
114: RemoteException {
115: boolean trace = log.isTraceEnabled();
116: if (trace)
117: log.trace("getSRPParameters, " + username);
118: SRPParameters params = null;
119: VerifierInfo info = null;
120: try {
121: info = verifierStore.getUserVerifier(username);
122: if (info == null)
123: throw new KeyException("Unknown username: " + username);
124: params = new SRPParameters(info.N, info.g, info.salt,
125: info.hashAlgorithm, info.cipherAlgorithm,
126: info.cipherIV);
127: if (log.isTraceEnabled()) {
128: log.trace("Params: " + params);
129: byte[] hn = Util.newDigest().digest(params.N);
130: log.trace("H(N): " + Util.tob64(hn));
131: byte[] hg = Util.newDigest().digest(params.g);
132: log.trace("H(g): " + Util.tob64(hg));
133: }
134: } catch (IOException e) {
135: throw new RemoteException(
136: "Error during user info retrieval", e);
137: } catch (KeyException e) {
138: throw e;
139: } catch (Throwable t) {
140: log.error("Unexpected exception in getSRPParameters", t);
141: throw new RemoteException(
142: "Unexpected exception in getSRPParameters", t);
143: }
144:
145: // Generate a session id if the user may run multiple sessions
146: Integer sessionID = SRPSessionKey.NO_SESSION_ID;
147: if (multipleSessions == true)
148: sessionID = nextSessionID();
149: Object[] sessionInfo = { params, sessionID };
150: // Create an SRP session
151: SRPSessionKey key = new SRPSessionKey(username, sessionID);
152: SRPServerSession session = new SRPServerSession(username,
153: info.verifier, params);
154: sessionMap.put(key, session);
155: if (trace)
156: log.trace("getSRPParameters, completed " + key);
157:
158: return sessionInfo;
159: }
160:
161: public byte[] init(String username, byte[] A)
162: throws SecurityException, NoSuchAlgorithmException,
163: RemoteException {
164: return this .init(username, A, 0);
165: }
166:
167: public byte[] init(String username, byte[] A, int sessionID)
168: throws SecurityException, NoSuchAlgorithmException,
169: RemoteException {
170: SRPSessionKey key = new SRPSessionKey(username, sessionID);
171: boolean trace = log.isTraceEnabled();
172: if (trace)
173: log.trace("init, " + key);
174: SRPServerSession session = (SRPServerSession) sessionMap
175: .get(key);
176: if (session == null)
177: throw new SecurityException(
178: "Failed to find active session for username: "
179: + username);
180:
181: byte[] B = session.exponential();
182: session.buildSessionKey(A);
183: if (trace)
184: log.trace("init, completed " + key);
185: return B;
186: }
187:
188: public byte[] verify(String username, byte[] M1)
189: throws SecurityException, RemoteException {
190: return this .verify(username, M1, null, 0);
191: }
192:
193: public byte[] verify(String username, byte[] M1, int sessionID)
194: throws SecurityException, RemoteException {
195: return this .verify(username, M1, null, sessionID);
196: }
197:
198: /** Verify the session key hash. The client sends their username and M1
199: hash to validate completion of the SRP handshake.
200:
201: @param username - the user ID by which the client is known. This is repeated to simplify
202: the server session management.
203: @param M1 - the client hash of the session key; M1 = H(H(N) xor H(g) | H(U) | A | B | K)
204: @param auxChallenge - an arbitrary addition data item that my be used as an additional
205: challenge. One example usage would be to send a hardware generated token that was encrypted
206: with the session private key for validation by the server.
207: @return M2, the server hash of the client challenge; M2 = H(A | M1 | K)
208: @throws SecurityException thrown if M1 cannot be verified by the server
209: @throws RemoteException thrown by remote implementations
210: */
211: public byte[] verify(String username, byte[] M1, Object auxChallenge)
212: throws SecurityException, RemoteException {
213: return this .verify(username, M1, auxChallenge, 0);
214: }
215:
216: public byte[] verify(String username, byte[] M1,
217: Object auxChallenge, int sessionID)
218: throws SecurityException, RemoteException {
219: SRPSessionKey key = new SRPSessionKey(username, sessionID);
220: boolean trace = log.isTraceEnabled();
221: if (trace)
222: log.trace("verify, " + key);
223: SRPServerSession session = (SRPServerSession) sessionMap
224: .get(key);
225: if (session == null)
226: throw new SecurityException(
227: "Failed to find active session for username: "
228: + username);
229:
230: if (session.verify(M1) == false)
231: throw new SecurityException("Failed to verify M1");
232:
233: /* If there is a auxChallenge have the verierStore verify the data
234: */
235: if (auxChallenge != null) {
236: // See if this is an encrypted object
237: if (auxChallenge instanceof SealedObject) {
238: if (trace)
239: log.trace("Decrypting sealed object");
240: SRPParameters params = session.getParameters();
241: Object challenge = null;
242: try {
243: byte[] skey = session.getSessionKey();
244: Object tmpKey = Util.createSecretKey(
245: params.cipherAlgorithm, skey);
246: challenge = Util.accessSealedObject(
247: params.cipherAlgorithm, tmpKey,
248: params.cipherIV, auxChallenge);
249: } catch (GeneralSecurityException e) {
250: throw new RemoteException(
251: "Failed to access SealedObject", e);
252: }
253: auxChallenge = challenge;
254: }
255: if (trace)
256: log.trace("Verifing aux challenge");
257: this .verifierStore.verifyUserChallenge(username,
258: auxChallenge);
259: } else if (requireAuxChallenge == true) {
260: throw new RemoteException(
261: "A non-null auxChallenge is required for verification");
262: }
263:
264: // Inform the listener the user has been validated
265: if (listener != null)
266: listener.verifiedUser(key, session);
267: if (trace)
268: log.trace("verify, completed " + key);
269:
270: return session.getServerResponse();
271: }
272:
273: /** Close the SRP session for the given username.
274: */
275: public void close(String username) throws SecurityException,
276: RemoteException {
277: this .close(username, 0);
278: }
279:
280: public void close(String username, int sessionID)
281: throws SecurityException, RemoteException {
282: SRPSessionKey key = new SRPSessionKey(username, sessionID);
283: boolean trace = log.isTraceEnabled();
284: if (trace)
285: log.trace("close, " + key);
286: SRPServerSession session = (SRPServerSession) sessionMap
287: .remove(key);
288: if (session == null)
289: throw new SecurityException(
290: "Failed to find active session for username: "
291: + username);
292: if (listener != null)
293: listener.closedUserSession(key);
294: if (trace)
295: log.trace("close, completed " + key);
296: }
297:
298: private static synchronized Integer nextSessionID() {
299: return new Integer(userSessionCount++);
300: }
301: }
|