1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62:
63:
103: public class PasswordFile
104: {
105:
106: private static final String USER_FIELD = "user";
107: private static final String VERIFIERS_FIELD = "verifier";
108: private static final String SALT_FIELD = "salt";
109: private static final String CONFIG_FIELD = "config";
110: private static String DEFAULT_FILE;
111: static
112: {
113: DEFAULT_FILE = System.getProperty(SRPRegistry.PASSWORD_FILE,
114: SRPRegistry.DEFAULT_PASSWORD_FILE);
115: }
116:
117: private static final HashMap srps;
118: static
119: {
120: final HashMap map = new HashMap(SRPRegistry.SRP_ALGORITHMS.length);
121:
122: map.put("0", SRP.instance(SRPRegistry.SRP_ALGORITHMS[0]));
123: for (int i = 1; i < SRPRegistry.SRP_ALGORITHMS.length; i++)
124: {
125: try
126: {
127: map.put(String.valueOf(i),
128: SRP.instance(SRPRegistry.SRP_ALGORITHMS[i]));
129: }
130: catch (Exception x)
131: {
132: System.err.println("Ignored: " + x);
133: x.printStackTrace(System.err);
134: }
135: }
136: srps = map;
137: }
138:
139: private String confName, pwName, pw2Name;
140: private File configFile, passwdFile, passwd2File;
141: private long lastmodPasswdFile, lastmodPasswd2File;
142: private HashMap entries = new HashMap();
143: private HashMap configurations = new HashMap();
144:
145: private static final BigInteger[] Nsrp = new BigInteger[] {
146: SRPAlgorithm.N_2048,
147: SRPAlgorithm.N_1536,
148: SRPAlgorithm.N_1280,
149: SRPAlgorithm.N_1024,
150: SRPAlgorithm.N_768,
151: SRPAlgorithm.N_640,
152: SRPAlgorithm.N_512 };
153:
154: public PasswordFile() throws IOException
155: {
156: this(DEFAULT_FILE);
157: }
158:
159: public PasswordFile(final File pwFile) throws IOException
160: {
161: this(pwFile.getAbsolutePath());
162: }
163:
164: public PasswordFile(final String pwName) throws IOException
165: {
166: this(pwName, pwName + "2", pwName + ".conf");
167: }
168:
169: public PasswordFile(final String pwName, final String confName)
170: throws IOException
171: {
172: this(pwName, pwName + "2", confName);
173: }
174:
175: public PasswordFile(final String pwName, final String pw2Name,
176: final String confName) throws IOException
177: {
178: super();
179:
180: this.pwName = pwName;
181: this.pw2Name = pw2Name;
182: this.confName = confName;
183:
184: readOrCreateConf();
185: update();
186: }
187:
188:
196: private static final String nameToID(final String mdName)
197: {
198: if (Registry.SHA_HASH.equalsIgnoreCase(mdName)
199: || Registry.SHA1_HASH.equalsIgnoreCase(mdName)
200: || Registry.SHA160_HASH.equalsIgnoreCase(mdName))
201: return "0";
202: else if (Registry.MD5_HASH.equalsIgnoreCase(mdName))
203: return "1";
204: else if (Registry.RIPEMD128_HASH.equalsIgnoreCase(mdName))
205: return "2";
206: else if (Registry.RIPEMD160_HASH.equalsIgnoreCase(mdName))
207: return "3";
208: else if (Registry.SHA256_HASH.equalsIgnoreCase(mdName))
209: return "4";
210: else if (Registry.SHA384_HASH.equalsIgnoreCase(mdName))
211: return "5";
212: else if (Registry.SHA512_HASH.equalsIgnoreCase(mdName))
213: return "6";
214: return "0";
215: }
216:
217:
234: public synchronized boolean containsConfig(final String index)
235: throws IOException
236: {
237: checkCurrent();
238: return configurations.containsKey(index);
239: }
240:
241:
254: public synchronized String[] lookupConfig(final String index)
255: throws IOException
256: {
257: checkCurrent();
258: String[] result = null;
259: if (configurations.containsKey(index))
260: result = (String[]) configurations.get(index);
261: return result;
262: }
263:
264: public synchronized boolean contains(final String user) throws IOException
265: {
266: checkCurrent();
267: return entries.containsKey(user);
268: }
269:
270: public synchronized void add(final String user, final String passwd,
271: final byte[] salt, final String index)
272: throws IOException
273: {
274: checkCurrent();
275: if (entries.containsKey(user))
276: throw new UserAlreadyExistsException(user);
277: final HashMap fields = new HashMap(4);
278: fields.put(USER_FIELD, user);
279: fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index));
280: fields.put(SALT_FIELD, Util.toBase64(salt));
281: fields.put(CONFIG_FIELD, index);
282: entries.put(user, fields);
283: savePasswd();
284: }
285:
286: public synchronized void changePasswd(final String user, final String passwd)
287: throws IOException
288: {
289: checkCurrent();
290: if (! entries.containsKey(user))
291: throw new NoSuchUserException(user);
292: final HashMap fields = (HashMap) entries.get(user);
293: final byte[] salt;
294: try
295: {
296: salt = Util.fromBase64((String) fields.get(SALT_FIELD));
297: }
298: catch (NumberFormatException x)
299: {
300: throw new IOException("Password file corrupt");
301: }
302: final String index = (String) fields.get(CONFIG_FIELD);
303: fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index));
304: entries.put(user, fields);
305: savePasswd();
306: }
307:
308: public synchronized void savePasswd() throws IOException
309: {
310: final FileOutputStream f1 = new FileOutputStream(passwdFile);
311: final FileOutputStream f2 = new FileOutputStream(passwd2File);
312: PrintWriter pw1 = null;
313: PrintWriter pw2 = null;
314: try
315: {
316: pw1 = new PrintWriter(f1, true);
317: pw2 = new PrintWriter(f2, true);
318: this.writePasswd(pw1, pw2);
319: }
320: finally
321: {
322: if (pw1 != null)
323: try
324: {
325: pw1.flush();
326: }
327: finally
328: {
329: pw1.close();
330: }
331: if (pw2 != null)
332: try
333: {
334: pw2.flush();
335: }
336: finally
337: {
338: pw2.close();
339: }
340: try
341: {
342: f1.close();
343: }
344: catch (IOException ignored)
345: {
346: }
347: try
348: {
349: f2.close();
350: }
351: catch (IOException ignored)
352: {
353: }
354: }
355: lastmodPasswdFile = passwdFile.lastModified();
356: lastmodPasswd2File = passwd2File.lastModified();
357: }
358:
359:
370: public synchronized String[] lookup(final String user, final String mdName)
371: throws IOException
372: {
373: checkCurrent();
374: if (! entries.containsKey(user))
375: throw new NoSuchUserException(user);
376: final HashMap fields = (HashMap) entries.get(user);
377: final HashMap verifiers = (HashMap) fields.get(VERIFIERS_FIELD);
378: final String salt = (String) fields.get(SALT_FIELD);
379: final String index = (String) fields.get(CONFIG_FIELD);
380: final String verifier = (String) verifiers.get(nameToID(mdName));
381: return new String[] { verifier, salt, index };
382: }
383:
384: private synchronized void readOrCreateConf() throws IOException
385: {
386: configurations.clear();
387: final FileInputStream fis;
388: configFile = new File(confName);
389: try
390: {
391: fis = new FileInputStream(configFile);
392: readConf(fis);
393: }
394: catch (FileNotFoundException x)
395: {
396: final String g = Util.toBase64(Util.trim(new BigInteger("2")));
397: String index, N;
398: for (int i = 0; i < Nsrp.length; i++)
399: {
400: index = String.valueOf(i + 1);
401: N = Util.toBase64(Util.trim(Nsrp[i]));
402: configurations.put(index, new String[] { N, g });
403: }
404: FileOutputStream f0 = null;
405: PrintWriter pw0 = null;
406: try
407: {
408: f0 = new FileOutputStream(configFile);
409: pw0 = new PrintWriter(f0, true);
410: this.writeConf(pw0);
411: }
412: finally
413: {
414: if (pw0 != null)
415: pw0.close();
416: else if (f0 != null)
417: f0.close();
418: }
419: }
420: }
421:
422: private void readConf(final InputStream in) throws IOException
423: {
424: final BufferedReader din = new BufferedReader(new InputStreamReader(in));
425: String line, index, N, g;
426: StringTokenizer st;
427: while ((line = din.readLine()) != null)
428: {
429: st = new StringTokenizer(line, ":");
430: try
431: {
432: index = st.nextToken();
433: N = st.nextToken();
434: g = st.nextToken();
435: }
436: catch (NoSuchElementException x)
437: {
438: throw new IOException("SRP password configuration file corrupt");
439: }
440: configurations.put(index, new String[] { N, g });
441: }
442: }
443:
444: private void writeConf(final PrintWriter pw)
445: {
446: String ndx;
447: String[] mpi;
448: StringBuffer sb;
449: for (Iterator it = configurations.keySet().iterator(); it.hasNext();)
450: {
451: ndx = (String) it.next();
452: mpi = (String[]) configurations.get(ndx);
453: sb = new StringBuffer(ndx)
454: .append(":").append(mpi[0])
455: .append(":").append(mpi[1]);
456: pw.println(sb.toString());
457: }
458: }
459:
460:
475: private HashMap newVerifiers(final String user, final byte[] s,
476: final String password, final String index)
477: throws UnsupportedEncodingException
478: {
479:
480: final String[] mpi = (String[]) configurations.get(index);
481: final BigInteger N = new BigInteger(1, Util.fromBase64(mpi[0]));
482: final BigInteger g = new BigInteger(1, Util.fromBase64(mpi[1]));
483: final HashMap result = new HashMap(srps.size());
484: BigInteger x, v;
485: SRP srp;
486: for (int i = 0; i < srps.size(); i++)
487: {
488: final String digestID = String.valueOf(i);
489: srp = (SRP) srps.get(digestID);
490: x = new BigInteger(1, srp.computeX(s, user, password));
491: v = g.modPow(x, N);
492: final String verifier = Util.toBase64(v.toByteArray());
493: result.put(digestID, verifier);
494: }
495: return result;
496: }
497:
498: private synchronized void update() throws IOException
499: {
500: entries.clear();
501: FileInputStream fis;
502: passwdFile = new File(pwName);
503: lastmodPasswdFile = passwdFile.lastModified();
504: try
505: {
506: fis = new FileInputStream(passwdFile);
507: readPasswd(fis);
508: }
509: catch (FileNotFoundException ignored)
510: {
511: }
512: passwd2File = new File(pw2Name);
513: lastmodPasswd2File = passwd2File.lastModified();
514: try
515: {
516: fis = new FileInputStream(passwd2File);
517: readPasswd2(fis);
518: }
519: catch (FileNotFoundException ignored)
520: {
521: }
522: }
523:
524: private void checkCurrent() throws IOException
525: {
526: if (passwdFile.lastModified() > lastmodPasswdFile
527: || passwd2File.lastModified() > lastmodPasswd2File)
528: update();
529: }
530:
531: private void readPasswd(final InputStream in) throws IOException
532: {
533: final BufferedReader din = new BufferedReader(new InputStreamReader(in));
534: String line, user, verifier, salt, index;
535: StringTokenizer st;
536: while ((line = din.readLine()) != null)
537: {
538: st = new StringTokenizer(line, ":");
539: try
540: {
541: user = st.nextToken();
542: verifier = st.nextToken();
543: salt = st.nextToken();
544: index = st.nextToken();
545: }
546: catch (NoSuchElementException x)
547: {
548: throw new IOException("SRP base password file corrupt");
549: }
550: final HashMap verifiers = new HashMap(6);
551: verifiers.put("0", verifier);
552: final HashMap fields = new HashMap(4);
553: fields.put(USER_FIELD, user);
554: fields.put(VERIFIERS_FIELD, verifiers);
555: fields.put(SALT_FIELD, salt);
556: fields.put(CONFIG_FIELD, index);
557: entries.put(user, fields);
558: }
559: }
560:
561: private void readPasswd2(final InputStream in) throws IOException
562: {
563: final BufferedReader din = new BufferedReader(new InputStreamReader(in));
564: String line, digestID, user, verifier;
565: StringTokenizer st;
566: HashMap fields, verifiers;
567: while ((line = din.readLine()) != null)
568: {
569: st = new StringTokenizer(line, ":");
570: try
571: {
572: digestID = st.nextToken();
573: user = st.nextToken();
574: verifier = st.nextToken();
575: }
576: catch (NoSuchElementException x)
577: {
578: throw new IOException("SRP extended password file corrupt");
579: }
580: fields = (HashMap) entries.get(user);
581: if (fields != null)
582: {
583: verifiers = (HashMap) fields.get(VERIFIERS_FIELD);
584: verifiers.put(digestID, verifier);
585: }
586: }
587: }
588:
589: private void writePasswd(final PrintWriter pw1, final PrintWriter pw2)
590: throws IOException
591: {
592: String user, digestID;
593: HashMap fields, verifiers;
594: StringBuffer sb1, sb2;
595: Iterator j;
596: final Iterator i = entries.keySet().iterator();
597: while (i.hasNext())
598: {
599: user = (String) i.next();
600: fields = (HashMap) entries.get(user);
601: if (! user.equals(fields.get(USER_FIELD)))
602: throw new IOException("Inconsistent SRP password data");
603: verifiers = (HashMap) fields.get(VERIFIERS_FIELD);
604: sb1 = new StringBuffer(user)
605: .append(":").append((String) verifiers.get("0"))
606: .append(":").append((String) fields.get(SALT_FIELD))
607: .append(":").append((String) fields.get(CONFIG_FIELD));
608: pw1.println(sb1.toString());
609:
610: j = verifiers.keySet().iterator();
611: while (j.hasNext())
612: {
613: digestID = (String) j.next();
614: if (! "0".equals(digestID))
615: {
616:
617: sb2 = new StringBuffer(digestID)
618: .append(":").append(user)
619: .append(":").append((String) verifiers.get(digestID));
620: pw2.println(sb2.toString());
621: }
622: }
623: }
624: }
625: }