9 changed files with 654 additions and 0 deletions
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
package org.springframework.security.token; |
||||
|
||||
import java.util.Date; |
||||
|
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* The default implementation of {@link Token}. |
||||
* |
||||
* @author Ben Alex |
||||
* @since 2.0.1 |
||||
*/ |
||||
public class DefaultToken implements Token { |
||||
private String key; |
||||
private long keyCreationTime; |
||||
private String extendedInformation; |
||||
|
||||
public DefaultToken(String key, long keyCreationTime, String extendedInformation) { |
||||
Assert.hasText(key, "Key required"); |
||||
Assert.notNull(extendedInformation, "Extended information cannot be null"); |
||||
this.key = key; |
||||
this.keyCreationTime = keyCreationTime; |
||||
this.extendedInformation = extendedInformation; |
||||
} |
||||
|
||||
public String getKey() { |
||||
return key; |
||||
} |
||||
|
||||
public long getKeyCreationTime() { |
||||
return keyCreationTime; |
||||
} |
||||
|
||||
public String getExtendedInformation() { |
||||
return extendedInformation; |
||||
} |
||||
|
||||
public boolean equals(Object obj) { |
||||
if (obj != null && obj instanceof DefaultToken) { |
||||
DefaultToken rhs = (DefaultToken) obj; |
||||
return this.key.equals(rhs.key) && this.keyCreationTime == rhs.keyCreationTime && this.extendedInformation.equals(rhs.extendedInformation); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public int hashCode() { |
||||
int code = 979; |
||||
code = code * key.hashCode(); |
||||
code = code * new Long(keyCreationTime).hashCode(); |
||||
code = code * extendedInformation.hashCode(); |
||||
return code; |
||||
} |
||||
|
||||
public String toString() { |
||||
return "DefaultToken[key=" + new String(key) + "; creation=" + new Date(keyCreationTime) + "; extended=" + extendedInformation + "]"; |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,170 @@
@@ -0,0 +1,170 @@
|
||||
package org.springframework.security.token; |
||||
|
||||
import java.io.UnsupportedEncodingException; |
||||
import java.security.SecureRandom; |
||||
import java.util.Date; |
||||
|
||||
import org.apache.commons.codec.binary.Base64; |
||||
import org.apache.commons.codec.binary.Hex; |
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.security.util.Sha512DigestUtils; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Basic implementation of {@link TokenService} that is compatible with clusters and across machine restarts, |
||||
* without requiring database persistence. |
||||
* |
||||
* <p> |
||||
* Keys are produced in the format: |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* Base64(creationTime + ":" + hex(pseudoRandomNumber) + ":" + extendedInformation + ":" + |
||||
* Sha512Hex(creationTime + ":" + hex(pseudoRandomNumber) + ":" + extendedInformation + ":" + serverSecret) ) |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* In the above, <code>creationTime</code>, <code>tokenKey</code> and <code>extendedInformation</code> |
||||
* are equal to that stored in {@link Token}. The <code>Sha512Hex</code> includes the same payload, |
||||
* plus a <code>serverSecret</code>. |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* The <code>serverSecret</code> varies every millisecond. It relies on two static server-side secrets. The first |
||||
* is a password, and the second is a server integer. Both of these must remain the same for any issued keys |
||||
* to subsequently be recognised. The applicable <code>serverSecret</code> in any millisecond is computed by |
||||
* <code>password</code> + ":" + (<code>creationTime</code> % <code>serverInteger</code>). This approach |
||||
* further obfuscates the actual server secret and renders attempts to compute the server secret more |
||||
* limited in usefulness (as any false tokens would be forced to have a <code>creationTime</code> equal |
||||
* to the computed hash). Recall that framework features depending on token services should reject tokens |
||||
* that are relatively old in any event. |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* A further consideration of this class is the requirement for cryptographically strong pseudo-random numbers. |
||||
* To this end, the use of {@link SecureRandomFactoryBean} is recommended to inject the property. |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* This implementation uses UTF-8 encoding internally for string manipulation. |
||||
* </p> |
||||
* |
||||
* @author Ben Alex |
||||
* |
||||
*/ |
||||
public class KeyBasedPersistenceTokenService implements TokenService, InitializingBean { |
||||
private int pseudoRandomNumberBits = 256; |
||||
private String serverSecret; |
||||
private Integer serverInteger; |
||||
private SecureRandom secureRandom; |
||||
|
||||
public Token allocateToken(String extendedInformation) { |
||||
Assert.notNull(extendedInformation, "Must provided non-null extendedInformation (but it can be empty)"); |
||||
long creationTime = new Date().getTime(); |
||||
String serverSecret = computeServerSecretApplicableAt(creationTime); |
||||
String pseudoRandomNumber = generatePseudoRandomNumber(); |
||||
String content = new Long(creationTime).toString() + ":" + pseudoRandomNumber + ":" + extendedInformation; |
||||
|
||||
// Compute key
|
||||
String sha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret); |
||||
String keyPayload = content + ":" + sha512Hex; |
||||
String key = convertToString(Base64.encodeBase64(convertToBytes(keyPayload))); |
||||
|
||||
return new DefaultToken(key, creationTime, extendedInformation); |
||||
} |
||||
|
||||
public Token verifyToken(String key) { |
||||
if (key == null || "".equals(key)) { |
||||
return null; |
||||
} |
||||
String[] tokens = StringUtils.delimitedListToStringArray(convertToString(Base64.decodeBase64(convertToBytes(key))), ":"); |
||||
Assert.isTrue(tokens.length >= 4, "Expected 4 or more tokens but found " + tokens.length); |
||||
|
||||
long creationTime; |
||||
try { |
||||
creationTime = Long.decode(tokens[0]).longValue(); |
||||
} catch (NumberFormatException nfe) { |
||||
throw new IllegalArgumentException("Expected number but found " + tokens[0]); |
||||
} |
||||
|
||||
String serverSecret = computeServerSecretApplicableAt(creationTime); |
||||
String pseudoRandomNumber = tokens[1]; |
||||
|
||||
// Permit extendedInfo to itself contain ":" characters
|
||||
StringBuffer extendedInfo = new StringBuffer(); |
||||
for (int i = 2; i < tokens.length-1; i++) { |
||||
if (i > 2) { |
||||
extendedInfo.append(":"); |
||||
} |
||||
extendedInfo.append(tokens[i]); |
||||
} |
||||
|
||||
String sha1Hex = tokens[tokens.length-1]; |
||||
|
||||
// Verification
|
||||
String content = new Long(creationTime).toString() + ":" + pseudoRandomNumber + ":" + extendedInfo.toString(); |
||||
String expectedSha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret); |
||||
Assert.isTrue(expectedSha512Hex.equals(sha1Hex), "Key verification failure"); |
||||
|
||||
return new DefaultToken(key, creationTime, extendedInfo.toString()); |
||||
} |
||||
|
||||
private byte[] convertToBytes(String input) { |
||||
try { |
||||
return input.getBytes("UTF-8"); |
||||
} catch (UnsupportedEncodingException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private String convertToString(byte[] bytes) { |
||||
try { |
||||
return new String(bytes, "UTF-8"); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @return a pseduo random number (hex encoded) |
||||
*/ |
||||
private String generatePseudoRandomNumber() { |
||||
byte[] randomizedBits = new byte[pseudoRandomNumberBits]; |
||||
secureRandom.nextBytes(randomizedBits); |
||||
return new String(Hex.encodeHex(randomizedBits)); |
||||
} |
||||
|
||||
private String computeServerSecretApplicableAt(long time) { |
||||
return serverSecret + ":" + new Long(time % serverInteger.intValue()).intValue(); |
||||
} |
||||
|
||||
/** |
||||
* @param serverSecret the new secret, which can contain a ":" if desired (never being sent to the client) |
||||
*/ |
||||
public void setServerSecret(String serverSecret) { |
||||
this.serverSecret = serverSecret; |
||||
} |
||||
|
||||
public void setSecureRandom(SecureRandom secureRandom) { |
||||
this.secureRandom = secureRandom; |
||||
} |
||||
|
||||
/** |
||||
* @param pseudoRandomNumberBits changes the number of bits issued (must be >= 0; defaults to 256) |
||||
*/ |
||||
public void setPseudoRandomNumberBits(int pseudoRandomNumberBits) { |
||||
Assert.isTrue(pseudoRandomNumberBits >= 0, "Must have a positive pseudo random number bit size"); |
||||
this.pseudoRandomNumberBits = pseudoRandomNumberBits; |
||||
} |
||||
|
||||
public void setServerInteger(Integer serverInteger) { |
||||
this.serverInteger = serverInteger; |
||||
} |
||||
|
||||
public void afterPropertiesSet() throws Exception { |
||||
Assert.hasText(serverSecret, "Server secret required"); |
||||
Assert.notNull(serverInteger, "Server integer required"); |
||||
Assert.notNull(secureRandom, "SecureRandom instance required"); |
||||
} |
||||
} |
||||
@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
|
||||
package org.springframework.security.token; |
||||
|
||||
import java.io.InputStream; |
||||
import java.security.SecureRandom; |
||||
|
||||
import org.springframework.beans.factory.FactoryBean; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.FileCopyUtils; |
||||
|
||||
/** |
||||
* Creates a {@link SecureRandom} instance. |
||||
* |
||||
* @author Ben Alex |
||||
* @since 2.0.1 |
||||
* |
||||
*/ |
||||
public class SecureRandomFactoryBean implements FactoryBean { |
||||
|
||||
private String algorithm = "SHA1PRNG"; |
||||
private Resource seed; |
||||
|
||||
public Object getObject() throws Exception { |
||||
SecureRandom rnd = SecureRandom.getInstance(algorithm); |
||||
|
||||
if (seed != null) { |
||||
// Seed specified, so use it
|
||||
byte[] seedBytes = FileCopyUtils.copyToByteArray(seed.getInputStream()); |
||||
rnd.setSeed(seedBytes); |
||||
} else { |
||||
// Request the next bytes, thus eagerly incurring the expense of default seeding
|
||||
rnd.nextBytes(new byte[1]); |
||||
} |
||||
|
||||
return rnd; |
||||
} |
||||
|
||||
public Class getObjectType() { |
||||
return SecureRandom.class; |
||||
} |
||||
|
||||
public boolean isSingleton() { |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Allows the Pseudo Random Number Generator (PRNG) algorithm to be nominated. Defaults to |
||||
* SHA1PRNG. |
||||
* |
||||
* @param algorithm to use (mandatory) |
||||
*/ |
||||
public void setAlgorithm(String algorithm) { |
||||
Assert.hasText(algorithm, "Algorithm required"); |
||||
this.algorithm = algorithm; |
||||
} |
||||
|
||||
/** |
||||
* Allows the user to specify a resource which will act as a seed for the {@link SecureRandom} |
||||
* instance. Specifically, the resource will be read into an {@link InputStream} and those |
||||
* bytes presented to the {@link SecureRandom#setSeed(byte[])} method. Note that this will |
||||
* simply supplement, rather than replace, the existing seed. As such, it is always safe to |
||||
* set a seed using this method (it never reduces randomness). |
||||
* |
||||
* @param seed to use, or <code>null</code> if no additional seeding is needed |
||||
*/ |
||||
public void setSeed(Resource seed) { |
||||
this.seed = seed; |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
package org.springframework.security.token; |
||||
|
||||
|
||||
/** |
||||
* A token issued by {@link TokenService}. |
||||
* |
||||
* <p> |
||||
* It is important that the keys assigned to tokens are sufficiently randomised and secured that |
||||
* they can serve as identifying a unique user session. Implementations of {@link TokenService} |
||||
* are free to use encryption or encoding strategies of their choice. It is strongly recommended that |
||||
* keys are of sufficient length to balance safety against persistence cost. In relation to persistence |
||||
* cost, it is strongly recommended that returned keys are small enough for encoding in a cookie. |
||||
* </p> |
||||
* |
||||
* @author Ben Alex |
||||
* @since 2.0.1 |
||||
*/ |
||||
public interface Token { |
||||
|
||||
/** |
||||
* Obtains the randomised, secure key assigned to this token. Presentation of this token to |
||||
* {@link TokenService} will always return a <code>Token</code> that is equal to the original |
||||
* <code>Token</code> issued for that key. |
||||
* |
||||
* @return a key with appropriate randomness and security. |
||||
*/ |
||||
String getKey(); |
||||
|
||||
/** |
||||
* The time the token key was initially created is available from this method. Note that a given |
||||
* token must never have this creation time changed. If necessary, a new token can be |
||||
* requested from the {@link TokenService} to replace the original token. |
||||
* |
||||
* @return the time this token key was created, in the same format as specified by {@link Date#getTime()). |
||||
*/ |
||||
long getKeyCreationTime(); |
||||
|
||||
/** |
||||
* Obtains the extended information associated within the token, which was presented when the token |
||||
* was first created. |
||||
* |
||||
* @return the user-specified extended information, if any |
||||
*/ |
||||
String getExtendedInformation(); |
||||
} |
||||
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
package org.springframework.security.token; |
||||
|
||||
|
||||
/** |
||||
* Provides a mechanism to allocate and rebuild secure, randomised tokens. |
||||
* |
||||
* <p> |
||||
* Implementations are solely concern with issuing a new {@link Token} on demand. The |
||||
* issued <code>Token</code> may contain user-specified extended information. The token also |
||||
* contains a cryptographically strong, byte array-based key. This permits the token to be |
||||
* used to identify a user session, if desired. The key can subsequently be re-presented |
||||
* to the <code>TokenService</code> for verification and reconstruction of a <code>Token</code> |
||||
* equal to the original <code>Token</code>. |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* Given the tightly-focused behaviour provided by this interface, it can serve as a building block |
||||
* for more sophisticated token-based solutions. For example, authentication systems that depend on |
||||
* stateless session keys. These could, for instance, place the username inside the user-specified |
||||
* extended information associated with the key). It is important to recognise that we do not intend |
||||
* for this interface to be expanded to provide such capabilities directly. |
||||
* </p> |
||||
* |
||||
* @author Ben Alex |
||||
* @since 2.0.1 |
||||
* |
||||
*/ |
||||
public interface TokenService { |
||||
/** |
||||
* Forces the allocation of a new {@link Token}. |
||||
* |
||||
* @param the extended information desired in the token (cannot be <code>null</code>, but can be empty) |
||||
* @return a new token that has not been issued previously, and is guaranteed to be recognised |
||||
* by this implementation's {@link #verifyToken(String)} at any future time. |
||||
*/ |
||||
Token allocateToken(String extendedInformation); |
||||
|
||||
/** |
||||
* Permits verification the <{@link Token#getKey()} was issued by this <code>TokenService</code> and |
||||
* reconstructs the corresponding <code>Token</code>. |
||||
* |
||||
* @param key as obtained from {@link Token#getKey()} and created by this implementation |
||||
* @return the token, or <code>null</code> if the token was not issued by this <code>TokenService</code> |
||||
*/ |
||||
Token verifyToken(String key); |
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
package org.springframework.security.token; |
||||
|
||||
import java.util.Date; |
||||
|
||||
import junit.framework.Assert; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
/** |
||||
* Tests {@link DefaultToken}. |
||||
* |
||||
* @author Ben Alex |
||||
* |
||||
*/ |
||||
public class DefaultTokenTests { |
||||
@Test |
||||
public void testEquality() { |
||||
String key = "key"; |
||||
long created = new Date().getTime(); |
||||
String extendedInformation = "extended"; |
||||
|
||||
DefaultToken t1 = new DefaultToken(key, created, extendedInformation); |
||||
DefaultToken t2 = new DefaultToken(key, created, extendedInformation); |
||||
Assert.assertEquals(t1, t2); |
||||
} |
||||
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void testRejectsNullExtendedInformation() { |
||||
String key = "key"; |
||||
long created = new Date().getTime(); |
||||
new DefaultToken(key, created, null); |
||||
} |
||||
|
||||
@Test |
||||
public void testEqualityWithDifferentExtendedInformation3() { |
||||
String key = "key"; |
||||
long created = new Date().getTime(); |
||||
|
||||
DefaultToken t1 = new DefaultToken(key, created, "length1"); |
||||
DefaultToken t2 = new DefaultToken(key, created, "longerLength2"); |
||||
Assert.assertFalse(t1.equals(t2)); |
||||
} |
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
|
||||
|
||||
package org.springframework.security.token; |
||||
|
||||
import java.security.SecureRandom; |
||||
import java.util.Date; |
||||
|
||||
import junit.framework.Assert; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
/** |
||||
* Tests {@link KeyBasedPersistenceTokenService}. |
||||
* |
||||
* @author Ben Alex |
||||
* |
||||
*/ |
||||
public class KeyBasedPersistenceTokenServiceTests { |
||||
|
||||
private KeyBasedPersistenceTokenService getService() { |
||||
SecureRandomFactoryBean fb = new SecureRandomFactoryBean(); |
||||
KeyBasedPersistenceTokenService service = new KeyBasedPersistenceTokenService(); |
||||
service.setServerSecret("MY:SECRET$$$#"); |
||||
service.setServerInteger(new Integer(454545)); |
||||
try { |
||||
SecureRandom rnd = (SecureRandom) fb.getObject(); |
||||
service.setSecureRandom(rnd); |
||||
service.afterPropertiesSet(); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
return service; |
||||
} |
||||
|
||||
@Test |
||||
public void testOperationWithSimpleExtendedInformation() { |
||||
KeyBasedPersistenceTokenService service = getService(); |
||||
Token token = service.allocateToken("Hello world"); |
||||
Token result = service.verifyToken(token.getKey()); |
||||
Assert.assertEquals(token, result); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void testOperationWithComplexExtendedInformation() { |
||||
KeyBasedPersistenceTokenService service = getService(); |
||||
Token token = service.allocateToken("Hello:world:::"); |
||||
Token result = service.verifyToken(token.getKey()); |
||||
Assert.assertEquals(token, result); |
||||
} |
||||
|
||||
@Test |
||||
public void testOperationWithEmptyRandomNumber() { |
||||
KeyBasedPersistenceTokenService service = getService(); |
||||
service.setPseudoRandomNumberBits(0); |
||||
Token token = service.allocateToken("Hello:world:::"); |
||||
Token result = service.verifyToken(token.getKey()); |
||||
Assert.assertEquals(token, result); |
||||
} |
||||
|
||||
@Test |
||||
public void testOperationWithNoExtendedInformation() { |
||||
KeyBasedPersistenceTokenService service = getService(); |
||||
Token token = service.allocateToken(""); |
||||
Token result = service.verifyToken(token.getKey()); |
||||
Assert.assertEquals(token, result); |
||||
} |
||||
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void testOperationWithMissingKey() { |
||||
KeyBasedPersistenceTokenService service = getService(); |
||||
Token token = new DefaultToken("", new Date().getTime(), ""); |
||||
service.verifyToken(token.getKey()); |
||||
} |
||||
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void testOperationWithTamperedKey() { |
||||
KeyBasedPersistenceTokenService service = getService(); |
||||
Token goodToken = service.allocateToken(""); |
||||
String fake = goodToken.getKey().toUpperCase(); |
||||
Token token = new DefaultToken(fake, new Date().getTime(), ""); |
||||
service.verifyToken(token.getKey()); |
||||
} |
||||
} |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
package org.springframework.security.token; |
||||
|
||||
import java.security.SecureRandom; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
|
||||
import junit.framework.Assert; |
||||
|
||||
/** |
||||
* Tests {@link SecureRandomFactoryBean}. |
||||
* |
||||
* @author Ben Alex |
||||
* |
||||
*/ |
||||
public class SecureRandomFactoryBeanTests { |
||||
@Test |
||||
public void testObjectType() { |
||||
SecureRandomFactoryBean factory = new SecureRandomFactoryBean(); |
||||
Assert.assertEquals(SecureRandom.class, factory.getObjectType()); |
||||
} |
||||
|
||||
@Test |
||||
public void testIsSingleton() { |
||||
SecureRandomFactoryBean factory = new SecureRandomFactoryBean(); |
||||
Assert.assertFalse(factory.isSingleton()); |
||||
} |
||||
|
||||
@Test |
||||
public void testCreatesUsingDefaults() throws Exception { |
||||
SecureRandomFactoryBean factory = new SecureRandomFactoryBean(); |
||||
Object result = factory.getObject(); |
||||
Assert.assertTrue(result instanceof SecureRandom); |
||||
int rnd = ((SecureRandom)result).nextInt(); |
||||
Assert.assertTrue(rnd != 0); |
||||
} |
||||
|
||||
@Test |
||||
public void testCreatesUsingSeed() throws Exception { |
||||
SecureRandomFactoryBean factory = new SecureRandomFactoryBean(); |
||||
Resource resource = new ClassPathResource("org/springframework/security/token/SecureRandomFactoryBeanTests.class"); |
||||
Assert.assertNotNull(resource); |
||||
factory.setSeed(resource); |
||||
Object result = factory.getObject(); |
||||
Assert.assertTrue(result instanceof SecureRandom); |
||||
int rnd = ((SecureRandom)result).nextInt(); |
||||
Assert.assertTrue(rnd != 0); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
package org.springframework.security.util; |
||||
|
||||
import java.security.MessageDigest; |
||||
import java.security.NoSuchAlgorithmException; |
||||
|
||||
import org.apache.commons.codec.binary.Hex; |
||||
|
||||
/** |
||||
* Provides SHA512 digest methods. |
||||
* |
||||
* <p> |
||||
* Based on Commons Codec, which does not presently provide SHA512 support. |
||||
* </p> |
||||
* |
||||
* @author Ben Alex |
||||
* @since 2.0.1 |
||||
* |
||||
*/ |
||||
public abstract class Sha512DigestUtils { |
||||
/** |
||||
* Returns a MessageDigest for the given <code>algorithm</code>. |
||||
* |
||||
* @param algorithm The MessageDigest algorithm name. |
||||
* @return An MD5 digest instance. |
||||
* @throws RuntimeException when a {@link java.security.NoSuchAlgorithmException} is caught, |
||||
*/ |
||||
static MessageDigest getDigest(String algorithm) { |
||||
try { |
||||
return MessageDigest.getInstance(algorithm); |
||||
} catch (NoSuchAlgorithmException e) { |
||||
throw new RuntimeException(e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns an SHA digest. |
||||
* |
||||
* @return An SHA digest instance. |
||||
* @throws RuntimeException when a {@link java.security.NoSuchAlgorithmException} is caught, |
||||
*/ |
||||
private static MessageDigest getSha512Digest() { |
||||
return getDigest("SHA-512"); |
||||
} |
||||
|
||||
/** |
||||
* Calculates the SHA digest and returns the value as a |
||||
* <code>byte[]</code>. |
||||
* |
||||
* @param data Data to digest |
||||
* @return SHA digest |
||||
*/ |
||||
public static byte[] sha(byte[] data) { |
||||
return getSha512Digest().digest(data); |
||||
} |
||||
|
||||
/** |
||||
* Calculates the SHA digest and returns the value as a |
||||
* <code>byte[]</code>. |
||||
* |
||||
* @param data Data to digest |
||||
* @return SHA digest |
||||
*/ |
||||
public static byte[] sha(String data) { |
||||
return sha(data.getBytes()); |
||||
} |
||||
|
||||
/** |
||||
* Calculates the SHA digest and returns the value as a hex string. |
||||
* |
||||
* @param data Data to digest |
||||
* @return SHA digest as a hex string |
||||
*/ |
||||
public static String shaHex(byte[] data) { |
||||
return new String(Hex.encodeHex(sha(data))); |
||||
} |
||||
|
||||
/** |
||||
* Calculates the SHA digest and returns the value as a hex string. |
||||
* |
||||
* @param data Data to digest |
||||
* @return SHA digest as a hex string |
||||
*/ |
||||
public static String shaHex(String data) { |
||||
return new String(Hex.encodeHex(sha(data))); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue