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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 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 }