001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.geronimo.system.configuration;
018
019 import java.io.File;
020 import java.io.FileInputStream;
021 import java.io.FileReader;
022 import java.io.FileWriter;
023 import java.io.IOException;
024 import java.io.InputStream;
025 import java.io.LineNumberReader;
026 import java.io.OutputStream;
027 import java.security.MessageDigest;
028 import java.security.NoSuchAlgorithmException;
029
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032
033 /**
034 * Utility methods for dealing with checksums (hashes) of files in the
035 * configuration store.
036 *
037 * @version $Rev: 550523 $ $Date: 2007-06-25 11:02:09 -0400 (Mon, 25 Jun 2007) $
038 */
039 public class ConfigurationStoreUtil {
040 private static final Log log = LogFactory.getLog(ConfigurationStoreUtil.class);
041
042 public static void writeChecksumFor(File file) throws IOException {
043 // check if the sum already exists
044 File sumFile = new File(file.getParentFile(), file.getName() + ".sha1");
045 if (sumFile.exists()) {
046 throw new IOException("Sum file already exists");
047 }
048
049 // calculate the checksum
050 String actualChecksum;
051 try {
052 actualChecksum = calculateChecksum(file, "SHA-1");
053 } catch (NoSuchAlgorithmException e) {
054 throw (IOException)new IOException("SHA-1 algorithm not available").initCause(e);
055 }
056
057 // write it
058 FileWriter writer = new FileWriter(sumFile);
059 try {
060 writer.write(actualChecksum);
061 } finally {
062 try {
063 writer.close();
064 } catch (IOException ignored) {
065 }
066 }
067 }
068
069 public static boolean verifyChecksum(File file) {
070 String expectedChecksum = getExpectedChecksum(file);
071 if (expectedChecksum == null) {
072 // log message already printed
073 return false;
074 }
075
076 String actualChecksum = getActualChecksum(file);
077 if (actualChecksum == null) {
078 // log message already printed
079 return false;
080 }
081
082
083 if (!actualChecksum.equals(expectedChecksum)) {
084 log.warn("Configuration file was modified: " + file.getAbsolutePath());
085 return false;
086 }
087
088 return true;
089 }
090
091 public static String getExpectedChecksum(File file) {
092 File sumFile = new File(file.getParentFile(), file.getName() + ".sha1");
093 if (!sumFile.exists()) {
094 log.warn("Checksum file not found: " + sumFile.getAbsolutePath());
095 return null;
096 }
097 if (!sumFile.canRead()) {
098 log.warn("Checksum file is not readable: " + sumFile.getAbsolutePath());
099 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 (IOException)new IOException("SHA-1 algorithm not available").initCause(e);
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 }