1 /**
2 *
3 * Copyright 2005 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.geronimo.system.configuration;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileReader;
22 import java.io.FileWriter;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.LineNumberReader;
26 import java.io.OutputStream;
27 import java.security.MessageDigest;
28 import java.security.NoSuchAlgorithmException;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32
33 /**
34 * Utility methods for dealing with checksums (hashes) of files in the
35 * configuration store.
36 *
37 * @version $Rev: 410741 $ $Date: 2006-05-31 21:35:48 -0700 (Wed, 31 May 2006) $
38 */
39 public class ConfigurationStoreUtil {
40 private static final Log log = LogFactory.getLog(ConfigurationStoreUtil.class);
41
42 public static void writeChecksumFor(File file) throws IOException {
43
44 File sumFile = new File(file.getParentFile(), file.getName() + ".sha1");
45 if (sumFile.exists()) {
46 throw new IOException("Sum file already exists");
47 }
48
49
50 String actualChecksum;
51 try {
52 actualChecksum = calculateChecksum(file, "SHA-1");
53 } catch (NoSuchAlgorithmException e) {
54 throw new IOException("SHA-1 algorithm not available");
55 }
56
57
58 FileWriter writer = new FileWriter(sumFile);
59 try {
60 writer.write(actualChecksum);
61 } finally {
62 try {
63 writer.close();
64 } catch (IOException ignored) {
65 }
66 }
67 }
68
69 public static boolean verifyChecksum(File file) {
70 String expectedChecksum = getExpectedChecksum(file);
71 if (expectedChecksum == null) {
72
73 return false;
74 }
75
76 String actualChecksum = getActualChecksum(file);
77 if (actualChecksum == null) {
78
79 return false;
80 }
81
82
83 if (!actualChecksum.equals(expectedChecksum)) {
84 log.warn("Configuration file was modified: " + file.getAbsolutePath());
85 return false;
86 }
87
88 return true;
89 }
90
91 public static String getExpectedChecksum(File file) {
92 File sumFile = new File(file.getParentFile(), file.getName() + ".sha1");
93 if (!sumFile.exists()) {
94 log.warn("Checksum file not found: " + sumFile.getAbsolutePath());
95 return null;
96 }
97 if (!sumFile.canRead()) {
98 log.warn("Checksum file is not readable: " + sumFile.getAbsolutePath());
99 return null;
100 }
101 LineNumberReader lineNumberReader = null;
102 try {
103 lineNumberReader = new LineNumberReader(new FileReader(sumFile));
104 String expectedChecksum = lineNumberReader.readLine();
105 if (expectedChecksum == null) {
106 log.error("Checksum file was empty: " + sumFile.getAbsolutePath());
107 return null;
108 }
109 return expectedChecksum.trim();
110 } catch (IOException e) {
111 log.error("Unable to read checksum file: " + sumFile.getAbsolutePath(), e);
112 } finally {
113 if (lineNumberReader != null) {
114 try {
115 lineNumberReader.close();
116 } catch (IOException ignored) {
117 }
118 }
119
120 }
121 return null;
122 }
123
124 public static String getActualChecksum(File file) {
125 return getActualChecksum(file, "SHA-1");
126 }
127 public static String getActualChecksum(File file, String algorithm) {
128 try {
129 return calculateChecksum(file, algorithm);
130 } catch (Exception e) {
131 log.error("Unable to calculate checksum for configuration file: " + file.getAbsolutePath(), e);
132 }
133 return null;
134 }
135
136 private static String calculateChecksum(File file, String algorithm) throws NoSuchAlgorithmException, IOException {
137
138 InputStream stream = null;
139 try {
140 stream = new FileInputStream(file);
141
142 MessageDigest digester = MessageDigest.getInstance(algorithm);
143 digester.reset();
144
145 byte buf[] = new byte[4096];
146 int len = 0;
147
148 while ((len = stream.read(buf, 0, 1024)) != -1) {
149 digester.update(buf, 0, len);
150 }
151
152 String actualChecksum = encode(digester.digest());
153 return actualChecksum;
154 } finally {
155 try {
156 if (stream != null)
157 stream.close();
158 } catch (IOException ignored) {
159 }
160 }
161 }
162
163 private static String encode(byte[] binaryData) {
164 if (binaryData.length != 16 && binaryData.length != 20) {
165 int bitLength = binaryData.length * 8;
166 throw new IllegalArgumentException("Unrecognised length for binary data: " + bitLength + " bits");
167 }
168
169 String retValue = "";
170
171 for (int i = 0; i < binaryData.length; i++) {
172 String t = Integer.toHexString(binaryData[i] & 0xff);
173
174 if (t.length() == 1) {
175 retValue += ("0" + t);
176 } else {
177 retValue += t;
178 }
179 }
180
181 return retValue.trim();
182 }
183
184 public static class ChecksumOutputStream extends OutputStream {
185 private final OutputStream out;
186 private MessageDigest digester;
187
188 public ChecksumOutputStream(OutputStream out) throws IOException {
189 this.out = out;
190 try {
191 digester = MessageDigest.getInstance("SHA-1");
192 digester.reset();
193 } catch (NoSuchAlgorithmException e) {
194 throw new IOException("SHA-1 algorithm not available");
195 }
196 }
197
198 public String getChecksum() {
199 String actualChecksum = encode(digester.digest());
200 return actualChecksum;
201 }
202
203 public void write(int b) throws IOException {
204 digester.update((byte) b);
205 out.write(b);
206 }
207
208 public void write(byte[] b) throws IOException {
209 digester.update(b);
210 out.write(b);
211 }
212
213 public void write(byte[] b, int off, int len) throws IOException {
214 digester.update(b, off, len);
215 out.write(b, off, len);
216 }
217
218 public void flush() throws IOException {
219 out.flush();
220 }
221
222 public void close() throws IOException {
223 out.close();
224 }
225 }
226 }