Source code: net/jxta/impl/id/binaryID/DigestTool.java
1 /*
2 * Copyright (c) 2001 Sun Microsystems, Inc. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The end-user documentation included with the redistribution,
18 * if any, must include the following acknowledgment:
19 * "This product includes software developed by the
20 * Sun Microsystems, Inc. for Project JXTA."
21 * Alternately, this acknowledgment may appear in the software itself,
22 * if and wherever such third-party acknowledgments normally appear.
23 *
24 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
25 * not be used to endorse or promote products derived from this
26 * software without prior written permission. For written
27 * permission, please contact Project JXTA at http://www.jxta.org.
28 *
29 * 5. Products derived from this software may not be called "JXTA",
30 * nor may "JXTA" appear in their name, without prior written
31 * permission of Sun.
32 *
33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36 * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR
37 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 * ====================================================================
46 *
47 * This software consists of voluntary contributions made by many
48 * individuals on behalf of Project JXTA. For more
49 * information on Project JXTA, please see
50 * <http://www.jxta.org/>.
51 *
52 * This license is based on the BSD license adopted by the Apache Foundation.
53 *
54 * $Id: DigestTool.java,v 1.6 2004/06/08 21:29:04 bondolo Exp $
55 */
56
57 package net.jxta.impl.id.binaryID;
58
59 /**
60 */
61 import net.jxta.impl.id.binaryID.*;
62 import java.io.*;
63
64 import java.security.*;
65
66 /**
67 * This is a utility class used to create pipe advertisement named and BinaryID for the pipeID to create
68 * a private address space that can be hosted in the public discovery system or sent over unencrypted
69 * channeds without revealing their intent or purpose. <p>
70 *
71 * We use a one-way hashing algorythum to create an ID from private information like
72 * a user's social security number or a user's email address.
73 * We search for the pipe by with this private information securly by creating the
74 * matching hash using the same methods. <p>
75 *
76 * The purpose of this system is to create a way to search
77 * for a pipe (or other BinaryID based system) without exposing the
78 * pipe owner's clearTextID while allowing for people that
79 * know what they are looking for to find the right pipe. The
80 * system also has the ability to create pipes that have a specific purpose.
81 * For example, the email address is appended with a function name. Say you
82 * have a pipe for messages and one for administrative purposes. You would
83 * supply the email and a string for the function. The same combination can be
84 * created by another peer to search for either of these pipes. <p>
85 *
86 * This implementation uses the "SHA-1" algorythum. This was selected for relitive
87 * speed. It is used as a one-way conversion that cannot be reversed engineered to
88 * create the original string. This allows you to publish the hash without the
89 * possibility of the contents being decoded. This allows for public indexing of
90 * data that is only known by the parties involved.<p>
91 *
92 * Note that this can also be used to generate safe password verification hash codes.
93 *Sample useage:
94 *<code>
95 * String clearTextID = "turbogeek@cluck.com";
96 * String function = "eventPipe";
97 * System.out.println("clear text ID: "+clearTextID);
98 * System.out.println("function text: "+function);
99 * String digest1 = DigestID.generateHashString(clearTextID, function);
100 * String digest2 = DigestID.generateHashString(clearTextID);
101 * System.out.println("Digest1: '"+digest1+"'");
102 * System.out.println("Digest2: '"+digest2+"'");
103 * System.out.println("test1: "+DigestID.test(clearTextID, function,digest1));
104 * System.out.println("test2: "+DigestID.test(clearTextID, digest2));
105 * System.out.println("Digest1 != Digest2: "+DigestID.test(clearTextID, function,digest2));
106 *</code><p>
107 *
108 * To use an algorythum other than SHA-1, you will need stronger encyption.
109 * The BouncyCastle that comes with JXTA is just a minimum implimentation so
110 * a good choice is the normal bouncy castle (it is much larger, nearing a meg,
111 * which is why it is not a part of the normal JXTA distribution. The full version
112 * of bouncy includes SHA-128, SHA-256, SHA-384, and SHA-512.<p>
113 *
114 * Here is how you create a provider from the full version of Bouncy. Once you do this, you can access the extended
115 * Digest ecryption levels.
116 *<code>
117 * provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
118 * System.out.println("provider:"+provider.getName());
119 * Security.addProvider(provider);
120 *</code><p>
121 * Security Note<p>
122 *<p>
123 * This class should have all of its fields and properties marked as 'final' to prevent overriding the default behavior.
124 * Failure to do so could allow a less scrupulous person to cause the BinaryID or hash codes to contain the original information.
125 * Note that the class itself is not final to allow for additional convienience methods to be added. There
126 * a no methods for creating ModuleClassBinaryID, ModuleSpecBinaryID, or CodatID because this is meant for general'
127 * use, not for extending platform (you can write your own using similar code). <p>
128 *
129 * @version $Revision: 1.6 $
130 * @author Daniel Brookshier <a HREF="mailto:turbogeek@cluck.com">turbogeek@cluck.com</a>
131 */
132 public class DigestTool {
133 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DigestTool.class.getName());
134 /** varaible used for conditional compile of debug printing.*/
135 public static final boolean debug = true;
136 /** 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). */
137 public static final String SHAOne = "SHA-1";
138 /** SHA-128 digest algorithm type. This is a 128 bit hash function (note: must have another provider registered to use). */
139 public static final String SHA128 = "SHA-128";
140 /** SHA-256 digest algorithm type. This is a 256 bit hash function (note: must have another provider registered to use). */
141 public static final String SHA256 = "SHA-256";
142 /** SHA-384 digest algorithm type. This is a 384 bit hash function (note: must have another provider registered to use). */
143 public static final String SHA384 = "SHA-384";
144 /** SHA-512 digest algorithm type. This is a 512 bit hash function (note: must have another provider registered to use). */
145 public static final String SHA512 = "SHA-512";
146 /** Tilde character used to seperate candidate strings from a function.*/
147 public final String functionSeperator = "~";
148 String algorithmType;
149 public DigestTool(){
150 algorithmType=SHAOne;
151 }
152 public DigestTool(String algorithmType){
153 this.algorithmType = algorithmType;
154 }
155
156 /**
157 * Create a PipeID based on the BinaryID type with a digest of the clearTextID and function.
158 *
159 * @param peerGroupID Parent peer group ID.
160 * @param clearTextID String used as the significant part of the address
161 * @param function String used to diferentiate different clearTextID addresses (can be null).
162 * @return PipeBinaryID with the digest hash of the string: clearTextID+"~"+function.
163 */
164 public final PipeBinaryID createPipeID(net.jxta.peergroup.PeerGroupID peerGroupID,String clearTextID, String function){
165 byte[] digest = generateHash(clearTextID, function);
166 PipeBinaryID pipe = new PipeBinaryID(peerGroupID,digest,false);
167 return pipe;
168 }
169 /**
170 * Create a PeerGroupID based on the BinaryID type with a digest of the clearTextID and function.
171 *
172 * @param peerGroupID Parent peer group ID.
173 * @param clearTextID String used as the significant part of the address
174 * @param function String used to diferentiate different clearTextID addresses (can be null).
175 * @return PeerGroupBinaryID with the digest hash of the string: clearTextID+"~"+function.
176 */
177 public final PeerGroupBinaryID createPeerGroupID(net.jxta.peergroup.PeerGroupID parentPeerGroupID,String clearTextID, String function){
178 byte[] digest = generateHash(clearTextID, function);
179 PeerGroupBinaryID peerGroupID = new PeerGroupBinaryID(parentPeerGroupID,digest,false);
180 return peerGroupID;
181 }
182 /**
183 * Create a PeerID based on the BinaryID type with a digest of the clearTextID and function.
184 *
185 * @param peerGroupID Parent peer group ID.
186 * @param clearTextID String used as the significant part of the address
187 * @param function String used to diferentiate different clearTextID addresses (can be null).
188 * @return PeerBinaryID with the digest hash of the string: clearTextID+"~"+function.
189 */
190 public final PeerBinaryID createPeerID(net.jxta.peergroup.PeerGroupID peerGroupID,String clearTextID, String function){
191 byte[] digest = generateHash(clearTextID, function);
192 PeerBinaryID peerID = new PeerBinaryID(peerGroupID,digest,false);
193 return peerID;
194 }
195
196 /**
197 * Creates a new instance of DigestPipe. Because this is a utility,
198 * this is private to prevent construction.
199 */
200 /**
201 * Generates a Base64 encoded string of an SHA-1 digest hash of the string: clearTextID.<p>
202 *
203 * @param clearTextID A string that is to be hashed. This can be any string used for hashing or hiding data.
204 *
205 * @return Base64 encoded string containing the hash of the string: clearTextID.
206 */
207 public final String generateHashString(String clearTextID){
208 try {
209 java.io.StringWriter base64 = new java.io.StringWriter();
210 net.jxta.impl.util.BASE64OutputStream encode = new net.jxta.impl.util.BASE64OutputStream( base64 );
211
212 encode.write( generateHash(clearTextID) );
213 encode.close();
214
215 return base64.toString();
216 } catch( Exception failed ) {
217 LOG.error("Unable to encode hash value.", failed);
218 throw new RuntimeException("Unable to encode hash value.");
219 }
220 }
221 /**
222 * Generates a Base64 encoded string of an SHA-1 digest hash of the string: clearTextID+"-"+function or: clearTextID if function was blank.<p>
223 *
224 * @param clearTextID A string that is to be hashed. This can be any string used for hashing or hiding data.
225 * @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.
226 *
227 * @return Base64 encoded string containing the hash of the string: clearTextID+"-"+function or clearTextID if function was blank.
228 */
229 public final String generateHashString(String clearTextID, String function){
230 try {
231 java.io.StringWriter base64 = new java.io.StringWriter();
232 net.jxta.impl.util.BASE64OutputStream encode = new net.jxta.impl.util.BASE64OutputStream( base64 );
233
234 encode.write( generateHash(clearTextID, function) );
235 encode.close();
236
237 return base64.toString();
238 } catch( Exception failed ) {
239 LOG.error("Unable to encode hash value.", failed);
240 throw new RuntimeException("Unable to encode hash value."); }
241 }
242
243 /**
244 * Generates a SHA-1 digest hash of the string: clearTextID.<p>
245 *
246 * @param clearTextID A string that is to be hashed. This can be any string used for hashing or hiding data.
247 *
248 * @return String containing the hash of the string: clearTextID.
249 */
250 public final byte[] generateHash(String clearTextID) {
251 return generateHash(clearTextID, null);
252 }
253
254 /**
255 * Generates an SHA-1 digest hash of the string: clearTextID+"-"+function or: clearTextID if function was blank.<p>
256 *
257 * Note that the SHA-1 used only creates a 20 byte hash.<p>
258 *
259 * @param clearTextID A string that is to be hashed. This can be any string used for hashing or hiding data.
260 * @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.
261 *
262 * @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.
263 */
264 public final byte[] generateHash(String clearTextID, String function) {
265 String id;
266
267 if (function == null) {
268 id = clearTextID;
269 } else {
270 id = clearTextID + functionSeperator + function;
271 }
272 byte[] buffer = id.getBytes();
273
274 MessageDigest algorithm = null;
275
276 try {
277 algorithm = MessageDigest.getInstance(algorithmType);
278 } catch (Exception e) {
279 LOG.error("Cannot load selected Digest Hash implementation",e);
280 return null;
281 }
282
283
284 // Generate the digest.
285 algorithm.reset();
286 algorithm.update(buffer);
287
288 try{
289 byte[] digest1 = algorithm.digest();
290 return digest1;
291 }catch(Exception de){
292 LOG.error("Failed to creat a digest.",de);
293 return null;
294 }
295 }
296 /**
297 * Generates an SHA-1 digest hash of the string: clearTextID.<p>
298 *
299 * @param clearTextID A string that is to be hashed. This can be any string used for hashing or hiding data.
300 *
301 * @return String containing the hash of the string: clearTextID.
302 */
303 public final boolean test( String clearTextID, String function, String testHash) {
304 String id = clearTextID + functionSeperator + function;
305 return test(id,testHash);
306
307 }
308 /** Compares a clear text code or ID with a candidate hash code.
309 * This is used to confirm that the clearTextID can be successfully converted to the hash.
310 * @param clearTextID A string that is to be hashed. This can be any string used for hashing or hiding data.
311 * @param testHash A string of hashed string.
312 * @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.
313 */
314 public final boolean test(String clearTextID, String testHash) {
315
316 byte[] digest1 = generateHash(clearTextID);
317 byte[] digest2;
318 try{
319 java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
320 net.jxta.impl.util.BASE64InputStream decoder = new net.jxta.impl.util.BASE64InputStream( new java.io.StringReader(testHash) );
321
322 while( true ) {
323 int c = decoder.read();
324 if( -1 == c ) {
325 break;
326 }
327
328 bos.write( c );
329 }
330
331 digest2 = bos.toByteArray();
332 }catch(Exception e){
333 LOG.error("Failed to create a digest.",e);
334 return false;
335 }
336
337
338 if (digest1.length != digest2.length) {
339 // Not a match! because of length.
340 return false;
341 }
342
343 for (int i = 0; i < digest1.length; i++) {
344 if (digest1[i] != digest2[i]) {
345 // Not a match because of byte:"+i+" did not match
346 return false;
347 }
348 }
349
350 // Match was ok
351 return true;
352 }
353 /** Compares a clear text code or ID with a candidate hash code.
354 * This is used to confirm that the clearTextID can be successfully converted to the hash.
355 * @param clearTextID A string that is to be hashed. This can be any string used for hashing or hiding data.
356 * @param testHash A string of hashed string.
357 * @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.
358 */
359 public final boolean test(String clearTextID,byte[] testHash) {
360
361 byte[] digest1 = generateHash(clearTextID);
362
363
364
365 if (digest1.length != testHash.length) {
366 // Not a match! because of length.
367 return false;
368 }
369
370 for (int i = 0; i < testHash.length; i++) {
371 if (digest1[i] != testHash[i]) {
372 // Not a match because of byte:"+i+" did not match
373 return false;
374 }
375 }
376
377 // Match was ok
378 return true;
379 }
380 }