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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 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 if(requestStatus.getProperty(id) == null) { 227 // No such request 228 return null; 229 } 230 try { 231 sNo = new BigInteger(requestStatus.getProperty(id)); 232 } catch(NumberFormatException e) { 233 // happens if the certificate request is not fulfilled 234 } 235 return sNo; 236 } 237 238 /** 239 * This method removes the certificate request id from the status list. 240 * @param id Id of the certificate request to be removed. 241 * @param sNo Serial number of certificate issued against the certificate request whose Id is to be removed. 242 */ 243 public void removeRequestStatus(String id, BigInteger sNo) { 244 if(id != null && requestStatus.containsKey(id)) { 245 requestStatus.remove(id); 246 storeRequestStatusFile(); 247 } else if(sNo != null && requestStatus.containsValue(sNo.toString())) { 248 String sNoTemp = sNo.toString(); 249 for(Iterator itr = requestStatus.entrySet().iterator(); itr.hasNext(); ) { 250 Map.Entry entry = (Map.Entry)itr.next(); 251 if(sNoTemp.equals(entry.getValue())) { 252 requestStatus.remove(entry.getKey()); 253 break; 254 } 255 } 256 storeRequestStatusFile(); 257 } 258 } 259 260 public void doFail() { 261 } 262 263 public void doStart() throws Exception { 264 serverInfo.resolveServer(directoryPath); 265 URI dirURI; 266 if (serverInfo != null) { 267 dirURI = serverInfo.resolve(directoryPath); 268 } else { 269 dirURI = directoryPath; 270 } 271 if (!dirURI.getScheme().equals("file")) { 272 throw new IllegalStateException("FileCertificateRequestStore must have a root that's a local directory (not " + dirURI + ")"); 273 } 274 dir = new File(dirURI); 275 if(!dir.exists()) { 276 dir.mkdirs(); 277 log.debug("Created directory "+dir.getAbsolutePath()); 278 } else if(!dir.isDirectory() || !dir.canRead()) { 279 throw new IllegalStateException("FileCertificateRequestStore must have a root that's a valid readable directory (not " + dir.getAbsolutePath() + ")"); 280 } 281 log.debug("CertificateRequestStore directory is " + dir.getAbsolutePath()); 282 File statusFile = new File(dir, CSR_STATUS_FILENAME); 283 if(!statusFile.exists()) { 284 statusFile.createNewFile(); 285 log.debug("Created request status file "+statusFile.getAbsolutePath()); 286 } 287 requestStatus = new Properties(); 288 FileInputStream fin = new FileInputStream(statusFile); 289 requestStatus.load(fin); 290 fin.close(); 291 } 292 293 public void doStop() throws Exception { 294 } 295 public static final GBeanInfo GBEAN_INFO; 296 297 static { 298 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(FileCertificateRequestStore.class, "CertificateRequestStore"); 299 infoFactory.addAttribute("directoryPath", URI.class, true, false); 300 infoFactory.addAttribute("kernel", Kernel.class, false); 301 infoFactory.addAttribute("abstractName", AbstractName.class, false); 302 infoFactory.addReference("ServerInfo", ServerInfo.class, NameFactory.GERONIMO_SERVICE); 303 infoFactory.addInterface(CertificateRequestStore.class); 304 infoFactory.setConstructor(new String[]{"ServerInfo", "directoryPath", "kernel", "abstractName"}); 305 306 GBEAN_INFO = infoFactory.getBeanInfo(); 307 } 308 309 public static GBeanInfo getGBeanInfo() { 310 return GBEAN_INFO; 311 } 312 313 /** 314 * This methods stores the certificate request status file to disk. 315 */ 316 private void storeRequestStatusFile() { 317 File statusFile = new File(dir, CSR_STATUS_FILENAME); 318 FileOutputStream fout = null; 319 try { 320 fout = new FileOutputStream(statusFile); 321 requestStatus.store(fout, CSR_STATUS_FILE_HEADER); 322 fout.close(); 323 } catch (Exception e) { 324 log.error("Errors while storing request status file "+statusFile.getAbsolutePath(), e); 325 } 326 } 327 }