added more options to HashBuilder and added extractor method

This commit is contained in:
Sebastian Sdorra
2012-02-17 21:26:15 +01:00
parent fa759e8502
commit 694bbbd9f0
11 changed files with 521 additions and 16 deletions

View File

@@ -39,6 +39,14 @@ package sonia.scm.security;
public interface HashBuilder public interface HashBuilder
{ {
/**
* Method description
*
*
* @return
*/
public HashBuilder appendSalt();
/** /**
* Method description * Method description
* *
@@ -57,6 +65,14 @@ public interface HashBuilder
*/ */
public HashBuilder createSalt(int length); public HashBuilder createSalt(int length);
/**
* Method description
*
*
* @return
*/
public HashBuilder enableLabel();
/** /**
* Method description * Method description
* *

View File

@@ -49,7 +49,7 @@ public class MD5HashBuilder extends MessageDigestHashBuilder
*/ */
public MD5HashBuilder() public MD5HashBuilder()
{ {
super(DIGEST, null, null, 0); super(DIGEST, null, null, 0, false, false);
} }
/** /**
@@ -60,7 +60,7 @@ public class MD5HashBuilder extends MessageDigestHashBuilder
*/ */
public MD5HashBuilder(String value) public MD5HashBuilder(String value)
{ {
super(DIGEST, value, null, 0); super(DIGEST, value, null, 0, false, false);
} }
/** /**
@@ -72,7 +72,7 @@ public class MD5HashBuilder extends MessageDigestHashBuilder
*/ */
public MD5HashBuilder(String value, byte[] salt) public MD5HashBuilder(String value, byte[] salt)
{ {
super(DIGEST, value, salt, 0); super(DIGEST, value, salt, 0, false, false);
} }
/** /**
@@ -85,6 +85,37 @@ public class MD5HashBuilder extends MessageDigestHashBuilder
*/ */
public MD5HashBuilder(String value, byte[] salt, int iterations) public MD5HashBuilder(String value, byte[] salt, int iterations)
{ {
super(DIGEST, value, salt, iterations); super(DIGEST, value, salt, iterations, false, false);
}
/**
* Constructs ...
*
*
* @param value
* @param salt
* @param iterations
* @param appendSalt
*/
public MD5HashBuilder(String value, byte[] salt, int iterations,
boolean appendSalt)
{
super(DIGEST, value, salt, iterations, appendSalt, false);
}
/**
* Constructs ...
*
*
* @param value
* @param salt
* @param iterations
* @param appendSalt
* @param enableLabel
*/
public MD5HashBuilder(String value, byte[] salt, int iterations,
boolean appendSalt, boolean enableLabel)
{
super(DIGEST, value, salt, iterations, appendSalt, enableLabel);
} }
} }

View File

