View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.util;
17  
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.nio.ByteBuffer;
23  import java.nio.channels.FileChannel;
24  import java.nio.channels.FileChannel.MapMode;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.security.MessageDigest;
28  import java.security.NoSuchAlgorithmException;
29  
30  /** Utilities around cryptographic digests */
31  public class DigestUtils {
32  	public final static String MD5 = "MD5";
33  	public final static String SHA1 = "SHA1";
34  	public final static String SHA256 = "SHA-256";
35  	public final static String SHA512 = "SHA-512";
36  
37  	private static Boolean debug = false;
38  	// TODO: make it writable
39  	private final static Integer byteBufferCapacity = 100 * 1024;// 100 KB
40  
41  	public static byte[] sha1(byte[] bytes) {
42  		try {
43  			MessageDigest digest = MessageDigest.getInstance(SHA1);
44  			digest.update(bytes);
45  			byte[] checksum = digest.digest();
46  			return checksum;
47  		} catch (Exception e) {
48  			throw new UtilsException("Cannot SHA1 digest", e);
49  		}
50  	}
51  
52  	public static String digest(String algorithm, byte[] bytes) {
53  		try {
54  			MessageDigest digest = MessageDigest.getInstance(algorithm);
55  			digest.update(bytes);
56  			byte[] checksum = digest.digest();
57  			String res = encodeHexString(checksum);
58  			return res;
59  		} catch (Exception e) {
60  			throw new UtilsException("Cannot digest with algorithm " + algorithm, e);
61  		}
62  	}
63  
64  	public static String digest(String algorithm, InputStream in) {
65  		try {
66  			MessageDigest digest = MessageDigest.getInstance(algorithm);
67  			// ReadableByteChannel channel = Channels.newChannel(in);
68  			// ByteBuffer bb = ByteBuffer.allocateDirect(byteBufferCapacity);
69  			// while (channel.read(bb) > 0)
70  			// digest.update(bb);
71  			byte[] buffer = new byte[byteBufferCapacity];
72  			int read = 0;
73  			while ((read = in.read(buffer)) > 0) {
74  				digest.update(buffer, 0, read);
75  			}
76  
77  			byte[] checksum = digest.digest();
78  			String res = encodeHexString(checksum);
79  			return res;
80  		} catch (Exception e) {
81  			throw new UtilsException("Cannot digest with algorithm " + algorithm, e);
82  		} finally {
83  			StreamUtils.closeQuietly(in);
84  		}
85  	}
86  
87  	public static String digest(String algorithm, File file) {
88  		FileInputStream fis = null;
89  		FileChannel fc = null;
90  		try {
91  			fis = new FileInputStream(file);
92  			fc = fis.getChannel();
93  
94  			// Get the file's size and then map it into memory
95  			int sz = (int) fc.size();
96  			ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
97  			return digest(algorithm, bb);
98  		} catch (IOException e) {
99  			throw new UtilsException("Cannot digest " + file + " with algorithm " + algorithm, e);
100 		} finally {
101 			StreamUtils.closeQuietly(fis);
102 			if (fc.isOpen())
103 				try {
104 					fc.close();
105 				} catch (IOException e) {
106 					// silent
107 				}
108 		}
109 	}
110 
111 	protected static String digest(String algorithm, ByteBuffer bb) {
112 		long begin = System.currentTimeMillis();
113 		try {
114 			MessageDigest digest = MessageDigest.getInstance(algorithm);
115 			digest.update(bb);
116 			byte[] checksum = digest.digest();
117 			String res = encodeHexString(checksum);
118 			long end = System.currentTimeMillis();
119 			if (debug)
120 				System.out.println((end - begin) + " ms / " + ((end - begin) / 1000) + " s");
121 			return res;
122 		} catch (NoSuchAlgorithmException e) {
123 			throw new UtilsException("Cannot digest with algorithm " + algorithm, e);
124 		}
125 	}
126 
127 	public static String sha1hex(Path path) {
128 		return digest(SHA1, path, byteBufferCapacity);
129 	}
130 
131 	public static String digest(String algorithm, Path path, long bufferSize) {
132 		byte[] digest = digestRaw(algorithm, path, bufferSize);
133 		return encodeHexString(digest);
134 	}
135 
136 	public static byte[] digestRaw(String algorithm, Path file, long bufferSize) {
137 		long begin = System.currentTimeMillis();
138 		try {
139 			MessageDigest md = MessageDigest.getInstance(algorithm);
140 			FileChannel fc = FileChannel.open(file);
141 			long fileSize = Files.size(file);
142 			if (fileSize <= bufferSize) {
143 				ByteBuffer bb = fc.map(MapMode.READ_ONLY, 0, fileSize);
144 				md.update(bb);
145 			} else {
146 				long lastCycle = (fileSize / bufferSize) - 1;
147 				long position = 0;
148 				for (int i = 0; i <= lastCycle; i++) {
149 					ByteBuffer bb;
150 					if (i != lastCycle) {
151 						bb = fc.map(MapMode.READ_ONLY, position, bufferSize);
152 						position = position + bufferSize;
153 					} else {
154 						bb = fc.map(MapMode.READ_ONLY, position, fileSize - position);
155 						position = fileSize;
156 					}
157 					md.update(bb);
158 				}
159 			}
160 			long end = System.currentTimeMillis();
161 			if (debug)
162 				System.out.println((end - begin) + " ms / " + ((end - begin) / 1000) + " s");
163 			return md.digest();
164 		} catch (Exception e) {
165 			throw new UtilsException("Cannot digest " + file + "  with algorithm " + algorithm, e);
166 		}
167 	}
168 
169 	public static void main(String[] args) {
170 		File file;
171 		if (args.length > 0)
172 			file = new File(args[0]);
173 		else {
174 			System.err.println("Usage: <file> [<algorithm>]" + " (see http://java.sun.com/j2se/1.5.0/"
175 					+ "docs/guide/security/CryptoSpec.html#AppA)");
176 			return;
177 		}
178 
179 		if (args.length > 1) {
180 			String algorithm = args[1];
181 			System.out.println(digest(algorithm, file));
182 		} else {
183 			String algorithm = "MD5";
184 			System.out.println(algorithm + ": " + digest(algorithm, file));
185 			algorithm = "SHA";
186 			System.out.println(algorithm + ": " + digest(algorithm, file));
187 			System.out.println(algorithm + ": " + sha1hex(file.toPath()));
188 			algorithm = "SHA-256";
189 			System.out.println(algorithm + ": " + digest(algorithm, file));
190 			algorithm = "SHA-512";
191 			System.out.println(algorithm + ": " + digest(algorithm, file));
192 		}
193 	}
194 
195 	final private static char[] hexArray = "0123456789abcdef".toCharArray();
196 
197 	/**
198 	 * From
199 	 * http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to
200 	 * -a-hex-string-in-java
201 	 */
202 	public static String encodeHexString(byte[] bytes) {
203 		char[] hexChars = new char[bytes.length * 2];
204 		for (int j = 0; j < bytes.length; j++) {
205 			int v = bytes[j] & 0xFF;
206 			hexChars[j * 2] = hexArray[v >>> 4];
207 			hexChars[j * 2 + 1] = hexArray[v & 0x0F];
208 		}
209 		return new String(hexChars);
210 	}
211 
212 }