View Javadoc
1   package org.argeo.osgi.useradmin;
2   
3   import java.math.BigInteger;
4   import java.nio.ByteBuffer;
5   import java.nio.CharBuffer;
6   import java.nio.charset.StandardCharsets;
7   import java.security.MessageDigest;
8   import java.security.spec.KeySpec;
9   import java.util.Arrays;
10  
11  import javax.crypto.SecretKeyFactory;
12  import javax.crypto.spec.PBEKeySpec;
13  
14  /** Utilities around digests, mostly those related to passwords. */
15  class DigestUtils {
16  	final static String PASSWORD_SCHEME_SHA = "SHA";
17  	final static String PASSWORD_SCHEME_PBKDF2_SHA256 = "PBKDF2_SHA256";
18  
19  	static byte[] sha1(byte[] bytes) {
20  		try {
21  			MessageDigest digest = MessageDigest.getInstance("SHA1");
22  			digest.update(bytes);
23  			byte[] checksum = digest.digest();
24  			return checksum;
25  		} catch (Exception e) {
26  			throw new UserDirectoryException("Cannot SHA1 digest", e);
27  		}
28  	}
29  
30  	static byte[] toPasswordScheme(String passwordScheme, char[] password, byte[] salt, Integer iterations,
31  			Integer keyLength) {
32  		try {
33  			if (PASSWORD_SCHEME_SHA.equals(passwordScheme)) {
34  				MessageDigest digest = MessageDigest.getInstance("SHA1");
35  				byte[] bytes = charsToBytes(password);
36  				digest.update(bytes);
37  				return digest.digest();
38  			} else if (PASSWORD_SCHEME_PBKDF2_SHA256.equals(passwordScheme)) {
39  				KeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
40  
41  				SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
42  				final int ITERATION_LENGTH = 4;
43  				byte[] key = f.generateSecret(spec).getEncoded();
44  				byte[] result = new byte[ITERATION_LENGTH + salt.length + key.length];
45  				byte iterationsArr[] = new BigInteger(iterations.toString()).toByteArray();
46  				if (iterationsArr.length < ITERATION_LENGTH) {
47  					Arrays.fill(result, 0, ITERATION_LENGTH - iterationsArr.length, (byte) 0);
48  					System.arraycopy(iterationsArr, 0, result, ITERATION_LENGTH - iterationsArr.length,
49  							iterationsArr.length);
50  				} else {
51  					System.arraycopy(iterationsArr, 0, result, 0, ITERATION_LENGTH);
52  				}
53  				System.arraycopy(salt, 0, result, ITERATION_LENGTH, salt.length);
54  				System.arraycopy(key, 0, result, ITERATION_LENGTH + salt.length, key.length);
55  				return result;
56  			} else {
57  				throw new UnsupportedOperationException("Unkown password scheme " + passwordScheme);
58  			}
59  		} catch (Exception e) {
60  			throw new UserDirectoryException("Cannot digest", e);
61  		}
62  	}
63  
64  	static char[] bytesToChars(Object obj) {
65  		if (obj instanceof char[])
66  			return (char[]) obj;
67  		if (!(obj instanceof byte[]))
68  			throw new IllegalArgumentException(obj.getClass() + " is not a byte array");
69  		ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj);
70  		CharBuffer toBuffer = StandardCharsets.UTF_8.decode(fromBuffer);
71  		char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(), toBuffer.limit());
72  		// Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data
73  		// Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data
74  		// Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data
75  		return res;
76  	}
77  
78  	static byte[] charsToBytes(char[] chars) {
79  		CharBuffer charBuffer = CharBuffer.wrap(chars);
80  		ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer);
81  		byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
82  		// Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data
83  		// Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
84  		return bytes;
85  	}
86  
87  	static String sha1str(String str) {
88  		byte[] hash = sha1(str.getBytes(StandardCharsets.UTF_8));
89  		return encodeHexString(hash);
90  	}
91  
92  	final private static char[] hexArray = "0123456789abcdef".toCharArray();
93  
94  	/**
95  	 * From
96  	 * http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to
97  	 * -a-hex-string-in-java
98  	 */
99  	public static String encodeHexString(byte[] bytes) {
100 		char[] hexChars = new char[bytes.length * 2];
101 		for (int j = 0; j < bytes.length; j++) {
102 			int v = bytes[j] & 0xFF;
103 			hexChars[j * 2] = hexArray[v >>> 4];
104 			hexChars[j * 2 + 1] = hexArray[v & 0x0F];
105 		}
106 		return new String(hexChars);
107 	}
108 
109 	private DigestUtils() {
110 	}
111 }