/* * Copyright 2010 NCHOVY * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.keystore; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collection; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; import org.krakenapps.api.Environment; import org.krakenapps.api.KeyStoreManager; import org.krakenapps.confdb.Config; import org.krakenapps.confdb.ConfigDatabase; import org.krakenapps.confdb.ConfigIterator; import org.krakenapps.confdb.ConfigService; import org.krakenapps.confdb.Predicates; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class KeyStoreManagerImpl implements KeyStoreManager { private final Logger logger = LoggerFactory.getLogger(KeyStoreManagerImpl.class.getName()); private ConfigService conf; private Preferences prefs; private Map<String, KeyStore> keyStoreMap; private Map<String, Properties> keyStoreProps; public KeyStoreManagerImpl(ConfigService conf, Preferences prefs) { this.conf = conf; this.prefs = prefs; this.keyStoreMap = new ConcurrentHashMap<String, KeyStore>(); this.keyStoreProps = new ConcurrentHashMap<String, Properties>(); migrateKeyStores(); loadKeyStoreFiles(); } private void loadKeyStoreFiles() { ConfigDatabase db = conf.ensureDatabase("kraken-core"); ConfigIterator it = db.findAll(KeyStoreConfig.class); for (KeyStoreConfig c : it.getDocuments(KeyStoreConfig.class)) { String type = c.getType(); String pp = c.getPassword(); char[] password = null; if (pp != null) password = pp.toCharArray(); String path = c.getPath(); FileInputStream fs = null; try { fs = new FileInputStream(new File(Environment.expandSystemProperties(path))); registerKeyStore(c.getAlias(), type, fs, password); Properties props = keyStoreProps.get(c.getAlias()); props.put("path", path); } catch (Exception e) { logger.warn("load key store error: ", e); } finally { if (fs != null) { try { fs.close(); } catch (IOException e) { } } } } } @Override public Collection<String> getKeyStoreNames() { return keyStoreMap.keySet(); } @Override public Properties getKeyStoreProperties(String alias) { return keyStoreProps.get(alias); } @Override public KeyStore getKeyStore(String alias) { if (alias == null) return null; FileInputStream is = null; ConfigDatabase db = conf.ensureDatabase("kraken-core"); Config c = db.findOne(KeyStoreConfig.class, Predicates.field("alias", alias)); if (c == null) return null; try { KeyStoreConfig ksc = c.getDocument(KeyStoreConfig.class); String type = ksc.getType(); String path = ksc.getPath(); String passwd = ksc.getPassword(); char[] password = null; if (passwd != null) password = passwd.toCharArray(); KeyStore ks = KeyStore.getInstance(type); if (ks == null) return null; is = new FileInputStream(new File(Environment.expandSystemProperties(path))); ks.load(is, password); return ks; } catch (Exception e) { logger.warn("getKeyStore() error: ", e); } finally { if (is != null) try { is.close(); } catch (IOException e) { } } return null; } @Override public void registerKeyStore(String alias, String type, String path, char[] password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { File file = new File(Environment.expandSystemProperties(path)); if (!file.exists()) throw new FileNotFoundException(); ConfigDatabase db = conf.ensureDatabase("kraken-core"); Config c = db.findOne(KeyStoreConfig.class, Predicates.field("alias", alias)); if (c != null) throw new RuntimeException("duplicated key store alias"); FileInputStream fs = null; try { fs = new FileInputStream(file); registerKeyStore(alias, type, fs, password); // add file path property Properties props = keyStoreProps.get(alias); props.put("path", path); } finally { if (fs != null) { try { fs.close(); } catch (IOException e) { } } } KeyStoreConfig ksc = new KeyStoreConfig(); ksc.setAlias(alias); ksc.setType(type); ksc.setPath(path); ksc.setPassword(new String(password)); db.add(ksc); } @Deprecated @Override public void registerKeyStore(String alias, String type, File file, char[] password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { registerKeyStore(alias, type, file.getAbsolutePath(), password); } @Override public void registerKeyStore(String alias, String type, InputStream is, char[] password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { if (keyStoreMap.containsKey(alias)) throw new RuntimeException("duplicated key store alias"); Properties props = newProperties(alias, type, password); keyStoreProps.put(alias, props); KeyStore ks = KeyStore.getInstance(type); ks.load(is, password); keyStoreMap.put(alias, ks); } private Properties newProperties(String alias, String type, char[] password) { Properties props = new Properties(); props.put("alias", alias); props.put("type", type); if (password != null) props.put("password", new String(password)); return props; } @Override public void unregisterKeyStore(String alias) { ConfigDatabase db = conf.ensureDatabase("kraken-core"); Config c = db.findOne(KeyStoreConfig.class, Predicates.field("alias", alias)); if (c == null) throw new IllegalStateException("keystore alias [" + alias + "] not found"); c.remove(); keyStoreMap.remove(alias); } @Override public KeyManagerFactory getKeyManagerFactory(String alias, String algorithm) throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException { KeyStore keystore = getKeyStore(alias); if (keystore == null) return null; char[] password = getKeyStorePassword(alias); KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); kmf.init(keystore, password); return kmf; } @Override public TrustManagerFactory getTrustManagerFactory(String alias, String algorithm) throws KeyStoreException, NoSuchAlgorithmException { KeyStore keystore = getKeyStore(alias); if (keystore == null) return null; TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); tmf.init(keystore); return tmf; } private char[] getKeyStorePassword(String alias) { ConfigDatabase db = conf.ensureDatabase("kraken-core"); Config c = db.findOne(KeyStoreConfig.class, Predicates.field("alias", alias)); if (c == null) return null; KeyStoreConfig ksc = c.getDocument(KeyStoreConfig.class); if (ksc.getPassword() == null) return null; return ksc.getPassword().toCharArray(); } private void migrateKeyStores() { Preferences prefs = getKeyStorePreferences(); try { String[] names = prefs.childrenNames(); if (names.length == 0) return; for (String alias : names) { Preferences p = prefs.node(alias); String type = p.get("type", null); String pp = p.get("password", null); char[] password = null; if (pp != null) password = pp.toCharArray(); String path = p.get("path", null); try { registerKeyStore(alias, type, path, password); } catch (Exception e) { logger.warn("kraken core: cannot migrate keystore entry", e); } p.removeNode(); } prefs.flush(); prefs.sync(); } catch (BackingStoreException e) { logger.warn("kraken core: cannot migrate keystore preferences", e); } keyStoreMap.clear(); keyStoreProps.clear(); } private Preferences getKeyStorePreferences() { return prefs.node("/keystore"); } }