MD5Crypt.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */


  20. package org.openspcoop2.utils.crypt;

  21. import java.security.MessageDigest;
  22. import java.security.NoSuchAlgorithmException;
  23. import java.security.SecureRandom;

  24. import org.openspcoop2.utils.random.RandomGenerator;

  25. /**
  26.  * A Java Implementation of the MD5Crypt function Modified from the GANYMEDE
  27.  * network directory management system released under the GNU General Public
  28.  * License by the University of Texas at Austin
  29.  * http://tools.arlut.utexas.edu/gash2/ Original version from :Jonathan Abbey,
  30.  * jonabbey@arlut.utexas.edu Modified by: Vladimir Silva,
  31.  * vladimir_silva@yahoo.com Modification history: 9/2005 - Removed dependencies
  32.  * on a MD5 private implementation - Added built-in java.security.MessageDigest
  33.  * (MD5) support - Code cleanup
  34.  *
  35.  * @author Sandra Giangrandi (sandra@link.it)
  36.  * @author $Author$
  37.  * @version $Rev$, $Date$
  38.  */

  39. @Deprecated
  40. public class MD5Crypt {
  41.     // Character set allowed for the salt string
  42.     static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

  43.     // Character set of the encrypted password: A-Za-z0-9./
  44.     static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

  45.     public static String newSalt() {

  46.         int c = 0;
  47.         byte[] arrByte = new byte[2];

  48.         RandomGenerator randomGenerator = new RandomGenerator(false);
  49.         randomGenerator.nextRandomBytes(arrByte);
  50.        
  51.         for (int i = 0; i<2; i++) {
  52.             c = arrByte[i] >> 6;  // div(64)
  53.             c = ((arrByte[i] & 0xff) - (c<<6));
  54.             if ( c <= 11 ) // 46-57 ./0..9
  55.                 c+=46;
  56.             if ( c >= 12 && c <= 37 ) // 65-90 a..z
  57.                c+=(65-12);    
  58.             if ( c >= 38 && c <= 63 ) // 97-122 A..Z
  59.                 c+=(97-38);
  60.             arrByte[i] = (byte) c;
  61.         }

  62.         String pw = new String(arrByte);
  63.         return pw;
  64.     }
  65.    
  66.     /**
  67.      * Function to return a string from the set: A-Za-z0-9./
  68.      *
  69.      * @return A string of size (size) from the set A-Za-z0-9./
  70.      * @param size
  71.      *            Length of the string
  72.      * @param v
  73.      *            value to be converted
  74.      */
  75.     static private final String to64(long v, int size) {
  76.         StringBuilder result = new StringBuilder();

  77.         while (--size >= 0) {
  78.             result.append(MD5Crypt.itoa64.charAt((int) (v & 0x3f)));
  79.             v >>>= 6;
  80.         }

  81.         return result.toString();
  82.     }

  83.     static private final void clearbits(byte bits[]) {
  84.         for (int i = 0; i < bits.length; i++) {
  85.             bits[i] = 0;
  86.         }
  87.     }

  88.     /**
  89.      * convert an encoded unsigned byte value into a int with the unsigned
  90.      * value.
  91.      */
  92.     static private final int bytes2u(byte inp) {
  93.         return inp & 0xff;
  94.     }

  95.     private static SecureRandom _rnd = null;
  96.     private static synchronized void initRandom() {
  97.         if(_rnd==null) {
  98.             _rnd = new SecureRandom();
  99.         }
  100.     }
  101.     private static java.util.Random getRandom() {
  102.         if(_rnd==null) {
  103.             initRandom();
  104.         }
  105.         return _rnd;
  106.     }
  107.    
  108.     /**
  109.      * LINUX/BSD MD5Crypt function
  110.      *
  111.      * @return The encrypted password as an MD5 hash
  112.      * @param password
  113.      *            Password to be encrypted
  114.      */
  115.     static public final String crypt(String password) {
  116.         StringBuilder salt = new StringBuilder();

  117.         // build a random 8 chars salt
  118.         while (salt.length() < 8) {
  119.             int index = (int) (getRandom().nextFloat() * MD5Crypt.SALTCHARS.length());
  120.             salt.append(MD5Crypt.SALTCHARS.substring(index, index + 1));
  121.         }

  122.         // crypt
  123.         return MD5Crypt.crypt(password, salt.toString(), "$1$");
  124.     }

  125.     /**
  126.      * LINUX/BSD MD5Crypt function
  127.      *
  128.      * @return The encrypted password as an MD5 hash
  129.      * @param salt
  130.      *            Random string used to initialize the MD5 engine
  131.      * @param password
  132.      *            Password to be encrypted
  133.      */
  134.     static public final String crypt(String password, String salt) {
  135.         return MD5Crypt.crypt(password, salt, "$1$");
  136.     }

  137.     /**
  138.      * Linux/BSD MD5Crypt function
  139.      *
  140.      * @throws java.lang.Exception
  141.      * @return The encrypted password as an MD5 hash
  142.      * @param magic
  143.      *            $1$ for Linux/BSB, $apr1$ for Apache crypt
  144.      * @param salt
  145.      *            8 byte permutation string
  146.      * @param password
  147.      *            user password
  148.      */
  149.     static public final String crypt(String password, String salt, String magic) {

  150.         byte finalState[];
  151.         long l;

  152.         /**
  153.          * Two MD5 hashes are used
  154.          */
  155.         MessageDigest ctx, ctx1;

  156.         try {
  157.             ctx = MessageDigest.getInstance("MD5");
  158.             ctx1 = MessageDigest.getInstance("MD5");
  159.         } catch (NoSuchAlgorithmException ex) {
  160.             System.err.println(ex);
  161.             return null;
  162.         }

  163.         /* Refine the Salt first */
  164.         /* If it starts with the magic string, then skip that */

  165.         if (salt.startsWith(magic)) {
  166.             salt = salt.substring(magic.length());
  167.         }

  168.         /* It stops at the first '$', max 8 chars */

  169.         if (salt.indexOf('$') != -1) {
  170.             salt = salt.substring(0, salt.indexOf('$'));
  171.         }

  172.         if (salt.length() > 8) {
  173.             salt = salt.substring(0, 8);
  174.         }

  175.         /**
  176.          * Transformation set #1: The password first, since that is what is most
  177.          * unknown Magic string Raw salt
  178.          */
  179.         ctx.update(password.getBytes());
  180.         ctx.update(magic.getBytes());
  181.         ctx.update(salt.getBytes());

  182.         /* Then just as many characters of the MD5(pw,salt,pw) */

  183.         ctx1.update(password.getBytes());
  184.         ctx1.update(salt.getBytes());
  185.         ctx1.update(password.getBytes());
  186.         finalState = ctx1.digest(); // ctx1.Final();

  187.         for (int pl = password.length(); pl > 0; pl -= 16) {
  188.             ctx.update(finalState, 0, pl > 16 ? 16 : pl);
  189.         }

  190.         /**
  191.          * the original code claimed that finalState was being cleared to keep
  192.          * dangerous bits out of memory, but doing this is also required in
  193.          * order to get the right output.
  194.          */

  195.         MD5Crypt.clearbits(finalState);

  196.         /* Then something really weird... */

  197.         for (int i = password.length(); i != 0; i >>>= 1) {
  198.             if ((i & 1) != 0) {
  199.                 ctx.update(finalState, 0, 1);
  200.             } else {
  201.                 ctx.update(password.getBytes(), 0, 1);
  202.             }
  203.         }

  204.         finalState = ctx.digest();

  205.         /**
  206.          * and now, just to make sure things don't run too fast On a 60 Mhz
  207.          * Pentium this takes 34 msec, so you would need 30 seconds to build a
  208.          * 1000 entry dictionary... (The above timings from the C version)
  209.          */

  210.         for (int i = 0; i < 1000; i++) {
  211.             try {
  212.                 ctx1 = MessageDigest.getInstance("MD5");
  213.             } catch (NoSuchAlgorithmException e0) {
  214.                 return null;
  215.             }

  216.             if ((i & 1) != 0) {
  217.                 ctx1.update(password.getBytes());
  218.             } else {
  219.                 ctx1.update(finalState, 0, 16);
  220.             }

  221.             if ((i % 3) != 0) {
  222.                 ctx1.update(salt.getBytes());
  223.             }

  224.             if ((i % 7) != 0) {
  225.                 ctx1.update(password.getBytes());
  226.             }

  227.             if ((i & 1) != 0) {
  228.                 ctx1.update(finalState, 0, 16);
  229.             } else {
  230.                 ctx1.update(password.getBytes());
  231.             }

  232.             finalState = ctx1.digest(); // Final();
  233.         }

  234.         /* Now make the output string */

  235.         StringBuilder result = new StringBuilder();

  236.         result.append(magic);
  237.         result.append(salt);
  238.         result.append("$");

  239.         /**
  240.          * Build a 22 byte output string from the set: A-Za-z0-9./
  241.          */
  242.         l = (MD5Crypt.bytes2u(finalState[0]) << 16) | (MD5Crypt.bytes2u(finalState[6]) << 8) | MD5Crypt.bytes2u(finalState[12]);
  243.         result.append(MD5Crypt.to64(l, 4));

  244.         l = (MD5Crypt.bytes2u(finalState[1]) << 16) | (MD5Crypt.bytes2u(finalState[7]) << 8) | MD5Crypt.bytes2u(finalState[13]);
  245.         result.append(MD5Crypt.to64(l, 4));

  246.         l = (MD5Crypt.bytes2u(finalState[2]) << 16) | (MD5Crypt.bytes2u(finalState[8]) << 8) | MD5Crypt.bytes2u(finalState[14]);
  247.         result.append(MD5Crypt.to64(l, 4));

  248.         l = (MD5Crypt.bytes2u(finalState[3]) << 16) | (MD5Crypt.bytes2u(finalState[9]) << 8) | MD5Crypt.bytes2u(finalState[15]);
  249.         result.append(MD5Crypt.to64(l, 4));

  250.         l = (MD5Crypt.bytes2u(finalState[4]) << 16) | (MD5Crypt.bytes2u(finalState[10]) << 8) | MD5Crypt.bytes2u(finalState[5]);
  251.         result.append(MD5Crypt.to64(l, 4));

  252.         l = MD5Crypt.bytes2u(finalState[11]);
  253.         result.append(MD5Crypt.to64(l, 2));

  254.         /* Don't leave anything around in vm they could use. */
  255.         MD5Crypt.clearbits(finalState);

  256.         return result.toString();
  257.     }

  258.     /**
  259.      * Test subroutine
  260.      *
  261.      * @param args
  262.      */
  263.     static final String USAGE = "MD5Crypt <password> <salt>";

  264.     public static void main(String[] args) {
  265.         try {
  266.             if (args.length != 2) {
  267.                 System.err.println(MD5Crypt.USAGE);
  268.             } else {
  269.                 System.out.println(MD5Crypt.crypt(args[0], args[1]));
  270.             }

  271.         } catch (Exception ex) {
  272.             System.err.println(ex);
  273.         }
  274.     }

  275. }