@@ -43,6 +43,9 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -60,6 +63,9 @@ public class MessageDigestHashBuilder implements HashBuilder
/** Field description */ /** Field description */
public static final String RANDOM_INSTANCE = "SHA1PRNG"; public static final String RANDOM_INSTANCE = "SHA1PRNG";
/** Field description */
private static Pattern PATTERN = Pattern.compile("\\{([^\\}]+)\\}(.*)");
//~--- constructors --------------------------------------------------------- //~--- constructors ---------------------------------------------------------
/** /**
@@ -70,18 +76,50 @@ public class MessageDigestHashBuilder implements HashBuilder
* @param value * @param value
* @param salt * @param salt
* @param iterations * @param iterations
* @param appendSalt
* @param enableLabel
*/ */
public MessageDigestHashBuilder(String digest, String value, byte[] salt, public MessageDigestHashBuilder(String digest, String value, byte[] salt,
int iterations) int iterations, boolean appendSalt,
boolean enableLabel)
{ {
this.digest = digest; this.digest = digest;
this.value = value; this.value = value;
this.salt = salt; this.salt = salt;
this.iterations = iterations; this.iterations = iterations;
this.appendSalt = appendSalt;
this.enableLable = enableLabel;
} }
//~--- methods -------------------------------------------------------------- //~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param hash
*
* @return
*/
public static Extractor createExtractor(String hash)
{
return new Extractor(hash);
}
/**
* Method description
*
*
* @return
*/
@Override
public HashBuilder appendSalt()
{
this.appendSalt = true;
return this;
}
/** /**
* Method description * Method description
* *
@@ -120,6 +158,20 @@ public class MessageDigestHashBuilder implements HashBuilder
return this; return this;
} }
/**
* Method description
*
*
* @return
*/
@Override
public HashBuilder enableLabel()
{
this.enableLable = true;
return this;
}
/** /**
* Method description * Method description
* *
@@ -152,6 +204,15 @@ public class MessageDigestHashBuilder implements HashBuilder
input = md.digest(input); input = md.digest(input);
} }
} }
if ((salt != null) && appendSalt)
{
byte[] content = new byte[input.length + salt.length];
System.arraycopy(input, 0, content, 0, input.length);
System.arraycopy(salt, 0, content, input.length, salt.length);
input = content;
}
} }
catch (UnsupportedEncodingException ex) catch (UnsupportedEncodingException ex)
{ {
@@ -174,7 +235,22 @@ public class MessageDigestHashBuilder implements HashBuilder
@Override @Override
public String toHexString() public String toHexString()
{ {
return Util.toString(toByteArray()); String hexString = null;
if (enableLable)
{
StringBuilder buffer = new StringBuilder();
buffer.append("{").append(digest).append("}");
buffer.append(Util.toString(toByteArray()));
hexString = buffer.toString();
}
else
{
hexString = Util.toString(toByteArray());
}
return hexString;
} }
//~--- get methods ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
@@ -260,11 +336,155 @@ public class MessageDigestHashBuilder implements HashBuilder
return this; return this;
} }
//~--- inner classes --------------------------------------------------------
/**
* Class description
*
*
* @version Enter version here..., 12/02/17
* @author Enter your name here...
*/
public static class Extractor
{
/**
* Constructs ...
*
*
* @param hash
*/
public Extractor(String hash)
{
this.hash = hash;
}
//~--- get methods --------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public MessageDigestHashBuilder getHashBuilder()
{
return getHashBuilder(-1);
}
/**
* Method description
*
*
* @param saltLength
*
* @return
*/
public MessageDigestHashBuilder getHashBuilder(int saltLength)
{
MessageDigestHashBuilder hashBuilder = null;
Matcher m = PATTERN.matcher(hash);
if (m.matches())
{
String digest = m.group(1);
if (digest != null)
{
byte[] salt = null;
if (saltLength > 0)
{
String hashWithoutPrefix = m.group(2);
salt = getSalt(hashWithoutPrefix, saltLength);
}
hashBuilder = new MessageDigestHashBuilder(digest, null, salt, 0,
salt != null, true);
}
}
return hashBuilder;
}
/**
* Method description
*
*
* @return
*/
public String getLabel()
{
String label = null;
Matcher m = PATTERN.matcher(hash);
if (m.matches())
{
label = m.group(1);
}
return label;
}
/**
* Method description
*
*
* @param length
*
* @return
*/
public byte[] getSalt(int length)
{
Matcher m = PATTERN.matcher(hash);
String hashWithoutPrefix = hash;
if (m.matches())
{
hashWithoutPrefix = m.group(2);
}
return getSalt(hashWithoutPrefix, length);
}
/**
* Method description
*
*
* @param hashWithoutPrefix
* @param length
*
* @return
*/
private byte[] getSalt(String hashWithoutPrefix, int length)
{
byte[] content = Util.fromHexString(hashWithoutPrefix);
byte[] salt = new byte[length];
System.arraycopy(content, content.length - length, salt, 0, length);
return salt;
}
//~--- fields -------------------------------------------------------------
/** Field description */
private String hash;
}
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */
private boolean appendSalt;
/** Field description */ /** Field description */
private String digest; private String digest;
/** Field description */
private boolean enableLable;
/** Field description */ /** Field description */
private int iterations; private int iterations;

