/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package org.codice.ddf.security.certificate.generator; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableEntryException; import java.util.Collections; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ddf.security.SecurityConstants; /** * Facade class for a Java Keystore (JKS) file. Exposes a few high-level behaviors to abstract away the * complexity of JCA/JCE, as well as file I/O operations. **/ public class KeyStoreFile { private static final Logger LOGGER = LoggerFactory.getLogger(KeyStoreFile.class); protected KeyStore keyStore; protected File file; private char[] password; static KeyStoreFile openFile(String filePath, char[] password) { KeyStoreFile facade = new KeyStoreFile(); File file; char[] pw = PkiTools.formatPassword(password); KeyStore keyStore = null; try { file = PkiTools.createFileObject(filePath); try (FileInputStream resource = new FileInputStream(file)) { keyStore = SecurityConstants.newKeystore(); keyStore.load(resource, pw); } } catch (GeneralSecurityException | IOException e) { String msg = "Could not create new instance of KeyStoreFile"; throw new CertificateGeneratorException(msg, e); } facade.file = file; facade.keyStore = keyStore; facade.password = pw; return facade; } /** * Return the aliases of the items in the key store. Return null if there is an error. * * @return List of aliases or throw an exception */ public List<String> aliases() { try { return Collections.list(keyStore.aliases()); } catch (KeyStoreException e) { throw new CertificateGeneratorException("Could not retrieve keys from keystore", e); } } public boolean isKey(String alias) { try { return keyStore.isKeyEntry(alias); } catch (KeyStoreException e) { throw new CertificateGeneratorException("Could not determine if alias is a key", e); } } /** * Retrieve keystore entry, given the entry's alias. If the entry is encrypted, this method * tries to decrypt it using the keystore's password. * * @param alias of the entry to retrieve * @return concrete subclass of Keystore.Entry */ public KeyStore.Entry getEntry(String alias) { KeyStore.Entry entry = null; try { entry = getProtectedEntry(alias); } catch (UnsupportedOperationException e) { //If keystore entry not password protected, using a password generates exception entry = getUnprotectedEntry(alias); } return entry; } KeyStore.Entry getUnprotectedEntry(String alias) { try { return keyStore.getEntry(alias, null); } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) { throw new RuntimeException(String.format("Could not get keystore entry %s", alias), e); } } KeyStore.Entry getProtectedEntry(String alias) { try { return keyStore.getEntry(alias, getPasswordObject()); } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) { throw new RuntimeException(String.format("Could not get keystore entry %s", alias), e); } } KeyStore.PasswordProtection getPasswordObject() { return new KeyStore.PasswordProtection(password); } /** * Add a new entry to the keystore. Use the given alias. * * @param alias of keystore entry * @param entry instance */ public void setEntry(String alias, KeyStore.Entry entry) { try { keyStore.setEntry(alias, entry, getPasswordObject()); } catch (KeyStoreException e) { throw new RuntimeException(String.format("Could not add %s to keystore", alias), e); } } /** * Remove the key store entry at the given alias. If the alias does not exist, log that it does not exist. * * @param alias the name of the entry in the keystore * @return true if entry is not in the keystore false otherwise */ public boolean deleteEntry(String alias) { try { keyStore.deleteEntry(alias); } catch (KeyStoreException e) { LOGGER.info("Attempted to remove key named {} from keyStore. No such key", alias); } return isKey(alias); } /** * Save the keyStore to the original file and encrypt it with the original password. */ public void save() { //Use the try-with-resources statement. If an exception is raised, rethrow the exception. try (FileOutputStream fd = new FileOutputStream(file)) { keyStore.store(fd, password); } catch (Exception e) { throw new RuntimeException(String.format("Could not save the keystore %s", file.getAbsolutePath()), e); } } }