package net.lightbody.bmp.mitm; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import net.lightbody.bmp.mitm.exception.CertificateSourceException; import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; import net.lightbody.bmp.mitm.tools.SecurityProviderTool; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; /** * Loads a KeyStore from a file or classpath resource. If configured with a File object, attempts to load the KeyStore * from the specified file. Otherwise, attempts to load the KeyStore from a classpath resource. */ public class KeyStoreFileCertificateSource implements CertificateAndKeySource { private static final Logger log = LoggerFactory.getLogger(KeyStoreFileCertificateSource.class); private final String keyStoreClasspathResource; private final File keyStoreFile; private final String keyStoreType; private final String keyStorePassword; private final String privateKeyAlias; private SecurityProviderTool securityProviderTool = new DefaultSecurityProviderTool(); private final Supplier<CertificateAndKey> certificateAndKey = Suppliers.memoize(new Supplier<CertificateAndKey>() { @Override public CertificateAndKey get() { return loadKeyStore(); } }); /** * Creates a {@link CertificateAndKeySource} that loads an existing {@link KeyStore} from a classpath resource. * @param keyStoreType the KeyStore type, such as PKCS12 or JKS * @param keyStoreClasspathResource classpath resource to load (for example, "/keystore.jks") * @param privateKeyAlias the alias of the private key in the KeyStore * @param keyStorePassword te KeyStore password */ public KeyStoreFileCertificateSource(String keyStoreType, String keyStoreClasspathResource, String privateKeyAlias, String keyStorePassword) { if (keyStoreClasspathResource == null) { throw new IllegalArgumentException("The classpath location of the KeyStore cannot be null"); } if (keyStoreType == null) { throw new IllegalArgumentException("KeyStore type cannot be null"); } if (privateKeyAlias == null) { throw new IllegalArgumentException("Alias of the private key in the KeyStore cannot be null"); } this.keyStoreClasspathResource = keyStoreClasspathResource; this.keyStoreFile = null; this.keyStoreType = keyStoreType; this.keyStorePassword = keyStorePassword; this.privateKeyAlias = privateKeyAlias; } /** * Creates a {@link CertificateAndKeySource} that loads an existing {@link KeyStore} from a classpath resource. * @param keyStoreType the KeyStore type, such as PKCS12 or JKS * @param keyStoreFile KeyStore file to load * @param privateKeyAlias the alias of the private key in the KeyStore * @param keyStorePassword te KeyStore password */ public KeyStoreFileCertificateSource(String keyStoreType, File keyStoreFile, String privateKeyAlias, String keyStorePassword) { if (keyStoreFile == null) { throw new IllegalArgumentException("The KeyStore file cannot be null"); } if (keyStoreType == null) { throw new IllegalArgumentException("KeyStore type cannot be null"); } if (privateKeyAlias == null) { throw new IllegalArgumentException("Alias of the private key in the KeyStore cannot be null"); } this.keyStoreFile = keyStoreFile; this.keyStoreClasspathResource = null; this.keyStoreType = keyStoreType; this.keyStorePassword = keyStorePassword; this.privateKeyAlias = privateKeyAlias; } /** * Override the default {@link SecurityProviderTool} used to load the KeyStore. */ public KeyStoreFileCertificateSource certificateTool(SecurityProviderTool securityProviderTool) { this.securityProviderTool = securityProviderTool; return this; } @Override public CertificateAndKey load() { return certificateAndKey.get(); } /** * Loads the {@link CertificateAndKey} from the KeyStore using the {@link SecurityProviderTool}. */ private CertificateAndKey loadKeyStore() { // load the KeyStore from the file or classpath resource, then delegate to a KeyStoreCertificateSource KeyStore keyStore; if (keyStoreFile != null) { keyStore = securityProviderTool.loadKeyStore(keyStoreFile, keyStoreType, keyStorePassword); } else { // copy the classpath resource to a temporary file and load the keystore from that temp file File tempKeyStoreFile = null; try{ InputStream keystoreAsStream = KeyStoreFileCertificateSource.class.getResourceAsStream(keyStoreClasspathResource); tempKeyStoreFile = File.createTempFile("keystore", "temp"); FileUtils.copyInputStreamToFile(keystoreAsStream, tempKeyStoreFile); keyStore = securityProviderTool.loadKeyStore(tempKeyStoreFile, keyStoreType, keyStorePassword); } catch (IOException e) { throw new CertificateSourceException("Unable to open KeyStore classpath resource: " + keyStoreClasspathResource, e); } finally { if (tempKeyStoreFile != null) { try { FileUtils.forceDelete(tempKeyStoreFile); } catch (IOException e) { log.warn("Unable to delete temporary KeyStore file: {}.", tempKeyStoreFile.getAbsoluteFile()); } } } } KeyStoreCertificateSource keyStoreCertificateSource = new KeyStoreCertificateSource(keyStore, privateKeyAlias, keyStorePassword); return keyStoreCertificateSource.load(); } }