View File

@@ -50,7 +50,7 @@ public class SHA1HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA1HashBuilder() public SHA1HashBuilder()
{ {
super(DIGEST, null, null, 0); super(DIGEST, null, null, 0, false, false);
} }
/** /**
@@ -61,7 +61,7 @@ public class SHA1HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA1HashBuilder(String value) public SHA1HashBuilder(String value)
{ {
super(DIGEST, value, null, 0); super(DIGEST, value, null, 0, false, false);
} }
/** /**
@@ -73,7 +73,7 @@ public class SHA1HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA1HashBuilder(String value, byte[] salt) public SHA1HashBuilder(String value, byte[] salt)
{ {
super(DIGEST, value, salt, 0); super(DIGEST, value, salt, 0, false, false);
} }
/** /**
@@ -86,6 +86,37 @@ public class SHA1HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA1HashBuilder(String value, byte[] salt, int iterations) public SHA1HashBuilder(String value, byte[] salt, int iterations)
{ {
super(DIGEST, value, salt, iterations); super(DIGEST, value, salt, iterations, false, false);
}
/**
* Constructs ...
*
*
* @param value
* @param salt
* @param iterations
* @param appendSalt
*/
public SHA1HashBuilder(String value, byte[] salt, int iterations,
boolean appendSalt)
{
super(DIGEST, value, salt, iterations, appendSalt, false);
}
/**
* Constructs ...
*
*
* @param value
* @param salt
* @param iterations
* @param appendSalt
* @param enableLable
*/
public SHA1HashBuilder(String value, byte[] salt, int iterations,
boolean appendSalt, boolean enableLabel)
{
super(DIGEST, value, salt, iterations, appendSalt, enableLabel);
} }
} }

View File

@@ -50,7 +50,7 @@ public class SHA512HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA512HashBuilder() public SHA512HashBuilder()
{ {
super(DIGEST, null, null, 0); super(DIGEST, null, null, 0, false, false);
} }
/** /**
@@ -61,7 +61,7 @@ public class SHA512HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA512HashBuilder(String value) public SHA512HashBuilder(String value)
{ {
super(DIGEST, value, null, 0); super(DIGEST, value, null, 0, false, false);
} }
/** /**
@@ -73,7 +73,7 @@ public class SHA512HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA512HashBuilder(String value, byte[] salt) public SHA512HashBuilder(String value, byte[] salt)
{ {
super(DIGEST, value, salt, 0); super(DIGEST, value, salt, 0, false, false);
} }
/** /**
@@ -86,6 +86,37 @@ public class SHA512HashBuilder extends MessageDigestHashBuilder
*/ */
public SHA512HashBuilder(String value, byte[] salt, int iterations) public SHA512HashBuilder(String value, byte[] salt, int iterations)
{ {
super(DIGEST, value, salt, iterations); super(DIGEST, value, salt, iterations, false, false);
}
/**
* Constructs ...
*
*
* @param value
* @param salt
* @param iterations
* @param appendSalt
*/
public SHA512HashBuilder(String value, byte[] salt, int iterations,
boolean appendSalt)
{
super(DIGEST, value, salt, iterations, appendSalt, false);
}
/**
* Constructs ...
*
*
* @param value
* @param salt
* @param iterations
* @param appendSalt
* @param enableLabel
*/
public SHA512HashBuilder(String value, byte[] salt, int iterations,
boolean appendSalt, boolean enableLabel)
{
super(DIGEST, value, salt, iterations, appendSalt, enableLabel);
} }
} }

View File

