/** * Generates an SHA-1 digest hash of the string: clearTextID+"-"+function or: clearTextID if * function was blank. * * <p>Note that the SHA-1 used only creates a 20 byte hash. * * <p> * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @param function A function related to the clearTextID string. This is used to create a hash * associated with clearTextID so that it is a uique code. * @return array of bytes containing the hash of the string: clearTextID+"-"+function or * clearTextID if function was blank. Can return null if SHA-1 does not exist on platform. */ public final byte[] generateHash(String clearTextID, String function) { String id; if (function == null) { id = clearTextID; } else { id = clearTextID + functionSeperator + function; } byte[] buffer = id.getBytes(); MessageDigest algorithm = null; try { algorithm = MessageDigest.getInstance(algorithmType); } catch (Exception e) { LOG.error("Cannot load selected Digest Hash implementation", e); return null; } // Generate the digest. algorithm.reset(); algorithm.update(buffer); try { byte[] digest1 = algorithm.digest(); return digest1; } catch (Exception de) { LOG.error("Failed to creat a digest.", de); return null; } }
/** * Generates a Base64 encoded string of an SHA-1 digest hash of the string: * clearTextID+"-"+function or: clearTextID if function was blank. * * <p> * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @param function A function related to the clearTextID string. This is used to create a hash * associated with clearTextID so that it is a uique code. * @return Base64 encoded string containing the hash of the string: clearTextID+"-"+function or * clearTextID if function was blank. */ public final String generateHashString(String clearTextID, String function) { try { java.io.StringWriter base64 = new java.io.StringWriter(); net.jxta.impl.util.BASE64OutputStream encode = new net.jxta.impl.util.BASE64OutputStream(base64); encode.write(generateHash(clearTextID, function)); encode.close(); return base64.toString(); } catch (Exception failed) { LOG.error("Unable to encode hash value.", failed); throw new RuntimeException("Unable to encode hash value."); } }
/** * Compares a clear text code or ID with a candidate hash code. This is used to confirm that the * clearTextID can be successfully converted to the hash. * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @param testHash A string of hashed string. * @return true if the hash created from clearTextID is equal to the testHash string.Can return * false if SHA-1 does not exist on platform. */ public final boolean test(String clearTextID, String testHash) { byte[] digest1 = generateHash(clearTextID); byte[] digest2; try { java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream(); net.jxta.impl.util.BASE64InputStream decoder = new net.jxta.impl.util.BASE64InputStream(new java.io.StringReader(testHash)); while (true) { int c = decoder.read(); if (-1 == c) { break; } bos.write(c); } digest2 = bos.toByteArray(); } catch (Exception e) { LOG.error("Failed to create a digest.", e); return false; } if (digest1.length != digest2.length) { // Not a match! because of length. return false; } for (int i = 0; i < digest1.length; i++) { if (digest1[i] != digest2[i]) { // Not a match because of byte:"+i+" did not match return false; } } // Match was ok return true; }
/** * This is a utility class used to create pipe advertisement named and BinaryID for the pipeID to * create a private address space that can be hosted in the public discovery system or sent over * unencrypted channeds without revealing their intent or purpose. * * <p>We use a one-way hashing algorythum to create an ID from private information like a user's * social security number or a user's email address. We search for the pipe by with this private * information securly by creating the matching hash using the same methods. * * <p>The purpose of this system is to create a way to search for a pipe (or other BinaryID based * system) without exposing the pipe owner's clearTextID while allowing for people that know what * they are looking for to find the right pipe. The system also has the ability to create pipes that * have a specific purpose. For example, the email address is appended with a function name. Say you * have a pipe for messages and one for administrative purposes. You would supply the email and a * string for the function. The same combination can be created by another peer to search for either * of these pipes. * * <p>This implementation uses the "SHA-1" algorythum. This was selected for relitive speed. It is * used as a one-way conversion that cannot be reversed engineered to create the original string. * This allows you to publish the hash without the possibility of the contents being decoded. This * allows for public indexing of data that is only known by the parties involved. * * <p>Note that this can also be used to generate safe password verification hash codes. Sample * useage: <code> * String clearTextID = "*****@*****.**"; * String function = "eventPipe"; * System.out.println("clear text ID: "+clearTextID); * System.out.println("function text: "+function); * String digest1 = DigestID.generateHashString(clearTextID, function); * String digest2 = DigestID.generateHashString(clearTextID); * System.out.println("Digest1: '"+digest1+"'"); * System.out.println("Digest2: '"+digest2+"'"); * System.out.println("test1: "+DigestID.test(clearTextID, function,digest1)); * System.out.println("test2: "+DigestID.test(clearTextID, digest2)); * System.out.println("Digest1 != Digest2: "+DigestID.test(clearTextID, function,digest2)); * </code> * * <p>To use an algorythum other than SHA-1, you will need stronger encyption. The BouncyCastle that * comes with JXTA is just a minimum implimentation so a good choice is the normal bouncy castle (it * is much larger, nearing a meg, which is why it is not a part of the normal JXTA distribution. The * full version of bouncy includes SHA-128, SHA-256, SHA-384, and SHA-512. * * <p>Here is how you create a provider from the full version of Bouncy. Once you do this, you can * access the extended Digest ecryption levels. <code> * provider = new org.bouncycastle.jce.provider.BouncyCastleProvider(); * System.out.println("provider:"+provider.getName()); * Security.addProvider(provider); * </code> * * <p>Security Note * * <p> * * <p>This class should have all of its fields and properties marked as 'final' to prevent * overriding the default behavior. Failure to do so could allow a less scrupulous person to cause * the BinaryID or hash codes to contain the original information. Note that the class itself is not * final to allow for additional convienience methods to be added. There a no methods for creating * ModuleClassBinaryID, ModuleSpecBinaryID, or CodatID because this is meant for general' use, not * for extending platform (you can write your own using similar code). * * <p> * * @version $Revision: 1.1 $ * @author Daniel Brookshier <a HREF="mailto:[email protected]">[email protected]</a> */ public class DigestTool { private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DigestTool.class.getName()); /** varaible used for conditional compile of debug printing. */ public static final boolean debug = true; /** * Defualt SHA-1 digest algorithm type. This is a 20 byte hash function (note: that MD5 is only 16 * so we don't use it). */ public static final String SHAOne = "SHA-1"; /** * SHA-128 digest algorithm type. This is a 128 bit hash function (note: must have another * provider registered to use). */ public static final String SHA128 = "SHA-128"; /** * SHA-256 digest algorithm type. This is a 256 bit hash function (note: must have another * provider registered to use). */ public static final String SHA256 = "SHA-256"; /** * SHA-384 digest algorithm type. This is a 384 bit hash function (note: must have another * provider registered to use). */ public static final String SHA384 = "SHA-384"; /** * SHA-512 digest algorithm type. This is a 512 bit hash function (note: must have another * provider registered to use). */ public static final String SHA512 = "SHA-512"; /** Tilde character used to seperate candidate strings from a function. */ public final String functionSeperator = "~"; String algorithmType; public DigestTool() { algorithmType = SHAOne; } public DigestTool(String algorithmType) { this.algorithmType = algorithmType; } /** * Create a PipeID based on the BinaryID type with a digest of the clearTextID and function. * * @param peerGroupID Parent peer group ID. * @param clearTextID String used as the significant part of the address * @param function String used to diferentiate different clearTextID addresses (can be null). * @return PipeBinaryID with the digest hash of the string: clearTextID+"~"+function. */ public final PipeBinaryID createPipeID( net.jxta.peergroup.PeerGroupID peerGroupID, String clearTextID, String function) { byte[] digest = generateHash(clearTextID, function); PipeBinaryID pipe = new PipeBinaryID(peerGroupID, digest, false); return pipe; } /** * Create a PeerGroupID based on the BinaryID type with a digest of the clearTextID and function. * * @param peerGroupID Parent peer group ID. * @param clearTextID String used as the significant part of the address * @param function String used to diferentiate different clearTextID addresses (can be null). * @return PeerGroupBinaryID with the digest hash of the string: clearTextID+"~"+function. */ public final PeerGroupBinaryID createPeerGroupID( net.jxta.peergroup.PeerGroupID parentPeerGroupID, String clearTextID, String function) { byte[] digest = generateHash(clearTextID, function); PeerGroupBinaryID peerGroupID = new PeerGroupBinaryID(parentPeerGroupID, digest, false); return peerGroupID; } /** * Create a PeerID based on the BinaryID type with a digest of the clearTextID and function. * * @param peerGroupID Parent peer group ID. * @param clearTextID String used as the significant part of the address * @param function String used to diferentiate different clearTextID addresses (can be null). * @return PeerBinaryID with the digest hash of the string: clearTextID+"~"+function. */ public final PeerBinaryID createPeerID( net.jxta.peergroup.PeerGroupID peerGroupID, String clearTextID, String function) { byte[] digest = generateHash(clearTextID, function); PeerBinaryID peerID = new PeerBinaryID(peerGroupID, digest, false); return peerID; } /** * Creates a new instance of DigestPipe. Because this is a utility, this is private to prevent * construction. */ /** * Generates a Base64 encoded string of an SHA-1 digest hash of the string: clearTextID. * * <p> * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @return Base64 encoded string containing the hash of the string: clearTextID. */ public final String generateHashString(String clearTextID) { try { java.io.StringWriter base64 = new java.io.StringWriter(); net.jxta.impl.util.BASE64OutputStream encode = new net.jxta.impl.util.BASE64OutputStream(base64); encode.write(generateHash(clearTextID)); encode.close(); return base64.toString(); } catch (Exception failed) { LOG.error("Unable to encode hash value.", failed); throw new RuntimeException("Unable to encode hash value."); } } /** * Generates a Base64 encoded string of an SHA-1 digest hash of the string: * clearTextID+"-"+function or: clearTextID if function was blank. * * <p> * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @param function A function related to the clearTextID string. This is used to create a hash * associated with clearTextID so that it is a uique code. * @return Base64 encoded string containing the hash of the string: clearTextID+"-"+function or * clearTextID if function was blank. */ public final String generateHashString(String clearTextID, String function) { try { java.io.StringWriter base64 = new java.io.StringWriter(); net.jxta.impl.util.BASE64OutputStream encode = new net.jxta.impl.util.BASE64OutputStream(base64); encode.write(generateHash(clearTextID, function)); encode.close(); return base64.toString(); } catch (Exception failed) { LOG.error("Unable to encode hash value.", failed); throw new RuntimeException("Unable to encode hash value."); } } /** * Generates a SHA-1 digest hash of the string: clearTextID. * * <p> * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @return String containing the hash of the string: clearTextID. */ public final byte[] generateHash(String clearTextID) { return generateHash(clearTextID, null); } /** * Generates an SHA-1 digest hash of the string: clearTextID+"-"+function or: clearTextID if * function was blank. * * <p>Note that the SHA-1 used only creates a 20 byte hash. * * <p> * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @param function A function related to the clearTextID string. This is used to create a hash * associated with clearTextID so that it is a uique code. * @return array of bytes containing the hash of the string: clearTextID+"-"+function or * clearTextID if function was blank. Can return null if SHA-1 does not exist on platform. */ public final byte[] generateHash(String clearTextID, String function) { String id; if (function == null) { id = clearTextID; } else { id = clearTextID + functionSeperator + function; } byte[] buffer = id.getBytes(); MessageDigest algorithm = null; try { algorithm = MessageDigest.getInstance(algorithmType); } catch (Exception e) { LOG.error("Cannot load selected Digest Hash implementation", e); return null; } // Generate the digest. algorithm.reset(); algorithm.update(buffer); try { byte[] digest1 = algorithm.digest(); return digest1; } catch (Exception de) { LOG.error("Failed to creat a digest.", de); return null; } } /** * Generates an SHA-1 digest hash of the string: clearTextID. * * <p> * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @return String containing the hash of the string: clearTextID. */ public final boolean test(String clearTextID, String function, String testHash) { String id = clearTextID + functionSeperator + function; return test(id, testHash); } /** * Compares a clear text code or ID with a candidate hash code. This is used to confirm that the * clearTextID can be successfully converted to the hash. * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @param testHash A string of hashed string. * @return true if the hash created from clearTextID is equal to the testHash string.Can return * false if SHA-1 does not exist on platform. */ public final boolean test(String clearTextID, String testHash) { byte[] digest1 = generateHash(clearTextID); byte[] digest2; try { java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream(); net.jxta.impl.util.BASE64InputStream decoder = new net.jxta.impl.util.BASE64InputStream(new java.io.StringReader(testHash)); while (true) { int c = decoder.read(); if (-1 == c) { break; } bos.write(c); } digest2 = bos.toByteArray(); } catch (Exception e) { LOG.error("Failed to create a digest.", e); return false; } if (digest1.length != digest2.length) { // Not a match! because of length. return false; } for (int i = 0; i < digest1.length; i++) { if (digest1[i] != digest2[i]) { // Not a match because of byte:"+i+" did not match return false; } } // Match was ok return true; } /** * Compares a clear text code or ID with a candidate hash code. This is used to confirm that the * clearTextID can be successfully converted to the hash. * * @param clearTextID A string that is to be hashed. This can be any string used for hashing or * hiding data. * @param testHash A string of hashed string. * @return true if the hash created from clearTextID is equal to the testHash string.Can return * false if SHA-1 does not exist on platform. */ public final boolean test(String clearTextID, byte[] testHash) { byte[] digest1 = generateHash(clearTextID); if (digest1.length != testHash.length) { // Not a match! because of length. return false; } for (int i = 0; i < testHash.length; i++) { if (digest1[i] != testHash[i]) { // Not a match because of byte:"+i+" did not match return false; } } // Match was ok return true; } }