/*******************************************************************************
* Copyright (c) 2011 Subgraph.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Subgraph - initial API and implementation
******************************************************************************/
package com.subgraph.vega.internal.http.proxy.ssl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* A CertificateStore object manages creation of and access to a KeyStore
* object which stores an authority certificate which will be used to sign
* certificates which are used to intercept SSL connections made though
* the proxy.
*/
public class CertificateStore {
private final static String STORE_FILENAME = "ca.p12";
private final static String STORE_TYPE = "PKCS12";
private final static String STORE_KEY = "CA";
private final File storeFile;
private final char[] password;
private final KeyStore keyStore;
private PrivateKey caPrivateKey;
private X509Certificate caCertificate;
CertificateStore(File storageDirectory, String password) throws GeneralSecurityException, IOException {
storeFile = new File(storageDirectory, STORE_FILENAME);
this.password = password.toCharArray();
keyStore = KeyStore.getInstance(STORE_TYPE);
initKeyStore();
}
private void initKeyStore() throws GeneralSecurityException, IOException {
if(!storeFile.exists() || !loadFromFile()) {
keyStore.load(null, password);
}
}
private boolean loadFromFile() throws GeneralSecurityException, IOException {
InputStream input = null;
try {
input = new FileInputStream(storeFile);
keyStore.load(input, password);
caPrivateKey = (PrivateKey) keyStore.getKey(STORE_KEY, password);
final Certificate[] chain = keyStore.getCertificateChain(STORE_KEY);
caCertificate = (X509Certificate) chain[0];
return true;
} catch (FileNotFoundException e) {
return false;
} finally {
if(input != null)
input.close();
}
}
public boolean containsCaCertificate() {
return (caPrivateKey != null && caCertificate != null);
}
public PrivateKey getCaPrivateKey() {
return caPrivateKey;
}
public X509Certificate getCaCertificate() {
return caCertificate;
}
public void saveCaCertificate(X509Certificate certificate, PrivateKey privateKey) throws CertificateException {
final Certificate[] chain = new Certificate[1];
chain[0] = certificate;
try {
keyStore.setKeyEntry(STORE_KEY, privateKey, password, chain);
final OutputStream output = new FileOutputStream(storeFile);
writeKeyStore(output);
storeFile.setWritable(false, false);
storeFile.setReadable(false, false);
storeFile.setReadable(true, true);
caCertificate = certificate;
caPrivateKey = privateKey;
} catch (KeyStoreException e) {
throw new CertificateException("Failed to store CA certificate in key store: "+ e.getMessage());
} catch (FileNotFoundException e) {
throw new CertificateException("Could not open key store file '" + storeFile + "' for writing.");
}
}
private void writeKeyStore(OutputStream output) throws CertificateException {
try {
keyStore.store(output, password);
} catch (KeyStoreException e) {
throw new CertificateException("Failed to store CA certificate in key store: "+ e.getMessage());
} catch (NoSuchAlgorithmException e) {
throw new CertificateException("Failed to find algorithm for serializing certificate data: "+ e.getMessage());
} catch (CertificateException e) {
throw new CertificateException("Attempt to store invalid certificate: "+ e.getMessage());
} catch (IOException e) {
throw new CertificateException("I/O error writing to certificate store file: "+ e.getMessage());
} finally {
try {
output.close();
} catch (IOException e) {
throw new CertificateException("I/O error closing certificate store", e);
}
}
}
}