@@ -35,6 +35,8 @@ package sonia.scm.util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.math.BigInteger;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -334,6 +336,22 @@ public class Util
* *
* @return * @return
*/ */
public static byte[] fromHexString(String value)
{
return new BigInteger(value, 16).toByteArray();
}
/**
* Method description
*
*
* @param value
*
*
* @since 1.13
*
* @return
*/
public static String nonNull(Object value) public static String nonNull(Object value)
{ {
return (value != null) return (value != null)

View File

@@ -39,6 +39,11 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
//~--- JDK imports ------------------------------------------------------------
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -54,6 +59,18 @@ public abstract class HashBuilderTestBase
*/ */
public abstract HashBuilder createHashBuilder(); public abstract HashBuilder createHashBuilder();
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
protected abstract String getLable();
//~--- methods --------------------------------------------------------------
/** /**
* Method description * Method description
* *
@@ -74,6 +91,30 @@ public abstract class HashBuilderTestBase
checkHash("hitcheker", hash, otherHash); checkHash("hitcheker", hash, otherHash);
} }
/**
* Method description
*
*/
@Test
public void testCreateLabledHash()
{
HashBuilder hashBuilder = createHashBuilder();
String hash = hashBuilder.enableLabel().setValue("hitcheker").toHexString();
System.out.println(hash);
checkHash("hitcheker", hash);
Pattern p = Pattern.compile("\\{([^\\}]+)\\}.*");
Matcher m = p.matcher(hash);
assertTrue(m.matches());
String lable = m.group(1);
assertNotNull(lable);
assertEquals(getLable(), lable);
}
/** /**
* Method description * Method description
* *
@@ -134,6 +175,19 @@ public abstract class HashBuilderTestBase
checkHash("hitcheker", hash, otherHash); checkHash("hitcheker", hash, otherHash);
} }
/**
* Method description
*
*
* @param plain
* @param hash
*/
private void checkHash(String plain, String hash)
{
assertNotNull(hash);
assertThat(hash, not(equalTo(plain)));
}
/** /**
* Method description * Method description
* *
@@ -144,8 +198,7 @@ public abstract class HashBuilderTestBase
*/ */
private void checkHash(String plain, String hash, String otherHash) private void checkHash(String plain, String hash, String otherHash)
{ {
assertNotNull(hash); checkHash(plain, hash);
assertThat(hash, not(equalTo("hitcheker")));
assertNotNull(otherHash); assertNotNull(otherHash);
assertThat(otherHash, not(equalTo("hitcheker"))); assertThat(otherHash, not(equalTo("hitcheker")));
assertEquals(hash, otherHash); assertEquals(hash, otherHash);

View File

@@ -49,4 +49,18 @@ public class MD5HashBuilderTest extends HashBuilderTestBase
{ {
return new MD5HashBuilder(); return new MD5HashBuilder();
} }
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
protected String getLable()
{
return MD5HashBuilder.DIGEST;
}
} }

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author Sebastian Sdorra
*/
public class MessageDigestHashBuilderTest
{
/**
* Method description
*
*/
@Test
public void testExtractor()
{
MessageDigestHashBuilder hashBuilder = new SHA1HashBuilder("hitcheker");
String hash =
hashBuilder.enableLabel().createSalt().appendSalt().toHexString();
assertNotNull(hash);
hashBuilder =
MessageDigestHashBuilder.createExtractor(hash).getHashBuilder(8);
assertEquals(hash, hashBuilder.setValue("hitcheker").toHexString());
}
}

View File

@@ -69,4 +69,18 @@ public class SHA1HashBuilderTest extends HashBuilderTestBase
assertEquals(hash, newHash); assertEquals(hash, newHash);
} }
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
protected String getLable()
{
return SHA1HashBuilder.DIGEST;
}
} }

View File

@@ -49,4 +49,18 @@ public class SHA512HashBuilderTest extends HashBuilderTestBase
{ {
return new SHA512HashBuilder(); return new SHA512HashBuilder();
} }
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
protected String getLable()
{
return SHA512HashBuilder.DIGEST;
}
} }