Java/Security/MD5

Материал из Java эксперт
Версия от 06:49, 1 июня 2010; Admin (обсуждение | вклад) (1 версия)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Applet to serve as an s/key calculator application wrapper around otp class

   
/* applet to serve as an s/key calculator application wrapper 
 * around otp class.
 *
 * Copyright 1996, Harry Mantakos, harry@cs.umd.edu
 */
import java.awt.Button;
import java.awt.Color;
import java.awt.Event;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.util.StringTokenizer;
public class jotp extends java.applet.Applet {
    TextField otptf, chaltf, pwtf;
    final String md4label = "compute with MD4";
    final String md5label = "compute with MD5";
    final String version = "jotp 0.8";
    /* Just takes challenge info and passphrase info on the 
     * command line (ick) and spits out the resulting otp "words".
     *
     * Mainly for testing.
     */
    public static void main(String[] argv) {
  String seed, passphrase;
  int seq;
  otp otpwd;
  int hashalg;
  String hashtype;
  if ((argv.length < 3) || (argv.length > 4)) {
      System.err.println("usage: jotp sequence seed passphrase" + 
             "[md4|md5]");
      return;
  }
  seq = new Integer(argv[0]).intValue();
  seed = new String(argv[1]);
  passphrase = new String(argv[2]);
  if ((argv.length == 3) || argv[3].equals("4") ||
      argv[3].equals("md4") || argv[3].equals("MD4")) {
      hashtype = "md4";
      hashalg = otp.MD4;
  } else if (argv[3].equals("5") || argv[3].equals("md5") ||
       argv[3].equals("MD5")) {
      hashtype = "md5";
      hashalg = otp.MD5;
  } else {
      System.err.println("usage: jotp sequence seed passphrase " +
             "[4|md4|5|md5]");
      return;
  }
  otpwd = new otp(seq, seed, passphrase, hashalg);
  System.out.println("Using " + hashtype + ". Thinking...");
  otpwd.calc();
  System.out.println(otpwd);
    }
    public void init() {
  setBackground(Color.white);
  setLayout(new GridLayout(6,1));
  Panel panel1 = new Panel();
  add (panel1);
  Font titlefont = new Font("TimesRoman", Font.BOLD, 14);
  panel1.setFont(titlefont);
  panel1.add(new Label(String.valueOf(version) + 
           ": The Java OTP (aka S/Key) calculator!"));
  Panel panel2 = new Panel();
  panel2.setLayout(new FlowLayout());
  add (panel2);
  panel2.add(new Label("Challenge (e.g. \"55 latour1\"):"));
        chaltf = new TextField(24);
  panel2.add(chaltf);
  Panel panel3 = new Panel();
  panel3.setLayout(new FlowLayout());
  add(panel3);
  panel3.add(new Label("Secret Password:"));
  pwtf = new TextField(24);
  pwtf.setEchoCharacter("*");
  panel3.add(pwtf);
  Panel panel4 = new Panel();
  panel4.setLayout(new FlowLayout());
  add(panel4);
  panel4.add (new Button(String.valueOf(md4label)));
  panel4.add (new Button(String.valueOf(md5label)));
  Panel panel6 = new Panel();
  panel6.setLayout(new FlowLayout());
  add(panel6);
  panel6.add(new Label("One-Time Password:", Label.LEFT));
  otptf = new TextField(40);
  panel6.add(otptf);
  Panel panel7 = new Panel();
  add(panel7);
  panel7.add(new Label("jotp by Harry Mantakos, " + 
           "http://www.cs.umd.edu/~harry/jotp"));
    }
    public boolean action(Event evt, Object arg) {
  String tmpstr, tmpstr2, seed, passphrase;
  int seq, hashalg;
  otp otpwd;
  if (evt.target instanceof Button) {
      if (arg.equals(md5label)) {
    hashalg = otp.MD5;
      } else {
    hashalg = otp.MD4;
      }
      /* Split up challenge */
      tmpstr = chaltf.getText();
      StringTokenizer st = new StringTokenizer(tmpstr);
      if (st.countTokens() != 2) {
    otptf.setText("bogus challenge");
    return true;
      }
      tmpstr2 = st.nextToken();
      try {
    seq = (Integer.parseInt(tmpstr2));
      } catch (NumberFormatException e) {
    otptf.setText("bogus sequence number "" + tmpstr2 + """);
    return true;
      }
      seed = st.nextToken();
      passphrase = pwtf.getText();
/*      passphrase = "eat me";*/
            System.out.println("passphrase = " + passphrase);
      otptf.setText("Okay, thinking...");
      otpwd = new otp(seq, seed, passphrase, hashalg);
      otpwd.calc();
      otptf.setText(otpwd.toString());
  }
  return true;
    }
}

/* Class for implementing OTP (aka s/key) one-time password calculation
 * using the accompanying md class for md4 (and hopefully md5 eventually) 
 * based key calculation.
 *
 * The constructor is used to set the challenge info and passphrase,
 * and the calc() method calculates the otp. The results can either
 * be retrieved using the tolong() method, which gives you the 64 bits
 * "folded" hash in a single word, or else as a String of otp "words"
 * via toString().
 *
 * Cripes this is slow. How can we make it faster? 
 *
 * Copyright 1996 Harry Mantakos, harry@cs.umd.edu
 */
class otp {
    int seq;
    String seed, passphrase;
    byte hash[];
    int sha;
    final static byte MD4 = 4;
    final static byte MD5 = 5;
    otp(int n, String s, String p, int hashalg) {
  this.seq = n;
  this.seed = s;
  this.passphrase = p;
  this.sha = hashalg;
    }
    void calc() {
  if (this.sha == MD5) {
      this.md5calc();
  } else {
      this.md4calc();
        }
    }
    void md4calc() {
  int tmpseq = this.seq;
  md4 mdc;
  mdc = new md4(this.seed + this.passphrase);
  mdc.calc();
  this.hash = otpfoldregs(mdc.getregs());
  while (tmpseq > 0) {
      mdc = new md4(hash);
      mdc.calc();
      this.hash = otpfoldregs(mdc.getregs());
      tmpseq--;
  }
    }
    void md5calc() {
  int tmpseq = this.seq;
  md5 mdc;
  mdc = new md5(this.seed + this.passphrase);
  mdc.calc();
  this.hash = otpfoldregs(mdc.getregs());
  while (tmpseq > 0) {
      mdc = new md5(hash);
      mdc.calc();
      this.hash = otpfoldregs(mdc.getregs());
      tmpseq--;
  }
    }
    static byte[] otpfoldregs(int regs[]) {
  int ac, bd, i;
  byte fold[] = new byte[8];
  ac = regs[0] ^ regs[2];
  bd = regs[1] ^ regs[3];
  for (i=0; i < 4; i++) {
      fold[i] = (byte) (ac & 0xff);
      ac >>= 8;
  }
  for (i=4; i < 8; i++) {
      fold[i] = (byte) (bd & 0xff);
      bd >>= 8;
  }
  return fold;
    }
    long tolong() {
  long wi;
  int i;
  wi = 0;
  for (i=0; i < 8; i++) {
      wi <<= 8;
      wi |= (this.hash[i] & 0xff);
  }
  return wi;
    }
    public String toString() {
  long wi, tmplong;
  String tmpstr;
  int i, j;
  byte parity;
  wi = this.tolong();
  tmplong = wi;
  tmpstr = "";
  parity = 0;
  for (i = 0; i < 64; i+=2) {
      parity += tmplong & 0x3;
      tmplong >>= 2;
  }
  for (i=4; i >= 0; i--) {
      tmpstr += btoe((int) 
          ((wi >> (i * 11 + 9)) & 0x7ff)) + " ";
  }
  tmpstr += btoe((int) ((wi << 2) & 0x7fc) | (parity & 0x03));
  return tmpstr;
    }
    public static String btoe(int index) {
        if (index < words.length) {
          return words[index];
        } else {
           return "bogus";
        }
    }
static String words[] =
{        "A",     "ABE",   "ACE",   "ACT",   "AD",    "ADA",   "ADD",
"AGO",   "AID",   "AIM",   "AIR",   "ALL",   "ALP",   "AM",    "AMY",
"AN",    "ANA",   "AND",   "ANN",   "ANT",   "ANY",   "APE",   "APS",
"APT",   "ARC",   "ARE",   "ARK",   "ARM",   "ART",   "AS",    "ASH",
"ASK",   "AT",    "ATE",   "AUG",   "AUK",   "AVE",   "AWE",   "AWK",
"AWL",   "AWN",   "AX",    "AYE",   "BAD",   "BAG",   "BAH",   "BAM",
"BAN",   "BAR",   "BAT",   "BAY",   "BE",    "BED",   "BEE",   "BEG",
"BEN",   "BET",   "BEY",   "BIB",   "BID",   "BIG",   "BIN",   "BIT",
"BOB",   "BOG",   "BON",   "BOO",   "BOP",   "BOW",   "BOY",   "BUB",
"BUD",   "BUG",   "BUM",   "BUN",   "BUS",   "BUT",   "BUY",   "BY",
"BYE",   "CAB",   "CAL",   "CAM",   "CAN",   "CAP",   "CAR",   "CAT",
"CAW",   "COD",   "COG",   "COL",   "CON",   "COO",   "COP",   "COT",
"COW",   "COY",   "CRY",   "CUB",   "CUE",   "CUP",   "CUR",   "CUT",
"DAB",   "DAD",   "DAM",   "DAN",   "DAR",   "DAY",   "DEE",   "DEL",
"DEN",   "DES",   "DEW",   "DID",   "DIE",   "DIG",   "DIN",   "DIP",
"DO",    "DOE",   "DOG",   "DON",   "DOT",   "DOW",   "DRY",   "DUB",
"DUD",   "DUE",   "DUG",   "DUN",   "EAR",   "EAT",   "ED",    "EEL",
"EGG",   "EGO",   "ELI",   "ELK",   "ELM",   "ELY",   "EM",    "END",
"EST",   "ETC",   "EVA",   "EVE",   "EWE",   "EYE",   "FAD",   "FAN",
"FAR",   "FAT",   "FAY",   "FED",   "FEE",   "FEW",   "FIB",   "FIG",
"FIN",   "FIR",   "FIT",   "FLO",   "FLY",   "FOE",   "FOG",   "FOR",
"FRY",   "FUM",   "FUN",   "FUR",   "GAB",   "GAD",   "GAG",   "GAL",
"GAM",   "GAP",   "GAS",   "GAY",   "GEE",   "GEL",   "GEM",   "GET",
"GIG",   "GIL",   "GIN",   "GO",    "GOT",   "GUM",   "GUN",   "GUS",
"GUT",   "GUY",   "GYM",   "GYP",   "HA",    "HAD",   "HAL",   "HAM",
"HAN",   "HAP",   "HAS",   "HAT",   "HAW",   "HAY",   "HE",    "HEM",
"HEN",   "HER",   "HEW",   "HEY",   "HI",    "HID",   "HIM",   "HIP",
"HIS",   "HIT",   "HO",    "HOB",   "HOC",   "HOE",   "HOG",   "HOP",
"HOT",   "HOW",   "HUB",   "HUE",   "HUG",   "HUH",   "HUM",   "HUT",
"I",     "ICY",   "IDA",   "IF",    "IKE",   "ILL",   "INK",   "INN",
"IO",    "ION",   "IQ",    "IRA",   "IRE",   "IRK",   "IS",    "IT",
"ITS",   "IVY",   "JAB",   "JAG",   "JAM",   "JAN",   "JAR",   "JAW",
"JAY",   "JET",   "JIG",   "JIM",   "JO",    "JOB",   "JOE",   "JOG",
"JOT",   "JOY",   "JUG",   "JUT",   "KAY",   "KEG",   "KEN",   "KEY",
"KID",   "KIM",   "KIN",   "KIT",   "LA",    "LAB",   "LAC",   "LAD",
"LAG",   "LAM",   "LAP",   "LAW",   "LAY",   "LEA",   "LED",   "LEE",
"LEG",   "LEN",   "LEO",   "LET",   "LEW",   "LID",   "LIE",   "LIN",
"LIP",   "LIT",   "LO",    "LOB",   "LOG",   "LOP",   "LOS",   "LOT",
"LOU",   "LOW",   "LOY",   "LUG",   "LYE",   "MA",    "MAC",   "MAD",
"MAE",   "MAN",   "MAO",   "MAP",   "MAT",   "MAW",   "MAY",   "ME",
"MEG",   "MEL",   "MEN",   "MET",   "MEW",   "MID",   "MIN",   "MIT",
"MOB",   "MOD",   "MOE",   "MOO",   "MOP",   "MOS",   "MOT",   "MOW",
"MUD",   "MUG",   "MUM",   "MY",    "NAB",   "NAG",   "NAN",   "NAP",
"NAT",   "NAY",   "NE",    "NED",   "NEE",   "NET",   "NEW",   "NIB",
"NIL",   "NIP",   "NIT",   "NO",    "NOB",   "NOD",   "NON",   "NOR",
"NOT",   "NOV",   "NOW",   "NU",    "NUN",   "NUT",   "O",     "OAF",
"OAK",   "OAR",   "OAT",   "ODD",   "ODE",   "OF",    "OFF",   "OFT",
"OH",    "OIL",   "OK",    "OLD",   "ON",    "ONE",   "OR",    "ORB",
"ORE",   "ORR",   "OS",    "OTT",   "OUR",   "OUT",   "OVA",   "OW",
"OWE",   "OWL",   "OWN",   "OX",    "PA",    "PAD",   "PAL",   "PAM",
"PAN",   "PAP",   "PAR",   "PAT",   "PAW",   "PAY",   "PEA",   "PEG",
"PEN",   "PEP",   "PER",   "PET",   "PEW",   "PHI",   "PI",    "PIE",
"PIN",   "PIT",   "PLY",   "PO",    "POD",   "POE",   "POP",   "POT",
"POW",   "PRO",   "PRY",   "PUB",   "PUG",   "PUN",   "PUP",   "PUT",
"QUO",   "RAG",   "RAM",   "RAN",   "RAP",   "RAT",   "RAW",   "RAY",
"REB",   "RED",   "REP",   "RET",   "RIB",   "RID",   "RIG",   "RIM",
"RIO",   "RIP",   "ROB",   "ROD",   "ROE",   "RON",   "ROT",   "ROW",
"ROY",   "RUB",   "RUE",   "RUG",   "RUM",   "RUN",   "RYE",   "SAC",
"SAD",   "SAG",   "SAL",   "SAM",   "SAN",   "SAP",   "SAT",   "SAW",
"SAY",   "SEA",   "SEC",   "SEE",   "SEN",   "SET",   "SEW",   "SHE",
"SHY",   "SIN",   "SIP",   "SIR",   "SIS",   "SIT",   "SKI",   "SKY",
"SLY",   "SO",    "SOB",   "SOD",   "SON",   "SOP",   "SOW",   "SOY",
"SPA",   "SPY",   "SUB",   "SUD",   "SUE",   "SUM",   "SUN",   "SUP",
"TAB",   "TAD",   "TAG",   "TAN",   "TAP",   "TAR",   "TEA",   "TED",
"TEE",   "TEN",   "THE",   "THY",   "TIC",   "TIE",   "TIM",   "TIN",
"TIP",   "TO",    "TOE",   "TOG",   "TOM",   "TON",   "TOO",   "TOP",
"TOW",   "TOY",   "TRY",   "TUB",   "TUG",   "TUM",   "TUN",   "TWO",
"UN",    "UP",    "US",    "USE",   "VAN",   "VAT",   "VET",   "VIE",
"WAD",   "WAG",   "WAR",   "WAS",   "WAY",   "WE",    "WEB",   "WED",
"WEE",   "WET",   "WHO",   "WHY",   "WIN",   "WIT",   "WOK",   "WON",
"WOO",   "WOW",   "WRY",   "WU",    "YAM",   "YAP",   "YAW",   "YE",
"YEA",   "YES",   "YET",   "YOU",   "ABED",  "ABEL",  "ABET",  "ABLE",
"ABUT",  "ACHE",  "ACID",  "ACME",  "ACRE",  "ACTA",  "ACTS",  "ADAM",
"ADDS",  "ADEN",  "AFAR",  "AFRO",  "AGEE",  "AHEM",  "AHOY",  "AIDA",
"AIDE",  "AIDS",  "AIRY",  "AJAR",  "AKIN",  "ALAN",  "ALEC",  "ALGA",
"ALIA",  "ALLY",  "ALMA",  "ALOE",  "ALSO",  "ALTO",  "ALUM",  "ALVA",
"AMEN",  "AMES",  "AMID",  "AMMO",  "AMOK",  "AMOS",  "AMRA",  "ANDY",
"ANEW",  "ANNA",  "ANNE",  "ANTE",  "ANTI",  "AQUA",  "ARAB",  "ARCH",
"AREA",  "ARGO",  "ARID",  "ARMY",  "ARTS",  "ARTY",  "ASIA",  "ASKS",
"ATOM",  "AUNT",  "AURA",  "AUTO",  "AVER",  "AVID",  "AVIS",  "AVON",
"AVOW",  "AWAY",  "AWRY",  "BABE",  "BABY",  "BACH",  "BACK",  "BADE",
"BAIL",  "BAIT",  "BAKE",  "BALD",  "BALE",  "BALI",  "BALK",  "BALL",
"BALM",  "BAND",  "BANE",  "BANG",  "BANK",  "BARB",  "BARD",  "BARE",
"BARK",  "BARN",  "BARR",  "BASE",  "BASH",  "BASK",  "BASS",  "BATE",
"BATH",  "BAWD",  "BAWL",  "BEAD",  "BEAK",  "BEAM",  "BEAN",  "BEAR",
"BEAT",  "BEAU",  "BECK",  "BEEF",  "BEEN",  "BEER",  "BEET",  "BELA",
"BELL",  "BELT",  "BEND",  "BENT",  "BERG",  "BERN",  "BERT",  "BESS",
"BEST",  "BETA",  "BETH",  "BHOY",  "BIAS",  "BIDE",  "BIEN",  "BILE",
"BILK",  "BILL",  "BIND",  "BING",  "BIRD",  "BITE",  "BITS",  "BLAB",
"BLAT",  "BLED",  "BLEW",  "BLOB",  "BLOC",  "BLOT",  "BLOW",  "BLUE",
"BLUM",  "BLUR",  "BOAR",  "BOAT",  "BOCA",  "BOCK",  "BODE",  "BODY",
"BOGY",  "BOHR",  "BOIL",  "BOLD",  "BOLO",  "BOLT",  "BOMB",  "BONA",
"BOND",  "BONE",  "BONG",  "BONN",  "BONY",  "BOOK",  "BOOM",  "BOON",
"BOOT",  "BORE",  "BORG",  "BORN",  "BOSE",  "BOSS",  "BOTH",  "BOUT",
"BOWL",  "BOYD",  "BRAD",  "BRAE",  "BRAG",  "BRAN",  "BRAY",  "BRED",
"BREW",  "BRIG",  "BRIM",  "BROW",  "BUCK",  "BUDD",  "BUFF",  "BULB",
"BULK",  "BULL",  "BUNK",  "BUNT",  "BUOY",  "BURG",  "BURL",  "BURN",
"BURR",  "BURT",  "BURY",  "BUSH",  "BUSS",  "BUST",  "BUSY",  "BYTE",
"CADY",  "CAFE",  "CAGE",  "CAIN",  "CAKE",  "CALF",  "CALL",  "CALM",
"CAME",  "CANE",  "CANT",  "CARD",  "CARE",  "CARL",  "CARR",  "CART",
"CASE",  "CASH",  "CASK",  "CAST",  "CAVE",  "CEIL",  "CELL",  "CENT",
"CERN",  "CHAD",  "CHAR",  "CHAT",  "CHAW",  "CHEF",  "CHEN",  "CHEW",
"CHIC",  "CHIN",  "CHOU",  "CHOW",  "CHUB",  "CHUG",  "CHUM",  "CITE",
"CITY",  "CLAD",  "CLAM",  "CLAN",  "CLAW",  "CLAY",  "CLOD",  "CLOG",
"CLOT",  "CLUB",  "CLUE",  "COAL",  "COAT",  "COCA",  "COCK",  "COCO",
"CODA",  "CODE",  "CODY",  "COED",  "COIL",  "COIN",  "COKE",  "COLA",
"COLD",  "COLT",  "COMA",  "COMB",  "COME",  "COOK",  "COOL",  "COON",
"COOT",  "CORD",  "CORE",  "CORK",  "CORN",  "COST",  "COVE",  "COWL",
"CRAB",  "CRAG",  "CRAM",  "CRAY",  "CREW",  "CRIB",  "CROW",  "CRUD",
"CUBA",  "CUBE",  "CUFF",  "CULL",  "CULT",  "CUNY",  "CURB",  "CURD",
"CURE",  "CURL",  "CURT",  "CUTS",  "DADE",  "DALE",  "DAME",  "DANA",
"DANE",  "DANG",  "DANK",  "DARE",  "DARK",  "DARN",  "DART",  "DASH",
"DATA",  "DATE",  "DAVE",  "DAVY",  "DAWN",  "DAYS",  "DEAD",  "DEAF",
"DEAL",  "DEAN",  "DEAR",  "DEBT",  "DECK",  "DEED",  "DEEM",  "DEER",
"DEFT",  "DEFY",  "DELL",  "DENT",  "DENY",  "DESK",  "DIAL",  "DICE",
"DIED",  "DIET",  "DIME",  "DINE",  "DING",  "DINT",  "DIRE",  "DIRT",
"DISC",  "DISH",  "DISK",  "DIVE",  "DOCK",  "DOES",  "DOLE",  "DOLL",
"DOLT",  "DOME",  "DONE",  "DOOM",  "DOOR",  "DORA",  "DOSE",  "DOTE",
"DOUG",  "DOUR",  "DOVE",  "DOWN",  "DRAB",  "DRAG",  "DRAM",  "DRAW",
"DREW",  "DRUB",  "DRUG",  "DRUM",  "DUAL",  "DUCK",  "DUCT",  "DUEL",
"DUET",  "DUKE",  "DULL",  "DUMB",  "DUNE",  "DUNK",  "DUSK",  "DUST",
"DUTY",  "EACH",  "EARL",  "EARN",  "EASE",  "EAST",  "EASY",  "EBEN",
"ECHO",  "EDDY",  "EDEN",  "EDGE",  "EDGY",  "EDIT",  "EDNA",  "EGAN",
"ELAN",  "ELBA",  "ELLA",  "ELSE",  "EMIL",  "EMIT",  "EMMA",  "ENDS",
"ERIC",  "EROS",  "EVEN",  "EVER",  "EVIL",  "EYED",  "FACE",  "FACT",
"FADE",  "FAIL",  "FAIN",  "FAIR",  "FAKE",  "FALL",  "FAME",  "FANG",
"FARM",  "FAST",  "FATE",  "FAWN",  "FEAR",  "FEAT",  "FEED",  "FEEL",
"FEET",  "FELL",  "FELT",  "FEND",  "FERN",  "FEST",  "FEUD",  "FIEF",
"FIGS",  "FILE",  "FILL",  "FILM",  "FIND",  "FINE",  "FINK",  "FIRE",
"FIRM",  "FISH",  "FISK",  "FIST",  "FITS",  "FIVE",  "FLAG",  "FLAK",
"FLAM",  "FLAT",  "FLAW",  "FLEA",  "FLED",  "FLEW",  "FLIT",  "FLOC",
"FLOG",  "FLOW",  "FLUB",  "FLUE",  "FOAL",  "FOAM",  "FOGY",  "FOIL",
"FOLD",  "FOLK",  "FOND",  "FONT",  "FOOD",  "FOOL",  "FOOT",  "FORD",
"FORE",  "FORK",  "FORM",  "FORT",  "FOSS",  "FOUL",  "FOUR",  "FOWL",
"FRAU",  "FRAY",  "FRED",  "FREE",  "FRET",  "FREY",  "FROG",  "FROM",
"FUEL",  "FULL",  "FUME",  "FUND",  "FUNK",  "FURY",  "FUSE",  "FUSS",
"GAFF",  "GAGE",  "GAIL",  "GAIN",  "GAIT",  "GALA",  "GALE",  "GALL",
"GALT",  "GAME",  "GANG",  "GARB",  "GARY",  "GASH",  "GATE",  "GAUL",
"GAUR",  "GAVE",  "GAWK",  "GEAR",  "GELD",  "GENE",  "GENT",  "GERM",
"GETS",  "GIBE",  "GIFT",  "GILD",  "GILL",  "GILT",  "GINA",  "GIRD",
"GIRL",  "GIST",  "GIVE",  "GLAD",  "GLEE",  "GLEN",  "GLIB",  "GLOB",
"GLOM",  "GLOW",  "GLUE",  "GLUM",  "GLUT",  "GOAD",  "GOAL",  "GOAT",
"GOER",  "GOES",  "GOLD",  "GOLF",  "GONE",  "GONG",  "GOOD",  "GOOF",
"GORE",  "GORY",  "GOSH",  "GOUT",  "GOWN",  "GRAB",  "GRAD",  "GRAY",
"GREG",  "GREW",  "GREY",  "GRID",  "GRIM",  "GRIN",  "GRIT",  "GROW",
"GRUB",  "GULF",  "GULL",  "GUNK",  "GURU",  "GUSH",  "GUST",  "GWEN",
"GWYN",  "HAAG",  "HAAS",  "HACK",  "HAIL",  "HAIR",  "HALE",  "HALF",
"HALL",  "HALO",  "HALT",  "HAND",  "HANG",  "HANK",  "HANS",  "HARD",
"HARK",  "HARM",  "HART",  "HASH",  "HAST",  "HATE",  "HATH",  "HAUL",
"HAVE",  "HAWK",  "HAYS",  "HEAD",  "HEAL",  "HEAR",  "HEAT",  "HEBE",
"HECK",  "HEED",  "HEEL",  "HEFT",  "HELD",  "HELL",  "HELM",  "HERB",
"HERD",  "HERE",  "HERO",  "HERS",  "HESS",  "HEWN",  "HICK",  "HIDE",
"HIGH",  "HIKE",  "HILL",  "HILT",  "HIND",  "HINT",  "HIRE",  "HISS",
"HIVE",  "HOBO",  "HOCK",  "HOFF",  "HOLD",  "HOLE",  "HOLM",  "HOLT",
"HOME",  "HONE",  "HONK",  "HOOD",  "HOOF",  "HOOK",  "HOOT",  "HORN",
"HOSE",  "HOST",  "HOUR",  "HOVE",  "HOWE",  "HOWL",  "HOYT",  "HUCK",
"HUED",  "HUFF",  "HUGE",  "HUGH",  "HUGO",  "HULK",  "HULL",  "HUNK",
"HUNT",  "HURD",  "HURL",  "HURT",  "HUSH",  "HYDE",  "HYMN",  "IBIS",
"ICON",  "IDEA",  "IDLE",  "IFFY",  "INCA",  "INCH",  "INTO",  "IONS",
"IOTA",  "IOWA",  "IRIS",  "IRMA",  "IRON",  "ISLE",  "ITCH",  "ITEM",
"IVAN",  "JACK",  "JADE",  "JAIL",  "JAKE",  "JANE",  "JAVA",  "JEAN",
"JEFF",  "JERK",  "JESS",  "JEST",  "JIBE",  "JILL",  "JILT",  "JIVE",
"JOAN",  "JOBS",  "JOCK",  "JOEL",  "JOEY",  "JOHN",  "JOIN",  "JOKE",
"JOLT",  "JOVE",  "JUDD",  "JUDE",  "JUDO",  "JUDY",  "JUJU",  "JUKE",
"JULY",  "JUNE",  "JUNK",  "JUNO",  "JURY",  "JUST",  "JUTE",  "KAHN",
"KALE",  "KANE",  "KANT",  "KARL",  "KATE",  "KEEL",  "KEEN",  "KENO",
"KENT",  "KERN",  "KERR",  "KEYS",  "KICK",  "KILL",  "KIND",  "KING",
"KIRK",  "KISS",  "KITE",  "KLAN",  "KNEE",  "KNEW",  "KNIT",  "KNOB",
"KNOT",  "KNOW",  "KOCH",  "KONG",  "KUDO",  "KURD",  "KURT",  "KYLE",
"LACE",  "LACK",  "LACY",  "LADY",  "LAID",  "LAIN",  "LAIR",  "LAKE",
"LAMB",  "LAME",  "LAND",  "LANE",  "LANG",  "LARD",  "LARK",  "LASS",
"LAST",  "LATE",  "LAUD",  "LAVA",  "LAWN",  "LAWS",  "LAYS",  "LEAD",
"LEAF",  "LEAK",  "LEAN",  "LEAR",  "LEEK",  "LEER",  "LEFT",  "LEND",
"LENS",  "LENT",  "LEON",  "LESK",  "LESS",  "LEST",  "LETS",  "LIAR",
"LICE",  "LICK",  "LIED",  "LIEN",  "LIES",  "LIEU",  "LIFE",  "LIFT",
"LIKE",  "LILA",  "LILT",  "LILY",  "LIMA",  "LIMB",  "LIME",  "LIND",
"LINE",  "LINK",  "LINT",  "LION",  "LISA",  "LIST",  "LIVE",  "LOAD",
"LOAF",  "LOAM",  "LOAN",  "LOCK",  "LOFT",  "LOGE",  "LOIS",  "LOLA",
"LONE",  "LONG",  "LOOK",  "LOON",  "LOOT",  "LORD",  "LORE",  "LOSE",
"LOSS",  "LOST",  "LOUD",  "LOVE",  "LOWE",  "LUCK",  "LUCY",  "LUGE",
"LUKE",  "LULU",  "LUND",  "LUNG",  "LURA",  "LURE",  "LURK",  "LUSH",
"LUST",  "LYLE",  "LYNN",  "LYON",  "LYRA",  "MACE",  "MADE",  "MAGI",
"MAID",  "MAIL",  "MAIN",  "MAKE",  "MALE",  "MALI",  "MALL",  "MALT",
"MANA",  "MANN",  "MANY",  "MARC",  "MARE",  "MARK",  "MARS",  "MART",
"MARY",  "MASH",  "MASK",  "MASS",  "MAST",  "MATE",  "MATH",  "MAUL",
"MAYO",  "MEAD",  "MEAL",  "MEAN",  "MEAT",  "MEEK",  "MEET",  "MELD",
"MELT",  "MEMO",  "MEND",  "MENU",  "MERT",  "MESH",  "MESS",  "MICE",
"MIKE",  "MILD",  "MILE",  "MILK",  "MILL",  "MILT",  "MIMI",  "MIND",
"MINE",  "MINI",  "MINK",  "MINT",  "MIRE",  "MISS",  "MIST",  "MITE",
"MITT",  "MOAN",  "MOAT",  "MOCK",  "MODE",  "MOLD",  "MOLE",  "MOLL",
"MOLT",  "MONA",  "MONK",  "MONT",  "MOOD",  "MOON",  "MOOR",  "MOOT",
"MORE",  "MORN",  "MORT",  "MOSS",  "MOST",  "MOTH",  "MOVE",  "MUCH",
"MUCK",  "MUDD",  "MUFF",  "MULE",  "MULL",  "MURK",  "MUSH",  "MUST",
"MUTE",  "MUTT",  "MYRA",  "MYTH",  "NAGY",  "NAIL",  "NAIR",  "NAME",
"NARY",  "NASH",  "NAVE",  "NAVY",  "NEAL",  "NEAR",  "NEAT",  "NECK",
"NEED",  "NEIL",  "NELL",  "NEON",  "NERO",  "NESS",  "NEST",  "NEWS",
"NEWT",  "NIBS",  "NICE",  "NICK",  "NILE",  "NINA",  "NINE",  "NOAH",
"NODE",  "NOEL",  "NOLL",  "NONE",  "NOOK",  "NOON",  "NORM",  "NOSE",
"NOTE",  "NOUN",  "NOVA",  "NUDE",  "NULL",  "NUMB",  "OATH",  "OBEY",
"OBOE",  "ODIN",  "OHIO",  "OILY",  "OINT",  "OKAY",  "OLAF",  "OLDY",
"OLGA",  "OLIN",  "OMAN",  "OMEN",  "OMIT",  "ONCE",  "ONES",  "ONLY",
"ONTO",  "ONUS",  "ORAL",  "ORGY",  "OSLO",  "OTIS",  "OTTO",  "OUCH",
"OUST",  "OUTS",  "OVAL",  "OVEN",  "OVER",  "OWLY",  "OWNS",  "QUAD",
"QUIT",  "QUOD",  "RACE",  "RACK",  "RACY",  "RAFT",  "RAGE",  "RAID",
"RAIL",  "RAIN",  "RAKE",  "RANK",  "RANT",  "RARE",  "RASH",  "RATE",
"RAVE",  "RAYS",  "READ",  "REAL",  "REAM",  "REAR",  "RECK",  "REED",
"REEF",  "REEK",  "REEL",  "REID",  "REIN",  "RENA",  "REND",  "RENT",
"REST",  "RICE",  "RICH",  "RICK",  "RIDE",  "RIFT",  "RILL",  "RIME",
"RING",  "RINK",  "RISE",  "RISK",  "RITE",  "ROAD",  "ROAM",  "ROAR",
"ROBE",  "ROCK",  "RODE",  "ROIL",  "ROLL",  "ROME",  "ROOD",  "ROOF",
"ROOK",  "ROOM",  "ROOT",  "ROSA",  "ROSE",  "ROSS",  "ROSY",  "ROTH",
"ROUT",  "ROVE",  "ROWE",  "ROWS",  "RUBE",  "RUBY",  "RUDE",  "RUDY",
"RUIN",  "RULE",  "RUNG",  "RUNS",  "RUNT",  "RUSE",  "RUSH",  "RUSK",
"RUSS",  "RUST",  "RUTH",  "SACK",  "SAFE",  "SAGE",  "SAID",  "SAIL",
"SALE",  "SALK",  "SALT",  "SAME",  "SAND",  "SANE",  "SANG",  "SANK",
"SARA",  "SAUL",  "SAVE",  "SAYS",  "SCAN",  "SCAR",  "SCAT",  "SCOT",
"SEAL",  "SEAM",  "SEAR",  "SEAT",  "SEED",  "SEEK",  "SEEM",  "SEEN",
"SEES",  "SELF",  "SELL",  "SEND",  "SENT",  "SETS",  "SEWN",  "SHAG",
"SHAM",  "SHAW",  "SHAY",  "SHED",  "SHIM",  "SHIN",  "SHOD",  "SHOE",
"SHOT",  "SHOW",  "SHUN",  "SHUT",  "SICK",  "SIDE",  "SIFT",  "SIGH",
"SIGN",  "SILK",  "SILL",  "SILO",  "SILT",  "SINE",  "SING",  "SINK",
"SIRE",  "SITE",  "SITS",  "SITU",  "SKAT",  "SKEW",  "SKID",  "SKIM",
"SKIN",  "SKIT",  "SLAB",  "SLAM",  "SLAT",  "SLAY",  "SLED",  "SLEW",
"SLID",  "SLIM",  "SLIT",  "SLOB",  "SLOG",  "SLOT",  "SLOW",  "SLUG",
"SLUM",  "SLUR",  "SMOG",  "SMUG",  "SNAG",  "SNOB",  "SNOW",  "SNUB",
"SNUG",  "SOAK",  "SOAR",  "SOCK",  "SODA",  "SOFA",  "SOFT",  "SOIL",
"SOLD",  "SOME",  "SONG",  "SOON",  "SOOT",  "SORE",  "SORT",  "SOUL",
"SOUR",  "SOWN",  "STAB",  "STAG",  "STAN",  "STAR",  "STAY",  "STEM",
"STEW",  "STIR",  "STOW",  "STUB",  "STUN",  "SUCH",  "SUDS",  "SUIT",
"SULK",  "SUMS",  "SUNG",  "SUNK",  "SURE",  "SURF",  "SWAB",  "SWAG",
"SWAM",  "SWAN",  "SWAT",  "SWAY",  "SWIM",  "SWUM",  "TACK",  "TACT",
"TAIL",  "TAKE",  "TALE",  "TALK",  "TALL",  "TANK",  "TASK",  "TATE",
"TAUT",  "TEAL",  "TEAM",  "TEAR",  "TECH",  "TEEM",  "TEEN",  "TEET",
"TELL",  "TEND",  "TENT",  "TERM",  "TERN",  "TESS",  "TEST",  "THAN",
"THAT",  "THEE",  "THEM",  "THEN",  "THEY",  "THIN",  "THIS",  "THUD",
"THUG",  "TICK",  "TIDE",  "TIDY",  "TIED",  "TIER",  "TILE",  "TILL",
"TILT",  "TIME",  "TINA",  "TINE",  "TINT",  "TINY",  "TIRE",  "TOAD",
"TOGO",  "TOIL",  "TOLD",  "TOLL",  "TONE",  "TONG",  "TONY",  "TOOK",
"TOOL",  "TOOT",  "TORE",  "TORN",  "TOTE",  "TOUR",  "TOUT",  "TOWN",
"TRAG",  "TRAM",  "TRAY",  "TREE",  "TREK",  "TRIG",  "TRIM",  "TRIO",
"TROD",  "TROT",  "TROY",  "TRUE",  "TUBA",  "TUBE",  "TUCK",  "TUFT",
"TUNA",  "TUNE",  "TUNG",  "TURF",  "TURN",  "TUSK",  "TWIG",  "TWIN",
"TWIT",  "ULAN",  "UNIT",  "URGE",  "USED",  "USER",  "USES",  "UTAH",
"VAIL",  "VAIN",  "VALE",  "VARY",  "VASE",  "VAST",  "VEAL",  "VEDA",
"VEIL",  "VEIN",  "VEND",  "VENT",  "VERB",  "VERY",  "VETO",  "VICE",
"VIEW",  "VINE",  "VISE",  "VOID",  "VOLT",  "VOTE",  "WACK",  "WADE",
"WAGE",  "WAIL",  "WAIT",  "WAKE",  "WALE",  "WALK",  "WALL",  "WALT",
"WAND",  "WANE",  "WANG",  "WANT",  "WARD",  "WARM",  "WARN",  "WART",
"WASH",  "WAST",  "WATS",  "WATT",  "WAVE",  "WAVY",  "WAYS",  "WEAK",
"WEAL",  "WEAN",  "WEAR",  "WEED",  "WEEK",  "WEIR",  "WELD",  "WELL",
"WELT",  "WENT",  "WERE",  "WERT",  "WEST",  "WHAM",  "WHAT",  "WHEE",
"WHEN",  "WHET",  "WHOA",  "WHOM",  "WICK",  "WIFE",  "WILD",  "WILL",
"WIND",  "WINE",  "WING",  "WINK",  "WINO",  "WIRE",  "WISE",  "WISH",
"WITH",  "WOLF",  "WONT",  "WOOD", "WOOL", "WORD", "WORE", "WORK", "WORM", "WORN",
      "WOVE", "WRIT", "WYNN", "YALE", "YANG", "YANK", "YARD", "YARN",
      "YAWL", "YAWN", "YEAH", "YEAR", "YELL", "YOGA", "YOKE" };
} /* End of class otp */
/* Class for implementing md4 hash algorithm (and hopefully md5 eventually).
 * There are constructors for prepping the hash algorithm (doing the
 * padding, mainly) for a String or a byte[], and an mdcalc() method 
 * for generating the hash. The results can be accessed as an int array 
 * by getregs(), or as a String of hex digits with toString().
 * 
 * Copyright 1996 Harry Mantakos, harry@cs.umd.edu
 */
