NIFI-2446: Add option to specify key password when different than keystore password

This commit is contained in:
Mark Payne 2016-08-03 10:49:17 -04:00
parent 54549891e3
commit aa4d4189c4
3 changed files with 98 additions and 6 deletions

View File

@ -48,7 +48,9 @@ public final class SslContextFactory {
}
/**
* Creates a SSLContext instance using the given information.
* Creates a SSLContext instance using the given information. The password for the key is assumed to be the same
* as the password for the keystore. If this is not the case, the {@link #createSslContext(String, char[], chart[], String, String, char[], String, ClientAuth, String)}
* method should be used instead
*
* @param keystore the full path to the keystore
* @param keystorePasswd the keystore password
@ -74,13 +76,48 @@ public final class SslContextFactory {
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
UnrecoverableKeyException, KeyManagementException {
// Pass the keystore password as both the keystore password and the key password.
return createSslContext(keystore, keystorePasswd, keystorePasswd, keystoreType, truststore, truststorePasswd, truststoreType, clientAuth, protocol);
}
/**
* Creates a SSLContext instance using the given information.
*
* @param keystore the full path to the keystore
* @param keystorePasswd the keystore password
* @param keystoreType the type of keystore (e.g., PKCS12, JKS)
* @param truststore the full path to the truststore
* @param truststorePasswd the truststore password
* @param truststoreType the type of truststore (e.g., PKCS12, JKS)
* @param clientAuth the type of client authentication
* @param protocol the protocol to use for the SSL connection
*
* @return a SSLContext instance
* @throws java.security.KeyStoreException if any issues accessing the keystore
* @throws java.io.IOException for any problems loading the keystores
* @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown
* @throws java.security.cert.CertificateException if there is an issue with the certificate
* @throws java.security.UnrecoverableKeyException if the key is insufficient
* @throws java.security.KeyManagementException if unable to manage the key
*/
public static SSLContext createSslContext(
final String keystore, final char[] keystorePasswd, final char[] keyPasswd, final String keystoreType,
final String truststore, final char[] truststorePasswd, final String truststoreType,
final ClientAuth clientAuth, final String protocol)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
UnrecoverableKeyException, KeyManagementException {
// prepare the keystore
final KeyStore keyStore = KeyStore.getInstance(keystoreType);
try (final InputStream keyStoreStream = new FileInputStream(keystore)) {
keyStore.load(keyStoreStream, keystorePasswd);
}
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keystorePasswd);
if (keyPasswd == null) {
keyManagerFactory.init(keyStore, keystorePasswd);
} else {
keyManagerFactory.init(keyStore, keyPasswd);
}
// prepare the truststore
final KeyStore trustStore = KeyStore.getInstance(truststoreType);
@ -105,6 +142,33 @@ public final class SslContextFactory {
}
/**
* Creates a SSLContext instance using the given information. This method assumes that the key password is
* the same as the keystore password. If this is not the case, use the {@link #createSslContext(String, char[], char[], String, String)}
* method instead.
*
* @param keystore the full path to the keystore
* @param keystorePasswd the keystore password
* @param keystoreType the type of keystore (e.g., PKCS12, JKS)
* @param protocol the protocol to use for the SSL connection
*
* @return a SSLContext instance
* @throws java.security.KeyStoreException if any issues accessing the keystore
* @throws java.io.IOException for any problems loading the keystores
* @throws java.security.NoSuchAlgorithmException if an algorithm is found to be used but is unknown
* @throws java.security.cert.CertificateException if there is an issue with the certificate
* @throws java.security.UnrecoverableKeyException if the key is insufficient
* @throws java.security.KeyManagementException if unable to manage the key
*/
public static SSLContext createSslContext(
final String keystore, final char[] keystorePasswd, final String keystoreType, final String protocol)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
UnrecoverableKeyException, KeyManagementException {
// create SSL Context passing keystore password as the key password
return createSslContext(keystore, keystorePasswd, keystorePasswd, keystoreType, protocol);
}
/**
* Creates a SSLContext instance using the given information.
*
@ -122,7 +186,7 @@ public final class SslContextFactory {
* @throws java.security.KeyManagementException if unable to manage the key
*/
public static SSLContext createSslContext(
final String keystore, final char[] keystorePasswd, final String keystoreType, final String protocol)
final String keystore, final char[] keystorePasswd, final char[] keyPasswd, final String keystoreType, final String protocol)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,
UnrecoverableKeyException, KeyManagementException {
@ -132,7 +196,11 @@ public final class SslContextFactory {
keyStore.load(keyStoreStream, keystorePasswd);
}
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keystorePasswd);
if (keyPasswd == null) {
keyManagerFactory.init(keyStore, keystorePasswd);
} else {
keyManagerFactory.init(keyStore, keyPasswd);
}
// initialize the ssl context
final SSLContext ctx = SSLContext.getInstance(protocol);

