/*
* Copyright 2012 jMethods, 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.
*/
package com.myjavaworld.jftp.ssl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
import java.util.Enumeration;
import com.myjavaworld.jftp.JFTP;
/**
* @author Sai Pullabhotla, psai [at] jMethods [dot] com
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class KeyStoreManager {
private static final String ALIAS_PREFIX = "JFTP";
private static KeyStore serverCertificateStore = null;
private static KeyStore clientCertificateStore = null;
private static boolean inited = false;
private static synchronized void init() throws KeyStoreException {
File file = new File(JFTP.prefs.getServerCertificateStore());
char[] password = JFTP.prefs.getServerCertificateStorePassword();
serverCertificateStore = getKeyStore(file, password);
file = new File(JFTP.prefs.getClientCertificateStore());
password = JFTP.prefs.getClientCertificateStorePassword();
clientCertificateStore = getKeyStore(file, password);
inited = true;
}
/**
* Returns the key store that has all trusted server certificates.
*
* @return trusted server certificates store.
* @throws KeyStoreException
*
*/
public static synchronized KeyStore getServerCertificateStore()
throws KeyStoreException {
if (!inited) {
init();
}
return serverCertificateStore;
}
/**
* Returns the key store that has all client certificates.
*
* @return client certificates store.
* @throws KeyStoreException
*
*/
public static synchronized KeyStore getClientCertificateStore()
throws KeyStoreException {
if (!inited) {
init();
}
return clientCertificateStore;
}
/**
* Adds the given certificate chain to the server certificates store.
*
* @param chain
* certificate chain
* @throws KeyStoreException
*
*/
public static synchronized void addServerCertificate(Certificate[] chain)
throws KeyStoreException {
if (!inited) {
init();
}
addCertificate(serverCertificateStore, chain);
saveServerCertificateStore();
}
/**
* Adds the given certificate chain to the client certificates store.
*
* @param chain
* certificate chain
* @throws KeyStoreException
*
*/
public static synchronized void addClientCertificate(Certificate[] chain)
throws KeyStoreException {
if (!inited) {
init();
}
addCertificate(clientCertificateStore, chain);
saveClientCertificateStore();
}
public static synchronized void deleteServerCertificate(String alias)
throws KeyStoreException {
if (!inited) {
init();
}
deleteCertificate(serverCertificateStore, alias);
saveServerCertificateStore();
}
public static synchronized void deleteClientCertificate(String alias)
throws KeyStoreException {
if (!inited) {
init();
}
deleteCertificate(clientCertificateStore, alias);
saveClientCertificateStore();
}
/**
* Returns a <code>KeyStore</code> object given the file name and password.
* If the given file does not exists, a file is created and formatted to
* store keys and certificates.
*
* @param fileName
* File name that has keys and/or certificates.
* @param password
* Password for the key srore.
*
* @return KeyStore.
*
* @throws KeyStoreException
*
*/
private static synchronized KeyStore getKeyStore(String fileName,
char[] password) throws KeyStoreException {
return getKeyStore(new File(fileName), password);
}
/**
* Returns a <code>KeyStore</code> object given the key store file and
* password. If the given file does not exists, a file is created and
* formatted to store keys and certificates.
*
* @param file
* File
* @param password
* Password for the key store
*
* @return KyStore object
*
* @throws KeyStoreException
*
*/
private static synchronized KeyStore getKeyStore(File file, char[] password)
throws KeyStoreException {
KeyStore keyStore = KeyStore.getInstance("JKS");
FileInputStream fin = null;
try {
if (!file.exists()) {
keyStore.load(null, password);
saveCertificateStore(file, password, keyStore);
}
fin = new FileInputStream(file);
keyStore.load(fin, password);
} catch (Exception exp) {
throw new KeyStoreException(exp.toString());
} finally {
if (fin != null) {
try {
fin.close();
} catch (Exception exp) {
}
fin = null;
}
}
return keyStore;
}
/**
* Saves the server certificate store.
*
* @throws KeyStoreException
*
*/
private static synchronized void saveServerCertificateStore()
throws KeyStoreException {
if (!inited) {
init();
}
File file = new File(JFTP.prefs.getServerCertificateStore());
char[] password = JFTP.prefs.getServerCertificateStorePassword();
saveCertificateStore(file, password, serverCertificateStore);
}
/**
* Saves the client certificate store.
*
* @throws KeyStoreException
*
*/
private static synchronized void saveClientCertificateStore()
throws KeyStoreException {
if (!inited) {
init();
}
File file = new File(JFTP.prefs.getClientCertificateStore());
char[] password = JFTP.prefs.getClientCertificateStorePassword();
saveCertificateStore(file, password, clientCertificateStore);
}
/**
* Adds the given certificate chain to the given key store. This method
* automatically assigns a unique alias to each of the certificate in the
* chain. If any of the certificates in the chain already exists in the key
* store, the ols entry will be replaced with the new entry.
*
* @param keyStore
* Key store
* @param chain
* Certificate chain to add
*
* @throws KeyStoreException
*/
private static synchronized void addCertificate(KeyStore keyStore,
Certificate[] chain) throws KeyStoreException {
if (chain == null || chain.length == 0) {
return;
}
for (int i = 0; i < chain.length; i++) {
String alias = keyStore.getCertificateAlias(chain[i]);
if (alias == null) {
alias = getNextAlias(keyStore);
}
keyStore.setCertificateEntry(alias, chain[i]);
}
}
/**
* Returns unique alias name that can be used for adding a new entry to the
* given key store.
*
* @param keyStore
* key store
*
* @return alias
*
* @throws KeyStoreException
*/
private static synchronized String getNextAlias(KeyStore keyStore)
throws KeyStoreException {
Enumeration aliases = keyStore.aliases();
int maxAliasID = 1000;
while (aliases.hasMoreElements()) {
String alias = (String) aliases.nextElement();
if (alias.toUpperCase().startsWith(ALIAS_PREFIX.toUpperCase())
&& alias.length() > ALIAS_PREFIX.length()) {
try {
int aliasID = Integer.parseInt(alias.substring(ALIAS_PREFIX
.length()));
maxAliasID = Math.max(maxAliasID, aliasID);
} catch (NumberFormatException exp) {
// That's OK.
}
}
}
maxAliasID++;
return ALIAS_PREFIX + (maxAliasID);
}
private static synchronized void deleteCertificate(KeyStore keyStore,
String alias) throws KeyStoreException {
keyStore.deleteEntry(alias);
}
/**
* Saves the given key store to the given file using the given password.
*
* @param file
* file
* @param password
* new password for the key store.
* @param keyStore
* key store
*
* @throws KeyStoreException
*/
private static synchronized void saveCertificateStore(File file,
char[] password, KeyStore keyStore) throws KeyStoreException {
FileOutputStream fout = null;
try {
fout = new FileOutputStream(file);
keyStore.store(fout, password);
} catch (Exception exp) {
throw new KeyStoreException("Failed to save the certificate "
+ "store: " + file);
} finally {
if (fout != null) {
try {
fout.close();
} catch (Exception exp) {
}
fout = null;
}
}
}
}