1 /**
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.apache.geronimo.security.keystore;
19
20 import java.io.BufferedOutputStream;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.math.BigInteger;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.security.KeyManagementException;
29 import java.security.KeyStore;
30 import java.security.KeyStoreException;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.NoSuchProviderException;
33 import java.security.PrivateKey;
34 import java.security.PublicKey;
35 import java.security.UnrecoverableKeyException;
36 import java.security.cert.CertificateException;
37 import java.security.cert.X509Certificate;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.Date;
41 import java.util.Hashtable;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Vector;
45 import javax.net.ssl.SSLServerSocketFactory;
46 import javax.net.ssl.SSLSocketFactory;
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49 import org.apache.geronimo.gbean.AbstractName;
50 import org.apache.geronimo.gbean.GBeanData;
51 import org.apache.geronimo.gbean.GBeanInfo;
52 import org.apache.geronimo.gbean.GBeanInfoBuilder;
53 import org.apache.geronimo.gbean.GBeanLifecycle;
54 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
55 import org.apache.geronimo.kernel.Kernel;
56 import org.apache.geronimo.kernel.config.ConfigurationUtil;
57 import org.apache.geronimo.kernel.config.EditableConfigurationManager;
58 import org.apache.geronimo.kernel.config.InvalidConfigException;
59 import org.apache.geronimo.management.geronimo.KeyIsLocked;
60 import org.apache.geronimo.management.geronimo.KeystoreException;
61 import org.apache.geronimo.management.geronimo.KeystoreInstance;
62 import org.apache.geronimo.management.geronimo.KeystoreIsLocked;
63 import org.apache.geronimo.management.geronimo.KeystoreManager;
64 import org.apache.geronimo.system.serverinfo.ServerInfo;
65 import org.apache.geronimo.util.jce.X509Principal;
66 import org.apache.geronimo.util.jce.X509V1CertificateGenerator;
67
68 /**
69 * An implementation of KeystoreManager that assumes every file in a specified
70 * directory is a keystore.
71 *
72 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
73 */
74 public class FileKeystoreManager implements KeystoreManager, GBeanLifecycle {
75 private static final Log log = LogFactory.getLog(FileKeystoreManager.class);
76 private File directory;
77 private ServerInfo serverInfo;
78 private URI configuredDir;
79 private Collection keystores;
80 private Kernel kernel;
81
82 public FileKeystoreManager(URI keystoreDir, ServerInfo serverInfo, Collection keystores, Kernel kernel) {
83 configuredDir = keystoreDir;
84 this.serverInfo = serverInfo;
85 this.keystores = keystores;
86 this.kernel = kernel;
87 }
88
89 public void doStart() throws Exception {
90 URI rootURI;
91 if (serverInfo != null) {
92 rootURI = serverInfo.resolve(configuredDir);
93 } else {
94 rootURI = configuredDir;
95 }
96 if (!rootURI.getScheme().equals("file")) {
97 throw new IllegalStateException("FileKeystoreManager must have a root that's a local directory (not " + rootURI + ")");
98 }
99 directory = new File(rootURI);
100 if (!directory.exists() || !directory.isDirectory() || !directory.canRead()) {
101 throw new IllegalStateException("FileKeystoreManager must have a root that's a valid readable directory (not " + directory.getAbsolutePath() + ")");
102 }
103 log.debug("Keystore directory is " + directory.getAbsolutePath());
104 }
105
106 public void doStop() throws Exception {
107 }
108
109 public void doFail() {
110 }
111
112 public String[] listKeystoreFiles() {
113 File[] files = directory.listFiles();
114 List list = new ArrayList();
115 for (int i = 0; i < files.length; i++) {
116 File file = files[i];
117 if(file.canRead() && !file.isDirectory()) {
118 list.add(file.getName());
119 }
120 }
121 return (String[]) list.toArray(new String[list.size()]);
122 }
123
124 public KeystoreInstance[] getKeystores() {
125 String[] names = listKeystoreFiles();
126 KeystoreInstance[] result = new KeystoreInstance[names.length];
127 for (int i = 0; i < result.length; i++) {
128 result[i] = getKeystore(names[i]);
129 if(result[i] == null) {
130 return null;
131 }
132 }
133 return result;
134 }
135
136 public KeystoreInstance getKeystore(String name) {
137 for (Iterator it = keystores.iterator(); it.hasNext();) {
138 KeystoreInstance instance = (KeystoreInstance) it.next();
139 if(instance.getKeystoreName().equals(name)) {
140 return instance;
141 }
142 }
143 File test = new File(directory, name);
144 if(!test.exists() || !test.canRead()) {
145 throw new IllegalArgumentException("Cannot access keystore "+test.getAbsolutePath()+"!");
146 }
147 AbstractName aName;
148 AbstractName myName = kernel.getAbstractNameFor(this);
149 aName = kernel.getNaming().createSiblingName(myName, name, NameFactory.KEYSTORE_INSTANCE);
150 GBeanData data = new GBeanData(aName, FileKeystoreInstance.getGBeanInfo());
151 try {
152 String path = configuredDir.toString();
153 if(!path.endsWith("/")) {
154 path += "/";
155 }
156 data.setAttribute("keystorePath", new URI(path +name));
157 } catch (URISyntaxException e) {
158 throw new IllegalStateException("Can't resolve keystore path: "+e.getMessage());
159 }
160 data.setReferencePattern("ServerInfo", kernel.getAbstractNameFor(serverInfo));
161 data.setAttribute("keystoreName", name);
162 EditableConfigurationManager mgr = ConfigurationUtil.getEditableConfigurationManager(kernel);
163 if(mgr != null) {
164 try {
165 mgr.addGBeanToConfiguration(myName.getArtifact(), data, true);
166 return (KeystoreInstance) kernel.getProxyManager().createProxy(aName, KeystoreInstance.class);
167 } catch (InvalidConfigException e) {
168 log.error("Should never happen", e);
169 throw new IllegalStateException("Unable to add Keystore GBean ("+e.getMessage()+")");
170 } finally {
171 ConfigurationUtil.releaseConfigurationManager(kernel, mgr);
172 }
173 } else {
174 log.warn("The ConfigurationManager in the kernel does not allow changes at runtime");
175 return null;
176 }
177 }
178
179 /**
180 * Gets a SocketFactory using one Keystore to access the private key
181 * and another to provide the list of trusted certificate authorities.
182 *
183 * @param provider The SSL provider to use, or null for the default
184 * @param protocol The SSL protocol to use
185 * @param algorithm The SSL algorithm to use
186 * @param trustStore The trust keystore name as provided by listKeystores.
187 * The KeystoreInstance for this keystore must have
188 * unlocked this key.
189 * @param loader The class loader used to resolve factory classes.
190 *
191 * @return A created SSLSocketFactory item created from the KeystoreManager.
192 * @throws KeystoreIsLocked
193 * Occurs when the requested key keystore cannot
194 * be used because it has not been unlocked.
195 * @throws KeyIsLocked
196 * Occurs when the requested private key in the key
197 * keystore cannot be used because it has not been
198 * unlocked.
199 * @throws NoSuchAlgorithmException
200 * @throws UnrecoverableKeyException
201 * @throws KeyStoreException
202 * @throws KeyManagementException
203 * @throws NoSuchProviderException
204 */
205 public SSLSocketFactory createSSLFactory(String provider, String protocol, String algorithm, String trustStore, ClassLoader loader) throws KeystoreException {
206
207 return createSSLFactory(provider, protocol, algorithm, null, null, trustStore, loader);
208 }
209
210 /**
211 * Gets a SocketFactory using one Keystore to access the private key
212 * and another to provide the list of trusted certificate authorities.
213 *
214 * @param provider The SSL provider to use, or null for the default
215 * @param protocol The SSL protocol to use
216 * @param algorithm The SSL algorithm to use
217 * @param keyStore The key keystore name as provided by listKeystores. The
218 * KeystoreInstance for this keystore must be unlocked.
219 * @param keyAlias The name of the private key in the keystore. The
220 * KeystoreInstance for this keystore must have unlocked
221 * this key.
222 * @param trustStore The trust keystore name as provided by listKeystores.
223 * The KeystoreInstance for this keystore must have
224 * unlocked this key.
225 * @param loader The class loader used to resolve factory classes.
226 *
227 * @return A created SSLSocketFactory item created from the KeystoreManager.
228 * @throws KeystoreIsLocked
229 * Occurs when the requested key keystore cannot
230 * be used because it has not been unlocked.
231 * @throws KeyIsLocked
232 * Occurs when the requested private key in the key
233 * keystore cannot be used because it has not been
234 * unlocked.
235 * @throws KeystoreException
236 */
237 public SSLSocketFactory createSSLFactory(String provider, String protocol, String algorithm, String keyStore, String keyAlias, String trustStore, ClassLoader loader) throws KeystoreException {
238
239 KeystoreInstance keyInstance = null;
240 if (keyStore != null) {
241 keyInstance = getKeystore(keyStore);
242 if(keyInstance.isKeystoreLocked()) {
243 throw new KeystoreIsLocked("Keystore '"+keyStore+"' is locked; please use the keystore page in the admin console to unlock it");
244 }
245 if(keyInstance.isKeyLocked(keyAlias)) {
246 throw new KeystoreIsLocked("Key '"+keyAlias+"' in keystore '"+keyStore+"' is locked; please use the keystore page in the admin console to unlock it");
247 }
248 }
249 KeystoreInstance trustInstance = trustStore == null ? null : getKeystore(trustStore);
250 if(trustInstance != null && trustInstance.isKeystoreLocked()) {
251 throw new KeystoreIsLocked("Keystore '"+trustStore+"' is locked; please use the keystore page in the admin console to unlock it");
252 }
253
254
255 try {
256 Class cls = loader.loadClass("javax.net.ssl.SSLContext");
257 Object ctx = cls.getMethod("getInstance", new Class[] {String.class}).invoke(null, new Object[]{protocol});
258 Class kmc = loader.loadClass("[Ljavax.net.ssl.KeyManager;");
259 Class tmc = loader.loadClass("[Ljavax.net.ssl.TrustManager;");
260 Class src = loader.loadClass("java.security.SecureRandom");
261 cls.getMethod("init", new Class[]{kmc, tmc, src}).invoke(ctx, new Object[]{
262 keyInstance == null ? null : keyInstance.getKeyManager(algorithm, keyAlias, null),
263 trustInstance == null ? null : trustInstance.getTrustManager(algorithm, null),
264 new java.security.SecureRandom()});
265 Object result = cls.getMethod("getSocketFactory", new Class[0]).invoke(ctx, new Object[0]);
266 return (SSLSocketFactory) result;
267 } catch (Exception e) {
268 throw new KeystoreException("Unable to create SSL Factory", e);
269 }
270 }
271
272 /**
273 * Gets a ServerSocketFactory using one Keystore to access the private key
274 * and another to provide the list of trusted certificate authorities.
275 * @param provider The SSL provider to use, or null for the default
276 * @param protocol The SSL protocol to use
277 * @param algorithm The SSL algorithm to use
278 * @param keyStore The key keystore name as provided by listKeystores. The
279 * KeystoreInstance for this keystore must be unlocked.
280 * @param keyAlias The name of the private key in the keystore. The
281 * KeystoreInstance for this keystore must have unlocked
282 * this key.
283 * @param trustStore The trust keystore name as provided by listKeystores.
284 * The KeystoreInstance for this keystore must have
285 * unlocked this key.
286 * @param loader The class loader used to resolve factory classes.
287 *
288 * @throws KeystoreIsLocked Occurs when the requested key keystore cannot
289 * be used because it has not been unlocked.
290 * @throws KeyIsLocked Occurs when the requested private key in the key
291 * keystore cannot be used because it has not been
292 * unlocked.
293 */
294 public SSLServerSocketFactory createSSLServerFactory(String provider, String protocol, String algorithm, String keyStore, String keyAlias, String trustStore, ClassLoader loader) throws KeystoreException {
295 KeystoreInstance keyInstance = getKeystore(keyStore);
296 if(keyInstance.isKeystoreLocked()) {
297 throw new KeystoreIsLocked("Keystore '"+keyStore+"' is locked; please use the keystore page in the admin console to unlock it");
298 }
299 if(keyInstance.isKeyLocked(keyAlias)) {
300 throw new KeystoreIsLocked("Key '"+keyAlias+"' in keystore '"+keyStore+"' is locked; please use the keystore page in the admin console to unlock it");
301 }
302 KeystoreInstance trustInstance = trustStore == null ? null : getKeystore(trustStore);
303 if(trustInstance != null && trustInstance.isKeystoreLocked()) {
304 throw new KeystoreIsLocked("Keystore '"+trustStore+"' is locked; please use the keystore page in the admin console to unlock it");
305 }
306
307
308 try {
309 Class cls = loader.loadClass("javax.net.ssl.SSLContext");
310 Object ctx = cls.getMethod("getInstance", new Class[] {String.class}).invoke(null, new Object[]{protocol});
311 Class kmc = loader.loadClass("[Ljavax.net.ssl.KeyManager;");
312 Class tmc = loader.loadClass("[Ljavax.net.ssl.TrustManager;");
313 Class src = loader.loadClass("java.security.SecureRandom");
314 cls.getMethod("init", new Class[]{kmc, tmc, src}).invoke(ctx, new Object[]{keyInstance.getKeyManager(algorithm, keyAlias, null),
315 trustInstance == null ? null : trustInstance.getTrustManager(algorithm, null),
316 new java.security.SecureRandom()});
317 Object result = cls.getMethod("getServerSocketFactory", new Class[0]).invoke(ctx, new Object[0]);
318 return (SSLServerSocketFactory) result;
319 } catch (Exception e) {
320 throw new KeystoreException("Unable to create SSL Server Factory", e);
321 }
322 }
323
324 public KeystoreInstance createKeystore(String name, char[] password) throws KeystoreException {
325 File test = new File(directory, name);
326 if(test.exists()) {
327 throw new IllegalArgumentException("Keystore already exists "+test.getAbsolutePath()+"!");
328 }
329 try {
330 KeyStore keystore = KeyStore.getInstance(FileKeystoreInstance.JKS);
331 keystore.load(null, password);
332 OutputStream out = new BufferedOutputStream(new FileOutputStream(test));
333 keystore.store(out, password);
334 out.flush();
335 out.close();
336 return getKeystore(name);
337 } catch (KeyStoreException e) {
338 throw new KeystoreException("Unable to create keystore", e);
339 } catch (IOException e) {
340 throw new KeystoreException("Unable to create keystore", e);
341 } catch (NoSuchAlgorithmException e) {
342 throw new KeystoreException("Unable to create keystore", e);
343 } catch (CertificateException e) {
344 throw new KeystoreException("Unable to create keystore", e);
345 }
346 }
347
348 public KeystoreInstance[] getUnlockedKeyStores() {
349 List results = new ArrayList();
350 for (Iterator it = keystores.iterator(); it.hasNext();) {
351 KeystoreInstance instance = (KeystoreInstance) it.next();
352 try {
353 if(!instance.isKeystoreLocked() && instance.getUnlockedKeys(null).length > 0) {
354 results.add(instance);
355 }
356 } catch (KeystoreException e) {}
357 }
358 return (KeystoreInstance[]) results.toArray(new KeystoreInstance[results.size()]);
359 }
360
361 public KeystoreInstance[] getUnlockedTrustStores() {
362 List results = new ArrayList();
363 for (Iterator it = keystores.iterator(); it.hasNext();) {
364 KeystoreInstance instance = (KeystoreInstance) it.next();
365 try {
366 if(!instance.isKeystoreLocked() && instance.isTrustStore(null)) {
367 results.add(instance);
368 }
369 } catch (KeystoreException e) {}
370 }
371 return (KeystoreInstance[]) results.toArray(new KeystoreInstance[results.size()]);
372 }
373
374 public static final GBeanInfo GBEAN_INFO;
375
376 static {
377 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(FileKeystoreManager.class);
378 infoFactory.addAttribute("keystoreDir", URI.class, true);
379 infoFactory.addAttribute("kernel", Kernel.class, false);
380 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
381 infoFactory.addReference("KeystoreInstances", KeystoreInstance.class, NameFactory.KEYSTORE_INSTANCE);
382 infoFactory.addInterface(KeystoreManager.class);
383 infoFactory.setConstructor(new String[]{"keystoreDir", "ServerInfo", "KeystoreInstances", "kernel"});
384
385 GBEAN_INFO = infoFactory.getBeanInfo();
386 }
387
388 public static GBeanInfo getGBeanInfo() {
389 return GBEAN_INFO;
390 }
391
392
393
394 public X509Certificate generateCert(PublicKey publicKey,
395 PrivateKey privateKey, String sigalg, int validity, String cn,
396 String ou, String o, String l, String st, String c)
397 throws java.security.SignatureException,
398 java.security.InvalidKeyException {
399 X509V1CertificateGenerator certgen = new X509V1CertificateGenerator();
400
401
402 Vector order = new Vector();
403 Hashtable attrmap = new Hashtable();
404
405 if (cn != null) {
406 attrmap.put(X509Principal.CN, cn);
407 order.add(X509Principal.CN);
408 }
409
410 if (ou != null) {
411 attrmap.put(X509Principal.OU, ou);
412 order.add(X509Principal.OU);
413 }
414
415 if (o != null) {
416 attrmap.put(X509Principal.O, o);
417 order.add(X509Principal.O);
418 }
419
420 if (l != null) {
421 attrmap.put(X509Principal.L, l);
422 order.add(X509Principal.L);
423 }
424
425 if (st != null) {
426 attrmap.put(X509Principal.ST, st);
427 order.add(X509Principal.ST);
428 }
429
430 if (c != null) {
431 attrmap.put(X509Principal.C, c);
432 order.add(X509Principal.C);
433 }
434
435 X509Principal issuerDN = new X509Principal(order, attrmap);
436 certgen.setIssuerDN(issuerDN);
437
438
439 long curr = System.currentTimeMillis();
440 long untill = curr + (long) validity * 24 * 60 * 60 * 1000;
441
442 certgen.setNotBefore(new Date(curr));
443 certgen.setNotAfter(new Date(untill));
444
445
446 certgen.setSubjectDN(issuerDN);
447
448
449 certgen.setPublicKey(publicKey);
450
451
452 certgen.setSignatureAlgorithm(sigalg);
453
454
455 certgen.setSerialNumber(new BigInteger(String.valueOf(curr)));
456
457
458 return certgen.generateX509Certificate(privateKey);
459 }
460 }