/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * 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. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.security; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.security.KeyStore; import java.security.cert.Certificate; import static com.thoughtworks.go.util.ExceptionUtils.bomb; import static com.thoughtworks.go.util.ExceptionUtils.bombIfNull; import org.apache.commons.io.IOUtils; public class KeyStoreManager { private static final String KEYSTORE_TYPE = "JKS"; private KeyStore lazyLoadedStore; public KeyStoreManager() { } public void storeX509Certificate(String friendlyName, File storeFile, String passwd, Registration entry) throws Exception { lazyLoadedStore = KeyStore.getInstance(KEYSTORE_TYPE); loadStore(lazyLoadedStore); storeCertificate(friendlyName, storeFile, passwd, entry); } public void storeCACertificate(File storeFile, String password, Certificate caCertificate, Registration entry) throws Exception { lazyLoadedStore = KeyStore.getInstance(KEYSTORE_TYPE); loadStore(lazyLoadedStore); lazyLoadedStore.setCertificateEntry("ca-cert", caCertificate); lazyLoadedStore.setEntry("ca-intermediate", entry.asKeyStoreEntry(), new KeyStore.PasswordProtection(password.toCharArray())); writeStore(storeFile, password); } public void storeCertificate(String friendlyName, File storeFile, String passwd, Registration entry) throws Exception { KeyStore storeToSave = loadOrEmpty(storeFile, passwd); bombIfNull(storeToSave, "Store not yet initialized"); storeToSave.setKeyEntry(friendlyName, entry.getPrivateKey(), passwd.toCharArray(), entry.getChain()); writeStore(storeToSave, storeFile, passwd); } public boolean hasCertificates(String friendlyName, File storeFile, String passwd) { try { KeyStore keyStore = loadOrEmpty(storeFile, passwd); bombIfNull(keyStore, "Store not yet initialized"); return keyStore.getCertificateChain(friendlyName) != null; } catch (Exception e) { return false; } } public void deleteEntry(String friendlyName, File storeFile, String passwd) throws Exception { bombIfNull(lazyLoadedStore, "Store not yet initialized"); lazyLoadedStore.deleteEntry(friendlyName); writeStore(storeFile, passwd); } private void loadStore(KeyStore store) { try { store.load(null, null); } catch (Exception e) { throw bomb(e); } } private void writeStore(File storeFile, String password) throws Exception { FileOutputStream fileOutputStream = null; try { fileOutputStream = maybeOutputStream(storeFile); lazyLoadedStore.store(fileOutputStream, maybePassword(password)); } finally { IOUtils.closeQuietly(fileOutputStream); } } private void writeStore(KeyStore store, File storeFile, String password) throws Exception { FileOutputStream fileOutputStream = null; try { fileOutputStream = maybeOutputStream(storeFile); store.store(fileOutputStream, maybePassword(password)); } finally { IOUtils.closeQuietly(fileOutputStream); } } public void preload(File keystoreFile, String password) throws Exception { this.lazyLoadedStore = loadOrEmpty(keystoreFile, password); } @Deprecated // Need to move the logic into this class so we don't have to touch the KeyStore in our code public KeyStore loadOrEmpty(File keystoreFile, String password) throws Exception { KeyStore store = tryLoad(keystoreFile, password); if (store == null) { store = load(null, password); } return store; } @Deprecated // Need to move the logic into this class so we don't have to touch the KeyStore in our code public KeyStore load(File keystoreFile, String password) throws Exception { FileInputStream inputStream = null; try { KeyStore store = KeyStore.getInstance(KEYSTORE_TYPE); inputStream = maybeInputStream(keystoreFile); store.load(inputStream, maybePassword(password)); return store; } finally { IOUtils.closeQuietly(inputStream); } } @Deprecated // Need to move the logic into this class so we don't have to touch the KeyStore in our code public KeyStore tryLoad(File keystoreFile, String password) { try { return load(keystoreFile, password); } catch (Exception e) { return null; } } private FileInputStream maybeInputStream(File file) throws FileNotFoundException { return file == null ? null : new FileInputStream(file); } private FileOutputStream maybeOutputStream(File file) throws FileNotFoundException { bombIfNull(file, "File cannot be null"); File parentFile = file.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } return new FileOutputStream(file); } private char[] maybePassword(String password) { return password == null ? null : password.toCharArray(); } }