package com.hwlcn.security.crypto.hash; import com.hwlcn.security.util.ByteSource; import com.hwlcn.security.crypto.RandomNumberGenerator; import com.hwlcn.security.crypto.SecureRandomNumberGenerator; public class DefaultHashService implements ConfigurableHashService { private RandomNumberGenerator rng; private String algorithmName; private ByteSource privateSalt; private int iterations; private boolean generatePublicSalt; public DefaultHashService() { this.algorithmName = "SHA-512"; this.iterations = 1; this.generatePublicSalt = false; this.rng = new SecureRandomNumberGenerator(); } public Hash computeHash(HashRequest request) { if (request == null || request.getSource() == null || request.getSource().isEmpty()) { return null; } String algorithmName = getAlgorithmName(request); ByteSource source = request.getSource(); int iterations = getIterations(request); ByteSource publicSalt = getPublicSalt(request); ByteSource privateSalt = getPrivateSalt(); ByteSource salt = combine(privateSalt, publicSalt); Hash computed = new SimpleHash(algorithmName, source, salt, iterations); SimpleHash result = new SimpleHash(algorithmName); result.setBytes(computed.getBytes()); result.setIterations(iterations); result.setSalt(publicSalt); return result; } protected String getAlgorithmName(HashRequest request) { String name = request.getAlgorithmName(); if (name == null) { name = getHashAlgorithmName(); } return name; } protected int getIterations(HashRequest request) { int iterations = Math.max(0, request.getIterations()); if (iterations < 1) { iterations = Math.max(1, getHashIterations()); } return iterations; } protected ByteSource getPublicSalt(HashRequest request) { ByteSource publicSalt = request.getSalt(); if (publicSalt != null && !publicSalt.isEmpty()) { return publicSalt; } publicSalt = null; ByteSource privateSalt = getPrivateSalt(); boolean privateSaltExists = privateSalt != null && !privateSalt.isEmpty(); if (privateSaltExists || isGeneratePublicSalt()) { publicSalt = getRandomNumberGenerator().nextBytes(); } return publicSalt; } protected ByteSource combine(ByteSource privateSalt, ByteSource publicSalt) { byte[] privateSaltBytes = privateSalt != null ? privateSalt.getBytes() : null; int privateSaltLength = privateSaltBytes != null ? privateSaltBytes.length : 0; byte[] publicSaltBytes = publicSalt != null ? publicSalt.getBytes() : null; int extraBytesLength = publicSaltBytes != null ? publicSaltBytes.length : 0; int length = privateSaltLength + extraBytesLength; if (length <= 0) { return null; } byte[] combined = new byte[length]; int i = 0; for (int j = 0; j < privateSaltLength; j++) { assert privateSaltBytes != null; combined[i++] = privateSaltBytes[j]; } for (int j = 0; j < extraBytesLength; j++) { assert publicSaltBytes != null; combined[i++] = publicSaltBytes[j]; } return ByteSource.Util.bytes(combined); } public void setHashAlgorithmName(String name) { this.algorithmName = name; } public String getHashAlgorithmName() { return this.algorithmName; } public void setPrivateSalt(ByteSource privateSalt) { this.privateSalt = privateSalt; } public ByteSource getPrivateSalt() { return this.privateSalt; } public void setHashIterations(int count) { this.iterations = count; } public int getHashIterations() { return this.iterations; } public void setRandomNumberGenerator(RandomNumberGenerator rng) { this.rng = rng; } public RandomNumberGenerator getRandomNumberGenerator() { return this.rng; } public boolean isGeneratePublicSalt() { return generatePublicSalt; } public void setGeneratePublicSalt(boolean generatePublicSalt) { this.generatePublicSalt = generatePublicSalt; } }