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 }