class md4 extends md {
  md4(String s) {
    super(s);
  }
  md4(byte in[]) {
    super(in);
  }
  static int F(int x, int y, int z) {
    return ((x & y) | (~x & z));
  }
  static int G(int x, int y, int z) {
    return ((x & y) | (x & z) | (y & z));
  }
  static int H(int x, int y, int z) {
    return (x ^ y ^ z);
  }
  void round1(int blk) {
    A = rotintlft((A + F(B, C, D) + d[0 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[1 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[2 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[3 + 16 * blk]), 19);
    A = rotintlft((A + F(B, C, D) + d[4 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[5 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[6 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[7 + 16 * blk]), 19);
    A = rotintlft((A + F(B, C, D) + d[8 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[9 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[10 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[11 + 16 * blk]), 19);
    A = rotintlft((A + F(B, C, D) + d[12 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[13 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[14 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[15 + 16 * blk]), 19);
  }
  void round2(int blk) {
    A = rotintlft((A + G(B, C, D) + d[0 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[4 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[8 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[12 + 16 * blk] + 0x5a827999), 13);
    A = rotintlft((A + G(B, C, D) + d[1 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[5 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[9 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[13 + 16 * blk] + 0x5a827999), 13);
    A = rotintlft((A + G(B, C, D) + d[2 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[6 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[10 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[14 + 16 * blk] + 0x5a827999), 13);
    A = rotintlft((A + G(B, C, D) + d[3 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[7 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[11 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[15 + 16 * blk] + 0x5a827999), 13);
  }
  void round3(int blk) {
    A = rotintlft((A + H(B, C, D) + d[0 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[8 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[4 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[12 + 16 * blk] + 0x6ed9eba1), 15);
    A = rotintlft((A + H(B, C, D) + d[2 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[10 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[6 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[14 + 16 * blk] + 0x6ed9eba1), 15);
    A = rotintlft((A + H(B, C, D) + d[1 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[9 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[5 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[13 + 16 * blk] + 0x6ed9eba1), 15);
    A = rotintlft((A + H(B, C, D) + d[3 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[11 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[7 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[15 + 16 * blk] + 0x6ed9eba1), 15);
  }
  void round4(int blk) {
    System.out.println(" must be md5, in round4!");
  }
}
class md5 extends md {
  md5(String s) {
    super(s);
  }
  md5(byte in[]) {
    super(in);
  }
  static int F(int x, int y, int z) {
    return ((x & y) | (~x & z));
  }
  static int G(int x, int y, int z) {
    return ((x & z) | (y & ~z));
  }
  static int H(int x, int y, int z) {
    return (x ^ y ^ z);
  }
  static int I(int x, int y, int z) {
    return (y ^ (x | ~z));
  }
  void round1(int blk) {
    A = rotintlft(A + F(B, C, D) + d[0 + 16 * blk] + 0xd76aa478, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[1 + 16 * blk] + 0xe8c7b756, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[2 + 16 * blk] + 0x242070db, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[3 + 16 * blk] + 0xc1bdceee, 22) + C;
    A = rotintlft(A + F(B, C, D) + d[4 + 16 * blk] + 0xf57c0faf, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[5 + 16 * blk] + 0x4787c62a, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[6 + 16 * blk] + 0xa8304613, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[7 + 16 * blk] + 0xfd469501, 22) + C;
    A = rotintlft(A + F(B, C, D) + d[8 + 16 * blk] + 0x698098d8, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[9 + 16 * blk] + 0x8b44f7af, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[10 + 16 * blk] + 0xffff5bb1, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[11 + 16 * blk] + 0x895cd7be, 22) + C;
    A = rotintlft(A + F(B, C, D) + d[12 + 16 * blk] + 0x6b901122, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[13 + 16 * blk] + 0xfd987193, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[14 + 16 * blk] + 0xa679438e, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[15 + 16 * blk] + 0x49b40821, 22) + C;
  }
  void round2(int blk) {
    A = rotintlft(A + G(B, C, D) + d[1 + 16 * blk] + 0xf61e2562, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[6 + 16 * blk] + 0xc040b340, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[11 + 16 * blk] + 0x265e5a51, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[0 + 16 * blk] + 0xe9b6c7aa, 20) + C;
    A = rotintlft(A + G(B, C, D) + d[5 + 16 * blk] + 0xd62f105d, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[10 + 16 * blk] + 0x02441453, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[15 + 16 * blk] + 0xd8a1e681, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[4 + 16 * blk] + 0xe7d3fbc8, 20) + C;
    A = rotintlft(A + G(B, C, D) + d[9 + 16 * blk] + 0x21e1cde6, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[14 + 16 * blk] + 0xc33707d6, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[3 + 16 * blk] + 0xf4d50d87, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[8 + 16 * blk] + 0x455a14ed, 20) + C;
    A = rotintlft(A + G(B, C, D) + d[13 + 16 * blk] + 0xa9e3e905, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[2 + 16 * blk] + 0xfcefa3f8, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[7 + 16 * blk] + 0x676f02d9, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[12 + 16 * blk] + 0x8d2a4c8a, 20) + C;
  }
  void round3(int blk) {
    A = rotintlft(A + H(B, C, D) + d[5 + 16 * blk] + 0xfffa3942, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[8 + 16 * blk] + 0x8771f681, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[11 + 16 * blk] + 0x6d9d6122, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[14 + 16 * blk] + 0xfde5380c, 23) + C;
    A = rotintlft(A + H(B, C, D) + d[1 + 16 * blk] + 0xa4beea44, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[4 + 16 * blk] + 0x4bdecfa9, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[7 + 16 * blk] + 0xf6bb4b60, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[10 + 16 * blk] + 0xbebfbc70, 23) + C;
    A = rotintlft(A + H(B, C, D) + d[13 + 16 * blk] + 0x289b7ec6, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[0 + 16 * blk] + 0xeaa127fa, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[3 + 16 * blk] + 0xd4ef3085, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[6 + 16 * blk] + 0x04881d05, 23) + C;
    A = rotintlft(A + H(B, C, D) + d[9 + 16 * blk] + 0xd9d4d039, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[12 + 16 * blk] + 0xe6db99e5, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[15 + 16 * blk] + 0x1fa27cf8, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[2 + 16 * blk] + 0xc4ac5665, 23) + C;
  }
  void round4(int blk) {
    A = rotintlft(A + I(B, C, D) + d[0 + 16 * blk] + 0xf4292244, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[7 + 16 * blk] + 0x432aff97, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[14 + 16 * blk] + 0xab9423a7, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[5 + 16 * blk] + 0xfc93a039, 21) + C;
    A = rotintlft(A + I(B, C, D) + d[12 + 16 * blk] + 0x655b59c3, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[3 + 16 * blk] + 0x8f0ccc92, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[10 + 16 * blk] + 0xffeff47d, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[1 + 16 * blk] + 0x85845dd1, 21) + C;
    A = rotintlft(A + I(B, C, D) + d[8 + 16 * blk] + 0x6fa87e4f, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[15 + 16 * blk] + 0xfe2ce6e0, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[6 + 16 * blk] + 0xa3014314, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[13 + 16 * blk] + 0x4e0811a1, 21) + C;
    A = rotintlft(A + I(B, C, D) + d[4 + 16 * blk] + 0xf7537e82, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[11 + 16 * blk] + 0xbd3af235, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[2 + 16 * blk] + 0x2ad7d2bb, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[9 + 16 * blk] + 0xeb86d391, 21) + C;
  }
}
class md {
  int A, B, C, D;
  int d[];
  int numwords;
  /*
   * For verification of a modicum of sanity, run a few test strings through
   */
  public static void main(String[] argv) {
    boolean doinmd4;
    String mdtype;
    /* Test cases, mostly taken from rfc 1320 */
    String str[] = {
        "",
        "a",
        "abc",
        "message digest",
        "abcdefghijklmnopqrstuvwxyz",
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
        "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
        "01234567890123456789012345678901234567890123456789012345" };
    if (argv.length == 0) {
      mdtype = "md4";
      doinmd4 = true;
    } else if (argv.length > 1) {
      System.err.println("Usage: md [4|5|md4|md5]");
      return;
    } else if ((argv[0].equals("4")) || (argv[0].equals("md4"))) {
      mdtype = "md4";
      doinmd4 = true;
    } else if ((argv[0].equals("5")) || (argv[0].equals("md5"))) {
      mdtype = "md5";
      doinmd4 = false;
    } else {
      System.err.println("Usage: md [4|5|md4|md5]");
      return;
    }
    for (int i = 0; i < str.length; i++) {
      if (doinmd4) {
        md4 mdc = new md4(str[i]);
        mdc.calc();
        System.out.println(mdtype + "(\"" + str[i] + "\") = " + mdc);
      } else {
        md5 mdc = new md5(str[i]);
        mdc.calc();
        System.out.println(mdtype + "(\"" + str[i] + "\") = " + mdc);
      }
    }
  }
  md(String s) {
    byte in[] = new byte[s.length()];
    int i;
    for (i = 0; i < s.length(); i++) {
      in[i] = (byte) (s.charAt(i) & 0xff);
    }
    mdinit(in);
  }
  md(byte in[]) {
    mdinit(in);
  }
  void mdinit(byte in[]) {
    int newlen, endblklen, pad, i;
    long datalenbits;
    datalenbits = in.length * 8;
    endblklen = in.length % 64;
    if (endblklen < 56) {
      pad = 64 - endblklen;
    } else {
      pad = (64 - endblklen) + 64;
    }
    newlen = in.length + pad;
    byte b[] = new byte[newlen];
    for (i = 0; i < in.length; i++) {
      b[i] = in[i];
    }
    b[in.length] = (byte) 0x80;
    for (i = b.length + 1; i < (newlen - 8); i++) {
      b[i] = 0;
    }
    for (i = 0; i < 8; i++) {
      b[newlen - 8 + i] = (byte) (datalenbits & 0xff);
      datalenbits >>= 8;
    }
    /* init registers */
    A = 0x67452301;
    B = 0xefcdab89;
    C = 0x98badcfe;
    D = 0x10325476;
    this.numwords = newlen / 4;
    this.d = new int[this.numwords];
    for (i = 0; i < newlen; i += 4) {
      this.d[i / 4] = (b[i] & 0xff) + ((b[i + 1] & 0xff) << 8)
          + ((b[i + 2] & 0xff) << 16) + ((b[i + 3] & 0xff) << 24);
    }
  }
  public String toString() {
    String s;
    return (tohex(A) + tohex(B) + tohex(C) + tohex(D));
  }
  int[] getregs() {
    int regs[] = { this.A, this.B, this.C, this.D };
    return regs;
  }
  void calc() {
    int AA, BB, CC, DD, i;
    for (i = 0; i < numwords / 16; i++) {
      AA = A;
      BB = B;
      CC = C;
      DD = D;
      round1(i);
      round2(i);
      round3(i);
      if (this instanceof md5) {
        round4(i);
      }
      A += AA;
      B += BB;
      C += CC;
      D += DD;
    }
  }
  /*
   * Dummy round*() methods. these are overriden in the md4 and md5 subclasses
   */
  void round1(int blk) {
    System.err.println("Danger! Danger! Someone called md.round1()!");
  }
  void round2(int blk) {
    System.err.println("Danger! Danger! Someone called md.round2()!");
  }
  void round3(int blk) {
    System.err.println("Danger! Danger! Someone called md.round3()!");
  }
  void round4(int blk) {
    System.err.println("Danger! Danger! Someone called md.round4()!");
  }
  static int rotintlft(int val, int numbits) {
    return ((val << numbits) | (val >>> (32 - numbits)));
  }
  static String tohex(int i) {
    int b;
    String tmpstr;
    tmpstr = "";
    for (b = 0; b < 4; b++) {
      tmpstr += Integer.toString((i >> 4) & 0xf, 16)
          + Integer.toString(i & 0xf, 16);
      i >>= 8;
    }
    return tmpstr;
  }
}





Contains internal state of the MD5 class

  
/*
 * MD5 in Java JDK Beta-2
 * written Santeri Paavolainen, Helsinki Finland 1996
 * (c) Santeri Paavolainen, Helsinki Finland 1996
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * See http://www.cs.hut.fi/~santtu/java/ for more information on this
 * class.
 * 
 * This is rather straight re-implementation of the reference implementation
 * given in RFC1321 by RSA.
 *
 * Passes MD5 test suite as defined in RFC1321.
 *
 *
 * This Java class has been derived from the RSA Data Security, Inc. MD5 
 * Message-Digest Algorithm and its reference implementation. 
 *
 *
 * Moved to the net.matuschek.util package for JoBo integration
 * replaced deprecated use of String.getBytes()
 */
/**
 * Contains internal state of the MD5 class
 */
class MD5State {
  /**
   * 128-byte state 
   */
  int    state[];
  /**
   * 64-bit character count (could be true Java long?)
   */
  int    count[];
  /**
   * 64-byte buffer (512 bits) for storing to-be-hashed characters
   */
  byte    buffer[];
  public MD5State() {
    buffer = new byte[64];
    count = new int[2];
    state = new int[4];
    
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
    count[0] = count[1] = 0;
  }
  /** Create this State as a copy of another state */
  public MD5State (MD5State from) {
    this();
    int i;
    for (i = 0; i < buffer.length; i++)
      this.buffer[i] = from.buffer[i];
    
    for (i = 0; i < state.length; i++)
      this.state[i] = from.state[i];
    for (i = 0; i < count.length; i++)
      this.count[i] = from.count[i];
  }
};

/**
 * Implementation of RSA"s MD5 hash generator
 *
 * @version    $Revision: 1.1 $
 * @author    Santeri Paavolainen <sjpaavol@cc.helsinki.fi>
 */
public class MD5 {
  /**
   * MD5 state
   */
  MD5State    state;
 
  /**
   * If Final() has been called, finals is set to the current finals
   * state. Any Update() causes this to be set to null.
   */
  MD5State     finals;
  /** 
   * Padding for Final()
   */
  static byte    padding[] = {
    (byte) 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  };
  /**
   * Initialize MD5 internal state (object can be reused just by
   * calling Init() after every Final()
   */
  public synchronized void Init () {
    state = new MD5State();
    finals = null;
  }
  /**
   * Class constructor
   */
  public MD5 () {
    this.Init();
  }
  /**
   * Initialize class, and update hash with ob.toString()
   *
   * @param    ob    Object, ob.toString() is used to update hash
   *            after initialization
   */
  public MD5 (Object ob) {
    this();
    Update(ob.toString());
  }
  
  private int rotate_left (int x, int n) {
    return (x << n) | (x >>> (32 - n));
  }
  /* I wonder how many loops and hoops you"ll have to go through to
     get unsigned add for longs in java */
  private int uadd (int a, int b) {
    long aa, bb;
    aa = ((long) a) & 0xffffffffL;
    bb = ((long) b) & 0xffffffffL;
    
    aa += bb;
    return (int) (aa & 0xffffffffL);
  }
  private int uadd (int a, int b, int c) {
    return uadd(uadd(a, b), c);
  }
  private int uadd (int a, int b, int c, int d) {
    return uadd(uadd(a, b, c), d);
  }
  private int FF (int a, int b, int c, int d, int x, int s, int ac) {
    a = uadd(a, ((b & c) | (~b & d)), x, ac);
    return uadd(rotate_left(a, s), b);
  }
  private int GG (int a, int b, int c, int d, int x, int s, int ac) {
    a = uadd(a, ((b & d) | (c & ~d)), x, ac);
    return uadd(rotate_left(a, s), b);
  }
  private int HH (int a, int b, int c, int d, int x, int s, int ac) { 
    a = uadd(a, (b ^ c ^ d), x, ac);
    return uadd(rotate_left(a, s) , b);
  }
  private int II (int a, int b, int c, int d, int x, int s, int ac) {  
    a = uadd(a, (c ^ (b | ~d)), x, ac);
    return uadd(rotate_left(a, s), b);
  }
  private int[] Decode (byte buffer[], int len, int shift) {
    int        out[];
    int     i, j;
    out = new int[16];
    for (i = j = 0; j < len; i++, j += 4) {
      out[i] = ((int) (buffer[j + shift] & 0xff)) | 
    (((int) (buffer[j + 1 + shift] & 0xff)) << 8) |
    (((int) (buffer[j + 2 + shift] & 0xff)) << 16) | 
    (((int) (buffer[j + 3 + shift] & 0xff)) << 24);
/*      System.out.println("out[" + i + "] = \t" +
             ((int) buffer[j + 0 + shift] & 0xff) + "\t|\t" +
             ((int) buffer[j + 1 + shift] & 0xff) + "\t|\t" +
             ((int) buffer[j + 2 + shift] & 0xff) + "\t|\t" +
             ((int) buffer[j + 3 + shift] & 0xff));*/
    }
    
    return out;
  }
  private void Transform (MD5State state, byte buffer[], int shift) {
    int    
      a = state.state[0],
      b = state.state[1],
      c = state.state[2],
      d = state.state[3],
      x[];
    x = Decode(buffer, 64, shift);
       
    /* Round 1 */
    a = FF (a, b, c, d, x[ 0],   7, 0xd76aa478); /* 1 */
    d = FF (d, a, b, c, x[ 1],  12, 0xe8c7b756); /* 2 */
    c = FF (c, d, a, b, x[ 2],  17, 0x242070db); /* 3 */
    b = FF (b, c, d, a, x[ 3],  22, 0xc1bdceee); /* 4 */
    a = FF (a, b, c, d, x[ 4],   7, 0xf57c0faf); /* 5 */
    d = FF (d, a, b, c, x[ 5],  12, 0x4787c62a); /* 6 */
    c = FF (c, d, a, b, x[ 6],  17, 0xa8304613); /* 7 */
    b = FF (b, c, d, a, x[ 7],  22, 0xfd469501); /* 8 */
    a = FF (a, b, c, d, x[ 8],   7, 0x698098d8); /* 9 */
    d = FF (d, a, b, c, x[ 9],  12, 0x8b44f7af); /* 10 */
    c = FF (c, d, a, b, x[10],  17, 0xffff5bb1); /* 11 */
    b = FF (b, c, d, a, x[11],  22, 0x895cd7be); /* 12 */
    a = FF (a, b, c, d, x[12],   7, 0x6b901122); /* 13 */
    d = FF (d, a, b, c, x[13],  12, 0xfd987193); /* 14 */
    c = FF (c, d, a, b, x[14],  17, 0xa679438e); /* 15 */
    b = FF (b, c, d, a, x[15],  22, 0x49b40821); /* 16 */
    /* Round 2 */
    a = GG (a, b, c, d, x[ 1],   5, 0xf61e2562); /* 17 */
    d = GG (d, a, b, c, x[ 6],   9, 0xc040b340); /* 18 */
    c = GG (c, d, a, b, x[11],  14, 0x265e5a51); /* 19 */
    b = GG (b, c, d, a, x[ 0],  20, 0xe9b6c7aa); /* 20 */
    a = GG (a, b, c, d, x[ 5],   5, 0xd62f105d); /* 21 */
    d = GG (d, a, b, c, x[10],   9,  0x2441453); /* 22 */
    c = GG (c, d, a, b, x[15],  14, 0xd8a1e681); /* 23 */
    b = GG (b, c, d, a, x[ 4],  20, 0xe7d3fbc8); /* 24 */
    a = GG (a, b, c, d, x[ 9],   5, 0x21e1cde6); /* 25 */
    d = GG (d, a, b, c, x[14],   9, 0xc33707d6); /* 26 */
    c = GG (c, d, a, b, x[ 3],  14, 0xf4d50d87); /* 27 */
    b = GG (b, c, d, a, x[ 8],  20, 0x455a14ed); /* 28 */
    a = GG (a, b, c, d, x[13],   5, 0xa9e3e905); /* 29 */
    d = GG (d, a, b, c, x[ 2],   9, 0xfcefa3f8); /* 30 */
    c = GG (c, d, a, b, x[ 7],  14, 0x676f02d9); /* 31 */
    b = GG (b, c, d, a, x[12],  20, 0x8d2a4c8a); /* 32 */
    /* Round 3 */
    a = HH (a, b, c, d, x[ 5],   4, 0xfffa3942); /* 33 */
    d = HH (d, a, b, c, x[ 8],  11, 0x8771f681); /* 34 */
    c = HH (c, d, a, b, x[11],  16, 0x6d9d6122); /* 35 */
    b = HH (b, c, d, a, x[14],  23, 0xfde5380c); /* 36 */
    a = HH (a, b, c, d, x[ 1],   4, 0xa4beea44); /* 37 */
    d = HH (d, a, b, c, x[ 4],  11, 0x4bdecfa9); /* 38 */
    c = HH (c, d, a, b, x[ 7],  16, 0xf6bb4b60); /* 39 */
    b = HH (b, c, d, a, x[10],  23, 0xbebfbc70); /* 40 */
    a = HH (a, b, c, d, x[13],   4, 0x289b7ec6); /* 41 */
    d = HH (d, a, b, c, x[ 0],  11, 0xeaa127fa); /* 42 */
    c = HH (c, d, a, b, x[ 3],  16, 0xd4ef3085); /* 43 */
    b = HH (b, c, d, a, x[ 6],  23,  0x4881d05); /* 44 */
    a = HH (a, b, c, d, x[ 9],   4, 0xd9d4d039); /* 45 */
    d = HH (d, a, b, c, x[12],  11, 0xe6db99e5); /* 46 */
    c = HH (c, d, a, b, x[15],  16, 0x1fa27cf8); /* 47 */
    b = HH (b, c, d, a, x[ 2],  23, 0xc4ac5665); /* 48 */
    /* Round 4 */
    a = II (a, b, c, d, x[ 0],   6, 0xf4292244); /* 49 */
    d = II (d, a, b, c, x[ 7],  10, 0x432aff97); /* 50 */
    c = II (c, d, a, b, x[14],  15, 0xab9423a7); /* 51 */
    b = II (b, c, d, a, x[ 5],  21, 0xfc93a039); /* 52 */
    a = II (a, b, c, d, x[12],   6, 0x655b59c3); /* 53 */
    d = II (d, a, b, c, x[ 3],  10, 0x8f0ccc92); /* 54 */
    c = II (c, d, a, b, x[10],  15, 0xffeff47d); /* 55 */
    b = II (b, c, d, a, x[ 1],  21, 0x85845dd1); /* 56 */
    a = II (a, b, c, d, x[ 8],   6, 0x6fa87e4f); /* 57 */
    d = II (d, a, b, c, x[15],  10, 0xfe2ce6e0); /* 58 */
    c = II (c, d, a, b, x[ 6],  15, 0xa3014314); /* 59 */
    b = II (b, c, d, a, x[13],  21, 0x4e0811a1); /* 60 */
    a = II (a, b, c, d, x[ 4],   6, 0xf7537e82); /* 61 */
    d = II (d, a, b, c, x[11],  10, 0xbd3af235); /* 62 */
    c = II (c, d, a, b, x[ 2],  15, 0x2ad7d2bb); /* 63 */
    b = II (b, c, d, a, x[ 9],  21, 0xeb86d391); /* 64 */
    state.state[0] += a;
    state.state[1] += b;
    state.state[2] += c;
    state.state[3] += d;
  }
  /**    
   * Updates hash with the bytebuffer given (using at maximum length bytes from
   * that buffer)
   *
   * @param state    Which state is updated
   * @param buffer    Array of bytes to be hashed
   * @param offset    Offset to buffer array
   * @param length    Use at maximum `length" bytes (absolute
   *            maximum is buffer.length)
   */
  public void Update (MD5State stat, byte buffer[], int offset, int length) {
    int    index, partlen, i, start;
    finals = null;
    /* Length can be told to be shorter, but not inter */
    if ((length - offset)> buffer.length)
      length = buffer.length - offset;
    /* compute number of bytes mod 64 */
    index = (int) (stat.count[0] >>> 3) & 0x3f;
    if ((stat.count[0] += (length << 3)) <
    (length << 3))
      stat.count[1]++;
    stat.count[1] += length >>> 29;
    partlen = 64 - index;
    if (length >= partlen) {
      for (i = 0; i < partlen; i++)
    stat.buffer[i + index] = buffer[i + offset];
      Transform(stat, stat.buffer, 0);
    
      for (i = partlen; (i + 63) < length; i+= 64)
    Transform(stat, buffer, i);
      index = 0;
    } else 
      i = 0;
    /* buffer remaining input */
    if (i < length) {
      start = i;
      for (; i < length; i++) 
    stat.buffer[index + i - start] = buffer[i + offset];
    }
  }
  /* 
   * Update()s for other datatypes than byte[] also. Update(byte[], int)
   * is only the main driver.
   */
  /**
   * Plain update, updates this object
   */
  public void Update (byte buffer[], int offset, int length) {
      Update(this.state, buffer, offset, length);
  }
  public void Update (byte buffer[], int length) {
      Update(this.state, buffer, 0, length);
  }
  /**
   * Updates hash with given array of bytes
   *
   * @param buffer    Array of bytes to use for updating the hash
   */
  public void Update (byte buffer[]) {
      Update(buffer, 0, buffer.length);
  }
  /**
   * Updates hash with a single byte
   *
   * @param b        Single byte to update the hash
   */
  public void Update (byte b) {
    byte buffer[] = new byte[1];
    buffer[0] = b;
    Update(buffer, 1);
  }
  
  /**
   * Update buffer with given string.
   *
   * @param s        String to be update to hash (is used as
   *                   s.getBytes())
   */
  public void Update (String s) {
    byte    chars[];
    chars = s.getBytes();
    Update(chars, chars.length);
  }
  /**
   * Update buffer with a single integer (only & 0xff part is used,
   * as a byte)
   *
   * @param i        Integer value, which is then converted to 
   *            byte as i & 0xff
   */
  public void Update (int i) {
      Update((byte) (i & 0xff));
  }
  private byte[] Encode (int input[], int len) {
    int        i, j;
    byte    out[];
    out = new byte[len];
    for (i = j = 0; j  < len; i++, j += 4) {
      out[j] = (byte) (input[i] & 0xff);
      out[j + 1] = (byte) ((input[i] >>> 8) & 0xff);
      out[j + 2] = (byte) ((input[i] >>> 16) & 0xff);
      out[j + 3] = (byte) ((input[i] >>> 24) & 0xff);
    }
    return out;
  }
  /**
   * Returns array of bytes (16 bytes) representing hash as of the
   * current state of this object. Note: getting a hash does not
   * invalidate the hash object, it only creates a copy of the real
   * state which is finalized. 
   *
   * @return    Array of 16 bytes, the hash of all updated bytes
   */
  public synchronized byte[] Final () {
    byte    bits[];
    int        index, padlen;
    MD5State    fin;
    if (finals == null) {
      fin = new MD5State(state);
      bits = Encode(fin.count, 8);
    
      index = (int) ((fin.count[0] >>> 3) & 0x3f);
      padlen = (index < 56) ? (56 - index) : (120 - index);
      Update(fin, padding, 0, padlen);
      /**/
      Update(fin, bits, 0, 8);    
      /* Update() sets finalds to null */
      finals = fin;
    } 
    return Encode(finals.state, 16);
  }    
  /**
   * Turns array of bytes into string representing each byte as
   * unsigned hex number.
   * 
   * @param hash    Array of bytes to convert to hex-string
   * @return    Generated hex string
   */
  public static String asHex (byte hash[]) {
    StringBuffer buf = new StringBuffer(hash.length * 2);
    int i;
    for (i = 0; i < hash.length; i++) {
      if (((int) hash[i] & 0xff) < 0x10) 
    buf.append("0");
      buf.append(Long.toString((int) hash[i] & 0xff, 16));
    }
    return buf.toString();
  }
  /**
   * Returns 32-character hex representation of this objects hash
   *
   * @return String of this object"s hash
   */
  public String asHex () {
    return asHex(this.Final());
  }
}





Creating a Keyed Digest Using MD5

   
import java.security.MessageDigest;
public class Main {
  public static void main(String[] a) throws Exception {
    byte[] buffer = new byte[10000];
    byte[] key = new byte[8];
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    md5.update(buffer);
    byte[] k = md5.digest(key);
  }
}





Fast implementation of RSA"s MD5 hash generator in Java JDK Beta-2 or higher

 

/**
 * Fast implementation of RSA"s MD5 hash generator in Java JDK Beta-2 or higher.
 * <p>
 * Originally written by Santeri Paavolainen, Helsinki Finland 1996.<br>
 * (c) Santeri Paavolainen, Helsinki Finland 1996<br>
 * Many changes Copyright (c) 2002 Timothy W Macinta<br>
 * <p>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * <p>
 * See http://www.twmacinta.ru/myjava/fast_md5.php for more information
 * on this file and the related files.
 * <p>
 * This was originally a rather straight re-implementation of the
 * reference implementation given in RFC1321 by RSA.  It passes the MD5
 * test suite as defined in RFC1321.
 * <p>
 * Many optimizations made by Timothy W Macinta.  Reduced time to checksum a
 * test file in Java alone to roughly half the time taken compared with
 * java.security.MessageDigest (within an intepretter).  Also added an
 * optional native method to reduce the time even further.
 * See http://www.twmacinta.ru/myjava/fast_md5.php for further information
 * on the time improvements achieved.
 * <p>
 * Some bug fixes also made by Timothy W Macinta.
 * <p>
 * Please note: I (Timothy Macinta) have put this code in the
 * com.twmacinta.util package only because it came without a package.  I
 * was not the the original author of the code, although I did
 * optimize it (substantially) and fix some bugs.
 * <p>
 * This Java class has been derived from the RSA Data Security, Inc. MD5 
 * Message-Digest Algorithm and its reference implementation.
 * <p>
 * This class will attempt to use a native method to quickly compute
 * checksums when the appropriate native library is available.  On Linux,
 * this library should be named "MD5.so" and on Windows it should be
 * named "MD5.dll".  The code will attempt to locate the library in the
 * following locations in the order given:
 *
 * <ol>
 *   <li>The path specified by the system property
 *       "com.twmacinta.util.MD5.NATIVE_LIB_FILE"
 *        (be sure to include "MD5.so" or "MD5.dll"
 *        as appropriate at the end of the path).
 *   <li>A platform specific directory beneath the "lib/arch/" directory.
 *       On Linux for x86, this is "lib/arch/linux_x86/".  On Windows for
 *       x86, this is "lib/arch/win32_x86/".
 *   <li>Within the "lib/" directory.
 *   <li>Within the current directory.
 * </ol>
 *
 * <p>
 * If the library is not found, the code will fall back to the default
 * (slower) Java code.
 * <p>
 * As a side effect of having the code search for the native library,
 * SecurityExceptions might be thrown on JVMs that have a restrictive
 * SecurityManager.  The initialization code attempts to silently discard
 * these exceptions and continue, but many SecurityManagers will
 * attempt to notify the user directly of all SecurityExceptions thrown.
 * Consequently, the code has provisions for skipping the search for
 * the native library.  Any of these provisions may be used to skip the
 * search as long as they are performed <i>before</i> the first
 * instance of a com.twmacinta.util.MD5 object is constructed (note that
 * the convenience stream objects will implicitly create an MD5 object).
 * <p>
 * The first option is to set the system property
 * "com.twmacinta.util.MD5.NO_NATIVE_LIB" to "true" or "1".
 * Unfortunately, SecurityManagers may also choose to disallow system
 * property setting, so this won"t be of use in all cases.
 * <p>
 * The second option is to call
 * com.twmacinta.util.MD5.initNativeLibrary(false) before any MD5 objects
 * are constructed.
 *
 * @author  Santeri Paavolainen <sjpaavol@cc.helsinki.fi>
 * @author  Timothy W Macinta (twm@alum.mit.edu) (optimizations and bug fixes)
 */
public class MD5 {
  /**
   * MD5 state
   */
  MD5State  state;
 
  /**
   * If Final() has been called, finals is set to the current finals
   * state. Any Update() causes this to be set to null.
   */
  MD5State  finals;
  /** 
   * Padding for Final()
   */
  static byte padding[] = {
    (byte) 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  };
  private static boolean native_lib_loaded = false;
  private static boolean native_lib_init_pending = true;
  
  /**
   * Initialize MD5 internal state (object can be reused just by
   * calling Init() after every Final()
   */
  public synchronized void Init () {
    state = new MD5State();
    finals = null;
  }
  /**
   * Class constructor
   */
  public MD5 () {
    if (native_lib_init_pending) _initNativeLibrary();
    this.Init();
  }
  /**
   * Initialize class, and update hash with ob.toString()
   *
   * @param ob  Object, ob.toString() is used to update hash
   *      after initialization
   */
  public MD5 (Object ob) {
    this();
    Update(ob.toString());
  }
  private void Decode (byte buffer[], int shift, int[] out) {
    /*len += shift;
    for (int i = 0; shift < len; i++, shift += 4) {
      out[i] = ((int) (buffer[shift] & 0xff)) |
  (((int) (buffer[shift + 1] & 0xff)) << 8) |
  (((int) (buffer[shift + 2] & 0xff)) << 16) |
  (((int)  buffer[shift + 3]) << 24);
    }*/
    // unrolled loop (original loop shown above)
    out[0] = ((int) (buffer[shift] & 0xff)) |
      (((int) (buffer[shift + 1] & 0xff)) << 8) |
      (((int) (buffer[shift + 2] & 0xff)) << 16) |
      (((int)  buffer[shift + 3]) << 24);
    out[1] = ((int) (buffer[shift + 4] & 0xff)) |
      (((int) (buffer[shift + 5] & 0xff)) << 8) |
      (((int) (buffer[shift + 6] & 0xff)) << 16) |
      (((int)  buffer[shift + 7]) << 24);
    out[2] = ((int) (buffer[shift + 8] & 0xff)) |
      (((int) (buffer[shift + 9] & 0xff)) << 8) |
      (((int) (buffer[shift + 10] & 0xff)) << 16) |
      (((int)  buffer[shift + 11]) << 24);
    out[3] = ((int) (buffer[shift + 12] & 0xff)) |
      (((int) (buffer[shift + 13] & 0xff)) << 8) |
      (((int) (buffer[shift + 14] & 0xff)) << 16) |
      (((int)  buffer[shift + 15]) << 24);
    out[4] = ((int) (buffer[shift + 16] & 0xff)) |
      (((int) (buffer[shift + 17] & 0xff)) << 8) |
      (((int) (buffer[shift + 18] & 0xff)) << 16) |
      (((int)  buffer[shift + 19]) << 24);
    out[5] = ((int) (buffer[shift + 20] & 0xff)) |
      (((int) (buffer[shift + 21] & 0xff)) << 8) |
      (((int) (buffer[shift + 22] & 0xff)) << 16) |
      (((int)  buffer[shift + 23]) << 24);
    out[6] = ((int) (buffer[shift + 24] & 0xff)) |
      (((int) (buffer[shift + 25] & 0xff)) << 8) |
      (((int) (buffer[shift + 26] & 0xff)) << 16) |
      (((int)  buffer[shift + 27]) << 24);
    out[7] = ((int) (buffer[shift + 28] & 0xff)) |
      (((int) (buffer[shift + 29] & 0xff)) << 8) |
      (((int) (buffer[shift + 30] & 0xff)) << 16) |
      (((int)  buffer[shift + 31]) << 24);
    out[8] = ((int) (buffer[shift + 32] & 0xff)) |
      (((int) (buffer[shift + 33] & 0xff)) << 8) |
      (((int) (buffer[shift + 34] & 0xff)) << 16) |
      (((int)  buffer[shift + 35]) << 24);
    out[9] = ((int) (buffer[shift + 36] & 0xff)) |
      (((int) (buffer[shift + 37] & 0xff)) << 8) |
      (((int) (buffer[shift + 38] & 0xff)) << 16) |
      (((int)  buffer[shift + 39]) << 24);
    out[10] = ((int) (buffer[shift + 40] & 0xff)) |
      (((int) (buffer[shift + 41] & 0xff)) << 8) |
      (((int) (buffer[shift + 42] & 0xff)) << 16) |
      (((int)  buffer[shift + 43]) << 24);
    out[11] = ((int) (buffer[shift + 44] & 0xff)) |
      (((int) (buffer[shift + 45] & 0xff)) << 8) |
      (((int) (buffer[shift + 46] & 0xff)) << 16) |
      (((int)  buffer[shift + 47]) << 24);
    out[12] = ((int) (buffer[shift + 48] & 0xff)) |
      (((int) (buffer[shift + 49] & 0xff)) << 8) |
      (((int) (buffer[shift + 50] & 0xff)) << 16) |
      (((int)  buffer[shift + 51]) << 24);
    out[13] = ((int) (buffer[shift + 52] & 0xff)) |
      (((int) (buffer[shift + 53] & 0xff)) << 8) |
      (((int) (buffer[shift + 54] & 0xff)) << 16) |
      (((int)  buffer[shift + 55]) << 24);
    out[14] = ((int) (buffer[shift + 56] & 0xff)) |
      (((int) (buffer[shift + 57] & 0xff)) << 8) |
      (((int) (buffer[shift + 58] & 0xff)) << 16) |
      (((int)  buffer[shift + 59]) << 24);
    out[15] = ((int) (buffer[shift + 60] & 0xff)) |
      (((int) (buffer[shift + 61] & 0xff)) << 8) |
      (((int) (buffer[shift + 62] & 0xff)) << 16) |
      (((int)  buffer[shift + 63]) << 24);
  }
  private native void Transform_native (int[] state, byte buffer[], int shift, int length);
  private void Transform (MD5State state, byte buffer[], int shift, int[] decode_buf) {
    int 
      a = state.state[0],
      b = state.state[1],
      c = state.state[2],
      d = state.state[3],
      x[] = decode_buf;
    Decode(buffer, shift, decode_buf);
    
    /* Round 1 */
    a += ((b & c) | (~b & d)) + x[ 0] + 0xd76aa478; /* 1 */
    a = ((a << 7) | (a >>> 25)) + b;
    d += ((a & b) | (~a & c)) + x[ 1] + 0xe8c7b756; /* 2 */
    d = ((d << 12) | (d >>> 20)) + a;
    c += ((d & a) | (~d & b)) + x[ 2] + 0x242070db; /* 3 */
    c = ((c << 17) | (c >>> 15)) + d;
    b += ((c & d) | (~c & a)) + x[ 3] + 0xc1bdceee; /* 4 */
    b = ((b << 22) | (b >>> 10)) + c;
    a += ((b & c) | (~b & d)) + x[ 4] + 0xf57c0faf; /* 5 */
    a = ((a << 7) | (a >>> 25)) + b;
    d += ((a & b) | (~a & c)) + x[ 5] + 0x4787c62a; /* 6 */
    d = ((d << 12) | (d >>> 20)) + a;
    c += ((d & a) | (~d & b)) + x[ 6] + 0xa8304613; /* 7 */
    c = ((c << 17) | (c >>> 15)) + d;
    b += ((c & d) | (~c & a)) + x[ 7] + 0xfd469501; /* 8 */
    b = ((b << 22) | (b >>> 10)) + c;
    a += ((b & c) | (~b & d)) + x[ 8] + 0x698098d8; /* 9 */
    a = ((a << 7) | (a >>> 25)) + b;
    d += ((a & b) | (~a & c)) + x[ 9] + 0x8b44f7af; /* 10 */
    d = ((d << 12) | (d >>> 20)) + a;
    c += ((d & a) | (~d & b)) + x[10] + 0xffff5bb1; /* 11 */
    c = ((c << 17) | (c >>> 15)) + d;
    b += ((c & d) | (~c & a)) + x[11] + 0x895cd7be; /* 12 */
    b = ((b << 22) | (b >>> 10)) + c;
    a += ((b & c) | (~b & d)) + x[12] + 0x6b901122; /* 13 */
    a = ((a << 7) | (a >>> 25)) + b;
    d += ((a & b) | (~a & c)) + x[13] + 0xfd987193; /* 14 */
    d = ((d << 12) | (d >>> 20)) + a;
    c += ((d & a) | (~d & b)) + x[14] + 0xa679438e; /* 15 */
    c = ((c << 17) | (c >>> 15)) + d;
    b += ((c & d) | (~c & a)) + x[15] + 0x49b40821; /* 16 */
    b = ((b << 22) | (b >>> 10)) + c;
    
    
    /* Round 2 */
    a += ((b & d) | (c & ~d)) + x[ 1] + 0xf61e2562; /* 17 */
    a = ((a << 5) | (a >>> 27)) + b;
    d += ((a & c) | (b & ~c)) + x[ 6] + 0xc040b340; /* 18 */
    d = ((d << 9) | (d >>> 23)) + a;
    c += ((d & b) | (a & ~b)) + x[11] + 0x265e5a51; /* 19 */
    c = ((c << 14) | (c >>> 18)) + d;
    b += ((c & a) | (d & ~a)) + x[ 0] + 0xe9b6c7aa; /* 20 */
    b = ((b << 20) | (b >>> 12)) + c;
    a += ((b & d) | (c & ~d)) + x[ 5] + 0xd62f105d; /* 21 */
    a = ((a << 5) | (a >>> 27)) + b;
    d += ((a & c) | (b & ~c)) + x[10] + 0x02441453; /* 22 */
    d = ((d << 9) | (d >>> 23)) + a;
    c += ((d & b) | (a & ~b)) + x[15] + 0xd8a1e681; /* 23 */
    c = ((c << 14) | (c >>> 18)) + d;
    b += ((c & a) | (d & ~a)) + x[ 4] + 0xe7d3fbc8; /* 24 */
    b = ((b << 20) | (b >>> 12)) + c;
    a += ((b & d) | (c & ~d)) + x[ 9] + 0x21e1cde6; /* 25 */
    a = ((a << 5) | (a >>> 27)) + b;
    d += ((a & c) | (b & ~c)) + x[14] + 0xc33707d6; /* 26 */
    d = ((d << 9) | (d >>> 23)) + a;
    c += ((d & b) | (a & ~b)) + x[ 3] + 0xf4d50d87; /* 27 */
    c = ((c << 14) | (c >>> 18)) + d;
    b += ((c & a) | (d & ~a)) + x[ 8] + 0x445a14ed; /* 28 */
    b = ((b << 20) | (b >>> 12)) + c;
    a += ((b & d) | (c & ~d)) + x[13] + 0xa9e3e905; /* 29 */
    a = ((a << 5) | (a >>> 27)) + b;
    d += ((a & c) | (b & ~c)) + x[ 2] + 0xfcefa3f8; /* 30 */
    d = ((d << 9) | (d >>> 23)) + a;
    c += ((d & b) | (a & ~b)) + x[ 7] + 0x676f02d9; /* 31 */
    c = ((c << 14) | (c >>> 18)) + d;
    b += ((c & a) | (d & ~a)) + x[12] + 0x8d2a4c8a; /* 32 */
    b = ((b << 20) | (b >>> 12)) + c;
    
    
    /* Round 3 */
    a += (b ^ c ^ d) + x[ 5] + 0xfffa3942;      /* 33 */
    a = ((a << 4) | (a >>> 28)) + b;
    d += (a ^ b ^ c) + x[ 8] + 0x8771f681;      /* 34 */
    d = ((d << 11) | (d >>> 21)) + a;
    c += (d ^ a ^ b) + x[11] + 0x6d9d6122;      /* 35 */
    c = ((c << 16) | (c >>> 16)) + d;
    b += (c ^ d ^ a) + x[14] + 0xfde5380c;      /* 36 */
    b = ((b << 23) | (b >>> 9)) + c;
    
    a += (b ^ c ^ d) + x[ 1] + 0xa4beea44;      /* 37 */
    a = ((a << 4) | (a >>> 28)) + b;
    d += (a ^ b ^ c) + x[ 4] + 0x4bdecfa9;      /* 38 */
    d = ((d << 11) | (d >>> 21)) + a;
    c += (d ^ a ^ b) + x[ 7] + 0xf6bb4b60;      /* 39 */
    c = ((c << 16) | (c >>> 16)) + d;
    b += (c ^ d ^ a) + x[10] + 0xbebfbc70;      /* 40 */
    b = ((b << 23) | (b >>> 9)) + c;
    
    a += (b ^ c ^ d) + x[13] + 0x289b7ec6;      /* 41 */
    a = ((a << 4) | (a >>> 28)) + b;
    d += (a ^ b ^ c) + x[ 0] + 0xeaa127fa;      /* 42 */
    d = ((d << 11) | (d >>> 21)) + a;
    c += (d ^ a ^ b) + x[ 3] + 0xd4ef3085;      /* 43 */
    c = ((c << 16) | (c >>> 16)) + d;
    b += (c ^ d ^ a) + x[ 6] + 0x04881d05;      /* 44 */
    b = ((b << 23) | (b >>> 9)) + c;
    
    a += (b ^ c ^ d) + x[ 9] + 0xd9d4d039;      /* 33 */
    a = ((a << 4) | (a >>> 28)) + b;
    d += (a ^ b ^ c) + x[12] + 0xe6db99e5;      /* 34 */
    d = ((d << 11) | (d >>> 21)) + a;
    c += (d ^ a ^ b) + x[15] + 0x1fa27cf8;      /* 35 */
    c = ((c << 16) | (c >>> 16)) + d;
    b += (c ^ d ^ a) + x[ 2] + 0xc4ac5665;      /* 36 */
    b = ((b << 23) | (b >>> 9)) + c;
    
    /* Round 4 */
    a += (c ^ (b | ~d)) + x[ 0] + 0xf4292244; /* 49 */
    a = ((a << 6) | (a >>> 26)) + b;
    d += (b ^ (a | ~c)) + x[ 7] + 0x432aff97; /* 50 */
    d = ((d << 10) | (d >>> 22)) + a;
    c += (a ^ (d | ~b)) + x[14] + 0xab9423a7; /* 51 */
    c = ((c << 15) | (c >>> 17)) + d;
    b += (d ^ (c | ~a)) + x[ 5] + 0xfc93a039; /* 52 */
    b = ((b << 21) | (b >>> 11)) + c;
    a += (c ^ (b | ~d)) + x[12] + 0x655b59c3; /* 53 */
    a = ((a << 6) | (a >>> 26)) + b;
    d += (b ^ (a | ~c)) + x[ 3] + 0x8f0ccc92; /* 54 */
    d = ((d << 10) | (d >>> 22)) + a;
    c += (a ^ (d | ~b)) + x[10] + 0xffeff47d; /* 55 */
    c = ((c << 15) | (c >>> 17)) + d;
    b += (d ^ (c | ~a)) + x[ 1] + 0x85845dd1; /* 56 */
    b = ((b << 21) | (b >>> 11)) + c;
    a += (c ^ (b | ~d)) + x[ 8] + 0x6fa87e4f; /* 57 */
    a = ((a << 6) | (a >>> 26)) + b;
    d += (b ^ (a | ~c)) + x[15] + 0xfe2ce6e0; /* 58 */
    d = ((d << 10) | (d >>> 22)) + a;
    c += (a ^ (d | ~b)) + x[ 6] + 0xa3014314; /* 59 */
    c = ((c << 15) | (c >>> 17)) + d;
    b += (d ^ (c | ~a)) + x[13] + 0x4e0811a1; /* 60 */
    b = ((b << 21) | (b >>> 11)) + c;
    a += (c ^ (b | ~d)) + x[ 4] + 0xf7537e82; /* 61 */
    a = ((a << 6) | (a >>> 26)) + b;
    d += (b ^ (a | ~c)) + x[11] + 0xbd3af235; /* 62 */
    d = ((d << 10) | (d >>> 22)) + a;
    c += (a ^ (d | ~b)) + x[ 2] + 0x2ad7d2bb; /* 63 */
    c = ((c << 15) | (c >>> 17)) + d;
    b += (d ^ (c | ~a)) + x[ 9] + 0xeb86d391; /* 64 */
    b = ((b << 21) | (b >>> 11)) + c;
    state.state[0] += a;
    state.state[1] += b;
    state.state[2] += c;
    state.state[3] += d;
  }
  /** 
   * Updates hash with the bytebuffer given (using at maximum length bytes from
   * that buffer)
   *
   * @param state Which state is updated
   * @param buffer  Array of bytes to be hashed
   * @param offset  Offset to buffer array
   * @param length  Use at maximum `length" bytes (absolute
   *      maximum is buffer.length)
   */
  public void Update (MD5State stat, byte buffer[], int offset, int length) {
    int index, partlen, i, start;
    finals = null;
    /* Length can be told to be shorter, but not inter */
    if ((length - offset)> buffer.length)
      length = buffer.length - offset;
    /* compute number of bytes mod 64 */
    index = (int) (stat.count & 0x3f);
    stat.count += length;
    
    partlen = 64 - index;
    if (length >= partlen) {
      if (native_lib_loaded) {
  
  // update state (using native method) to reflect input
  if (partlen == 64) {
    partlen = 0;
  } else {
    for (i = 0; i < partlen; i++)
      stat.buffer[i + index] = buffer[i + offset];
    Transform_native(stat.state, stat.buffer, 0, 64);
  }
  Transform_native(stat.state, buffer, partlen + offset, length - partlen);
  i = partlen + ((length - partlen) / 64) * 64;
      } else {
  
  // update state (using only Java) to reflect input
  int[] decode_buf = new int[16];
  if (partlen == 64) {
    partlen = 0;
  } else {
    for (i = 0; i < partlen; i++)
      stat.buffer[i + index] = buffer[i + offset];
    Transform(stat, stat.buffer, 0, decode_buf);
  }
  for (i = partlen; (i + 63) < length; i+= 64) {
    Transform(stat, buffer, i + offset, decode_buf);
  }
      }
      index = 0;
    } else 
      i = 0;
    /* buffer remaining input */
    if (i < length) {
      start = i;
      for (; i < length; i++) {
  stat.buffer[index + i - start] = buffer[i + offset];
      }
    }
  }
  /* 
   * Update()s for other datatypes than byte[] also. Update(byte[], int)
   * is only the main driver.
   */
  /**
   * Plain update, updates this object
   */
  public void Update (byte buffer[], int offset, int length) {
      Update(this.state, buffer, offset, length);
  }
  public void Update (byte buffer[], int length) {
      Update(this.state, buffer, 0, length);
  }
  /**
   * Updates hash with given array of bytes
   *
   * @param buffer  Array of bytes to use for updating the hash
   */
  public void Update (byte buffer[]) {
      Update(buffer, 0, buffer.length);
  }
  /**
   * Updates hash with a single byte
   *
   * @param b   Single byte to update the hash
   */
  public void Update (byte b) {
    byte buffer[] = new byte[1];
    buffer[0] = b;
    Update(buffer, 1);
  }
  
  /**
   * Update buffer with given string.  Note that because the version of
   * the s.getBytes() method without parameters is used to convert the
   * string to a byte array, the results of this method may be different
   * on different platforms.  The s.getBytes() method converts the string
   * into a byte array using the current platform"s default character set
   * and may therefore have different results on platforms with different
   * default character sets.  If a version that works consistently
   * across platforms with different default character sets is desired,
   * use the overloaded version of the Update() method which takes a
   * string and a character encoding.
   *
   * @param s   String to be update to hash (is used as
   *            s.getBytes())
   */
  public void Update (String s) {
    byte chars[] = s.getBytes();
    Update(chars, chars.length);
  }
  /**
   * Update buffer with given string using the given encoding.  If the
   * given encoding is null, the encoding "ISO8859_1" is used.
   *
   * @param s   String to be update to hash (is used as
   *            s.getBytes(charset_name))
   * @param charset_name The character set to use to convert s to a
   *                    byte array, or null if the "ISO8859_1"
   *                    character set is desired.
   * @exception         java.io.UnsupportedEncodingException If the named
   *                    charset is not supported.
   */
  public void Update (String s, String charset_name) throws java.io.UnsupportedEncodingException {
    if (charset_name == null) charset_name = "ISO8859_1";
    byte chars[] = s.getBytes(charset_name);
    Update(chars, chars.length);
  }
  /**
   * Update buffer with a single integer (only & 0xff part is used,
   * as a byte)
   *
   * @param i   Integer value, which is then converted to 
   *      byte as i & 0xff
   */
  public void Update (int i) {
      Update((byte) (i & 0xff));
  }
  private byte[] Encode (int input[], int len) {
    int   i, j;
    byte  out[];
    out = new byte[len];
    for (i = j = 0; j  < len; i++, j += 4) {
      out[j] = (byte) (input[i] & 0xff);
      out[j + 1] = (byte) ((input[i] >>> 8) & 0xff);
      out[j + 2] = (byte) ((input[i] >>> 16) & 0xff);
      out[j + 3] = (byte) ((input[i] >>> 24) & 0xff);
    }
    return out;
  }
  /**
   * Returns array of bytes (16 bytes) representing hash as of the
   * current state of this object. Note: getting a hash does not
   * invalidate the hash object, it only creates a copy of the real
   * state which is finalized. 
   *
   * @return  Array of 16 bytes, the hash of all updated bytes
   */
  public synchronized byte[] Final () {
    byte  bits[];
    int   index, padlen;
    MD5State  fin;
    if (finals == null) {
      fin = new MD5State(state);
      int[] count_ints = {(int) (fin.count << 3), (int) (fin.count >> 29)};
      bits = Encode(count_ints, 8);
    
      index = (int) (fin.count & 0x3f);
      padlen = (index < 56) ? (56 - index) : (120 - index);
      Update(fin, padding, 0, padlen);
      Update(fin, bits, 0, 8);  
      /* Update() sets finals to null */
      finals = fin;
    } 
    return Encode(finals.state, 16);
  }    
  /**
   * Turns array of bytes into string representing each byte as
   * unsigned hex number.
   * 
   * @param hash  Array of bytes to convert to hex-string
   * @return  Generated hex string
   */
  public static String asHex (byte hash[]) {
    StringBuffer buf = new StringBuffer(hash.length * 2);
    int i;
    for (i = 0; i < hash.length; i++) {
      if (((int) hash[i] & 0xff) < 0x10) 
  buf.append("0");
      buf.append(Long.toString((int) hash[i] & 0xff, 16));
    }
    return buf.toString();
  }
  /**
   * Returns 32-character hex representation of this objects hash
   *
   * @return String of this object"s hash
   */
  public String asHex () {
    return asHex(this.Final());
  }
  public static synchronized final void initNativeLibrary(boolean disallow_lib_loading) {
    if (disallow_lib_loading) {
      native_lib_init_pending = false;
    } else {
      _initNativeLibrary();
    }
  }
  private static synchronized final void  _initNativeLibrary() {
    if (!native_lib_init_pending) return;
    native_lib_loaded = _loadNativeLibrary();
    native_lib_init_pending = false;
  }
  private static synchronized final boolean _loadNativeLibrary() {
    return false;
  }
}

/**
 * Fast implementation of RSA"s MD5 hash generator in Java JDK Beta-2 or higher<br>
 * Originally written by Santeri Paavolainen, Helsinki Finland 1996 <br>
 * (c) Santeri Paavolainen, Helsinki Finland 1996 <br>
 * Some changes Copyright (c) 2002 Timothy W Macinta <br>
 * <p>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * <p>
 * See http://www.twmacinta.ru/myjava/fast_md5.php for more information
 * on this file.
 * <p>
 * Contains internal state of the MD5 class
 * <p>
 * Please note: I (Timothy Macinta) have put this code in the
 * com.twmacinta.util package only because it came without a package.  I
 * was not the the original author of the code, although I did
 * optimize it (substantially) and fix some bugs.
 *
 * @author  Santeri Paavolainen <sjpaavol@cc.helsinki.fi>
 * @author  Timothy W Macinta (twm@alum.mit.edu) (optimizations and bug fixes)
 **/
class MD5State {
  /**
   * 128-bit state 
   */
  int state[];
  
  /**
   * 64-bit character count
   */
  long count;
  
  /**
   * 64-byte buffer (512 bits) for storing to-be-hashed characters
   */
  byte  buffer[];
  public MD5State() {
    buffer = new byte[64];
    count = 0;
    state = new int[4];
    
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
  }
  /** Create this State as a copy of another state */
  public MD5State (MD5State from) {
    this();
    
    int i;
    
    for (i = 0; i < buffer.length; i++)
      this.buffer[i] = from.buffer[i];
    
    for (i = 0; i < state.length; i++)
      this.state[i] = from.state[i];
    
    this.count = from.count;
  }
};





Implements MD5 functionality on a stream.

 
/*
 * Implements MD5 functionality on a stream.
 *
 * written Santeri Paavolainen, Helsinki Finland 1996
 * (c) Santeri Paavolainen, Helsinki Finland 1996
 * modifications Copyright (C) 2002 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * See COPYING.TXT for details.
 *
 * The original work by Santeri Paavolainen can be found a
 * http://www.helsinki.fi/~sjpaavol/programs/md5/
 */
import java.io.*;
/**
 * Implements MD5 functionality on a stream.
 * More information about this class is available from .
 * <p>
 * This class produces a 128-bit "fingerprint" or "message digest" for
 * all data written to this stream.
 * It is conjectured that it is computationally infeasible to produce
 * two messages having the same message digest, or to produce any
 * message having a given pre-specified target message digest. The MD5
 * algorithm is intended for digital signature applications, where a
 * large file must be "compressed" in a secure manner before being
 * encrypted with a private (secret) key under a public-key cryptosystem
 * such as RSA.
 * <p>
 * For more information see RFC1321.
 *
 * @see MD5
 * @see MD5InputStream
 *
 * @author Santeri Paavolainen http://www.helsinki.fi/~sjpaavol/programs/md5/
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.00.00
 */
public class MD5OutputStream extends FilterOutputStream {
  /**
   * MD5 context
   */
  private MD5 md5;
  /**
   * Creates MD5OutputStream
   * @param out The output stream
   *
   * @since ostermillerutils 1.00.00
   */
  public MD5OutputStream(OutputStream out) {
    super(out);
    md5 = new MD5();
  }
  /**
   * Writes the specified byte to this output stream.
   *
   * @param b the byte.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  @Override public void write(int b) throws IOException {
    out.write(b);
    md5.update((byte)(b & 0xff));
  }
  /**
   * Writes length bytes from the specified byte array starting a
   * offset off to this output stream.
   *
   * @param b the data.
   * @param off the start offset in the data.
   * @param len the number of bytes to write.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  @Override public void write(byte b[], int off, int len) throws IOException {
    out.write(b, off, len);
    md5.update(b, off, len);
  }
  /**
   * Returns array of bytes representing hash of the stream so far.
   *
   * @return Array of 16 bytes, the hash of all written bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public byte[] getHash(){
    return md5.getHash();
  }
  /**
   * Get a 32-character hex representation representing hash of the stream so far.
   *
   * @return A string containing  the hash of all written bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public String getHashString(){
    return md5.getHashString();
  }
}
class MD5 {
  /**
   * Class constructor
   *
   * @since ostermillerutils 1.00.00
   */
  public MD5 () {
    reset();
  }
  /**
   * Command line program that will take files as arguments
   * and output the MD5 sum for each file.
   *
   * @param args command line arguments
   *
   * @since ostermillerutils 1.00.00
   */
  public static void main (String[] args){
    if (args.length == 0){
      System.err.println("Please specify a file.");
    } else {
      for (String element: args) {
        try {
          System.out.println(MD5.getHashString(new File(element)) + " " + element);
        } catch (IOException x){
          System.err.println(x.getMessage());
        }
      }
    }
  }
  /**
   * Gets this hash sum as an array of 16 bytes.
   *
   * @return Array of 16 bytes, the hash of all updated bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public byte[] getHash() {
    if (!finalState.valid) {
      finalState.copy(workingState);
      long bitCount = finalState.bitCount;
      // Compute the number of left over bits
      int leftOver = (int) (((bitCount >>> 3)) & 0x3f);
      // Compute the amount of padding to add based on number of left over bits.
      int padlen = (leftOver < 56) ? (56 - leftOver) : (120 - leftOver);
      // add the padding
      update(finalState, padding, 0, padlen);
      // add the length (computed before padding was added)
      update(finalState, encode(bitCount), 0, 8);
      finalState.valid = true;
    }
    // make a copy of the hash before returning it.
    return encode(finalState.state, 16);
  }
  /**
   * Returns 32-character hex representation of this hash.
   *
   * @return String representation of this object"s hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public String getHashString(){
    return toHex(this.getHash());
  }
  /**
   * Gets the MD5 hash of the given byte array.
   *
   * @param b byte array for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(byte[] b){
    MD5 md5 = new MD5();
    md5.update(b);
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash of the given byte array.
   *
   * @param b byte array for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(byte[] b){
    MD5 md5 = new MD5();
    md5.update(b);
    return md5.getHashString();
  }
  /**
   * Gets the MD5 hash the data on the given InputStream.
   *
   * @param in byte array for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(InputStream in) throws IOException {
    MD5 md5 = new MD5();
    byte[] buffer = new byte[1024];
    int read;
    while ((read = in.read(buffer)) != -1){
      md5.update(buffer, read);
    }
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash the data on the given InputStream.
   *
   * @param in byte array for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(InputStream in) throws IOException {
    MD5 md5 = new MD5();
    byte[] buffer = new byte[1024];
    int read;
    while ((read = in.read(buffer)) != -1){
      md5.update(buffer, read);
    }
    return md5.getHashString();
  }
  /**
   * Gets the MD5 hash of the given file.
   *
   * @param f file for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(File f) throws IOException {
    InputStream is = new FileInputStream(f);
    byte[] hash = getHash(is);
    is.close();
    return hash;
  }
  /**
   * Gets the MD5 hash of the given file.
   *
   * @param f file array for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(File f) throws IOException {
    InputStream is = new FileInputStream(f);
    String hash = getHashString(is);
    is.close();
    return hash;
  }
  /**
   * Gets the MD5 hash of the given String.
   * The string is converted to bytes using the current
   * platform"s default character encoding.
   *
   * @param s String for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(String s){
    MD5 md5 = new MD5();
    md5.update(s);
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash of the given String.
   * The string is converted to bytes using the current
   * platform"s default character encoding.
   *
   * @param s String for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(String s){
    MD5 md5 = new MD5();
    md5.update(s);
    return md5.getHashString();
  }

  /**
   * Gets the MD5 hash of the given String.
   *
   * @param s String for which an MD5 hash is desired.
   * @param enc The name of a supported character encoding.
   * @return Array of 16 bytes, the hash of all updated bytes.
   * @throws UnsupportedEncodingException If the named encoding is not supported.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(String s, String enc) throws UnsupportedEncodingException {
    MD5 md5 = new MD5();
    md5.update(s, enc);
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash of the given String.
   *
   * @param s String for which an MD5 hash is desired.
   * @param enc The name of a supported character encoding.
   * @return 32-character hex representation the data"s MD5 hash.
   * @throws UnsupportedEncodingException If the named encoding is not supported.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(String s, String enc) throws UnsupportedEncodingException {
    MD5 md5 = new MD5();
    md5.update(s, enc);
    return md5.getHashString();
  }

  /**
   * Reset the MD5 sum to its initial state.
   *
   * @since ostermillerutils 1.00.00
   */
  public void reset() {
    workingState.reset();
    finalState.valid = false;
  }
  /**
   * Returns 32-character hex representation of this hash.
   *
   * @return String representation of this object"s hash.
   *
   * @since ostermillerutils 1.00.00
   */
  @Override public String toString(){
    return getHashString();
  }
  /**
   * Update this hash with the given data.
   * <p>
   * A state may be passed into this method so that we can add padding
   * and finalize a md5 hash without limiting our ability to update
   * more data later.
   * <p>
   * If length bytes are not available to be hashed, as many bytes as
   * possible will be hashed.
   *
   * @param state Which state is updated.
   * @param buffer Array of bytes to be hashed.
   * @param offset Offset to buffer array.
   * @param length number of bytes to hash.
   *
   * @since ostermillerutils 1.00.00
   */
  private void update (MD5State state, byte buffer[], int offset, int length) {
    finalState.valid = false;
    // if length goes beyond the end of the buffer, cut it short.
    if ((length + offset) > buffer.length){
      length = buffer.length - offset;
    }
    // compute number of bytes mod 64
    // this is what we have sitting in a buffer
    // that have not been hashed yet
    int index = (int) (state.bitCount >>> 3) & 0x3f;
    // add the length to the count (translate bytes to bits)
    state.bitCount += length << 3;
    int partlen = 64 - index;
    int i = 0;
    if (length >= partlen) {
      System.arraycopy(buffer, offset, state.buffer, index, partlen);
      transform(state, decode(state.buffer, 64, 0));
      for (i = partlen; (i + 63) < length; i+= 64){
        transform(state, decode(buffer, 64, i));
      }
      index = 0;
    }
    // buffer remaining input
    if (i < length) {
      for (int start = i; i < length; i++) {
        state.buffer[index + i - start] = buffer[i + offset];
      }
    }
  }
  /**
   * Update this hash with the given data.
   * <p>
   * If length bytes are not available to be hashed, as many bytes as
   * possible will be hashed.
   *
   * @param buffer Array of bytes to be hashed.
   * @param offset Offset to buffer array.
   * @param length number of bytes to hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte buffer[], int offset, int length) {
    update(workingState, buffer, offset, length);
  }
  /**
   * Update this hash with the given data.
   * <p>
   * If length bytes are not available to be hashed, as many bytes as
   * possible will be hashed.
   *
   * @param buffer Array of bytes to be hashed.
   * @param length number of bytes to hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte buffer[], int length) {
    update(buffer, 0, length);
  }
  /**
   * Update this hash with the given data.
   *
   * @param buffer Array of bytes to be hashed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte buffer[]) {
    update(buffer, 0, buffer.length);
  }
  /**
   * Updates this hash with a single byte.
   *
   * @param b byte to be hashed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte b) {
    byte buffer[] = new byte[1];
    buffer[0] = b;
    update(buffer, 1);
  }
  /**
   * Update this hash with a String.
   * The string is converted to bytes using the current
   * platform"s default character encoding.
   *
   * @param s String to be hashed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (String s) {
    update(s.getBytes());
  }
  /**
   * Update this hash with a String.
   *
   * @param s String to be hashed.
   * @param enc The name of a supported character encoding.
   * @throws UnsupportedEncodingException If the named encoding is not supported.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (String s, String enc) throws UnsupportedEncodingException {
    update(s.getBytes(enc));
  }
  /**
   * The current state from which the hash sum
   * can be computed or updated.
   *
   * @since ostermillerutils 1.00.00
   */
  private MD5State workingState = new MD5State();
  /**
   * Cached copy of the final MD5 hash sum.  This is created when
   * the hash is requested and it is invalidated when the hash
   * is updated.
   *
   * @since ostermillerutils 1.00.00
   */
  private MD5State finalState = new MD5State();
  /**
   * Temporary buffer cached here for performance reasons.
   *
   * @since ostermillerutils 1.00.00
   */
  private int[] decodeBuffer = new int[16];
  /**
   * 64 bytes of padding that can be added if the length
   * is not divisible by 64.
   *
   * @since ostermillerutils 1.00.00
   */
  private static final byte padding[] = {
    (byte) 0x80, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
  };
  /**
   * Contains internal state of the MD5 class.
   * Passes MD5 test suite as defined in RFC1321.
   *
   * @since ostermillerutils 1.00.00
   */
  private class MD5State {
    /**
     * True if this state is valid.
     *
     * @since ostermillerutils 1.00.00
     */
    private boolean valid = true;
    /**
     * Reset to initial state.
     *
     * @since ostermillerutils 1.00.00
     */
    private void reset(){
      state[0] = 0x67452301;
      state[1] = 0xefcdab89;
      state[2] = 0x98badcfe;
      state[3] = 0x10325476;
      bitCount = 0;
    }
    /**
     * 128-byte state
     *
     * @since ostermillerutils 1.00.00
     */
    private int state[] = new int[4];
    /**
     * 64-bit count of the number of bits that have been hashed.
     *
     * @since ostermillerutils 1.00.00
     */
    private long bitCount;
    /**
     * 64-byte buffer (512 bits) for storing to-be-hashed characters
     *
     * @since ostermillerutils 1.00.00
     */
    private byte buffer[] = new byte[64];
    private MD5State() {
      reset();
    }
    /**
     * Set this state to be exactly the same as some other.
     *
     * @param from state to copy from.
     *
     * @since ostermillerutils 1.00.00
     */
    private void copy(MD5State from) {
      System.arraycopy(from.buffer, 0, this.buffer, 0, this.buffer.length);
      System.arraycopy(from.state, 0, this.state, 0, this.state.length);
      this.valid = from.valid;
      this.bitCount = from.bitCount;
    }
  }

  /**
   * Turns array of bytes into string representing each byte as
   * a two digit unsigned hex number.
   *
   * @param hash Array of bytes to convert to hex-string
   * @return  Generated hex string
   *
   * @since ostermillerutils 1.00.00
   */
  private static String toHex(byte hash[]){
    StringBuffer buf = new StringBuffer(hash.length * 2);
    for (byte element: hash) {
      int intVal = element & 0xff;
      if (intVal < 0x10){
        // append a zero before a one digit hex
        // number to make it two digits.
        buf.append("0");
      }
      buf.append(Integer.toHexString(intVal));
    }
    return buf.toString();
  }
  private static int FF (int a, int b, int c, int d, int x, int s, int ac) {
    a += ((b & c) | (~b & d));
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static int GG (int a, int b, int c, int d, int x, int s, int ac) {
    a += ((b & d) | (c & ~d));
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static int HH (int a, int b, int c, int d, int x, int s, int ac) {
    a += (b ^ c ^ d);
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static int II (int a, int b, int c, int d, int x, int s, int ac) {
    a += (c ^ (b | ~d));
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static byte[] encode(long l){
    byte[] out = new byte[8];
    out[0] = (byte) (l & 0xff);
    out[1] = (byte) ((l >>> 8) & 0xff);
    out[2] = (byte) ((l >>> 16) & 0xff);
    out[3] = (byte) ((l >>> 24) & 0xff);
    out[4] = (byte) ((l >>> 32) & 0xff);
    out[5] = (byte) ((l >>> 40) & 0xff);
    out[6] = (byte) ((l >>> 48) & 0xff);
    out[7] = (byte) ((l >>> 56) & 0xff);
    return out;
  }
  private static byte[] encode(int input[], int len){
    byte[] out = new byte[len];
    int i, j;
    for (i = j = 0; j  < len; i++, j += 4) {
      out[j] = (byte) (input[i] & 0xff);
      out[j + 1] = (byte) ((input[i] >>> 8) & 0xff);
      out[j + 2] = (byte) ((input[i] >>> 16) & 0xff);
      out[j + 3] = (byte) ((input[i] >>> 24) & 0xff);
    }
    return out;
  }
  private int[] decode(byte buffer[], int len, int offset){
    int i, j;
    for (i = j = 0; j < len; i++, j += 4) {
      decodeBuffer[i] = (
        (buffer[j + offset] & 0xff)) |
        (((buffer[j + 1 + offset] & 0xff)) << 8) |
        (((buffer[j + 2 + offset] & 0xff)) << 16) |
        (((buffer[j + 3 + offset] & 0xff)) << 24
      );
    }
    return decodeBuffer;
  }
  private static void transform(MD5State state, int[] x){
    int a = state.state[0];
    int b = state.state[1];
    int c = state.state[2];
    int d = state.state[3];
    /* Round 1 */
    a = FF (a, b, c, d, x[ 0],   7, 0xd76aa478); /* 1 */
    d = FF (d, a, b, c, x[ 1],  12, 0xe8c7b756); /* 2 */
    c = FF (c, d, a, b, x[ 2],  17, 0x242070db); /* 3 */
    b = FF (b, c, d, a, x[ 3],  22, 0xc1bdceee); /* 4 */
    a = FF (a, b, c, d, x[ 4],   7, 0xf57c0faf); /* 5 */
    d = FF (d, a, b, c, x[ 5],  12, 0x4787c62a); /* 6 */
    c = FF (c, d, a, b, x[ 6],  17, 0xa8304613); /* 7 */
    b = FF (b, c, d, a, x[ 7],  22, 0xfd469501); /* 8 */
    a = FF (a, b, c, d, x[ 8],   7, 0x698098d8); /* 9 */
    d = FF (d, a, b, c, x[ 9],  12, 0x8b44f7af); /* 10 */
    c = FF (c, d, a, b, x[10],  17, 0xffff5bb1); /* 11 */
    b = FF (b, c, d, a, x[11],  22, 0x895cd7be); /* 12 */
    a = FF (a, b, c, d, x[12],   7, 0x6b901122); /* 13 */
    d = FF (d, a, b, c, x[13],  12, 0xfd987193); /* 14 */
    c = FF (c, d, a, b, x[14],  17, 0xa679438e); /* 15 */
    b = FF (b, c, d, a, x[15],  22, 0x49b40821); /* 16 */
    /* Round 2 */
    a = GG (a, b, c, d, x[ 1],   5, 0xf61e2562); /* 17 */
    d = GG (d, a, b, c, x[ 6],   9, 0xc040b340); /* 18 */
    c = GG (c, d, a, b, x[11],  14, 0x265e5a51); /* 19 */
    b = GG (b, c, d, a, x[ 0],  20, 0xe9b6c7aa); /* 20 */
    a = GG (a, b, c, d, x[ 5],   5, 0xd62f105d); /* 21 */
    d = GG (d, a, b, c, x[10],   9, 0x02441453); /* 22 */
    c = GG (c, d, a, b, x[15],  14, 0xd8a1e681); /* 23 */
    b = GG (b, c, d, a, x[ 4],  20, 0xe7d3fbc8); /* 24 */
    a = GG (a, b, c, d, x[ 9],   5, 0x21e1cde6); /* 25 */
    d = GG (d, a, b, c, x[14],   9, 0xc33707d6); /* 26 */
    c = GG (c, d, a, b, x[ 3],  14, 0xf4d50d87); /* 27 */
    b = GG (b, c, d, a, x[ 8],  20, 0x455a14ed); /* 28 */
    a = GG (a, b, c, d, x[13],   5, 0xa9e3e905); /* 29 */
    d = GG (d, a, b, c, x[ 2],   9, 0xfcefa3f8); /* 30 */
    c = GG (c, d, a, b, x[ 7],  14, 0x676f02d9); /* 31 */
    b = GG (b, c, d, a, x[12],  20, 0x8d2a4c8a); /* 32 */
    /* Round 3 */
    a = HH (a, b, c, d, x[ 5],   4, 0xfffa3942); /* 33 */
    d = HH (d, a, b, c, x[ 8],  11, 0x8771f681); /* 34 */
    c = HH (c, d, a, b, x[11],  16, 0x6d9d6122); /* 35 */
    b = HH (b, c, d, a, x[14],  23, 0xfde5380c); /* 36 */
    a = HH (a, b, c, d, x[ 1],   4, 0xa4beea44); /* 37 */
    d = HH (d, a, b, c, x[ 4],  11, 0x4bdecfa9); /* 38 */
    c = HH (c, d, a, b, x[ 7],  16, 0xf6bb4b60); /* 39 */
    b = HH (b, c, d, a, x[10],  23, 0xbebfbc70); /* 40 */
    a = HH (a, b, c, d, x[13],   4, 0x289b7ec6); /* 41 */
    d = HH (d, a, b, c, x[ 0],  11, 0xeaa127fa); /* 42 */
    c = HH (c, d, a, b, x[ 3],  16, 0xd4ef3085); /* 43 */
    b = HH (b, c, d, a, x[ 6],  23, 0x04881d05); /* 44 */
    a = HH (a, b, c, d, x[ 9],   4, 0xd9d4d039); /* 45 */
    d = HH (d, a, b, c, x[12],  11, 0xe6db99e5); /* 46 */
    c = HH (c, d, a, b, x[15],  16, 0x1fa27cf8); /* 47 */
    b = HH (b, c, d, a, x[ 2],  23, 0xc4ac5665); /* 48 */
    /* Round 4 */
    a = II (a, b, c, d, x[ 0],   6, 0xf4292244); /* 49 */
    d = II (d, a, b, c, x[ 7],  10, 0x432aff97); /* 50 */
    c = II (c, d, a, b, x[14],  15, 0xab9423a7); /* 51 */
    b = II (b, c, d, a, x[ 5],  21, 0xfc93a039); /* 52 */
    a = II (a, b, c, d, x[12],   6, 0x655b59c3); /* 53 */
    d = II (d, a, b, c, x[ 3],  10, 0x8f0ccc92); /* 54 */
    c = II (c, d, a, b, x[10],  15, 0xffeff47d); /* 55 */
    b = II (b, c, d, a, x[ 1],  21, 0x85845dd1); /* 56 */
    a = II (a, b, c, d, x[ 8],   6, 0x6fa87e4f); /* 57 */
    d = II (d, a, b, c, x[15],  10, 0xfe2ce6e0); /* 58 */
    c = II (c, d, a, b, x[ 6],  15, 0xa3014314); /* 59 */
    b = II (b, c, d, a, x[13],  21, 0x4e0811a1); /* 60 */
    a = II (a, b, c, d, x[ 4],   6, 0xf7537e82); /* 61 */
    d = II (d, a, b, c, x[11],  10, 0xbd3af235); /* 62 */
    c = II (c, d, a, b, x[ 2],  15, 0x2ad7d2bb); /* 63 */
    b = II (b, c, d, a, x[ 9],  21, 0xeb86d391); /* 64 */
    state.state[0] += a;
    state.state[1] += b;
    state.state[2] += c;
    state.state[3] += d;
  }
}





MD5 algorithm RFC 1321

 

/***
* jwma Java WebMail
* Copyright (c) 2000-2003 jwma team
*
* jwma is free software; you can distribute and use this source
* under the terms of the BSD-style license received along with
* the distribution.
 ***/
/**
 * This class implements the MD5 algorithm.
 * <p>
 * The specification is available from RFC 1321, and there are numerous
 * implementations out there, this one was tuned specifically for hashing short
 * strings (i.e. passwords).
 * <p>
 * I do not recommend to use this implementation for anything else but that, and
 * if anybody feels that this code is to "close" to something available on the
 * net, please contact me, and I will certainly take steps to clear up the
 * situation.
 */
public final class MD5 {
  /**
   * Returns the MD5 hash digest transformed into a hex
   * representation as <tt>String</tt>.
   *
   * @return the MD5 hash of the input as <tt>String</tt>.
   */
  public static final String hash(String str) {
    return MD5.asHex(MD5.digest(str.getBytes()));
  }//hash
  /**
   * Returns the MD5 hash of the input data.
   * <p>
   * Following the the MD5 standard specification, the result
   * is returned least significant byte first, however,
   * many applications use the most significant byte first (more conventional).
   *
   * @param msg the <tt>byte[]</tt> to be digested.
   *
   * @return the MD5 hash of the data as <tt>String</tt>.
   */
  public static final byte[] digest(byte[] msg) {
    int padsize = (120 - (msg.length % 64)) % 64;
    int i;
    long l = msg.length * 8;
    //MD5 registers
    int a = 0x67452301, aa,
        b = 0xefcdab89, bb,
        c = 0x98badcfe, cc,
        d = 0x10325476, dd;
    byte[] src = new byte[msg.length + padsize + 8];
    //padding
    try {
      System.arraycopy(msg, 0, src, 0, msg.length);
    } catch (Exception ex) {
      //should not happen, but calm"s the compiler
    }
    src[msg.length] = (byte) 0x80;
    for (i = msg.length + 1; i < msg.length + padsize; i++) src[i] = 0;
    //append length
    for (i = src.length - 8; i < src.length; i++) {
      src[i] = (byte) l;
      l >>>= 8;
    }
    int[] x = new int[16];
    //prcess the data 16-word blocks
    for (i = 0; i < src.length; i += 64) {
      //construct block
      for (int j = 0; j < 16; j++) {
        x[j] = 0;
        for (int k = 3; k >= 0; k--) {
          x[j] <<= 8;
          x[j] += src[i + j * 4 + k] & 0xff;
        }
      }
      aa = a;
      bb = b;
      cc = c;
      dd = d;
      a = round1(a, b, c, d, 0, 7, 1, x);
      d = round1(d, a, b, c, 1, 12, 2, x);
      c = round1(c, d, a, b, 2, 17, 3, x);
      b = round1(b, c, d, a, 3, 22, 4, x);
      a = round1(a, b, c, d, 4, 7, 5, x);
      d = round1(d, a, b, c, 5, 12, 6, x);
      c = round1(c, d, a, b, 6, 17, 7, x);
      b = round1(b, c, d, a, 7, 22, 8, x);
      a = round1(a, b, c, d, 8, 7, 9, x);
      d = round1(d, a, b, c, 9, 12, 10, x);
      c = round1(c, d, a, b, 10, 17, 11, x);
      b = round1(b, c, d, a, 11, 22, 12, x);
      a = round1(a, b, c, d, 12, 7, 13, x);
      d = round1(d, a, b, c, 13, 12, 14, x);
      c = round1(c, d, a, b, 14, 17, 15, x);
      b = round1(b, c, d, a, 15, 22, 16, x);
      a = round2(a, b, c, d, 1, 5, 17, x);
      d = round2(d, a, b, c, 6, 9, 18, x);
      c = round2(c, d, a, b, 11, 14, 19, x);
      b = round2(b, c, d, a, 0, 20, 20, x);
      a = round2(a, b, c, d, 5, 5, 21, x);
      d = round2(d, a, b, c, 10, 9, 22, x);
      c = round2(c, d, a, b, 15, 14, 23, x);
      b = round2(b, c, d, a, 4, 20, 24, x);
      a = round2(a, b, c, d, 9, 5, 25, x);
      d = round2(d, a, b, c, 14, 9, 26, x);
      c = round2(c, d, a, b, 3, 14, 27, x);
      b = round2(b, c, d, a, 8, 20, 28, x);
      a = round2(a, b, c, d, 13, 5, 29, x);
      d = round2(d, a, b, c, 2, 9, 30, x);
      c = round2(c, d, a, b, 7, 14, 31, x);
      b = round2(b, c, d, a, 12, 20, 32, x);
      a = round3(a, b, c, d, 5, 4, 33, x);
      d = round3(d, a, b, c, 8, 11, 34, x);
      c = round3(c, d, a, b, 11, 16, 35, x);
      b = round3(b, c, d, a, 14, 23, 36, x);
      a = round3(a, b, c, d, 1, 4, 37, x);
      d = round3(d, a, b, c, 4, 11, 38, x);
      c = round3(c, d, a, b, 7, 16, 39, x);
      b = round3(b, c, d, a, 10, 23, 40, x);
      a = round3(a, b, c, d, 13, 4, 41, x);
      d = round3(d, a, b, c, 0, 11, 42, x);
      c = round3(c, d, a, b, 3, 16, 43, x);
      b = round3(b, c, d, a, 6, 23, 44, x);
      a = round3(a, b, c, d, 9, 4, 45, x);
      d = round3(d, a, b, c, 12, 11, 46, x);
      c = round3(c, d, a, b, 15, 16, 47, x);
      b = round3(b, c, d, a, 2, 23, 48, x);
      a = round4(a, b, c, d, 0, 6, 49, x);
      d = round4(d, a, b, c, 7, 10, 50, x);
      c = round4(c, d, a, b, 14, 15, 51, x);
      b = round4(b, c, d, a, 5, 21, 52, x);
      a = round4(a, b, c, d, 12, 6, 53, x);
      d = round4(d, a, b, c, 3, 10, 54, x);
      c = round4(c, d, a, b, 10, 15, 55, x);
      b = round4(b, c, d, a, 1, 21, 56, x);
      a = round4(a, b, c, d, 8, 6, 57, x);
      d = round4(d, a, b, c, 15, 10, 58, x);
      c = round4(c, d, a, b, 6, 15, 59, x);
      b = round4(b, c, d, a, 13, 21, 60, x);
      a = round4(a, b, c, d, 4, 6, 61, x);
      d = round4(d, a, b, c, 11, 10, 62, x);
      c = round4(c, d, a, b, 2, 15, 63, x);
      b = round4(b, c, d, a, 9, 21, 64, x);
      a += aa;
      b += bb;
      c += cc;
      d += dd;
    }
    byte[] ret = new byte[16];
    for (i = 0; i < 4; i++) {
      ret[i] = (byte) a;
      a >>>= 8;
    }
    for (; i < 8; i++) {
      ret[i] = (byte) b;
      b >>>= 8;
    }
    for (; i < 12; i++) {
      ret[i] = (byte) c;
      c >>>= 8;
    }
    for (; i < 16; i++) {
      ret[i] = (byte) d;
      d >>>= 8;
    }
    return ret;
  }//digest

  /** MD5 Transformation routines *********************************************/
  private static final int rot(int x, int s) {
    return x << s | x >>> (32 - s);
  }//rot
  private static final int F(int x, int y, int z) {
    return (x & y) | (~x & z);
  }//F
  private static final int G(int x, int y, int z) {
    return (x & z) | (y & ~z);
  }//G
  private static final int H(int x, int y, int z) {
    return x ^ y ^ z;
  }//H
  private static final int I(int x, int y, int z) {
    return y ^ (x | ~z);
  }//I
  private static final int round1(int a, int b, int c,
                                  int d, int k, int s,
                                  int i, int[] x) {
    return b + rot((a + F(b, c, d) + x[k] + T[i - 1]), s);
  }//round1
  private static final int round2(int a, int b, int c,
                                  int d, int k, int s,
                                  int i, int[] x) {
    return b + rot((a + G(b, c, d) + x[k] + T[i - 1]), s);
  }//round2
  private static final int round3(int a, int b, int c,
                                  int d, int k, int s,
                                  int i, int[] x) {
    return b + rot((a + H(b, c, d) + x[k] + T[i - 1]), s);
  }//round3
  private static final int round4(int a, int b, int c,
                                  int d, int k, int s,
                                  int i, int[] x) {
    return b + rot((a + I(b, c, d) + x[k] + T[i - 1]), s);
  }//round4
  private static final int T[] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf,
    0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af,
    0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e,
    0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6,
    0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
    0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122,
    0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039,
    0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,
    0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d,
    0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
  };
  /** END MD5 Transformation routines *****************************************/
  /**
   * Returns a <tt>String</tt> containing unsigned hexadecimal
   * numbers as digits.
   * <p>
   * Contains two hex digit characters for each byte from the passed in
   * <tt>byte[]</tt>.
   *
   * @param data the array of bytes to be converted into a hex-string.
   * @return  the generated hexadecimal representation as
   *          <tt>String</tt>.
   */
  public static final String asHex(byte[] data) {
    //double size, two bytes (hex range) for one byte
    StringBuffer buf = new StringBuffer(data.length * 2);
    for (int i = 0; i < data.length; i++) {
      //don"t forget the second hex digit
      if (((int) data[i] & 0xff) < 0x10) {
        buf.append("0");
      }
      buf.append(Long.toString((int) data[i] & 0xff, 16));
    }
    return buf.toString();
  }//asHex
  /**
   * A main, to allow using this class from the command line.
   */
  public static void main(String[] args) {
    try {
      if (args == null || args.length == 0) {
        System.out.println("Usage: java dtw.webmail.util.MD5 <password>");
      }
      System.out.println("Hash=" + hash(args[0]));
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }//main
}//class MD5





MD5 BASE64 checksum for the specified input string.

  
import javax.swing.JOptionPane;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
 * MD5 common utils. Check and calculate MD5 BASE64 sum.
 * 
 * @author Luigi Zurolo - Liscio Luca
 * @version 1.1
 */
public class MD5 {

  /**
   * MD5 BASE64 checksum for the specified input string.
   * 
   * @param input -
   *          Specified input string
   * @return String - MD5 BASE64 sum
   */
  public static String checkMD5(String input) {
    try {
      MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(input.getBytes());
      byte[] enc = md.digest();
      String md5Sum = new sun.misc.BASE64Encoder().encode(enc);
      return md5Sum;
    } catch (NoSuchAlgorithmException nsae) {
      System.out.println(nsae.getMessage());
      return null;
    }
  }
}





MD5 InputStream

 
/*
 * Implements MD5 functionality on a stream.
 *
 * written Santeri Paavolainen, Helsinki Finland 1996
 * (c) Santeri Paavolainen, Helsinki Finland 1996
 * modifications Copyright (C) 2002 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * See COPYING.TXT for details.
 *
 * The original work by Santeri Paavolainen can be found a
 * http://www.helsinki.fi/~sjpaavol/programs/md5/
 */
import java.io.*;
/**
 * Implements MD5 functionality on a stream.
 * More information about this class is available from .
 * <p>
 * This class produces a 128-bit "fingerprint" or "message digest" for
 * all data read from this stream.
 * It is conjectured that it is computationally infeasible to produce
 * two messages having the same message digest, or to produce any
 * message having a given pre-specified target message digest. The MD5
 * algorithm is intended for digital signature applications, where a
 * large file must be "compressed" in a secure manner before being
 * encrypted with a private (secret) key under a public-key cryptosystem
 * such as RSA.
 * <p>
 * For more information see RFC1321.
 *
 * @see MD5
 * @see MD5OutputStream
 *
 * @author Santeri Paavolainen http://www.helsinki.fi/~sjpaavol/programs/md5/
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.00.00
 */
public class MD5InputStream extends FilterInputStream {
  /**
   * MD5 context
   */
  private MD5 md5;
  /**
   * Creates a MD5InputStream
   * @param in the underlying input stream
   */
  public MD5InputStream (InputStream in) {
    super(in);
    md5 = new MD5();
  }
  /**
   * Reads the next byte of data from this input stream. The value byte
   * is returned as an int in the range 0 to 255. If no byte is available
   * because the end of the stream has been reached, the value -1 is returned.
   * This method blocks until input data is available, the end of the stream is
   * detected, or an exception is thrown.
   * <p>
   * This method simply performs in.read() and returns the result.
   *
   * @return the next byte of data, or -1 if the end of the stream is reached.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  @Override public int read() throws IOException {
    int c = in.read();
    if (c == -1) {
      return -1;
    }
    md5.update((byte)(c & 0xff));
    return c;
}
  /**
   * Reads up to length bytes of data from this input stream into an
   * array of bytes. This method blocks until some input is available.
   *
   * @param bytes the buffer into which the data is read.
   * @param offset the start offset of the data.
   * @param length the maximum number of bytes read.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  @Override public int read(byte[] bytes, int offset, int length) throws IOException {
    int r;
    if ((r = in.read(bytes, offset, length)) == -1) {
      return r;
    }
    md5.update(bytes, offset, r);
    return r;
  }
  /**
   * Returns array of bytes representing hash of the stream so far.
   *
   * @return Array of 16 bytes, the hash of all read bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public byte[] getHash(){
    return md5.getHash();
  }
  /**
   * Get a 32-character hex representation representing hash of the stream so far.
   *
   * @return A string containing  the hash of all written bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public String getHashString(){
    return md5.getHashString();
  }
}
class MD5 {
  /**
   * Class constructor
   *
   * @since ostermillerutils 1.00.00
   */
  public MD5 () {
    reset();
  }
  /**
   * Command line program that will take files as arguments
   * and output the MD5 sum for each file.
   *
   * @param args command line arguments
   *
   * @since ostermillerutils 1.00.00
   */
  public static void main (String[] args){
    if (args.length == 0){
      System.err.println("Please specify a file.");
    } else {
      for (String element: args) {
        try {
          System.out.println(MD5.getHashString(new File(element)) + " " + element);
        } catch (IOException x){
          System.err.println(x.getMessage());
        }
      }
    }
  }
  /**
   * Gets this hash sum as an array of 16 bytes.
   *
   * @return Array of 16 bytes, the hash of all updated bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public byte[] getHash() {
    if (!finalState.valid) {
      finalState.copy(workingState);
      long bitCount = finalState.bitCount;
      // Compute the number of left over bits
      int leftOver = (int) (((bitCount >>> 3)) & 0x3f);
      // Compute the amount of padding to add based on number of left over bits.
      int padlen = (leftOver < 56) ? (56 - leftOver) : (120 - leftOver);
      // add the padding
      update(finalState, padding, 0, padlen);
      // add the length (computed before padding was added)
      update(finalState, encode(bitCount), 0, 8);
      finalState.valid = true;
    }
    // make a copy of the hash before returning it.
    return encode(finalState.state, 16);
  }
  /**
   * Returns 32-character hex representation of this hash.
   *
   * @return String representation of this object"s hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public String getHashString(){
    return toHex(this.getHash());
  }
  /**
   * Gets the MD5 hash of the given byte array.
   *
   * @param b byte array for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(byte[] b){
    MD5 md5 = new MD5();
    md5.update(b);
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash of the given byte array.
   *
   * @param b byte array for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(byte[] b){
    MD5 md5 = new MD5();
    md5.update(b);
    return md5.getHashString();
  }
  /**
   * Gets the MD5 hash the data on the given InputStream.
   *
   * @param in byte array for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(InputStream in) throws IOException {
    MD5 md5 = new MD5();
    byte[] buffer = new byte[1024];
    int read;
    while ((read = in.read(buffer)) != -1){
      md5.update(buffer, read);
    }
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash the data on the given InputStream.
   *
   * @param in byte array for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(InputStream in) throws IOException {
    MD5 md5 = new MD5();
    byte[] buffer = new byte[1024];
    int read;
    while ((read = in.read(buffer)) != -1){
      md5.update(buffer, read);
    }
    return md5.getHashString();
  }
  /**
   * Gets the MD5 hash of the given file.
   *
   * @param f file for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(File f) throws IOException {
    InputStream is = new FileInputStream(f);
    byte[] hash = getHash(is);
    is.close();
    return hash;
  }
  /**
   * Gets the MD5 hash of the given file.
   *
   * @param f file array for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   * @throws IOException if an I/O error occurs.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(File f) throws IOException {
    InputStream is = new FileInputStream(f);
    String hash = getHashString(is);
    is.close();
    return hash;
  }
  /**
   * Gets the MD5 hash of the given String.
   * The string is converted to bytes using the current
   * platform"s default character encoding.
   *
   * @param s String for which an MD5 hash is desired.
   * @return Array of 16 bytes, the hash of all updated bytes.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(String s){
    MD5 md5 = new MD5();
    md5.update(s);
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash of the given String.
   * The string is converted to bytes using the current
   * platform"s default character encoding.
   *
   * @param s String for which an MD5 hash is desired.
   * @return 32-character hex representation the data"s MD5 hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(String s){
    MD5 md5 = new MD5();
    md5.update(s);
    return md5.getHashString();
  }

  /**
   * Gets the MD5 hash of the given String.
   *
   * @param s String for which an MD5 hash is desired.
   * @param enc The name of a supported character encoding.
   * @return Array of 16 bytes, the hash of all updated bytes.
   * @throws UnsupportedEncodingException If the named encoding is not supported.
   *
   * @since ostermillerutils 1.00.00
   */
  public static byte[] getHash(String s, String enc) throws UnsupportedEncodingException {
    MD5 md5 = new MD5();
    md5.update(s, enc);
    return md5.getHash();
  }
  /**
   * Gets the MD5 hash of the given String.
   *
   * @param s String for which an MD5 hash is desired.
   * @param enc The name of a supported character encoding.
   * @return 32-character hex representation the data"s MD5 hash.
   * @throws UnsupportedEncodingException If the named encoding is not supported.
   *
   * @since ostermillerutils 1.00.00
   */
  public static String getHashString(String s, String enc) throws UnsupportedEncodingException {
    MD5 md5 = new MD5();
    md5.update(s, enc);
    return md5.getHashString();
  }

  /**
   * Reset the MD5 sum to its initial state.
   *
   * @since ostermillerutils 1.00.00
   */
  public void reset() {
    workingState.reset();
    finalState.valid = false;
  }
  /**
   * Returns 32-character hex representation of this hash.
   *
   * @return String representation of this object"s hash.
   *
   * @since ostermillerutils 1.00.00
   */
  @Override public String toString(){
    return getHashString();
  }
  /**
   * Update this hash with the given data.
   * <p>
   * A state may be passed into this method so that we can add padding
   * and finalize a md5 hash without limiting our ability to update
   * more data later.
   * <p>
   * If length bytes are not available to be hashed, as many bytes as
   * possible will be hashed.
   *
   * @param state Which state is updated.
   * @param buffer Array of bytes to be hashed.
   * @param offset Offset to buffer array.
   * @param length number of bytes to hash.
   *
   * @since ostermillerutils 1.00.00
   */
  private void update (MD5State state, byte buffer[], int offset, int length) {
    finalState.valid = false;
    // if length goes beyond the end of the buffer, cut it short.
    if ((length + offset) > buffer.length){
      length = buffer.length - offset;
    }
    // compute number of bytes mod 64
    // this is what we have sitting in a buffer
    // that have not been hashed yet
    int index = (int) (state.bitCount >>> 3) & 0x3f;
    // add the length to the count (translate bytes to bits)
    state.bitCount += length << 3;
    int partlen = 64 - index;
    int i = 0;
    if (length >= partlen) {
      System.arraycopy(buffer, offset, state.buffer, index, partlen);
      transform(state, decode(state.buffer, 64, 0));
      for (i = partlen; (i + 63) < length; i+= 64){
        transform(state, decode(buffer, 64, i));
      }
      index = 0;
    }
    // buffer remaining input
    if (i < length) {
      for (int start = i; i < length; i++) {
        state.buffer[index + i - start] = buffer[i + offset];
      }
    }
  }
  /**
   * Update this hash with the given data.
   * <p>
   * If length bytes are not available to be hashed, as many bytes as
   * possible will be hashed.
   *
   * @param buffer Array of bytes to be hashed.
   * @param offset Offset to buffer array.
   * @param length number of bytes to hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte buffer[], int offset, int length) {
    update(workingState, buffer, offset, length);
  }
  /**
   * Update this hash with the given data.
   * <p>
   * If length bytes are not available to be hashed, as many bytes as
   * possible will be hashed.
   *
   * @param buffer Array of bytes to be hashed.
   * @param length number of bytes to hash.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte buffer[], int length) {
    update(buffer, 0, length);
  }
  /**
   * Update this hash with the given data.
   *
   * @param buffer Array of bytes to be hashed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte buffer[]) {
    update(buffer, 0, buffer.length);
  }
  /**
   * Updates this hash with a single byte.
   *
   * @param b byte to be hashed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (byte b) {
    byte buffer[] = new byte[1];
    buffer[0] = b;
    update(buffer, 1);
  }
  /**
   * Update this hash with a String.
   * The string is converted to bytes using the current
   * platform"s default character encoding.
   *
   * @param s String to be hashed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (String s) {
    update(s.getBytes());
  }
  /**
   * Update this hash with a String.
   *
   * @param s String to be hashed.
   * @param enc The name of a supported character encoding.
   * @throws UnsupportedEncodingException If the named encoding is not supported.
   *
   * @since ostermillerutils 1.00.00
   */
  public void update (String s, String enc) throws UnsupportedEncodingException {
    update(s.getBytes(enc));
  }
  /**
   * The current state from which the hash sum
   * can be computed or updated.
   *
   * @since ostermillerutils 1.00.00
   */
  private MD5State workingState = new MD5State();
  /**
   * Cached copy of the final MD5 hash sum.  This is created when
   * the hash is requested and it is invalidated when the hash
   * is updated.
   *
   * @since ostermillerutils 1.00.00
   */
  private MD5State finalState = new MD5State();
  /**
   * Temporary buffer cached here for performance reasons.
   *
   * @since ostermillerutils 1.00.00
   */
  private int[] decodeBuffer = new int[16];
  /**
   * 64 bytes of padding that can be added if the length
   * is not divisible by 64.
   *
   * @since ostermillerutils 1.00.00
   */
  private static final byte padding[] = {
    (byte) 0x80, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
  };
  /**
   * Contains internal state of the MD5 class.
   * Passes MD5 test suite as defined in RFC1321.
   *
   * @since ostermillerutils 1.00.00
   */
  private class MD5State {
    /**
     * True if this state is valid.
     *
     * @since ostermillerutils 1.00.00
     */
    private boolean valid = true;
    /**
     * Reset to initial state.
     *
     * @since ostermillerutils 1.00.00
     */
    private void reset(){
      state[0] = 0x67452301;
      state[1] = 0xefcdab89;
      state[2] = 0x98badcfe;
      state[3] = 0x10325476;
      bitCount = 0;
    }
    /**
     * 128-byte state
     *
     * @since ostermillerutils 1.00.00
     */
    private int state[] = new int[4];
    /**
     * 64-bit count of the number of bits that have been hashed.
     *
     * @since ostermillerutils 1.00.00
     */
    private long bitCount;
    /**
     * 64-byte buffer (512 bits) for storing to-be-hashed characters
     *
     * @since ostermillerutils 1.00.00
     */
    private byte buffer[] = new byte[64];
    private MD5State() {
      reset();
    }
    /**
     * Set this state to be exactly the same as some other.
     *
     * @param from state to copy from.
     *
     * @since ostermillerutils 1.00.00
     */
    private void copy(MD5State from) {
      System.arraycopy(from.buffer, 0, this.buffer, 0, this.buffer.length);
      System.arraycopy(from.state, 0, this.state, 0, this.state.length);
      this.valid = from.valid;
      this.bitCount = from.bitCount;
    }
  }

  /**
   * Turns array of bytes into string representing each byte as
   * a two digit unsigned hex number.
   *
   * @param hash Array of bytes to convert to hex-string
   * @return  Generated hex string
   *
   * @since ostermillerutils 1.00.00
   */
  private static String toHex(byte hash[]){
    StringBuffer buf = new StringBuffer(hash.length * 2);
    for (byte element: hash) {
      int intVal = element & 0xff;
      if (intVal < 0x10){
        // append a zero before a one digit hex
        // number to make it two digits.
        buf.append("0");
      }
      buf.append(Integer.toHexString(intVal));
    }
    return buf.toString();
  }
  private static int FF (int a, int b, int c, int d, int x, int s, int ac) {
    a += ((b & c) | (~b & d));
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static int GG (int a, int b, int c, int d, int x, int s, int ac) {
    a += ((b & d) | (c & ~d));
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static int HH (int a, int b, int c, int d, int x, int s, int ac) {
    a += (b ^ c ^ d);
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static int II (int a, int b, int c, int d, int x, int s, int ac) {
    a += (c ^ (b | ~d));
    a += x;
    a += ac;
    //return rotateLeft(a, s) + b;
    a = (a << s) | (a >>> (32 - s));
    return a + b;
  }
  private static byte[] encode(long l){
    byte[] out = new byte[8];
    out[0] = (byte) (l & 0xff);
    out[1] = (byte) ((l >>> 8) & 0xff);
    out[2] = (byte) ((l >>> 16) & 0xff);
    out[3] = (byte) ((l >>> 24) & 0xff);
    out[4] = (byte) ((l >>> 32) & 0xff);
    out[5] = (byte) ((l >>> 40) & 0xff);
    out[6] = (byte) ((l >>> 48) & 0xff);
    out[7] = (byte) ((l >>> 56) & 0xff);
    return out;
  }
  private static byte[] encode(int input[], int len){
    byte[] out = new byte[len];
    int i, j;
    for (i = j = 0; j  < len; i++, j += 4) {
      out[j] = (byte) (input[i] & 0xff);
      out[j + 1] = (byte) ((input[i] >>> 8) & 0xff);
      out[j + 2] = (byte) ((input[i] >>> 16) & 0xff);
      out[j + 3] = (byte) ((input[i] >>> 24) & 0xff);
    }
    return out;
  }
  private int[] decode(byte buffer[], int len, int offset){
    int i, j;
    for (i = j = 0; j < len; i++, j += 4) {
      decodeBuffer[i] = (
        (buffer[j + offset] & 0xff)) |
        (((buffer[j + 1 + offset] & 0xff)) << 8) |
        (((buffer[j + 2 + offset] & 0xff)) << 16) |
        (((buffer[j + 3 + offset] & 0xff)) << 24
      );
    }
    return decodeBuffer;
  }
  private static void transform(MD5State state, int[] x){
    int a = state.state[0];
    int b = state.state[1];
    int c = state.state[2];
    int d = state.state[3];
    /* Round 1 */
    a = FF (a, b, c, d, x[ 0],   7, 0xd76aa478); /* 1 */
    d = FF (d, a, b, c, x[ 1],  12, 0xe8c7b756); /* 2 */
    c = FF (c, d, a, b, x[ 2],  17, 0x242070db); /* 3 */
    b = FF (b, c, d, a, x[ 3],  22, 0xc1bdceee); /* 4 */
    a = FF (a, b, c, d, x[ 4],   7, 0xf57c0faf); /* 5 */
    d = FF (d, a, b, c, x[ 5],  12, 0x4787c62a); /* 6 */
    c = FF (c, d, a, b, x[ 6],  17, 0xa8304613); /* 7 */
    b = FF (b, c, d, a, x[ 7],  22, 0xfd469501); /* 8 */
    a = FF (a, b, c, d, x[ 8],   7, 0x698098d8); /* 9 */
    d = FF (d, a, b, c, x[ 9],  12, 0x8b44f7af); /* 10 */
    c = FF (c, d, a, b, x[10],  17, 0xffff5bb1); /* 11 */
    b = FF (b, c, d, a, x[11],  22, 0x895cd7be); /* 12 */
    a = FF (a, b, c, d, x[12],   7, 0x6b901122); /* 13 */
    d = FF (d, a, b, c, x[13],  12, 0xfd987193); /* 14 */
    c = FF (c, d, a, b, x[14],  17, 0xa679438e); /* 15 */
    b = FF (b, c, d, a, x[15],  22, 0x49b40821); /* 16 */
    /* Round 2 */
    a = GG (a, b, c, d, x[ 1],   5, 0xf61e2562); /* 17 */
    d = GG (d, a, b, c, x[ 6],   9, 0xc040b340); /* 18 */
    c = GG (c, d, a, b, x[11],  14, 0x265e5a51); /* 19 */
    b = GG (b, c, d, a, x[ 0],  20, 0xe9b6c7aa); /* 20 */
    a = GG (a, b, c, d, x[ 5],   5, 0xd62f105d); /* 21 */
    d = GG (d, a, b, c, x[10],   9, 0x02441453); /* 22 */
    c = GG (c, d, a, b, x[15],  14, 0xd8a1e681); /* 23 */
    b = GG (b, c, d, a, x[ 4],  20, 0xe7d3fbc8); /* 24 */
    a = GG (a, b, c, d, x[ 9],   5, 0x21e1cde6); /* 25 */
    d = GG (d, a, b, c, x[14],   9, 0xc33707d6); /* 26 */
    c = GG (c, d, a, b, x[ 3],  14, 0xf4d50d87); /* 27 */
    b = GG (b, c, d, a, x[ 8],  20, 0x455a14ed); /* 28 */
    a = GG (a, b, c, d, x[13],   5, 0xa9e3e905); /* 29 */
    d = GG (d, a, b, c, x[ 2],   9, 0xfcefa3f8); /* 30 */
    c = GG (c, d, a, b, x[ 7],  14, 0x676f02d9); /* 31 */
    b = GG (b, c, d, a, x[12],  20, 0x8d2a4c8a); /* 32 */
    /* Round 3 */
    a = HH (a, b, c, d, x[ 5],   4, 0xfffa3942); /* 33 */
    d = HH (d, a, b, c, x[ 8],  11, 0x8771f681); /* 34 */
    c = HH (c, d, a, b, x[11],  16, 0x6d9d6122); /* 35 */
    b = HH (b, c, d, a, x[14],  23, 0xfde5380c); /* 36 */
    a = HH (a, b, c, d, x[ 1],   4, 0xa4beea44); /* 37 */
    d = HH (d, a, b, c, x[ 4],  11, 0x4bdecfa9); /* 38 */
    c = HH (c, d, a, b, x[ 7],  16, 0xf6bb4b60); /* 39 */
    b = HH (b, c, d, a, x[10],  23, 0xbebfbc70); /* 40 */
    a = HH (a, b, c, d, x[13],   4, 0x289b7ec6); /* 41 */
    d = HH (d, a, b, c, x[ 0],  11, 0xeaa127fa); /* 42 */
    c = HH (c, d, a, b, x[ 3],  16, 0xd4ef3085); /* 43 */
    b = HH (b, c, d, a, x[ 6],  23, 0x04881d05); /* 44 */
    a = HH (a, b, c, d, x[ 9],   4, 0xd9d4d039); /* 45 */
    d = HH (d, a, b, c, x[12],  11, 0xe6db99e5); /* 46 */
    c = HH (c, d, a, b, x[15],  16, 0x1fa27cf8); /* 47 */
    b = HH (b, c, d, a, x[ 2],  23, 0xc4ac5665); /* 48 */
    /* Round 4 */
    a = II (a, b, c, d, x[ 0],   6, 0xf4292244); /* 49 */
    d = II (d, a, b, c, x[ 7],  10, 0x432aff97); /* 50 */
    c = II (c, d, a, b, x[14],  15, 0xab9423a7); /* 51 */
    b = II (b, c, d, a, x[ 5],  21, 0xfc93a039); /* 52 */
    a = II (a, b, c, d, x[12],   6, 0x655b59c3); /* 53 */
    d = II (d, a, b, c, x[ 3],  10, 0x8f0ccc92); /* 54 */
    c = II (c, d, a, b, x[10],  15, 0xffeff47d); /* 55 */
    b = II (b, c, d, a, x[ 1],  21, 0x85845dd1); /* 56 */
    a = II (a, b, c, d, x[ 8],   6, 0x6fa87e4f); /* 57 */
    d = II (d, a, b, c, x[15],  10, 0xfe2ce6e0); /* 58 */
    c = II (c, d, a, b, x[ 6],  15, 0xa3014314); /* 59 */
    b = II (b, c, d, a, x[13],  21, 0x4e0811a1); /* 60 */
    a = II (a, b, c, d, x[ 4],   6, 0xf7537e82); /* 61 */
    d = II (d, a, b, c, x[11],  10, 0xbd3af235); /* 62 */
    c = II (c, d, a, b, x[ 2],  15, 0x2ad7d2bb); /* 63 */
    b = II (b, c, d, a, x[ 9],  21, 0xeb86d391); /* 64 */
    state.state[0] += a;
    state.state[1] += b;
    state.state[2] += c;
    state.state[3] += d;
  }
}





OTP one-time password calculation

   
/* Class for implementing OTP (aka s/key) one-time password calculation
 * using the accompanying md class for md4 (and hopefully md5 eventually) 
 * based key calculation.
 *
 * The constructor is used to set the challenge info and passphrase,
 * and the calc() method calculates the otp. The results can either
 * be retrieved using the tolong() method, which gives you the 64 bits
 * "folded" hash in a single word, or else as a String of otp "words"
 * via toString().
 *
 * Cripes this is slow. How can we make it faster? 
 *
 * Copyright 1996 Harry Mantakos, harry@cs.umd.edu
 */
class otp {
    int seq;
    String seed, passphrase;
    byte hash[];
    int sha;
    final static byte MD4 = 4;
    final static byte MD5 = 5;
    otp(int n, String s, String p, int hashalg) {
  this.seq = n;
  this.seed = s;
  this.passphrase = p;
  this.sha = hashalg;
    }
    void calc() {
  if (this.sha == MD5) {
      this.md5calc();
  } else {
      this.md4calc();
        }
    }
    void md4calc() {
  int tmpseq = this.seq;
  md4 mdc;
  mdc = new md4(this.seed + this.passphrase);
  mdc.calc();
  this.hash = otpfoldregs(mdc.getregs());
  while (tmpseq > 0) {
      mdc = new md4(hash);
      mdc.calc();
      this.hash = otpfoldregs(mdc.getregs());
      tmpseq--;
  }
    }
    void md5calc() {
  int tmpseq = this.seq;
  md5 mdc;
  mdc = new md5(this.seed + this.passphrase);
  mdc.calc();
  this.hash = otpfoldregs(mdc.getregs());
  while (tmpseq > 0) {
      mdc = new md5(hash);
      mdc.calc();
      this.hash = otpfoldregs(mdc.getregs());
      tmpseq--;
  }
    }
    static byte[] otpfoldregs(int regs[]) {
  int ac, bd, i;
  byte fold[] = new byte[8];
  ac = regs[0] ^ regs[2];
  bd = regs[1] ^ regs[3];
  for (i=0; i < 4; i++) {
      fold[i] = (byte) (ac & 0xff);
      ac >>= 8;
  }
  for (i=4; i < 8; i++) {
      fold[i] = (byte) (bd & 0xff);
      bd >>= 8;
  }
  return fold;
    }
    long tolong() {
  long wi;
  int i;
  wi = 0;
  for (i=0; i < 8; i++) {
      wi <<= 8;
      wi |= (this.hash[i] & 0xff);
  }
  return wi;
    }
    public String toString() {
  long wi, tmplong;
  String tmpstr;
  int i, j;
  byte parity;
  wi = this.tolong();
  tmplong = wi;
  tmpstr = "";
  parity = 0;
  for (i = 0; i < 64; i+=2) {
      parity += tmplong & 0x3;
      tmplong >>= 2;
  }
  for (i=4; i >= 0; i--) {
      tmpstr += btoe((int) 
          ((wi >> (i * 11 + 9)) & 0x7ff)) + " ";
  }
  tmpstr += btoe((int) ((wi << 2) & 0x7fc) | (parity & 0x03));
  return tmpstr;
    }
    public static String btoe(int index) {
        if (index < words.length) {
          return words[index];
        } else {
           return "bogus";
        }
    }
static String words[] =
{        "A",     "ABE",   "ACE",   "ACT",   "AD",    "ADA",   "ADD",
"AGO",   "AID",   "AIM",   "AIR",   "ALL",   "ALP",   "AM",    "AMY",
"AN",    "ANA",   "AND",   "ANN",   "ANT",   "ANY",   "APE",   "APS",
"APT",   "ARC",   "ARE",   "ARK",   "ARM",   "ART",   "AS",    "ASH",
"ASK",   "AT",    "ATE",   "AUG",   "AUK",   "AVE",   "AWE",   "AWK",
"AWL",   "AWN",   "AX",    "AYE",   "BAD",   "BAG",   "BAH",   "BAM",
"BAN",   "BAR",   "BAT",   "BAY",   "BE",    "BED",   "BEE",   "BEG",
"BEN",   "BET",   "BEY",   "BIB",   "BID",   "BIG",   "BIN",   "BIT",
"BOB",   "BOG",   "BON",   "BOO",   "BOP",   "BOW",   "BOY",   "BUB",
"BUD",   "BUG",   "BUM",   "BUN",   "BUS",   "BUT",   "BUY",   "BY",
"BYE",   "CAB",   "CAL",   "CAM",   "CAN",   "CAP",   "CAR",   "CAT",
"CAW",   "COD",   "COG",   "COL",   "CON",   "COO",   "COP",   "COT",
"COW",   "COY",   "CRY",   "CUB",   "CUE",   "CUP",   "CUR",   "CUT",
"DAB",   "DAD",   "DAM",   "DAN",   "DAR",   "DAY",   "DEE",   "DEL",
"DEN",   "DES",   "DEW",   "DID",   "DIE",   "DIG",   "DIN",   "DIP",
"DO",    "DOE",   "DOG",   "DON",   "DOT",   "DOW",   "DRY",   "DUB",
"DUD",   "DUE",   "DUG",   "DUN",   "EAR",   "EAT",   "ED",    "EEL",
"EGG",   "EGO",   "ELI",   "ELK",   "ELM",   "ELY",   "EM",    "END",
"EST",   "ETC",   "EVA",   "EVE",   "EWE",   "EYE",   "FAD",   "FAN",
"FAR",   "FAT",   "FAY",   "FED",   "FEE",   "FEW",   "FIB",   "FIG",
"FIN",   "FIR",   "FIT",   "FLO",   "FLY",   "FOE",   "FOG",   "FOR",
"FRY",   "FUM",   "FUN",   "FUR",   "GAB",   "GAD",   "GAG",   "GAL",
"GAM",   "GAP",   "GAS",   "GAY",   "GEE",   "GEL",   "GEM",   "GET",
"GIG",   "GIL",   "GIN",   "GO",    "GOT",   "GUM",   "GUN",   "GUS",
"GUT",   "GUY",   "GYM",   "GYP",   "HA",    "HAD",   "HAL",   "HAM",
"HAN",   "HAP",   "HAS",   "HAT",   "HAW",   "HAY",   "HE",    "HEM",
"HEN",   "HER",   "HEW",   "HEY",   "HI",    "HID",   "HIM",   "HIP",
"HIS",   "HIT",   "HO",    "HOB",   "HOC",   "HOE",   "HOG",   "HOP",
"HOT",   "HOW",   "HUB",   "HUE",   "HUG",   "HUH",   "HUM",   "HUT",
"I",     "ICY",   "IDA",   "IF",    "IKE",   "ILL",   "INK",   "INN",
"IO",    "ION",   "IQ",    "IRA",   "IRE",   "IRK",   "IS",    "IT",
"ITS",   "IVY",   "JAB",   "JAG",   "JAM",   "JAN",   "JAR",   "JAW",
"JAY",   "JET",   "JIG",   "JIM",   "JO",    "JOB",   "JOE",   "JOG",
"JOT",   "JOY",   "JUG",   "JUT",   "KAY",   "KEG",   "KEN",   "KEY",
"KID",   "KIM",   "KIN",   "KIT",   "LA",    "LAB",   "LAC",   "LAD",
"LAG",   "LAM",   "LAP",   "LAW",   "LAY",   "LEA",   "LED",   "LEE",
"LEG",   "LEN",   "LEO",   "LET",   "LEW",   "LID",   "LIE",   "LIN",
"LIP",   "LIT",   "LO",    "LOB",   "LOG",   "LOP",   "LOS",   "LOT",
"LOU",   "LOW",   "LOY",   "LUG",   "LYE",   "MA",    "MAC",   "MAD",
"MAE",   "MAN",   "MAO",   "MAP",   "MAT",   "MAW",   "MAY",   "ME",
"MEG",   "MEL",   "MEN",   "MET",   "MEW",   "MID",   "MIN",   "MIT",
"MOB",   "MOD",   "MOE",   "MOO",   "MOP",   "MOS",   "MOT",   "MOW",
"MUD",   "MUG",   "MUM",   "MY",    "NAB",   "NAG",   "NAN",   "NAP",
"NAT",   "NAY",   "NE",    "NED",   "NEE",   "NET",   "NEW",   "NIB",
"NIL",   "NIP",   "NIT",   "NO",    "NOB",   "NOD",   "NON",   "NOR",
"NOT",   "NOV",   "NOW",   "NU",    "NUN",   "NUT",   "O",     "OAF",
"OAK",   "OAR",   "OAT",   "ODD",   "ODE",   "OF",    "OFF",   "OFT",
"OH",    "OIL",   "OK",    "OLD",   "ON",    "ONE",   "OR",    "ORB",
"ORE",   "ORR",   "OS",    "OTT",   "OUR",   "OUT",   "OVA",   "OW",
"OWE",   "OWL",   "OWN",   "OX",    "PA",    "PAD",   "PAL",   "PAM",
"PAN",   "PAP",   "PAR",   "PAT",   "PAW",   "PAY",   "PEA",   "PEG",
"PEN",   "PEP",   "PER",   "PET",   "PEW",   "PHI",   "PI",    "PIE",
"PIN",   "PIT",   "PLY",   "PO",    "POD",   "POE",   "POP",   "POT",
"POW",   "PRO",   "PRY",   "PUB",   "PUG",   "PUN",   "PUP",   "PUT",
"QUO",   "RAG",   "RAM",   "RAN",   "RAP",   "RAT",   "RAW",   "RAY",
"REB",   "RED",   "REP",   "RET",   "RIB",   "RID",   "RIG",   "RIM",
"RIO",   "RIP",   "ROB",   "ROD",   "ROE",   "RON",   "ROT",   "ROW",
"ROY",   "RUB",   "RUE",   "RUG",   "RUM",   "RUN",   "RYE",   "SAC",
"SAD",   "SAG",   "SAL",   "SAM",   "SAN",   "SAP",   "SAT",   "SAW",
"SAY",   "SEA",   "SEC",   "SEE",   "SEN",   "SET",   "SEW",   "SHE",
"SHY",   "SIN",   "SIP",   "SIR",   "SIS",   "SIT",   "SKI",   "SKY",
"SLY",   "SO",    "SOB",   "SOD",   "SON",   "SOP",   "SOW",   "SOY",
"SPA",   "SPY",   "SUB",   "SUD",   "SUE",   "SUM",   "SUN",   "SUP",
"TAB",   "TAD",   "TAG",   "TAN",   "TAP",   "TAR",   "TEA",   "TED",
"TEE",   "TEN",   "THE",   "THY",   "TIC",   "TIE",   "TIM",   "TIN",
"TIP",   "TO",    "TOE",   "TOG",   "TOM",   "TON",   "TOO",   "TOP",
"TOW",   "TOY",   "TRY",   "TUB",   "TUG",   "TUM",   "TUN",   "TWO",
"UN",    "UP",    "US",    "USE",   "VAN",   "VAT",   "VET",   "VIE",
"WAD",   "WAG",   "WAR",   "WAS",   "WAY",   "WE",    "WEB",   "WED",
"WEE",   "WET",   "WHO",   "WHY",   "WIN",   "WIT",   "WOK",   "WON",
"WOO",   "WOW",   "WRY",   "WU",    "YAM",   "YAP",   "YAW",   "YE",
"YEA",   "YES",   "YET",   "YOU",   "ABED",  "ABEL",  "ABET",  "ABLE",
"ABUT",  "ACHE",  "ACID",  "ACME",  "ACRE",  "ACTA",  "ACTS",  "ADAM",
"ADDS",  "ADEN",  "AFAR",  "AFRO",  "AGEE",  "AHEM",  "AHOY",  "AIDA",
"AIDE",  "AIDS",  "AIRY",  "AJAR",  "AKIN",  "ALAN",  "ALEC",  "ALGA",
"ALIA",  "ALLY",  "ALMA",  "ALOE",  "ALSO",  "ALTO",  "ALUM",  "ALVA",
"AMEN",  "AMES",  "AMID",  "AMMO",  "AMOK",  "AMOS",  "AMRA",  "ANDY",
"ANEW",  "ANNA",  "ANNE",  "ANTE",  "ANTI",  "AQUA",  "ARAB",  "ARCH",
"AREA",  "ARGO",  "ARID",  "ARMY",  "ARTS",  "ARTY",  "ASIA",  "ASKS",
"ATOM",  "AUNT",  "AURA",  "AUTO",  "AVER",  "AVID",  "AVIS",  "AVON",
"AVOW",  "AWAY",  "AWRY",  "BABE",  "BABY",  "BACH",  "BACK",  "BADE",
"BAIL",  "BAIT",  "BAKE",  "BALD",  "BALE",  "BALI",  "BALK",  "BALL",
"BALM",  "BAND",  "BANE",  "BANG",  "BANK",  "BARB",  "BARD",  "BARE",
"BARK",  "BARN",  "BARR",  "BASE",  "BASH",  "BASK",  "BASS",  "BATE",
"BATH",  "BAWD",  "BAWL",  "BEAD",  "BEAK",  "BEAM",  "BEAN",  "BEAR",
"BEAT",  "BEAU",  "BECK",  "BEEF",  "BEEN",  "BEER",  "BEET",  "BELA",
"BELL",  "BELT",  "BEND",  "BENT",  "BERG",  "BERN",  "BERT",  "BESS",
"BEST",  "BETA",  "BETH",  "BHOY",  "BIAS",  "BIDE",  "BIEN",  "BILE",
"BILK",  "BILL",  "BIND",  "BING",  "BIRD",  "BITE",  "BITS",  "BLAB",
"BLAT",  "BLED",  "BLEW",  "BLOB",  "BLOC",  "BLOT",  "BLOW",  "BLUE",
"BLUM",  "BLUR",  "BOAR",  "BOAT",  "BOCA",  "BOCK",  "BODE",  "BODY",
"BOGY",  "BOHR",  "BOIL",  "BOLD",  "BOLO",  "BOLT",  "BOMB",  "BONA",
"BOND",  "BONE",  "BONG",  "BONN",  "BONY",  "BOOK",  "BOOM",  "BOON",
"BOOT",  "BORE",  "BORG",  "BORN",  "BOSE",  "BOSS",  "BOTH",  "BOUT",
"BOWL",  "BOYD",  "BRAD",  "BRAE",  "BRAG",  "BRAN",  "BRAY",  "BRED",
"BREW",  "BRIG",  "BRIM",  "BROW",  "BUCK",  "BUDD",  "BUFF",  "BULB",
"BULK",  "BULL",  "BUNK",  "BUNT",  "BUOY",  "BURG",  "BURL",  "BURN",
"BURR",  "BURT",  "BURY",  "BUSH",  "BUSS",  "BUST",  "BUSY",  "BYTE",
"CADY",  "CAFE",  "CAGE",  "CAIN",  "CAKE",  "CALF",  "CALL",  "CALM",
"CAME",  "CANE",  "CANT",  "CARD",  "CARE",  "CARL",  "CARR",  "CART",
"CASE",  "CASH",  "CASK",  "CAST",  "CAVE",  "CEIL",  "CELL",  "CENT",
"CERN",  "CHAD",  "CHAR",  "CHAT",  "CHAW",  "CHEF",  "CHEN",  "CHEW",
"CHIC",  "CHIN",  "CHOU",  "CHOW",  "CHUB",  "CHUG",  "CHUM",  "CITE",
"CITY",  "CLAD",  "CLAM",  "CLAN",  "CLAW",  "CLAY",  "CLOD",  "CLOG",
"CLOT",  "CLUB",  "CLUE",  "COAL",  "COAT",  "COCA",  "COCK",  "COCO",
"CODA",  "CODE",  "CODY",  "COED",  "COIL",  "COIN",  "COKE",  "COLA",
"COLD",  "COLT",  "COMA",  "COMB",  "COME",  "COOK",  "COOL",  "COON",
"COOT",  "CORD",  "CORE",  "CORK",  "CORN",  "COST",  "COVE",  "COWL",
"CRAB",  "CRAG",  "CRAM",  "CRAY",  "CREW",  "CRIB",  "CROW",  "CRUD",
"CUBA",  "CUBE",  "CUFF",  "CULL",  "CULT",  "CUNY",  "CURB",  "CURD",
"CURE",  "CURL",  "CURT",  "CUTS",  "DADE",  "DALE",  "DAME",  "DANA",
"DANE",  "DANG",  "DANK",  "DARE",  "DARK",  "DARN",  "DART",  "DASH",
"DATA",  "DATE",  "DAVE",  "DAVY",  "DAWN",  "DAYS",  "DEAD",  "DEAF",
"DEAL",  "DEAN",  "DEAR",  "DEBT",  "DECK",  "DEED",  "DEEM",  "DEER",
"DEFT",  "DEFY",  "DELL",  "DENT",  "DENY",  "DESK",  "DIAL",  "DICE",
"DIED",  "DIET",  "DIME",  "DINE",  "DING",  "DINT",  "DIRE",  "DIRT",
"DISC",  "DISH",  "DISK",  "DIVE",  "DOCK",  "DOES",  "DOLE",  "DOLL",
"DOLT",  "DOME",  "DONE",  "DOOM",  "DOOR",  "DORA",  "DOSE",  "DOTE",
"DOUG",  "DOUR",  "DOVE",  "DOWN",  "DRAB",  "DRAG",  "DRAM",  "DRAW",
"DREW",  "DRUB",  "DRUG",  "DRUM",  "DUAL",  "DUCK",  "DUCT",  "DUEL",
"DUET",  "DUKE",  "DULL",  "DUMB",  "DUNE",  "DUNK",  "DUSK",  "DUST",
"DUTY",  "EACH",  "EARL",  "EARN",  "EASE",  "EAST",  "EASY",  "EBEN",
"ECHO",  "EDDY",  "EDEN",  "EDGE",  "EDGY",  "EDIT",  "EDNA",  "EGAN",
"ELAN",  "ELBA",  "ELLA",  "ELSE",  "EMIL",  "EMIT",  "EMMA",  "ENDS",
"ERIC",  "EROS",  "EVEN",  "EVER",  "EVIL",  "EYED",  "FACE",  "FACT",
"FADE",  "FAIL",  "FAIN",  "FAIR",  "FAKE",  "FALL",  "FAME",  "FANG",
"FARM",  "FAST",  "FATE",  "FAWN",  "FEAR",  "FEAT",  "FEED",  "FEEL",
"FEET",  "FELL",  "FELT",  "FEND",  "FERN",  "FEST",  "FEUD",  "FIEF",
"FIGS",  "FILE",  "FILL",  "FILM",  "FIND",  "FINE",  "FINK",  "FIRE",
"FIRM",  "FISH",  "FISK",  "FIST",  "FITS",  "FIVE",  "FLAG",  "FLAK",
"FLAM",  "FLAT",  "FLAW",  "FLEA",  "FLED",  "FLEW",  "FLIT",  "FLOC",
"FLOG",  "FLOW",  "FLUB",  "FLUE",  "FOAL",  "FOAM",  "FOGY",  "FOIL",
"FOLD",  "FOLK",  "FOND",  "FONT",  "FOOD",  "FOOL",  "FOOT",  "FORD",
"FORE",  "FORK",  "FORM",  "FORT",  "FOSS",  "FOUL",  "FOUR",  "FOWL",
"FRAU",  "FRAY",  "FRED",  "FREE",  "FRET",  "FREY",  "FROG",  "FROM",
"FUEL",  "FULL",  "FUME",  "FUND",  "FUNK",  "FURY",  "FUSE",  "FUSS",
"GAFF",  "GAGE",  "GAIL",  "GAIN",  "GAIT",  "GALA",  "GALE",  "GALL",
"GALT",  "GAME",  "GANG",  "GARB",  "GARY",  "GASH",  "GATE",  "GAUL",
"GAUR",  "GAVE",  "GAWK",  "GEAR",  "GELD",  "GENE",  "GENT",  "GERM",
"GETS",  "GIBE",  "GIFT",  "GILD",  "GILL",  "GILT",  "GINA",  "GIRD",
"GIRL",  "GIST",  "GIVE",  "GLAD",  "GLEE",  "GLEN",  "GLIB",  "GLOB",
"GLOM",  "GLOW",  "GLUE",  "GLUM",  "GLUT",  "GOAD",  "GOAL",  "GOAT",
"GOER",  "GOES",  "GOLD",  "GOLF",  "GONE",  "GONG",  "GOOD",  "GOOF",
"GORE",  "GORY",  "GOSH",  "GOUT",  "GOWN",  "GRAB",  "GRAD",  "GRAY",
"GREG",  "GREW",  "GREY",  "GRID",  "GRIM",  "GRIN",  "GRIT",  "GROW",
"GRUB",  "GULF",  "GULL",  "GUNK",  "GURU",  "GUSH",  "GUST",  "GWEN",
"GWYN",  "HAAG",  "HAAS",  "HACK",  "HAIL",  "HAIR",  "HALE",  "HALF",
"HALL",  "HALO",  "HALT",  "HAND",  "HANG",  "HANK",  "HANS",  "HARD",
"HARK",  "HARM",  "HART",  "HASH",  "HAST",  "HATE",  "HATH",  "HAUL",
"HAVE",  "HAWK",  "HAYS",  "HEAD",  "HEAL",  "HEAR",  "HEAT",  "HEBE",
"HECK",  "HEED",  "HEEL",  "HEFT",  "HELD",  "HELL",  "HELM",  "HERB",
"HERD",  "HERE",  "HERO",  "HERS",  "HESS",  "HEWN",  "HICK",  "HIDE",
"HIGH",  "HIKE",  "HILL",  "HILT",  "HIND",  "HINT",  "HIRE",  "HISS",
"HIVE",  "HOBO",  "HOCK",  "HOFF",  "HOLD",  "HOLE",  "HOLM",  "HOLT",
"HOME",  "HONE",  "HONK",  "HOOD",  "HOOF",  "HOOK",  "HOOT",  "HORN",
"HOSE",  "HOST",  "HOUR",  "HOVE",  "HOWE",  "HOWL",  "HOYT",  "HUCK",
"HUED",  "HUFF",  "HUGE",  "HUGH",  "HUGO",  "HULK",  "HULL",  "HUNK",
"HUNT",  "HURD",  "HURL",  "HURT",  "HUSH",  "HYDE",  "HYMN",  "IBIS",
"ICON",  "IDEA",  "IDLE",  "IFFY",  "INCA",  "INCH",  "INTO",  "IONS",
"IOTA",  "IOWA",  "IRIS",  "IRMA",  "IRON",  "ISLE",  "ITCH",  "ITEM",
"IVAN",  "JACK",  "JADE",  "JAIL",  "JAKE",  "JANE",  "JAVA",  "JEAN",
"JEFF",  "JERK",  "JESS",  "JEST",  "JIBE",  "JILL",  "JILT",  "JIVE",
"JOAN",  "JOBS",  "JOCK",  "JOEL",  "JOEY",  "JOHN",  "JOIN",  "JOKE",
"JOLT",  "JOVE",  "JUDD",  "JUDE",  "JUDO",  "JUDY",  "JUJU",  "JUKE",
"JULY",  "JUNE",  "JUNK",  "JUNO",  "JURY",  "JUST",  "JUTE",  "KAHN",
"KALE",  "KANE",  "KANT",  "KARL",  "KATE",  "KEEL",  "KEEN",  "KENO",
"KENT",  "KERN",  "KERR",  "KEYS",  "KICK",  "KILL",  "KIND",  "KING",
"KIRK",  "KISS",  "KITE",  "KLAN",  "KNEE",  "KNEW",  "KNIT",  "KNOB",
"KNOT",  "KNOW",  "KOCH",  "KONG",  "KUDO",  "KURD",  "KURT",  "KYLE",
"LACE",  "LACK",  "LACY",  "LADY",  "LAID",  "LAIN",  "LAIR",  "LAKE",
"LAMB",  "LAME",  "LAND",  "LANE",  "LANG",  "LARD",  "LARK",  "LASS",
"LAST",  "LATE",  "LAUD",  "LAVA",  "LAWN",  "LAWS",  "LAYS",  "LEAD",
"LEAF",  "LEAK",  "LEAN",  "LEAR",  "LEEK",  "LEER",  "LEFT",  "LEND",
"LENS",  "LENT",  "LEON",  "LESK",  "LESS",  "LEST",  "LETS",  "LIAR",
"LICE",  "LICK",  "LIED",  "LIEN",  "LIES",  "LIEU",  "LIFE",  "LIFT",
"LIKE",  "LILA",  "LILT",  "LILY",  "LIMA",  "LIMB",  "LIME",  "LIND",
"LINE",  "LINK",  "LINT",  "LION",  "LISA",  "LIST",  "LIVE",  "LOAD",
"LOAF",  "LOAM",  "LOAN",  "LOCK",  "LOFT",  "LOGE",  "LOIS",  "LOLA",
"LONE",  "LONG",  "LOOK",  "LOON",  "LOOT",  "LORD",  "LORE",  "LOSE",
"LOSS",  "LOST",  "LOUD",  "LOVE",  "LOWE",  "LUCK",  "LUCY",  "LUGE",
"LUKE",  "LULU",  "LUND",  "LUNG",  "LURA",  "LURE",  "LURK",  "LUSH",
"LUST",  "LYLE",  "LYNN",  "LYON",  "LYRA",  "MACE",  "MADE",  "MAGI",
"MAID",  "MAIL",  "MAIN",  "MAKE",  "MALE",  "MALI",  "MALL",  "MALT",
"MANA",  "MANN",  "MANY",  "MARC",  "MARE",  "MARK",  "MARS",  "MART",
"MARY",  "MASH",  "MASK",  "MASS",  "MAST",  "MATE",  "MATH",  "MAUL",
"MAYO",  "MEAD",  "MEAL",  "MEAN",  "MEAT",  "MEEK",  "MEET",  "MELD",
"MELT",  "MEMO",  "MEND",  "MENU",  "MERT",  "MESH",  "MESS",  "MICE",
"MIKE",  "MILD",  "MILE",  "MILK",  "MILL",  "MILT",  "MIMI",  "MIND",
"MINE",  "MINI",  "MINK",  "MINT",  "MIRE",  "MISS",  "MIST",  "MITE",
"MITT",  "MOAN",  "MOAT",  "MOCK",  "MODE",  "MOLD",  "MOLE",  "MOLL",
"MOLT",  "MONA",  "MONK",  "MONT",  "MOOD",  "MOON",  "MOOR",  "MOOT",
"MORE",  "MORN",  "MORT",  "MOSS",  "MOST",  "MOTH",  "MOVE",  "MUCH",
"MUCK",  "MUDD",  "MUFF",  "MULE",  "MULL",  "MURK",  "MUSH",  "MUST",
"MUTE",  "MUTT",  "MYRA",  "MYTH",  "NAGY",  "NAIL",  "NAIR",  "NAME",
"NARY",  "NASH",  "NAVE",  "NAVY",  "NEAL",  "NEAR",  "NEAT",  "NECK",
"NEED",  "NEIL",  "NELL",  "NEON",  "NERO",  "NESS",  "NEST",  "NEWS",
"NEWT",  "NIBS",  "NICE",  "NICK",  "NILE",  "NINA",  "NINE",  "NOAH",
"NODE",  "NOEL",  "NOLL",  "NONE",  "NOOK",  "NOON",  "NORM",  "NOSE",
"NOTE",  "NOUN",  "NOVA",  "NUDE",  "NULL",  "NUMB",  "OATH",  "OBEY",
"OBOE",  "ODIN",  "OHIO",  "OILY",  "OINT",  "OKAY",  "OLAF",  "OLDY",
"OLGA",  "OLIN",  "OMAN",  "OMEN",  "OMIT",  "ONCE",  "ONES",  "ONLY",
"ONTO",  "ONUS",  "ORAL",  "ORGY",  "OSLO",  "OTIS",  "OTTO",  "OUCH",
"OUST",  "OUTS",  "OVAL",  "OVEN",  "OVER",  "OWLY",  "OWNS",  "QUAD",
"QUIT",  "QUOD",  "RACE",  "RACK",  "RACY",  "RAFT",  "RAGE",  "RAID",
"RAIL",  "RAIN",  "RAKE",  "RANK",  "RANT",  "RARE",  "RASH",  "RATE",
"RAVE",  "RAYS",  "READ",  "REAL",  "REAM",  "REAR",  "RECK",  "REED",
"REEF",  "REEK",  "REEL",  "REID",  "REIN",  "RENA",  "REND",  "RENT",
"REST",  "RICE",  "RICH",  "RICK",  "RIDE",  "RIFT",  "RILL",  "RIME",
"RING",  "RINK",  "RISE",  "RISK",  "RITE",  "ROAD",  "ROAM",  "ROAR",
"ROBE",  "ROCK",  "RODE",  "ROIL",  "ROLL",  "ROME",  "ROOD",  "ROOF",
"ROOK",  "ROOM",  "ROOT",  "ROSA",  "ROSE",  "ROSS",  "ROSY",  "ROTH",
"ROUT",  "ROVE",  "ROWE",  "ROWS",  "RUBE",  "RUBY",  "RUDE",  "RUDY",
"RUIN",  "RULE",  "RUNG",  "RUNS",  "RUNT",  "RUSE",  "RUSH",  "RUSK",
"RUSS",  "RUST",  "RUTH",  "SACK",  "SAFE",  "SAGE",  "SAID",  "SAIL",
"SALE",  "SALK",  "SALT",  "SAME",  "SAND",  "SANE",  "SANG",  "SANK",
"SARA",  "SAUL",  "SAVE",  "SAYS",  "SCAN",  "SCAR",  "SCAT",  "SCOT",
"SEAL",  "SEAM",  "SEAR",  "SEAT",  "SEED",  "SEEK",  "SEEM",  "SEEN",
"SEES",  "SELF",  "SELL",  "SEND",  "SENT",  "SETS",  "SEWN",  "SHAG",
"SHAM",  "SHAW",  "SHAY",  "SHED",  "SHIM",  "SHIN",  "SHOD",  "SHOE",
"SHOT",  "SHOW",  "SHUN",  "SHUT",  "SICK",  "SIDE",  "SIFT",  "SIGH",
"SIGN",  "SILK",  "SILL",  "SILO",  "SILT",  "SINE",  "SING",  "SINK",
"SIRE",  "SITE",  "SITS",  "SITU",  "SKAT",  "SKEW",  "SKID",  "SKIM",
"SKIN",  "SKIT",  "SLAB",  "SLAM",  "SLAT",  "SLAY",  "SLED",  "SLEW",
"SLID",  "SLIM",  "SLIT",  "SLOB",  "SLOG",  "SLOT",  "SLOW",  "SLUG",
"SLUM",  "SLUR",  "SMOG",  "SMUG",  "SNAG",  "SNOB",  "SNOW",  "SNUB",
"SNUG",  "SOAK",  "SOAR",  "SOCK",  "SODA",  "SOFA",  "SOFT",  "SOIL",
"SOLD",  "SOME",  "SONG",  "SOON",  "SOOT",  "SORE",  "SORT",  "SOUL",
"SOUR",  "SOWN",  "STAB",  "STAG",  "STAN",  "STAR",  "STAY",  "STEM",
"STEW",  "STIR",  "STOW",  "STUB",  "STUN",  "SUCH",  "SUDS",  "SUIT",
"SULK",  "SUMS",  "SUNG",  "SUNK",  "SURE",  "SURF",  "SWAB",  "SWAG",
"SWAM",  "SWAN",  "SWAT",  "SWAY",  "SWIM",  "SWUM",  "TACK",  "TACT",
"TAIL",  "TAKE",  "TALE",  "TALK",  "TALL",  "TANK",  "TASK",  "TATE",
"TAUT",  "TEAL",  "TEAM",  "TEAR",  "TECH",  "TEEM",  "TEEN",  "TEET",
"TELL",  "TEND",  "TENT",  "TERM",  "TERN",  "TESS",  "TEST",  "THAN",
"THAT",  "THEE",  "THEM",  "THEN",  "THEY",  "THIN",  "THIS",  "THUD",
"THUG",  "TICK",  "TIDE",  "TIDY",  "TIED",  "TIER",  "TILE",  "TILL",
"TILT",  "TIME",  "TINA",  "TINE",  "TINT",  "TINY",  "TIRE",  "TOAD",
"TOGO",  "TOIL",  "TOLD",  "TOLL",  "TONE",  "TONG",  "TONY",  "TOOK",
"TOOL",  "TOOT",  "TORE",  "TORN",  "TOTE",  "TOUR",  "TOUT",  "TOWN",
"TRAG",  "TRAM",  "TRAY",  "TREE",  "TREK",  "TRIG",  "TRIM",  "TRIO",
"TROD",  "TROT",  "TROY",  "TRUE",  "TUBA",  "TUBE",  "TUCK",  "TUFT",
"TUNA",  "TUNE",  "TUNG",  "TURF",  "TURN",  "TUSK",  "TWIG",  "TWIN",
"TWIT",  "ULAN",  "UNIT",  "URGE",  "USED",  "USER",  "USES",  "UTAH",
"VAIL",  "VAIN",  "VALE",  "VARY",  "VASE",  "VAST",  "VEAL",  "VEDA",
"VEIL",  "VEIN",  "VEND",  "VENT",  "VERB",  "VERY",  "VETO",  "VICE",
"VIEW",  "VINE",  "VISE",  "VOID",  "VOLT",  "VOTE",  "WACK",  "WADE",
"WAGE",  "WAIL",  "WAIT",  "WAKE",  "WALE",  "WALK",  "WALL",  "WALT",
"WAND",  "WANE",  "WANG",  "WANT",  "WARD",  "WARM",  "WARN",  "WART",
"WASH",  "WAST",  "WATS",  "WATT",  "WAVE",  "WAVY",  "WAYS",  "WEAK",
"WEAL",  "WEAN",  "WEAR",  "WEED",  "WEEK",  "WEIR",  "WELD",  "WELL",
"WELT",  "WENT",  "WERE",  "WERT",  "WEST",  "WHAM",  "WHAT",  "WHEE",
"WHEN",  "WHET",  "WHOA",  "WHOM",  "WICK",  "WIFE",  "WILD",  "WILL",
"WIND",  "WINE",  "WING",  "WINK",  "WINO",  "WIRE",  "WISE",  "WISH",
"WITH",  "WOLF",  "WONT",  "WOOD", "WOOL", "WORD", "WORE", "WORK", "WORM", "WORN",
      "WOVE", "WRIT", "WYNN", "YALE", "YANG", "YANK", "YARD", "YARN",
      "YAWL", "YAWN", "YEAH", "YEAR", "YELL", "YOGA", "YOKE" };
} /* End of class otp */
/* Class for implementing md4 hash algorithm (and hopefully md5 eventually).
 * There are constructors for prepping the hash algorithm (doing the
 * padding, mainly) for a String or a byte[], and an mdcalc() method 
 * for generating the hash. The results can be accessed as an int array 
 * by getregs(), or as a String of hex digits with toString().
 * 
 * Copyright 1996 Harry Mantakos, harry@cs.umd.edu
 */
class md4 extends md {
  md4(String s) {
    super(s);
  }
  md4(byte in[]) {
    super(in);
  }
  static int F(int x, int y, int z) {
    return ((x & y) | (~x & z));
  }
  static int G(int x, int y, int z) {
    return ((x & y) | (x & z) | (y & z));
  }
  static int H(int x, int y, int z) {
    return (x ^ y ^ z);
  }
  void round1(int blk) {
    A = rotintlft((A + F(B, C, D) + d[0 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[1 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[2 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[3 + 16 * blk]), 19);
    A = rotintlft((A + F(B, C, D) + d[4 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[5 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[6 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[7 + 16 * blk]), 19);
    A = rotintlft((A + F(B, C, D) + d[8 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[9 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[10 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[11 + 16 * blk]), 19);
    A = rotintlft((A + F(B, C, D) + d[12 + 16 * blk]), 3);
    D = rotintlft((D + F(A, B, C) + d[13 + 16 * blk]), 7);
    C = rotintlft((C + F(D, A, B) + d[14 + 16 * blk]), 11);
    B = rotintlft((B + F(C, D, A) + d[15 + 16 * blk]), 19);
  }
  void round2(int blk) {
    A = rotintlft((A + G(B, C, D) + d[0 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[4 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[8 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[12 + 16 * blk] + 0x5a827999), 13);
    A = rotintlft((A + G(B, C, D) + d[1 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[5 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[9 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[13 + 16 * blk] + 0x5a827999), 13);
    A = rotintlft((A + G(B, C, D) + d[2 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[6 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[10 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[14 + 16 * blk] + 0x5a827999), 13);
    A = rotintlft((A + G(B, C, D) + d[3 + 16 * blk] + 0x5a827999), 3);
    D = rotintlft((D + G(A, B, C) + d[7 + 16 * blk] + 0x5a827999), 5);
    C = rotintlft((C + G(D, A, B) + d[11 + 16 * blk] + 0x5a827999), 9);
    B = rotintlft((B + G(C, D, A) + d[15 + 16 * blk] + 0x5a827999), 13);
  }
  void round3(int blk) {
    A = rotintlft((A + H(B, C, D) + d[0 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[8 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[4 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[12 + 16 * blk] + 0x6ed9eba1), 15);
    A = rotintlft((A + H(B, C, D) + d[2 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[10 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[6 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[14 + 16 * blk] + 0x6ed9eba1), 15);
    A = rotintlft((A + H(B, C, D) + d[1 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[9 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[5 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[13 + 16 * blk] + 0x6ed9eba1), 15);
    A = rotintlft((A + H(B, C, D) + d[3 + 16 * blk] + 0x6ed9eba1), 3);
    D = rotintlft((D + H(A, B, C) + d[11 + 16 * blk] + 0x6ed9eba1), 9);
    C = rotintlft((C + H(D, A, B) + d[7 + 16 * blk] + 0x6ed9eba1), 11);
    B = rotintlft((B + H(C, D, A) + d[15 + 16 * blk] + 0x6ed9eba1), 15);
  }
  void round4(int blk) {
    System.out.println(" must be md5, in round4!");
  }
}
class md5 extends md {
  md5(String s) {
    super(s);
  }
  md5(byte in[]) {
    super(in);
  }
  static int F(int x, int y, int z) {
    return ((x & y) | (~x & z));
  }
  static int G(int x, int y, int z) {
    return ((x & z) | (y & ~z));
  }
  static int H(int x, int y, int z) {
    return (x ^ y ^ z);
  }
  static int I(int x, int y, int z) {
    return (y ^ (x | ~z));
  }
  void round1(int blk) {
    A = rotintlft(A + F(B, C, D) + d[0 + 16 * blk] + 0xd76aa478, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[1 + 16 * blk] + 0xe8c7b756, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[2 + 16 * blk] + 0x242070db, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[3 + 16 * blk] + 0xc1bdceee, 22) + C;
    A = rotintlft(A + F(B, C, D) + d[4 + 16 * blk] + 0xf57c0faf, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[5 + 16 * blk] + 0x4787c62a, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[6 + 16 * blk] + 0xa8304613, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[7 + 16 * blk] + 0xfd469501, 22) + C;
    A = rotintlft(A + F(B, C, D) + d[8 + 16 * blk] + 0x698098d8, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[9 + 16 * blk] + 0x8b44f7af, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[10 + 16 * blk] + 0xffff5bb1, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[11 + 16 * blk] + 0x895cd7be, 22) + C;
    A = rotintlft(A + F(B, C, D) + d[12 + 16 * blk] + 0x6b901122, 7) + B;
    D = rotintlft(D + F(A, B, C) + d[13 + 16 * blk] + 0xfd987193, 12) + A;
    C = rotintlft(C + F(D, A, B) + d[14 + 16 * blk] + 0xa679438e, 17) + D;
    B = rotintlft(B + F(C, D, A) + d[15 + 16 * blk] + 0x49b40821, 22) + C;
  }
  void round2(int blk) {
    A = rotintlft(A + G(B, C, D) + d[1 + 16 * blk] + 0xf61e2562, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[6 + 16 * blk] + 0xc040b340, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[11 + 16 * blk] + 0x265e5a51, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[0 + 16 * blk] + 0xe9b6c7aa, 20) + C;
    A = rotintlft(A + G(B, C, D) + d[5 + 16 * blk] + 0xd62f105d, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[10 + 16 * blk] + 0x02441453, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[15 + 16 * blk] + 0xd8a1e681, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[4 + 16 * blk] + 0xe7d3fbc8, 20) + C;
    A = rotintlft(A + G(B, C, D) + d[9 + 16 * blk] + 0x21e1cde6, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[14 + 16 * blk] + 0xc33707d6, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[3 + 16 * blk] + 0xf4d50d87, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[8 + 16 * blk] + 0x455a14ed, 20) + C;
    A = rotintlft(A + G(B, C, D) + d[13 + 16 * blk] + 0xa9e3e905, 5) + B;
    D = rotintlft(D + G(A, B, C) + d[2 + 16 * blk] + 0xfcefa3f8, 9) + A;
    C = rotintlft(C + G(D, A, B) + d[7 + 16 * blk] + 0x676f02d9, 14) + D;
    B = rotintlft(B + G(C, D, A) + d[12 + 16 * blk] + 0x8d2a4c8a, 20) + C;
  }
  void round3(int blk) {
    A = rotintlft(A + H(B, C, D) + d[5 + 16 * blk] + 0xfffa3942, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[8 + 16 * blk] + 0x8771f681, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[11 + 16 * blk] + 0x6d9d6122, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[14 + 16 * blk] + 0xfde5380c, 23) + C;
    A = rotintlft(A + H(B, C, D) + d[1 + 16 * blk] + 0xa4beea44, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[4 + 16 * blk] + 0x4bdecfa9, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[7 + 16 * blk] + 0xf6bb4b60, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[10 + 16 * blk] + 0xbebfbc70, 23) + C;
    A = rotintlft(A + H(B, C, D) + d[13 + 16 * blk] + 0x289b7ec6, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[0 + 16 * blk] + 0xeaa127fa, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[3 + 16 * blk] + 0xd4ef3085, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[6 + 16 * blk] + 0x04881d05, 23) + C;
    A = rotintlft(A + H(B, C, D) + d[9 + 16 * blk] + 0xd9d4d039, 4) + B;
    D = rotintlft(D + H(A, B, C) + d[12 + 16 * blk] + 0xe6db99e5, 11) + A;
    C = rotintlft(C + H(D, A, B) + d[15 + 16 * blk] + 0x1fa27cf8, 16) + D;
    B = rotintlft(B + H(C, D, A) + d[2 + 16 * blk] + 0xc4ac5665, 23) + C;
  }
  void round4(int blk) {
    A = rotintlft(A + I(B, C, D) + d[0 + 16 * blk] + 0xf4292244, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[7 + 16 * blk] + 0x432aff97, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[14 + 16 * blk] + 0xab9423a7, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[5 + 16 * blk] + 0xfc93a039, 21) + C;
    A = rotintlft(A + I(B, C, D) + d[12 + 16 * blk] + 0x655b59c3, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[3 + 16 * blk] + 0x8f0ccc92, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[10 + 16 * blk] + 0xffeff47d, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[1 + 16 * blk] + 0x85845dd1, 21) + C;
    A = rotintlft(A + I(B, C, D) + d[8 + 16 * blk] + 0x6fa87e4f, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[15 + 16 * blk] + 0xfe2ce6e0, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[6 + 16 * blk] + 0xa3014314, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[13 + 16 * blk] + 0x4e0811a1, 21) + C;
    A = rotintlft(A + I(B, C, D) + d[4 + 16 * blk] + 0xf7537e82, 6) + B;
    D = rotintlft(D + I(A, B, C) + d[11 + 16 * blk] + 0xbd3af235, 10) + A;
    C = rotintlft(C + I(D, A, B) + d[2 + 16 * blk] + 0x2ad7d2bb, 15) + D;
    B = rotintlft(B + I(C, D, A) + d[9 + 16 * blk] + 0xeb86d391, 21) + C;
  }
}
public class md {
  int A, B, C, D;
  int d[];
  int numwords;
  /*
   * For verification of a modicum of sanity, run a few test strings through
   */
  public static void main(String[] argv) {
    boolean doinmd4;
    String mdtype;
    /* Test cases, mostly taken from rfc 1320 */
    String str[] = {
        "",
        "a",
        "abc",
        "message digest",
        "abcdefghijklmnopqrstuvwxyz",
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
        "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
        "01234567890123456789012345678901234567890123456789012345" };
    if (argv.length == 0) {
      mdtype = "md4";
      doinmd4 = true;
    } else if (argv.length > 1) {
      System.err.println("Usage: md [4|5|md4|md5]");
      return;
    } else if ((argv[0].equals("4")) || (argv[0].equals("md4"))) {
      mdtype = "md4";
      doinmd4 = true;
    } else if ((argv[0].equals("5")) || (argv[0].equals("md5"))) {
      mdtype = "md5";
      doinmd4 = false;
    } else {
      System.err.println("Usage: md [4|5|md4|md5]");
      return;
    }
    for (int i = 0; i < str.length; i++) {
      if (doinmd4) {
        md4 mdc = new md4(str[i]);
        mdc.calc();
        System.out.println(mdtype + "(\"" + str[i] + "\") = " + mdc);
      } else {
        md5 mdc = new md5(str[i]);
        mdc.calc();
        System.out.println(mdtype + "(\"" + str[i] + "\") = " + mdc);
      }
    }
  }
  md(String s) {
    byte in[] = new byte[s.length()];
    int i;
    for (i = 0; i < s.length(); i++) {
      in[i] = (byte) (s.charAt(i) & 0xff);
    }
    mdinit(in);
  }
  md(byte in[]) {
    mdinit(in);
  }
  void mdinit(byte in[]) {
    int newlen, endblklen, pad, i;
    long datalenbits;
    datalenbits = in.length * 8;
    endblklen = in.length % 64;
    if (endblklen < 56) {
      pad = 64 - endblklen;
    } else {
      pad = (64 - endblklen) + 64;
    }
    newlen = in.length + pad;
    byte b[] = new byte[newlen];
    for (i = 0; i < in.length; i++) {
      b[i] = in[i];
    }
    b[in.length] = (byte) 0x80;
    for (i = b.length + 1; i < (newlen - 8); i++) {
      b[i] = 0;
    }
    for (i = 0; i < 8; i++) {
      b[newlen - 8 + i] = (byte) (datalenbits & 0xff);
      datalenbits >>= 8;
    }
    /* init registers */
    A = 0x67452301;
    B = 0xefcdab89;
    C = 0x98badcfe;
    D = 0x10325476;
    this.numwords = newlen / 4;
    this.d = new int[this.numwords];
    for (i = 0; i < newlen; i += 4) {
      this.d[i / 4] = (b[i] & 0xff) + ((b[i + 1] & 0xff) << 8)
          + ((b[i + 2] & 0xff) << 16) + ((b[i + 3] & 0xff) << 24);
    }
  }
  public String toString() {
    String s;
    return (tohex(A) + tohex(B) + tohex(C) + tohex(D));
  }
  int[] getregs() {
    int regs[] = { this.A, this.B, this.C, this.D };
    return regs;
  }
  void calc() {
    int AA, BB, CC, DD, i;
    for (i = 0; i < numwords / 16; i++) {
      AA = A;
      BB = B;
      CC = C;
      DD = D;
      round1(i);
      round2(i);
      round3(i);
      if (this instanceof md5) {
        round4(i);
      }
      A += AA;
      B += BB;
      C += CC;
      D += DD;
    }
  }
  /*
   * Dummy round*() methods. these are overriden in the md4 and md5 subclasses
   */
  void round1(int blk) {
    System.err.println("Danger! Danger! Someone called md.round1()!");
  }
  void round2(int blk) {
    System.err.println("Danger! Danger! Someone called md.round2()!");
  }
  void round3(int blk) {
    System.err.println("Danger! Danger! Someone called md.round3()!");
  }
  void round4(int blk) {
    System.err.println("Danger! Danger! Someone called md.round4()!");
  }
  static int rotintlft(int val, int numbits) {
    return ((val << numbits) | (val >>> (32 - numbits)));
  }
  static String tohex(int i) {
    int b;
    String tmpstr;
    tmpstr = "";
    for (b = 0; b < 4; b++) {
      tmpstr += Integer.toString((i >> 4) & 0xf, 16)
          + Integer.toString(i & 0xf, 16);
      i >>= 8;
    }
    return tmpstr;
  }
}