001package com.bitbucket.thinbus.srp6.js;
002
003import static com.nimbusds.srp6.BigIntegerUtils.toHex;
004
005import java.math.BigInteger;
006import java.nio.charset.Charset;
007import java.security.MessageDigest;
008
009/**
010 * Secure Remote Password (SRP-6a) hashing routine for Java compatible with
011 * browser implementations by using hashing of string concatenated hex strings.
012 * 
013 * <p>
014 * Specification RFC 2945
015 * 
016 * @author Simon Massey
017 */
018public class HexHashedRoutines {
019        
020        public final static Charset utf8 = utf8();
021
022        static Charset utf8() {
023                return Charset.forName("UTF8");
024        }
025
026        public static BigInteger hashValues(final MessageDigest digest, final String... values) {
027                final StringBuilder builder = new StringBuilder();
028                for (String v : values) {
029                        builder.append(v);
030                }
031                final byte[] bytes = builder.toString().getBytes(utf8);
032                digest.update(bytes, 0, bytes.length);
033                return new BigInteger(1, digest.digest());
034        }
035
036        private HexHashedRoutines() {
037                // empty
038        }
039
040        public static String leadingZerosPad(String value, int desiredLength) {
041                StringBuilder builder = new StringBuilder();
042                int difference = desiredLength - value.length();
043                for (int i = 0; i < difference; i++) {
044                        builder.append('0');
045                }
046                builder.append(value);
047                return builder.toString();
048        }
049
050        public static String hashCredentials(MessageDigest digest, String salt,
051                        String identity, String password) {
052                digest.reset();
053
054                String concat = identity + ":" + password;
055
056                digest.update(concat.getBytes(utf8));
057                byte[] output = digest.digest();
058                digest.reset();
059
060                final String hash1 = toHex(new BigInteger(1, output));
061                
062                concat = (salt + hash1).toUpperCase();
063                
064                digest.update(concat.getBytes(utf8));
065                output = digest.digest();
066
067                return toHexString(output);
068        }
069
070        final private static char[] hexArray = "0123456789abcdef".toCharArray();
071
072        /**
073         * http://stackoverflow.com/a/9855338
074         * 
075         * Compute a String in HexDigit from the input. Note that this string may
076         * have leading zeros but hex strings created by toString(16) of BigInteger
077         * would strip leading zeros.
078         * 
079         * @param bytes
080         *            Raw byte array
081         * @return Hex encoding of the input
082         */
083        public static String toHexString(final byte[] bytes) {
084                char[] hexChars = new char[bytes.length * 2];
085                for (int j = 0; j < bytes.length; j++) {
086                        int v = bytes[j] & 0xFF;
087                        hexChars[j * 2] = hexArray[v >>> 4];
088                        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
089                }
090                return new String(hexChars);
091        }
092}