001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.geronimo.security.ca; 019 020 import java.io.File; 021 import java.io.FileInputStream; 022 import java.io.FileOutputStream; 023 import java.io.FilenameFilter; 024 import java.math.BigInteger; 025 import java.net.URI; 026 import java.util.ArrayList; 027 import java.util.Iterator; 028 import java.util.Map; 029 import java.util.Properties; 030 031 import org.apache.commons.logging.Log; 032 import org.apache.commons.logging.LogFactory; 033 import org.apache.geronimo.gbean.AbstractName; 034 import org.apache.geronimo.gbean.GBeanInfo; 035 import org.apache.geronimo.gbean.GBeanInfoBuilder; 036 import org.apache.geronimo.gbean.GBeanLifecycle; 037 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 038 import org.apache.geronimo.kernel.Kernel; 039 import org.apache.geronimo.management.geronimo.CertificateRequestStore; 040 import org.apache.geronimo.system.serverinfo.ServerInfo; 041 042 /** 043 * A certificate request store implementation using disk files. 044 * 045 * @version $Rev: 476291 $ $Date: 2006-11-17 15:05:24 -0500 (Fri, 17 Nov 2006) $ 046 */ 047 public class FileCertificateRequestStore implements CertificateRequestStore, GBeanLifecycle { 048 private final static Log log = LogFactory.getLog(FileCertificateRequestStore.class); 049 050 // File name to store certificate request status 051 private static final String CSR_STATUS_FILENAME = "csr-status.properties"; 052 // File header for certificate request status file 053 private static final String CSR_STATUS_FILE_HEADER = "CSR Status File"; 054 // Status showing the request as received 055 private static final String STATUS_RECEIVED = "R"; 056 // Status showing the request as verified 057 private static final String STATUS_VERIFIED = "V"; 058 // Prefix for certificate request files 059 private static final String CERT_REQ_FILE_PREFIX = "csr"; 060 // Extension for certificate request files 061 private static final String CERT_REQ_FILE_SUFFIX = ".txt"; 062 063 private ServerInfo serverInfo; 064 private Kernel kernel; 065 private AbstractName abstractName; 066 private URI directoryPath; 067 private File dir; 068 private Properties requestStatus; 069 070 /** 071 * Constructor 072 */ 073 public FileCertificateRequestStore(ServerInfo serverInfo, URI directoryPath, Kernel kernel, AbstractName abstractName) { 074 this.serverInfo = serverInfo; 075 this.kernel = kernel; 076 this.abstractName = abstractName; 077 this.directoryPath = directoryPath; 078 } 079 080 /** 081 * This method deletes a certificate request with the specified id. 082 * @param id Id of the certificate request to be deleted. 083 * @return True if the request is deleted succssfully 084 */ 085 public boolean deleteRequest(String id) { 086 if(requestStatus.containsKey(id)) { 087 requestStatus.remove(id); 088 storeRequestStatusFile(); 089 } 090 return new File(dir, id+CERT_REQ_FILE_SUFFIX).delete(); 091 } 092 093 /** 094 * This method returns the ids of all certificate requests in the store. 095 */ 096 public String[] getAllRequestIds() { 097 File[] results = dir.listFiles(new FilenameFilter(){ 098 public boolean accept(File dir, String name) { 099 return name.endsWith(CERT_REQ_FILE_SUFFIX); 100 }}); 101 String[] reqIds = new String[results.length]; 102 int suffixLength = CERT_REQ_FILE_SUFFIX.length(); 103 for(int i = 0; i < results.length; ++i) { 104 String name = results[i].getName(); 105 reqIds[i] = name.substring(0, name.length() - suffixLength); 106 } 107 return reqIds; 108 } 109 110 /** 111 * This method returns the ids of all certificate requests with verification due. 112 */ 113 public String[] getVerificatonDueRequestIds() { 114 ArrayList ids = new ArrayList(); 115 for(Iterator itr = requestStatus.entrySet().iterator(); itr.hasNext();) { 116 Map.Entry entry = (Map.Entry) itr.next(); 117 if(entry.getValue().equals(STATUS_RECEIVED)) { 118 ids.add(entry.getKey()); 119 } 120 } 121 122 return (String[]) ids.toArray(new String[0]); 123 } 124 125 /** 126 * This method returns the ids of all certificate requests that are verified. 127 */ 128 public String[] getVerifiedRequestIds() { 129 ArrayList ids = new ArrayList(); 130 for(Iterator itr = requestStatus.entrySet().iterator(); itr.hasNext();) { 131 Map.Entry entry = (Map.Entry) itr.next(); 132 if(entry.getValue().equals(STATUS_VERIFIED)) { 133 ids.add(entry.getKey()); 134 } 135 } 136 137 return (String[]) ids.toArray(new String[0]); 138 } 139 140 /** 141 * This method sets the status of the specifed certificate request as verified. 142 * @param id Id of the certificate request 143 * @return True if the status is set successfully. 144 */ 145 public boolean setRequestVerified(String id) { 146 if(requestStatus.containsKey(id)) { 147 requestStatus.setProperty(id, STATUS_VERIFIED); 148 storeRequestStatusFile(); 149 return true; 150 } else { 151 return false; 152 } 153 } 154 155 /** 156 * This method sets the status of a certificate request as fulfilled. 157 * @param id Id of the certificate request 158 * @param sNo Serial number of the certificate issued against the certificate request. 159 * @return True if the operation is successfull. 160 */ 161 public boolean setRequestFulfilled(String id, BigInteger sNo) { 162 if(requestStatus.containsKey(id)) { 163 deleteRequest(id); 164 requestStatus.setProperty(id, sNo.toString()); 165 storeRequestStatusFile(); 166 return true; 167 } else { 168 return false; 169 } 170 } 171 172 /** 173 * This method returns the certificate request text corresponding to a specified id. 174 * @param id Id of the certificate request. 175 */ 176 public String getRequest(String id) { 177 try { 178 FileInputStream fin = new FileInputStream(new File(dir, id+CERT_REQ_FILE_SUFFIX)); 179 byte[] data = new byte[fin.available()]; 180 fin.read(data); 181 fin.close(); 182 return new String(data); 183 } catch (Exception e) { 184 log.error("Error reading CSR. id = "+id, e); 185 } 186 return null; 187 } 188 189 /** 190 * This method stores the given certificate request under the given id. If a request with the id 191 * exists in the store, it will generate a new id and store the request under that id. 192 * @param id Id under which the certificate request is to be stored 193 * @param csrText Certificate Request text 194 * @return Id under which the certificate request is stored 195 */ 196 public String storeRequest(String id, String csr) { 197 try { 198 File csrFile = null; 199 if(id == null || new File(dir, id+CERT_REQ_FILE_SUFFIX).exists()) { 200 csrFile = File.createTempFile(CERT_REQ_FILE_PREFIX, CERT_REQ_FILE_SUFFIX, dir); 201 id = csrFile.getName().substring(0, csrFile.getName().length() - CERT_REQ_FILE_SUFFIX.length()); 202 } else { 203 csrFile = new File(dir, id+CERT_REQ_FILE_SUFFIX); 204 } 205 FileOutputStream fout = new FileOutputStream(csrFile); 206 fout.write(csr.getBytes()); 207 requestStatus.setProperty(id, STATUS_RECEIVED); 208 storeRequestStatusFile(); 209 fout.close(); 210 return id; 211 } catch(Exception e) { 212 log.error("Error storing CSR. id = "+id, e); 213 } 214 return null; 215 } 216 217 /** 218 * This method returns the Serial number of the certificate issued against the certificate request 219 * specified by the given id. 220 * @param id Id of the certificate request 221 * @return Serial number of the certificate issued. 222 * @return null if there is no such certificate request or the certificate request is not fulfilled. 223 */ 224 public BigInteger getSerialNumberForRequest(String id) { 225 BigInteger sNo = null; 226 try { 227 sNo = new BigInteger(requestStatus.getProperty(id)); 228 } catch(NumberFormatException e) { 229 // happens if the certificate request is not fulfilled 230 } 231 return sNo; 232 } 233 234 /** 235 * This method removes the certificate request id from the status list. 236 * @param id Id of the certificate request to be removed. 237 * @param sNo Serial number of certificate issued against the certificate request whose Id is to be removed. 238 */ 239 public void removeRequestStatus(String id, BigInteger sNo) { 240 if(id != null && requestStatus.containsKey(id)) { 241 requestStatus.remove(id); 242 storeRequestStatusFile(); 243 } else if(sNo != null && requestStatus.containsValue(sNo.toString())) { 244 String sNoTemp = sNo.toString(); 245 for(Iterator itr = requestStatus.entrySet().iterator(); itr.hasNext(); ) { 246 Map.Entry entry = (Map.Entry)itr.next(); 247 if(sNoTemp.equals(entry.getValue())) { 248 requestStatus.remove(entry.getKey()); 249 break; 250 } 251 } 252 storeRequestStatusFile(); 253 } 254 } 255 256 public void doFail() { 257 } 258 259 public void doStart() throws Exception { 260 serverInfo.resolveServer(directoryPath); 261 URI dirURI; 262 if (serverInfo != null) { 263 dirURI = serverInfo.resolve(directoryPath); 264 } else { 265 dirURI = directoryPath; 266 } 267 if (!dirURI.getScheme().equals("file")) { 268 throw new IllegalStateException("FileCertificateRequestStore must have a root that's a local directory (not " + dirURI + ")"); 269 } 270 dir = new File(dirURI); 271 if(!dir.exists()) { 272 dir.mkdirs(); 273 log.debug("Created directory "+dir.getAbsolutePath()); 274 } else if(!dir.isDirectory() || !dir.canRead()) { 275 throw new IllegalStateException("FileCertificateRequestStore must have a root that's a valid readable directory (not " + dir.getAbsolutePath() + ")"); 276 } 277 log.debug("CertificateRequestStore directory is " + dir.getAbsolutePath()); 278 File statusFile = new File(dir, CSR_STATUS_FILENAME); 279 if(!statusFile.exists()) { 280 statusFile.createNewFile(); 281 log.debug("Created request status file "+statusFile.getAbsolutePath()); 282 } 283 requestStatus = new Properties(); 284 FileInputStream fin = new FileInputStream(statusFile); 285 requestStatus.load(fin); 286 fin.close(); 287 } 288 289 public void doStop() throws Exception { 290 } 291 public static final GBeanInfo GBEAN_INFO; 292 293 static { 294 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(FileCertificateRequestStore.class, "CertificateRequestStore"); 295 infoFactory.addAttribute("directoryPath", URI.class, true, false); 296 infoFactory.addAttribute("kernel", Kernel.class, false); 297 infoFactory.addAttribute("abstractName", AbstractName.class, false); 298 infoFactory.addReference("ServerInfo", ServerInfo.class, NameFactory.GERONIMO_SERVICE); 299 infoFactory.addInterface(CertificateRequestStore.class); 300 infoFactory.setConstructor(new String[]{"ServerInfo", "directoryPath", "kernel", "abstractName"}); 301 302 GBEAN_INFO = infoFactory.getBeanInfo(); 303 } 304 305 public static GBeanInfo getGBeanInfo() { 306 return GBEAN_INFO; 307 } 308 309 /** 310 * This methods stores the certificate request status file to disk. 311 */ 312 private void storeRequestStatusFile() { 313 File statusFile = new File(dir, CSR_STATUS_FILENAME); 314 FileOutputStream fout = null; 315 try { 316 fout = new FileOutputStream(statusFile); 317 requestStatus.store(fout, CSR_STATUS_FILE_HEADER); 318 fout.close(); 319 } catch (Exception e) { 320 log.error("Errors while storing request status file "+statusFile.getAbsolutePath(), e); 321 } 322 } 323 }