/* * Basic attempt at an MD5 encryption htpasswd utility class. * Why don't we get the same hash as htpasswd -m ??? Maybe MD5 algols are different? * hmmmm. * */ package org.crosswire.utils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.security.MessageDigest; import java.util.HashMap; import java.util.Set; import java.util.UUID; public class HTPassword { static final String AP_MD5PW_ID = "$apr1$"; static final int APR_MD5_DIGESTSIZE = 16; String file = null; static class Entry { public Entry(String user, String pw) { this.user = user; this.pw = pw; } public String user; public String pw; } HashMap entries = new HashMap(); public HTPassword(String file) { this.file = file; BufferedReader in; try { in = new BufferedReader(new FileReader(file)); for (String line = in.readLine(); line != null; line = in.readLine()) { int offset = line.indexOf(":"); if (offset > 0) { entries.put(line.substring(0, offset), new Entry(line.substring(0, offset), line.substring(offset+1))); } } in.close(); } catch (Exception e) { e.printStackTrace(); } } public Set getUsers() { return entries.keySet(); } public String getMunged(String user) { String pw = null; Entry e = entries.get(user); if (e != null) pw = e.pw; return pw; } public String encryptString(String input) { return encryptString(input, AP_MD5PW_ID + UUID.randomUUID().toString()); } static String to64(int v, int n) { String retVal = ""; String itoa64 = /* 0 ... 63 => ASCII - 64 */ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; while (--n >= 0) { retVal += itoa64.charAt(v & 0x3f); v >>= 6; } return retVal; } private static int unsigned(byte v) { return ((v<0)?256+v:v); } public String encryptString(String input, String salt) { String encrypted = null; try { MessageDigest md = MessageDigest.getInstance("MD5"); MessageDigest md2 = (MessageDigest)md.clone(); md.reset(); md2.reset(); // if it starts with magic string, skip it if (salt.startsWith(AP_MD5PW_ID)) { salt = salt.substring(AP_MD5PW_ID.length()); } int ep = salt.indexOf('$'); if (ep < 0) ep = salt.length(); if (ep > 8) ep = 8; salt = salt.substring(0, ep); md.update(input.getBytes()); md.update(AP_MD5PW_ID.getBytes()); md.update(salt.getBytes()); md2.update(input.getBytes()); md2.update(salt.getBytes()); md2.update(input.getBytes()); byte md2digest[] = md2.digest(); for (int pl = input.length(); pl > 0; pl -= APR_MD5_DIGESTSIZE) { md.update(md2digest, 0, (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); } for (int i = input.length(); i != 0; i >>= 1) { if ((i & 1) != 0) { md.update(md2digest, 0, 1); } else { md.update(input.getBytes(), 0, 1); } } encrypted = AP_MD5PW_ID; encrypted += salt; encrypted += "$"; byte mddigest[] = md.digest(); for (int i = 0; i < 1000; i++) { md2.reset(); if ((i & 1) != 0) { md2.update(input.getBytes()); } else { md2.update(mddigest, 0, APR_MD5_DIGESTSIZE); } if ((i % 3) != 0) { md2.update(salt.getBytes()); } if ((i % 7) != 0) { md2.update(input.getBytes()); } if ((i & 1) != 0) { md2.update(mddigest, 0, APR_MD5_DIGESTSIZE); } else { md2.update(input.getBytes()); } mddigest = md2.digest(); } int hash[] = new int[APR_MD5_DIGESTSIZE]; for (int i = 0; i < APR_MD5_DIGESTSIZE; i++) hash[i] = unsigned(mddigest[i]); int l = (hash[0] << 16) | (hash[6] << 8) | hash[12]; encrypted += to64(l, 4); l = (hash[1] << 16) | (hash[7] << 8) | hash[13]; encrypted += to64(l, 4); l = (hash[2] << 16) | (hash[8] << 8) | hash[14]; encrypted += to64(l, 4); l = (hash[3] << 16) | (hash[9] << 8) | hash[15]; encrypted += to64(l, 4); l = (hash[4] << 16) | (hash[10] << 8) | hash[5]; encrypted += to64(l, 4); l = hash[11]; encrypted += to64(l, 2); } catch (Exception e) { e.printStackTrace(); } return encrypted; } public boolean checkPassword(String user, String pw) { String munged = getMunged(user); return encryptString(pw, munged).equals(munged); } static synchronized public void setAccount(String file, String user, String pw) { HTPassword p = new HTPassword(file); p.entries.put(user, new Entry(user, p.encryptString(pw))); BufferedWriter out; try { out = new BufferedWriter(new FileWriter(file, false)); for (Entry e: p.entries.values()) { out.write(e.user+":"+e.pw); out.newLine(); } out.close(); } catch (Exception e) { e.printStackTrace(); } } /** * test code */ public static void main(String[] args) { HTPassword password = new HTPassword("passwd"); for (String user : password.getUsers()) { System.out.println(user); } HTPassword.setAccount("passwd", "scribe", "hmmmm"); HTPassword.setAccount("passwd", "scrsdfaibe", "hmmmm"); password = new HTPassword("passwd"); System.out.println("working: "+password.checkPassword("scribe", "hmmmm")); } }