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