View File

@ -21,6 +21,7 @@ import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.annotation.lifecycle.OnEnabled;
import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
@ -96,6 +97,14 @@ public class StandardSSLContextService extends AbstractControllerService impleme
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.sensitive(true)
.build();
static final PropertyDescriptor KEY_PASSWORD = new PropertyDescriptor.Builder()
.name("Key Password")
.description("The password for the key. If this is not specified, but the Keystore Filename, Password, and Type are specified, "
+ "then the Keystore Password will be assumed to be the same as the Key Password.")
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.sensitive(true)
.required(false)
.build();
public static final PropertyDescriptor SSL_ALGORITHM = new PropertyDescriptor.Builder()
.name("SSL Protocol")
.defaultValue("TLS")
@ -113,6 +122,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
List<PropertyDescriptor> props = new ArrayList<>();
props.add(KEYSTORE);
props.add(KEYSTORE_PASSWORD);
props.add(KEY_PASSWORD);
props.add(KEYSTORE_TYPE);
props.add(TRUSTSTORE);
props.add(TRUSTSTORE_PASSWORD);
@ -260,19 +270,26 @@ public class StandardSSLContextService extends AbstractControllerService impleme
public SSLContext createSSLContext(final ClientAuth clientAuth) throws ProcessException {
final String protocol = configContext.getProperty(SSL_ALGORITHM).getValue();
try {
final PropertyValue keyPasswdProp = configContext.getProperty(KEY_PASSWORD);
final char[] keyPassword = keyPasswdProp.isSet() ? keyPasswdProp.getValue().toCharArray() : null;
final String keystoreFile = configContext.getProperty(KEYSTORE).getValue();
if (keystoreFile == null) {
// If keystore not specified, create SSL Context based only on trust store.
return SslContextFactory.createTrustSslContext(
configContext.getProperty(TRUSTSTORE).getValue(),
configContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(),
configContext.getProperty(TRUSTSTORE_TYPE).getValue(),
protocol);
}
final String truststoreFile = configContext.getProperty(TRUSTSTORE).getValue();
if (truststoreFile == null) {
// If truststore not specified, create SSL Context based only on key store.
return SslContextFactory.createSslContext(
configContext.getProperty(KEYSTORE).getValue(),
configContext.getProperty(KEYSTORE_PASSWORD).getValue().toCharArray(),
keyPassword,
configContext.getProperty(KEYSTORE_TYPE).getValue(),
protocol);
}
@ -280,6 +297,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
return SslContextFactory.createSslContext(
configContext.getProperty(KEYSTORE).getValue(),
configContext.getProperty(KEYSTORE_PASSWORD).getValue().toCharArray(),
keyPassword,
configContext.getProperty(KEYSTORE_TYPE).getValue(),
configContext.getProperty(TRUSTSTORE).getValue(),
configContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(),
@ -326,6 +344,11 @@ public class StandardSSLContextService extends AbstractControllerService impleme
return configContext.getProperty(KEYSTORE_PASSWORD).getValue();
}
@Override
public String getKeyPassword() {
return configContext.getProperty(KEY_PASSWORD).getValue();
}
@Override
public boolean isKeyStoreConfigured() {
return getKeyStoreFile() != null && getKeyStorePassword() != null && getKeyStoreType() != null;
@ -371,8 +394,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
.build());
} else {
try {
final boolean storeValid = CertificateUtils
.isStoreValid(file.toURI().toURL(), KeystoreType.valueOf(type), password.toCharArray());
final boolean storeValid = CertificateUtils.isStoreValid(file.toURI().toURL(), KeystoreType.valueOf(type), password.toCharArray());
if (!storeValid) {
results.add(new ValidationResult.Builder()
.subject(keystoreDesc + " Properties")

View File

@ -55,6 +55,8 @@ public interface SSLContextService extends ControllerService {
public String getKeyStorePassword();
public String getKeyPassword();
public boolean isKeyStoreConfigured();
String getSslAlgorithm();