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.servicemix.jbi.security.keystore.impl;
018
019 import java.io.BufferedInputStream;
020 import java.io.File;
021 import java.io.FileInputStream;
022 import java.io.IOException;
023 import java.io.InputStream;
024 import java.security.Key;
025 import java.security.KeyStore;
026 import java.security.KeyStoreException;
027 import java.security.NoSuchAlgorithmException;
028 import java.security.PrivateKey;
029 import java.security.UnrecoverableKeyException;
030 import java.security.cert.Certificate;
031 import java.security.cert.CertificateException;
032 import java.util.ArrayList;
033 import java.util.Enumeration;
034 import java.util.HashMap;
035 import java.util.List;
036 import java.util.Map;
037
038 import javax.net.ssl.KeyManager;
039 import javax.net.ssl.KeyManagerFactory;
040 import javax.net.ssl.TrustManager;
041 import javax.net.ssl.TrustManagerFactory;
042
043 import org.apache.commons.logging.Log;
044 import org.apache.commons.logging.LogFactory;
045 import org.apache.servicemix.jbi.security.keystore.KeystoreInstance;
046 import org.apache.servicemix.jbi.security.keystore.KeystoreIsLocked;
047 import org.springframework.core.io.Resource;
048
049 /**
050 *
051 * @org.apache.xbean.XBean element="keystore"
052 */
053 public class FileKeystoreInstance implements KeystoreInstance {
054
055 private static final Log LOG = LogFactory.getLog(FileKeystoreInstance.class);
056 private static final String JKS = "JKS";
057
058 private Resource path;
059 private String name;
060 private String keystorePassword;
061 private Map keyPasswords = new HashMap();
062 private File keystoreFile; // Only valid after startup
063
064 // The following variables are the state of the keystore, which should be chucked if the file on disk changes
065 private List privateKeys = new ArrayList();
066 private List trustCerts = new ArrayList();
067 private KeyStore keystore;
068 private long keystoreReadDate = Long.MIN_VALUE;
069
070 /**
071 * @param keyPasswords the keyPasswords to set
072 */
073 public void setKeyPasswords(String keyPasswords) {
074 if (keyPasswords != null) {
075 String[] keys = keyPasswords.split("\\]\\!\\[");
076 for (int i = 0; i < keys.length; i++) {
077 String key = keys[i];
078 int pos = key.indexOf('=');
079 this.keyPasswords.put(key.substring(0, pos), key.substring(pos + 1).toCharArray());
080 }
081 }
082 }
083
084 /**
085 * @return the keystoreName
086 */
087 public String getName() {
088 return name;
089 }
090
091 /**
092 * @param keystoreName the keystoreName to set
093 */
094 public void setName(String keystoreName) {
095 this.name = keystoreName;
096 }
097
098 /**
099 * @param keystorePassword the keystorePassword to set
100 */
101 public void setKeystorePassword(String keystorePassword) {
102 this.keystorePassword = keystorePassword;
103 }
104
105 /**
106 * @return the keystorePath
107 */
108 public Resource getPath() {
109 return path;
110 }
111
112 /**
113 * @param keystorePath the keystorePath to set
114 */
115 public void setPath(Resource keystorePath) throws IOException {
116 this.path = keystorePath;
117 this.keystoreFile = keystorePath.getFile();
118 }
119
120 public Certificate getCertificate(String alias) {
121 if (!loadKeystoreData()) {
122 return null;
123 }
124 try {
125 return keystore.getCertificate(alias);
126 } catch (KeyStoreException e) {
127 LOG.error("Unable to read certificate from keystore", e);
128 }
129 return null;
130 }
131
132 public String getCertificateAlias(Certificate cert) {
133 if (!loadKeystoreData()) {
134 return null;
135 }
136 try {
137 return keystore.getCertificateAlias(cert);
138 } catch (KeyStoreException e) {
139 LOG.error("Unable to read retrieve alias for given certificate from keystore", e);
140 }
141 return null;
142 }
143
144 public Certificate[] getCertificateChain(String alias) {
145 if (!loadKeystoreData()) {
146 return null;
147 }
148 try {
149 return keystore.getCertificateChain(alias);
150 } catch (KeyStoreException e) {
151 LOG.error("Unable to read certificate chain from keystore", e);
152 }
153 return null;
154 }
155
156 public KeyManager[] getKeyManager(String algorithm, String keyAlias) throws KeystoreIsLocked,
157 NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
158 if (isKeystoreLocked()) {
159 throw new KeystoreIsLocked("Keystore '" + name + "' is locked; please unlock it in the console.");
160 }
161 if (keystore == null || keystoreReadDate < keystoreFile.lastModified()) {
162 loadKeystoreData();
163 }
164 KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(algorithm);
165 keyFactory.init(keystore, (char[]) keyPasswords.get(keyAlias));
166 return keyFactory.getKeyManagers();
167 }
168
169 public PrivateKey getPrivateKey(String alias) {
170 if (!loadKeystoreData()) {
171 return null;
172 }
173 try {
174 if (isKeyLocked(alias)) {
175 return null;
176 }
177 Key key = keystore.getKey(alias, (char[]) keyPasswords.get(alias));
178 if (key instanceof PrivateKey) {
179 return (PrivateKey) key;
180 }
181 } catch (KeyStoreException e) {
182 LOG.error("Unable to read private key from keystore", e);
183 } catch (NoSuchAlgorithmException e) {
184 LOG.error("Unable to read private key from keystore", e);
185 } catch (UnrecoverableKeyException e) {
186 LOG.error("Unable to read private key from keystore", e);
187 }
188 return null;
189 }
190
191 public TrustManager[] getTrustManager(String algorithm) throws KeyStoreException,
192 NoSuchAlgorithmException, KeystoreIsLocked {
193 if (isKeystoreLocked()) {
194 throw new KeystoreIsLocked("Keystore '" + name + "' is locked; please unlock it in the console.");
195 }
196 if (!loadKeystoreData()) {
197 return null;
198 }
199 TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(algorithm);
200 trustFactory.init(keystore);
201 return trustFactory.getTrustManagers();
202 }
203
204 public boolean isKeyLocked(String keyAlias) {
205 return keyPasswords.get(keyAlias) == null;
206 }
207
208 public boolean isKeystoreLocked() {
209 return keystorePassword == null;
210 }
211
212 public String[] listPrivateKeys() {
213 if (!loadKeystoreData()) {
214 return null;
215 }
216 return (String[]) privateKeys.toArray(new String[privateKeys.size()]);
217 }
218
219 public String[] listTrustCertificates() {
220 if (!loadKeystoreData()) {
221 return null;
222 }
223 return (String[]) trustCerts.toArray(new String[trustCerts.size()]);
224 }
225
226 // ==================== Internals =====================
227
228 private boolean loadKeystoreData() {
229 if (keystoreFile == null) {
230 throw new IllegalArgumentException("keystorePath not set");
231 }
232 if (keystoreReadDate >= keystoreFile.lastModified()) {
233 return true;
234 }
235 if (!keystoreFile.exists() || !keystoreFile.canRead()) {
236 throw new IllegalArgumentException("Invalid keystore file (" + path + " = " + keystoreFile.getAbsolutePath() + ")");
237 }
238 try {
239 keystoreReadDate = System.currentTimeMillis();
240 privateKeys.clear();
241 trustCerts.clear();
242 if (keystore == null) {
243 keystore = KeyStore.getInstance(JKS);
244 }
245 InputStream in = new BufferedInputStream(new FileInputStream(keystoreFile));
246 keystore.load(in, keystorePassword == null ? new char[0] : keystorePassword.toCharArray());
247 in.close();
248 Enumeration aliases = keystore.aliases();
249 while (aliases.hasMoreElements()) {
250 String alias = (String) aliases.nextElement();
251 if (keystore.isKeyEntry(alias)) {
252 privateKeys.add(alias);
253 } else if (keystore.isCertificateEntry(alias)) {
254 trustCerts.add(alias);
255 }
256 }
257 return true;
258 } catch (KeyStoreException e) {
259 LOG.error("Unable to open keystore with provided password", e);
260 } catch (IOException e) {
261 LOG.error("Unable to open keystore with provided password", e);
262 } catch (NoSuchAlgorithmException e) {
263 LOG.error("Unable to open keystore with provided password", e);
264 } catch (CertificateException e) {
265 LOG.error("Unable to open keystore with provided password", e);
266 }
267 return false;
268 }
269
270 }