diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java index 9a2a6028a8a..a88cd9e248f 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java @@ -34,6 +34,7 @@ import org.elasticsearch.xpack.monitoring.MonitoringSettings; import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; import org.junit.Before; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -220,7 +221,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase public static class InternalXPackPlugin extends XPackPlugin { - public InternalXPackPlugin(Settings settings) { + public InternalXPackPlugin(Settings settings) throws IOException { super(settings); licensing = new InternalLicensing(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java index 5573eb79cec..6d2b17d60d7 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.monitoring.MonitoringLicensee; import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -161,7 +162,7 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase { } public static class InternalXPackPlugin extends XPackPlugin { - public InternalXPackPlugin(Settings settings) { + public InternalXPackPlugin(Settings settings) throws IOException { super(settings); licensing = new MockLicensing(); } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java index 3200cc209f5..048819c998a 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/test/MonitoringIntegTestCase.java @@ -37,7 +37,7 @@ import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.security.authc.support.Hasher; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.watcher.Watcher; import org.hamcrest.Matcher; import org.jboss.netty.util.internal.SystemPropertyUtil; @@ -537,7 +537,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase { .put("xpack.security.authc.realms.esusers.files.users", writeFile(folder, "users", USERS)) .put("xpack.security.authc.realms.esusers.files.users_roles", writeFile(folder, "users_roles", USER_ROLES)) .put(FileRolesStore.ROLES_FILE_SETTING.getKey(), writeFile(folder, "roles.yml", ROLES)) - .put(InternalCryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) + .put(CryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) .put("xpack.security.authc.sign_user_header", false) .put("xpack.security.audit.enabled", auditLogsEnabled); } catch (IOException ex) { @@ -547,7 +547,7 @@ public abstract class MonitoringIntegTestCase extends ESIntegTestCase { static byte[] generateKey() { try { - return InternalCryptoService.generateKey(); + return CryptoService.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java index d9c7a821fb0..e8b03c7fc73 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -5,6 +5,16 @@ */ package org.elasticsearch.xpack.security; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.support.ActionFilter; @@ -12,6 +22,7 @@ import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Module; +import org.elasticsearch.common.inject.util.Providers; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.logging.Loggers; @@ -21,6 +32,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexModule; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.rest.RestHandler; @@ -64,8 +76,7 @@ import org.elasticsearch.xpack.security.authz.accesscontrol.OptOutQueryCache; import org.elasticsearch.xpack.security.authz.accesscontrol.SecurityIndexSearcherWrapper; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; import org.elasticsearch.xpack.security.authz.store.NativeRolesStore; -import org.elasticsearch.xpack.security.crypto.CryptoModule; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.rest.SecurityRestModule; import org.elasticsearch.xpack.security.rest.action.RestAuthenticateAction; import org.elasticsearch.xpack.security.rest.action.realm.RestClearRealmCacheAction; @@ -90,15 +101,6 @@ import org.elasticsearch.xpack.security.user.AnonymousUser; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -117,13 +119,17 @@ public class Security implements ActionPlugin { private final boolean enabled; private final boolean transportClientMode; private SecurityLicenseState securityLicenseState; + private final CryptoService cryptoService; - public Security(Settings settings) { + public Security(Settings settings, Environment env) throws IOException { this.settings = settings; this.transportClientMode = XPackPlugin.transportClientMode(settings); this.enabled = XPackPlugin.featureEnabled(settings, NAME, true); - if (enabled && !transportClientMode) { + if (enabled && transportClientMode == false) { validateAutoCreateIndex(settings); + cryptoService = new CryptoService(settings, env); + } else { + cryptoService = null; } } @@ -143,8 +149,8 @@ public class Security implements ActionPlugin { modules.add(new AuthenticationModule(settings)); modules.add(new AuthorizationModule(settings)); if (enabled == false) { + modules.add(b -> b.bind(CryptoService.class).toProvider(Providers.of(null))); modules.add(new SecurityModule(settings, securityLicenseState)); - modules.add(new CryptoModule(settings)); modules.add(new AuditTrailModule(settings)); modules.add(new SecurityTransportModule(settings)); return modules; @@ -154,8 +160,8 @@ public class Security implements ActionPlugin { // which might not be the case during Plugin class instantiation. Once nodeModules are pulled // everything should have been loaded securityLicenseState = new SecurityLicenseState(); + modules.add(b -> b.bind(CryptoService.class).toInstance(cryptoService)); modules.add(new SecurityModule(settings, securityLicenseState)); - modules.add(new CryptoModule(settings)); modules.add(new AuditTrailModule(settings)); modules.add(new SecurityRestModule(settings)); modules.add(new SecurityActionModule(settings)); @@ -185,13 +191,18 @@ public class Security implements ActionPlugin { return Settings.EMPTY; } + return additionalSettings(settings); + } + + // pkg private for testing + static Settings additionalSettings(Settings settings) { Settings.Builder settingsBuilder = Settings.builder(); settingsBuilder.put(NetworkModule.TRANSPORT_TYPE_KEY, Security.NAME); settingsBuilder.put(NetworkModule.TRANSPORT_SERVICE_TYPE_KEY, Security.NAME); settingsBuilder.put(NetworkModule.HTTP_TYPE_SETTING.getKey(), Security.NAME); SecurityNettyHttpServerTransport.overrideSettings(settingsBuilder, settings); - addUserSettings(settingsBuilder); - addTribeSettings(settingsBuilder); + addUserSettings(settings, settingsBuilder); + addTribeSettings(settings, settingsBuilder); return settingsBuilder.build(); } @@ -233,7 +244,7 @@ public class Security implements ActionPlugin { SecurityNettyHttpServerTransport.addSettings(settingsList); // encryption settings - InternalCryptoService.addSettings(settingsList); + CryptoService.addSettings(settingsList); // hide settings settingsList.add(Setting.listSetting(setting("hide_settings"), Collections.emptyList(), Function.identity(), @@ -345,7 +356,7 @@ public class Security implements ActionPlugin { } } - private void addUserSettings(Settings.Builder settingsBuilder) { + private static void addUserSettings(Settings settings, Settings.Builder settingsBuilder) { String authHeaderSettingName = ThreadContext.PREFIX + "." + UsernamePasswordToken.BASIC_AUTH_HEADER; if (settings.get(authHeaderSettingName) != null) { return; @@ -373,7 +384,7 @@ public class Security implements ActionPlugin { * * - forcibly enabling it (that means it's not possible to disable security on the tribe clients) */ - private void addTribeSettings(Settings.Builder settingsBuilder) { + private static void addTribeSettings(Settings settings, Settings.Builder settingsBuilder) { Map tribesSettings = settings.getGroups("tribe", true); if (tribesSettings.isEmpty()) { // it's not a tribe node diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoModule.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoModule.java deleted file mode 100644 index 7b400e7d8e5..00000000000 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoModule.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.security.crypto; - -import org.elasticsearch.common.inject.util.Providers; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xpack.security.support.AbstractSecurityModule; - -/** - * - */ -public class CryptoModule extends AbstractSecurityModule.Node { - - public CryptoModule(Settings settings) { - super(settings); - } - - @Override - protected void configureNode() { - if (securityEnabled == false) { - bind(CryptoService.class).toProvider(Providers.of(null)); - return; - } - bind(InternalCryptoService.class).asEagerSingleton(); - bind(CryptoService.class).to(InternalCryptoService.class).asEagerSingleton(); - } -} diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java index 4e978f8cba7..4447c1c0f47 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/CryptoService.java @@ -5,55 +5,497 @@ */ package org.elasticsearch.xpack.security.crypto; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.component.AbstractComponent; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.xpack.XPackPlugin; +import org.elasticsearch.xpack.security.authc.support.CharArrays; + +import static org.elasticsearch.xpack.security.Security.setting; +import static org.elasticsearch.xpack.security.authc.support.SecuredString.constantTimeEquals; /** * Service that provides cryptographic methods based on a shared system key */ -public interface CryptoService { +public class CryptoService extends AbstractComponent { + + public static final String KEY_ALGO = "HmacSHA512"; + public static final int KEY_SIZE = 1024; + + static final String FILE_NAME = "system_key"; + static final String HMAC_ALGO = "HmacSHA1"; + static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CTR/NoPadding"; + static final String DEFAULT_KEY_ALGORITH = "AES"; + static final String ENCRYPTED_TEXT_PREFIX = "::es_encrypted::"; + static final byte[] ENCRYPTED_BYTE_PREFIX = ENCRYPTED_TEXT_PREFIX.getBytes(StandardCharsets.UTF_8); + static final int DEFAULT_KEY_LENGTH = 128; + static final int RANDOM_KEY_SIZE = 128; + + private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$[^\\$]*\\$\\$.+"); + private static final byte[] HKDF_APP_INFO = "es-security-crypto-service".getBytes(StandardCharsets.UTF_8); + + public static final Setting FILE_SETTING = Setting.simpleString(setting("system_key.file"), Setting.Property.NodeScope); + public static final Setting ENCRYPTION_ALGO_SETTING = + new Setting<>(setting("encryption.algorithm"), s -> DEFAULT_ENCRYPTION_ALGORITHM, s -> s, Setting.Property.NodeScope); + public static final Setting ENCRYPTION_KEY_LENGTH_SETTING = + Setting.intSetting(setting("encryption_key.length"), DEFAULT_KEY_LENGTH, Setting.Property.NodeScope); + public static final Setting ENCRYPTION_KEY_ALGO_SETTING = + new Setting<>(setting("encryption_key.algorithm"), DEFAULT_KEY_ALGORITH, s -> s, Setting.Property.NodeScope); + + private final SecureRandom secureRandom = new SecureRandom(); + private final String encryptionAlgorithm; + private final String keyAlgorithm; + private final int keyLength; + private final int ivLength; + + private final Path keyFile; + + private final SecretKey randomKey; + private final String randomKeyBase64; + + private final SecretKey encryptionKey; + private final SecretKey systemKey; + private final SecretKey signingKey; + + public CryptoService(Settings settings, Environment env) throws IOException { + super(settings); + this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings); + this.keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings); + this.ivLength = keyLength / 8; + this.keyAlgorithm = ENCRYPTION_KEY_ALGO_SETTING.get(settings); + + if (keyLength % 8 != 0) { + throw new IllegalArgumentException("invalid key length [" + keyLength + "]. value must be a multiple of 8"); + } + + keyFile = resolveSystemKey(settings, env); + systemKey = readSystemKey(keyFile); + randomKey = generateSecretKey(RANDOM_KEY_SIZE); + randomKeyBase64 = Base64.getUrlEncoder().encodeToString(randomKey.getEncoded()); + + signingKey = createSigningKey(systemKey, randomKey); + + try { + encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm); + } catch (NoSuchAlgorithmException nsae) { + throw new ElasticsearchException("failed to start crypto service. could not load encryption key", nsae); + } + logger.info("system key [{}] has been loaded", keyFile.toAbsolutePath()); + } + + public static byte[] generateKey() { + return generateSecretKey(KEY_SIZE).getEncoded(); + } + + static SecretKey generateSecretKey(int keyLength) { + try { + KeyGenerator generator = KeyGenerator.getInstance(KEY_ALGO); + generator.init(keyLength); + return generator.generateKey(); + } catch (NoSuchAlgorithmException e) { + throw new ElasticsearchException("failed to generate key", e); + } + } + + public static Path resolveSystemKey(Settings settings, Environment env) { + String location = FILE_SETTING.get(settings); + if (location.isEmpty()) { + return XPackPlugin.resolveConfigFile(env, FILE_NAME); + } + return XPackPlugin.resolveConfigFile(env, location); + } + + static SecretKey createSigningKey(@Nullable SecretKey systemKey, SecretKey randomKey) { + assert randomKey != null; + if (systemKey != null) { + return systemKey; + } else { + // the random key is only 128 bits so we use HKDF to expand to 1024 bits with some application specific data mixed in + byte[] keyBytes = HmacSHA1HKDF.extractAndExpand(null, randomKey.getEncoded(), HKDF_APP_INFO, (KEY_SIZE / 8)); + assert keyBytes.length * 8 == KEY_SIZE; + return new SecretKeySpec(keyBytes, KEY_ALGO); + } + } + + private static SecretKey readSystemKey(Path file) { + if (!Files.exists(file)) { + return null; + } + try { + byte[] bytes = Files.readAllBytes(file); + return new SecretKeySpec(bytes, KEY_ALGO); + } catch (IOException e) { + throw new ElasticsearchException("could not read secret key", e); + } + } /** * Signs the given text and returns the signed text (original text + signature) * @param text the string to sign */ - String sign(String text) throws IOException; + public String sign(String text) throws IOException { + String sigStr = signInternal(text, signingKey); + return "$$" + sigStr.length() + "$$" + (systemKey == signingKey ? "" : randomKeyBase64) + "$$" + sigStr + text; + } /** * Unsigns the given signed text, verifies the original text with the attached signature and if valid returns * the unsigned (original) text. If signature verification fails a {@link IllegalArgumentException} is thrown. - * @param text the string to unsign and verify + * @param signedText the string to unsign and verify */ - String unsignAndVerify(String text); + public String unsignAndVerify(String signedText) { + if (!signedText.startsWith("$$") || signedText.length() < 2) { + throw new IllegalArgumentException("tampered signed text"); + } + + // $$34$$randomKeyBase64$$sigtext + String[] pieces = signedText.split("\\$\\$"); + if (pieces.length != 4 || !pieces[0].equals("")) { + logger.debug("received signed text [{}] with [{}] parts", signedText, pieces.length); + throw new IllegalArgumentException("tampered signed text"); + } + String text; + String base64RandomKey; + String receivedSignature; + try { + int length = Integer.parseInt(pieces[1]); + base64RandomKey = pieces[2]; + receivedSignature = pieces[3].substring(0, length); + text = pieces[3].substring(length); + } catch (Exception e) { + logger.error("error occurred while parsing signed text", e); + throw new IllegalArgumentException("tampered signed text"); + } + + SecretKey signingKey; + // no random key, so we must have a system key + if (base64RandomKey.isEmpty()) { + if (systemKey == null) { + logger.debug("received signed text without random key information and no system key is present"); + throw new IllegalArgumentException("tampered signed text"); + } + signingKey = systemKey; + } else if (systemKey != null) { + // we have a system key and there is some random key data, this is an error + logger.debug("received signed text with random key information but a system key is present"); + throw new IllegalArgumentException("tampered signed text"); + } else { + byte[] randomKeyBytes; + try { + randomKeyBytes = Base64.getUrlDecoder().decode(base64RandomKey); + } catch (IllegalArgumentException e) { + logger.error("error occurred while decoding key data", e); + throw new IllegalStateException("error while verifying the signed text"); + } + if (randomKeyBytes.length * 8 != RANDOM_KEY_SIZE) { + logger.debug("incorrect random key data length. received [{}] bytes", randomKeyBytes.length); + throw new IllegalArgumentException("tampered signed text"); + } + SecretKey randomKey = new SecretKeySpec(randomKeyBytes, KEY_ALGO); + signingKey = createSigningKey(systemKey, randomKey); + } + + try { + String sig = signInternal(text, signingKey); + if (constantTimeEquals(sig, receivedSignature)) { + return text; + } + } catch (Exception e) { + logger.error("error occurred while verifying signed text", e); + throw new IllegalStateException("error while verifying the signed text"); + } + + throw new IllegalArgumentException("tampered signed text"); + } /** * Checks whether the given text is signed. */ - boolean isSigned(String text); + public boolean isSigned(String text) { + return SIG_PATTERN.matcher(text).matches(); + } /** * Encrypts the provided char array and returns the encrypted values in a char array * @param chars the characters to encrypt * @return character array representing the encrypted data */ - char[] encrypt(char[] chars); + public char[] encrypt(char[] chars) { + SecretKey key = this.encryptionKey; + if (key == null) { + logger.warn("encrypt called without a key, returning plain text. run syskeygen and copy same key to all nodes to enable " + + "encryption"); + return chars; + } + + byte[] charBytes = CharArrays.toUtf8Bytes(chars); + String base64 = Base64.getEncoder().encodeToString(encryptInternal(charBytes, key)); + return ENCRYPTED_TEXT_PREFIX.concat(base64).toCharArray(); + } /** * Decrypts the provided char array and returns the plain-text chars * @param chars the data to decrypt * @return plaintext chars */ - char[] decrypt(char[] chars); + public char[] decrypt(char[] chars) { + if (encryptionKey == null) { + return chars; + } + + if (!isEncrypted(chars)) { + // Not encrypted + return chars; + } + + String encrypted = new String(chars, ENCRYPTED_TEXT_PREFIX.length(), chars.length - ENCRYPTED_TEXT_PREFIX.length()); + byte[] bytes; + try { + bytes = Base64.getDecoder().decode(encrypted); + } catch (IllegalArgumentException e) { + throw new ElasticsearchException("unable to decode encrypted data", e); + } + + byte[] decrypted = decryptInternal(bytes, encryptionKey); + return CharArrays.utf8BytesToChars(decrypted); + } /** * Checks whether the given chars are encrypted * @param chars the chars to check if they are encrypted * @return true is data is encrypted */ - boolean isEncrypted(char[] chars); + public boolean isEncrypted(char[] chars) { + return CharArrays.charsBeginsWith(ENCRYPTED_TEXT_PREFIX, chars); + } /** * Flag for callers to determine if values will actually be encrypted or returned plaintext * @return true if values will be encrypted */ - boolean isEncryptionEnabled(); + public boolean isEncryptionEnabled() { + return this.encryptionKey != null; + } + + private byte[] encryptInternal(byte[] bytes, SecretKey key) { + byte[] iv = new byte[ivLength]; + secureRandom.nextBytes(iv); + Cipher cipher = cipher(Cipher.ENCRYPT_MODE, encryptionAlgorithm, key, iv); + try { + byte[] encrypted = cipher.doFinal(bytes); + byte[] output = new byte[iv.length + encrypted.length]; + System.arraycopy(iv, 0, output, 0, iv.length); + System.arraycopy(encrypted, 0, output, iv.length, encrypted.length); + return output; + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new ElasticsearchException("error encrypting data", e); + } + } + + private byte[] decryptInternal(byte[] bytes, SecretKey key) { + if (bytes.length < ivLength) { + logger.error("received data for decryption with size [{}] that is less than IV length [{}]", bytes.length, ivLength); + throw new IllegalArgumentException("invalid data to decrypt"); + } + + byte[] iv = new byte[ivLength]; + System.arraycopy(bytes, 0, iv, 0, ivLength); + byte[] data = new byte[bytes.length - ivLength]; + System.arraycopy(bytes, ivLength, data, 0, bytes.length - ivLength); + + Cipher cipher = cipher(Cipher.DECRYPT_MODE, encryptionAlgorithm, key, iv); + try { + return cipher.doFinal(data); + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new IllegalStateException("error decrypting data", e); + } + } + + static Mac createMac(SecretKey key) { + try { + Mac mac = HmacSHA1Provider.hmacSHA1(); + mac.init(key); + return mac; + } catch (Exception e) { + throw new ElasticsearchException("could not initialize mac", e); + } + } + + private static String signInternal(String text, SecretKey key) throws IOException { + Mac mac = createMac(key); + byte[] sig = mac.doFinal(text.getBytes(StandardCharsets.UTF_8)); + return Base64.getUrlEncoder().encodeToString(sig); + } + + + static Cipher cipher(int mode, String encryptionAlgorithm, SecretKey key, byte[] initializationVector) { + try { + Cipher cipher = Cipher.getInstance(encryptionAlgorithm); + cipher.init(mode, key, new IvParameterSpec(initializationVector)); + return cipher; + } catch (Exception e) { + throw new ElasticsearchException("error creating cipher", e); + } + } + + static SecretKey encryptionKey(SecretKey systemKey, int keyLength, String algorithm) throws NoSuchAlgorithmException { + if (systemKey == null) { + return null; + } + + byte[] bytes = systemKey.getEncoded(); + if ((bytes.length * 8) < keyLength) { + throw new IllegalArgumentException("at least " + keyLength + " bits should be provided as key data"); + } + + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + byte[] digest = messageDigest.digest(bytes); + assert digest.length == (256 / 8); + + if ((digest.length * 8) < keyLength) { + throw new IllegalArgumentException("requested key length is too large"); + } + byte[] truncatedDigest = Arrays.copyOfRange(digest, 0, (keyLength / 8)); + + return new SecretKeySpec(truncatedDigest, algorithm); + } + + /** + * Provider class for the HmacSHA1 {@link Mac} that provides an optimization by using a thread local instead of calling + * Mac#getInstance and obtaining a lock (in the internals) + */ + private static class HmacSHA1Provider { + + private static final ThreadLocal MAC = ThreadLocal.withInitial(() -> { + try { + return Mac.getInstance(HMAC_ALGO); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("could not create Mac instance with algorithm [" + HMAC_ALGO + "]", e); + } + }); + + private static Mac hmacSHA1() { + Mac instance = MAC.get(); + instance.reset(); + return instance; + } + + } + + /** + * Simplified implementation of HKDF using the HmacSHA1 algortihm. + * + * @see RFC 5869 + */ + private static class HmacSHA1HKDF { + private static final int HMAC_SHA1_BYTE_LENGTH = 20; + private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; + + /** + * This method performs the extract and expand steps of HKDF in one call with the given + * data. The output of the extract step is used as the input to the expand step + * + * @param salt optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros. + * @param ikm the input keying material + * @param info optional context and application specific information; if not provided a zero length byte[] is used + * @param outputLength length of output keying material in octets (<= 255*HashLen) + * @return the output keying material + */ + static byte[] extractAndExpand(@Nullable SecretKey salt, byte[] ikm, @Nullable byte[] info, int outputLength) { + // arg checking + Objects.requireNonNull(ikm, "the input keying material must not be null"); + if (outputLength < 1) { + throw new IllegalArgumentException("output length must be positive int >= 1"); + } + if (outputLength > 255 * HMAC_SHA1_BYTE_LENGTH) { + throw new IllegalArgumentException("output length must be <= 255*" + HMAC_SHA1_BYTE_LENGTH); + } + if (salt == null) { + salt = new SecretKeySpec(new byte[HMAC_SHA1_BYTE_LENGTH], HMAC_SHA1_ALGORITHM); + } + if (info == null) { + info = new byte[0]; + } + + // extract + Mac mac = createMac(salt); + byte[] keyBytes = mac.doFinal(ikm); + final SecretKey pseudoRandomKey = new SecretKeySpec(keyBytes, HMAC_SHA1_ALGORITHM); + + /* + * The output OKM is calculated as follows: + * N = ceil(L/HashLen) + * T = T(1) | T(2) | T(3) | ... | T(N) + * OKM = first L octets of T + * + * where: + * T(0) = empty string (zero length) + * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) + * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) + * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) + * ... + * + * (where the constant concatenated to the end of each T(n) is a single octet.) + */ + int n = (outputLength % HMAC_SHA1_BYTE_LENGTH == 0) ? + outputLength / HMAC_SHA1_BYTE_LENGTH : + (outputLength / HMAC_SHA1_BYTE_LENGTH) + 1; + + byte[] hashRound = new byte[0]; + + ByteBuffer generatedBytes = ByteBuffer.allocate(Math.multiplyExact(n, HMAC_SHA1_BYTE_LENGTH)); + try { + // initiliaze the mac with the new key + mac.init(pseudoRandomKey); + } catch (InvalidKeyException e) { + throw new ElasticsearchException("failed to initialize the mac", e); + } + for (int roundNum = 1; roundNum <= n; roundNum++) { + mac.reset(); + mac.update(hashRound); + mac.update(info); + mac.update((byte) roundNum); + hashRound = mac.doFinal(); + generatedBytes.put(hashRound); + } + + byte[] result = new byte[outputLength]; + generatedBytes.rewind(); + generatedBytes.get(result, 0, outputLength); + return result; + } + } + + public static void addSettings(List> settings) { + settings.add(FILE_SETTING); + settings.add(ENCRYPTION_KEY_LENGTH_SETTING); + settings.add(ENCRYPTION_KEY_ALGO_SETTING); + settings.add(ENCRYPTION_ALGO_SETTING); + } } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/InternalCryptoService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/InternalCryptoService.java deleted file mode 100644 index 8ad15024c5e..00000000000 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/InternalCryptoService.java +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.security.crypto; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; -import java.util.Objects; -import java.util.regex.Pattern; - -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.component.AbstractComponent; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.settings.Setting.Property; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.xpack.XPackPlugin; -import org.elasticsearch.xpack.security.authc.support.CharArrays; - -import static org.elasticsearch.xpack.security.Security.setting; -import static org.elasticsearch.xpack.security.authc.support.SecuredString.constantTimeEquals; - -public class InternalCryptoService extends AbstractComponent implements CryptoService { - - public static final String KEY_ALGO = "HmacSHA512"; - public static final int KEY_SIZE = 1024; - - static final String FILE_NAME = "system_key"; - static final String HMAC_ALGO = "HmacSHA1"; - static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CTR/NoPadding"; - static final String DEFAULT_KEY_ALGORITH = "AES"; - static final String ENCRYPTED_TEXT_PREFIX = "::es_encrypted::"; - static final byte[] ENCRYPTED_BYTE_PREFIX = ENCRYPTED_TEXT_PREFIX.getBytes(StandardCharsets.UTF_8); - static final int DEFAULT_KEY_LENGTH = 128; - static final int RANDOM_KEY_SIZE = 128; - - private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$[^\\$]*\\$\\$.+"); - private static final byte[] HKDF_APP_INFO = "es-security-crypto-service".getBytes(StandardCharsets.UTF_8); - - public static final Setting FILE_SETTING = Setting.simpleString(setting("system_key.file"), Property.NodeScope); - public static final Setting ENCRYPTION_ALGO_SETTING = - new Setting<>(setting("encryption.algorithm"), s -> DEFAULT_ENCRYPTION_ALGORITHM, s -> s, Property.NodeScope); - public static final Setting ENCRYPTION_KEY_LENGTH_SETTING = - Setting.intSetting(setting("encryption_key.length"), DEFAULT_KEY_LENGTH, Property.NodeScope); - public static final Setting ENCRYPTION_KEY_ALGO_SETTING = - new Setting<>(setting("encryption_key.algorithm"), DEFAULT_KEY_ALGORITH, s -> s, Property.NodeScope); - - private final SecureRandom secureRandom = new SecureRandom(); - private final String encryptionAlgorithm; - private final String keyAlgorithm; - private final int keyLength; - private final int ivLength; - - private final Path keyFile; - - private final SecretKey randomKey; - private final String randomKeyBase64; - - private final SecretKey encryptionKey; - private final SecretKey systemKey; - private final SecretKey signingKey; - - @Inject - public InternalCryptoService(Settings settings, Environment env) throws Exception { - super(settings); - this.encryptionAlgorithm = ENCRYPTION_ALGO_SETTING.get(settings); - this.keyLength = ENCRYPTION_KEY_LENGTH_SETTING.get(settings); - this.ivLength = keyLength / 8; - this.keyAlgorithm = ENCRYPTION_KEY_ALGO_SETTING.get(settings); - - if (keyLength % 8 != 0) { - throw new IllegalArgumentException("invalid key length [" + keyLength + "]. value must be a multiple of 8"); - } - - keyFile = resolveSystemKey(settings, env); - systemKey = readSystemKey(keyFile); - randomKey = generateSecretKey(RANDOM_KEY_SIZE); - randomKeyBase64 = Base64.getUrlEncoder().encodeToString(randomKey.getEncoded()); - - signingKey = createSigningKey(systemKey, randomKey); - - try { - encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm); - } catch (NoSuchAlgorithmException nsae) { - throw new ElasticsearchException("failed to start crypto service. could not load encryption key", nsae); - } - logger.info("system key [{}] has been loaded", keyFile.toAbsolutePath()); - } - - public static byte[] generateKey() { - return generateSecretKey(KEY_SIZE).getEncoded(); - } - - static SecretKey generateSecretKey(int keyLength) { - try { - KeyGenerator generator = KeyGenerator.getInstance(KEY_ALGO); - generator.init(keyLength); - return generator.generateKey(); - } catch (NoSuchAlgorithmException e) { - throw new ElasticsearchException("failed to generate key", e); - } - } - - public static Path resolveSystemKey(Settings settings, Environment env) { - String location = FILE_SETTING.get(settings); - if (location.isEmpty()) { - return XPackPlugin.resolveConfigFile(env, FILE_NAME); - } - return XPackPlugin.resolveConfigFile(env, location); - } - - static SecretKey createSigningKey(@Nullable SecretKey systemKey, SecretKey randomKey) { - assert randomKey != null; - if (systemKey != null) { - return systemKey; - } else { - // the random key is only 128 bits so we use HKDF to expand to 1024 bits with some application specific data mixed in - byte[] keyBytes = HmacSHA1HKDF.extractAndExpand(null, randomKey.getEncoded(), HKDF_APP_INFO, (KEY_SIZE / 8)); - assert keyBytes.length * 8 == KEY_SIZE; - return new SecretKeySpec(keyBytes, KEY_ALGO); - } - } - - private static SecretKey readSystemKey(Path file) { - if (!Files.exists(file)) { - return null; - } - try { - byte[] bytes = Files.readAllBytes(file); - return new SecretKeySpec(bytes, KEY_ALGO); - } catch (IOException e) { - throw new ElasticsearchException("could not read secret key", e); - } - } - - @Override - public String sign(String text) throws IOException { - String sigStr = signInternal(text, signingKey); - return "$$" + sigStr.length() + "$$" + (systemKey == signingKey ? "" : randomKeyBase64) + "$$" + sigStr + text; - } - - @Override - public String unsignAndVerify(String signedText) { - if (!signedText.startsWith("$$") || signedText.length() < 2) { - throw new IllegalArgumentException("tampered signed text"); - } - - // $$34$$randomKeyBase64$$sigtext - String[] pieces = signedText.split("\\$\\$"); - if (pieces.length != 4 || !pieces[0].equals("")) { - logger.debug("received signed text [{}] with [{}] parts", signedText, pieces.length); - throw new IllegalArgumentException("tampered signed text"); - } - String text; - String base64RandomKey; - String receivedSignature; - try { - int length = Integer.parseInt(pieces[1]); - base64RandomKey = pieces[2]; - receivedSignature = pieces[3].substring(0, length); - text = pieces[3].substring(length); - } catch (Exception e) { - logger.error("error occurred while parsing signed text", e); - throw new IllegalArgumentException("tampered signed text"); - } - - SecretKey signingKey; - // no random key, so we must have a system key - if (base64RandomKey.isEmpty()) { - if (systemKey == null) { - logger.debug("received signed text without random key information and no system key is present"); - throw new IllegalArgumentException("tampered signed text"); - } - signingKey = systemKey; - } else if (systemKey != null) { - // we have a system key and there is some random key data, this is an error - logger.debug("received signed text with random key information but a system key is present"); - throw new IllegalArgumentException("tampered signed text"); - } else { - byte[] randomKeyBytes; - try { - randomKeyBytes = Base64.getUrlDecoder().decode(base64RandomKey); - } catch (IllegalArgumentException e) { - logger.error("error occurred while decoding key data", e); - throw new IllegalStateException("error while verifying the signed text"); - } - if (randomKeyBytes.length * 8 != RANDOM_KEY_SIZE) { - logger.debug("incorrect random key data length. received [{}] bytes", randomKeyBytes.length); - throw new IllegalArgumentException("tampered signed text"); - } - SecretKey randomKey = new SecretKeySpec(randomKeyBytes, KEY_ALGO); - signingKey = createSigningKey(systemKey, randomKey); - } - - try { - String sig = signInternal(text, signingKey); - if (constantTimeEquals(sig, receivedSignature)) { - return text; - } - } catch (Exception e) { - logger.error("error occurred while verifying signed text", e); - throw new IllegalStateException("error while verifying the signed text"); - } - - throw new IllegalArgumentException("tampered signed text"); - } - - @Override - public boolean isSigned(String text) { - return SIG_PATTERN.matcher(text).matches(); - } - - @Override - public char[] encrypt(char[] chars) { - SecretKey key = this.encryptionKey; - if (key == null) { - logger.warn("encrypt called without a key, returning plain text. run syskeygen and copy same key to all nodes to enable " + - "encryption"); - return chars; - } - - byte[] charBytes = CharArrays.toUtf8Bytes(chars); - String base64 = Base64.getEncoder().encodeToString(encryptInternal(charBytes, key)); - return ENCRYPTED_TEXT_PREFIX.concat(base64).toCharArray(); - } - - @Override - public char[] decrypt(char[] chars) { - if (encryptionKey == null) { - return chars; - } - - if (!isEncrypted(chars)) { - // Not encrypted - return chars; - } - - String encrypted = new String(chars, ENCRYPTED_TEXT_PREFIX.length(), chars.length - ENCRYPTED_TEXT_PREFIX.length()); - byte[] bytes; - try { - bytes = Base64.getDecoder().decode(encrypted); - } catch (IllegalArgumentException e) { - throw new ElasticsearchException("unable to decode encrypted data", e); - } - - byte[] decrypted = decryptInternal(bytes, encryptionKey); - return CharArrays.utf8BytesToChars(decrypted); - } - - @Override - public boolean isEncrypted(char[] chars) { - return CharArrays.charsBeginsWith(ENCRYPTED_TEXT_PREFIX, chars); - } - - @Override - public boolean isEncryptionEnabled() { - return this.encryptionKey != null; - } - - private byte[] encryptInternal(byte[] bytes, SecretKey key) { - byte[] iv = new byte[ivLength]; - secureRandom.nextBytes(iv); - Cipher cipher = cipher(Cipher.ENCRYPT_MODE, encryptionAlgorithm, key, iv); - try { - byte[] encrypted = cipher.doFinal(bytes); - byte[] output = new byte[iv.length + encrypted.length]; - System.arraycopy(iv, 0, output, 0, iv.length); - System.arraycopy(encrypted, 0, output, iv.length, encrypted.length); - return output; - } catch (BadPaddingException | IllegalBlockSizeException e) { - throw new ElasticsearchException("error encrypting data", e); - } - } - - private byte[] decryptInternal(byte[] bytes, SecretKey key) { - if (bytes.length < ivLength) { - logger.error("received data for decryption with size [{}] that is less than IV length [{}]", bytes.length, ivLength); - throw new IllegalArgumentException("invalid data to decrypt"); - } - - byte[] iv = new byte[ivLength]; - System.arraycopy(bytes, 0, iv, 0, ivLength); - byte[] data = new byte[bytes.length - ivLength]; - System.arraycopy(bytes, ivLength, data, 0, bytes.length - ivLength); - - Cipher cipher = cipher(Cipher.DECRYPT_MODE, encryptionAlgorithm, key, iv); - try { - return cipher.doFinal(data); - } catch (BadPaddingException | IllegalBlockSizeException e) { - throw new IllegalStateException("error decrypting data", e); - } - } - - static Mac createMac(SecretKey key) { - try { - Mac mac = HmacSHA1Provider.hmacSHA1(); - mac.init(key); - return mac; - } catch (Exception e) { - throw new ElasticsearchException("could not initialize mac", e); - } - } - - private static String signInternal(String text, SecretKey key) throws IOException { - Mac mac = createMac(key); - byte[] sig = mac.doFinal(text.getBytes(StandardCharsets.UTF_8)); - return Base64.getUrlEncoder().encodeToString(sig); - } - - - static Cipher cipher(int mode, String encryptionAlgorithm, SecretKey key, byte[] initializationVector) { - try { - Cipher cipher = Cipher.getInstance(encryptionAlgorithm); - cipher.init(mode, key, new IvParameterSpec(initializationVector)); - return cipher; - } catch (Exception e) { - throw new ElasticsearchException("error creating cipher", e); - } - } - - static SecretKey encryptionKey(SecretKey systemKey, int keyLength, String algorithm) throws NoSuchAlgorithmException { - if (systemKey == null) { - return null; - } - - byte[] bytes = systemKey.getEncoded(); - if ((bytes.length * 8) < keyLength) { - throw new IllegalArgumentException("at least " + keyLength + " bits should be provided as key data"); - } - - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - byte[] digest = messageDigest.digest(bytes); - assert digest.length == (256 / 8); - - if ((digest.length * 8) < keyLength) { - throw new IllegalArgumentException("requested key length is too large"); - } - byte[] truncatedDigest = Arrays.copyOfRange(digest, 0, (keyLength / 8)); - - return new SecretKeySpec(truncatedDigest, algorithm); - } - - /** - * Provider class for the HmacSHA1 {@link Mac} that provides an optimization by using a thread local instead of calling - * Mac#getInstance and obtaining a lock (in the internals) - */ - private static class HmacSHA1Provider { - - private static final ThreadLocal MAC = ThreadLocal.withInitial(() -> { - try { - return Mac.getInstance(HMAC_ALGO); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("could not create Mac instance with algorithm [" + HMAC_ALGO + "]", e); - } - }); - - private static Mac hmacSHA1() { - Mac instance = MAC.get(); - instance.reset(); - return instance; - } - - } - - /** - * Simplified implementation of HKDF using the HmacSHA1 algortihm. - * - * @see RFC 5869 - */ - private static class HmacSHA1HKDF { - private static final int HMAC_SHA1_BYTE_LENGTH = 20; - private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; - - /** - * This method performs the extract and expand steps of HKDF in one call with the given - * data. The output of the extract step is used as the input to the expand step - * - * @param salt optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros. - * @param ikm the input keying material - * @param info optional context and application specific information; if not provided a zero length byte[] is used - * @param outputLength length of output keying material in octets (<= 255*HashLen) - * @return the output keying material - */ - static byte[] extractAndExpand(@Nullable SecretKey salt, byte[] ikm, @Nullable byte[] info, int outputLength) { - // arg checking - Objects.requireNonNull(ikm, "the input keying material must not be null"); - if (outputLength < 1) { - throw new IllegalArgumentException("output length must be positive int >= 1"); - } - if (outputLength > 255 * HMAC_SHA1_BYTE_LENGTH) { - throw new IllegalArgumentException("output length must be <= 255*" + HMAC_SHA1_BYTE_LENGTH); - } - if (salt == null) { - salt = new SecretKeySpec(new byte[HMAC_SHA1_BYTE_LENGTH], HMAC_SHA1_ALGORITHM); - } - if (info == null) { - info = new byte[0]; - } - - // extract - Mac mac = createMac(salt); - byte[] keyBytes = mac.doFinal(ikm); - final SecretKey pseudoRandomKey = new SecretKeySpec(keyBytes, HMAC_SHA1_ALGORITHM); - - /* - * The output OKM is calculated as follows: - * N = ceil(L/HashLen) - * T = T(1) | T(2) | T(3) | ... | T(N) - * OKM = first L octets of T - * - * where: - * T(0) = empty string (zero length) - * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) - * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) - * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) - * ... - * - * (where the constant concatenated to the end of each T(n) is a single octet.) - */ - int n = (outputLength % HMAC_SHA1_BYTE_LENGTH == 0) ? - outputLength / HMAC_SHA1_BYTE_LENGTH : - (outputLength / HMAC_SHA1_BYTE_LENGTH) + 1; - - byte[] hashRound = new byte[0]; - - ByteBuffer generatedBytes = ByteBuffer.allocate(Math.multiplyExact(n, HMAC_SHA1_BYTE_LENGTH)); - try { - // initiliaze the mac with the new key - mac.init(pseudoRandomKey); - } catch (InvalidKeyException e) { - throw new ElasticsearchException("failed to initialize the mac", e); - } - for (int roundNum = 1; roundNum <= n; roundNum++) { - mac.reset(); - mac.update(hashRound); - mac.update(info); - mac.update((byte) roundNum); - hashRound = mac.doFinal(); - generatedBytes.put(hashRound); - } - - byte[] result = new byte[outputLength]; - generatedBytes.rewind(); - generatedBytes.get(result, 0, outputLength); - return result; - } - } - - public static void addSettings(List> settings) { - settings.add(FILE_SETTING); - settings.add(ENCRYPTION_KEY_LENGTH_SETTING); - settings.add(ENCRYPTION_KEY_ALGO_SETTING); - settings.add(ENCRYPTION_ALGO_SETTING); - } -} diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java index 602b6c4d8a8..24a4e277bc6 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java @@ -17,7 +17,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.env.Environment; import org.elasticsearch.node.internal.InternalSettingsPreparer; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import java.nio.file.Files; import java.nio.file.Path; @@ -66,12 +66,12 @@ public class SystemKeyTool extends SettingCommand { } keyPath = parsePath(args.get(0)); } else { - keyPath = InternalCryptoService.resolveSystemKey(env.settings(), env); + keyPath = CryptoService.resolveSystemKey(env.settings(), env); } // write the key terminal.println(Terminal.Verbosity.VERBOSE, "generating..."); - byte[] key = InternalCryptoService.generateKey(); + byte[] key = CryptoService.generateKey(); terminal.println(String.format(Locale.ROOT, "Storing generated key in [%s]...", keyPath.toAbsolutePath())); Files.write(keyPath, key, StandardOpenOption.CREATE_NEW); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java index 68d59a26fe9..f9f33bde1ca 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java @@ -42,6 +42,7 @@ import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; import org.junit.After; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -265,7 +266,7 @@ public class LicensingTests extends SecurityIntegTestCase { public static class InternalXPackPlugin extends XPackPlugin { - public InternalXPackPlugin(Settings settings) { + public InternalXPackPlugin(Settings settings) throws IOException { super(settings); licensing = new InternalLicensing(); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java index 4117bac7b8b..ab0609141af 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/ScrollIdSigningTests.java @@ -11,7 +11,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.xpack.security.crypto.CryptoService; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.test.SecurityIntegTestCase; import java.util.Locale; @@ -106,7 +106,7 @@ public class ScrollIdSigningTests extends SecurityIntegTestCase { } private void assertSigned(String scrollId) { - CryptoService cryptoService = internalCluster().getDataNodeInstance(InternalCryptoService.class); + CryptoService cryptoService = internalCluster().getDataNodeInstance(CryptoService.class); String message = String.format(Locale.ROOT, "Expected scrollId [%s] to be signed, but was not", scrollId); assertThat(message, cryptoService.isSigned(scrollId), is(true)); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java index 0a026b3b6f8..6c0396fa494 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java @@ -21,7 +21,7 @@ import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail; import org.elasticsearch.xpack.security.authc.support.Hasher; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.test.SecurityTestUtils; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerTransport; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport; @@ -131,7 +131,7 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas .put(LoggingAuditTrail.HOST_ADDRESS_SETTING.getKey(), randomBoolean()) .put(LoggingAuditTrail.HOST_NAME_SETTING.getKey(), randomBoolean()) .put(LoggingAuditTrail.NODE_NAME_SETTING.getKey(), randomBoolean()) - .put(InternalCryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key", systemKey)) + .put(CryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key", systemKey)) .put("xpack.security.authc.realms.file.type", FileRealm.TYPE) .put("xpack.security.authc.realms.file.order", 0) .put("xpack.security.authc.realms.file.files.users", writeFile(folder, "users", configUsers())) @@ -206,7 +206,7 @@ public class SecuritySettingsSource extends ClusterDiscoveryConfiguration.Unicas private static byte[] generateKey() { try { - return InternalCryptoService.generateKey(); + return CryptoService.generateKey(); } catch (Exception e) { throw new ElasticsearchException("exception while generating the system key", e); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java index 9a1b790556e..b38dbc16371 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/test/SettingsFilterTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.security.ssl.SSLConfiguration; import org.elasticsearch.xpack.XPackPlugin; import org.hamcrest.Matcher; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -28,7 +29,7 @@ public class SettingsFilterTests extends ESTestCase { private Settings.Builder configuredSettingsBuilder = Settings.builder(); private Map settingsMatcherMap = new HashMap<>(); - public void testFiltering() { + public void testFiltering() throws IOException { configureUnfilteredSetting("xpack.security.authc.realms.file.type", "file"); // ldap realm filtering diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java index 64eda1a9f47..daffe7a159e 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecuritySettingsTests.java @@ -5,8 +5,11 @@ */ package org.elasticsearch.xpack.security; +import java.io.IOException; + import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.security.audit.AuditTrailModule; import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail; import org.elasticsearch.test.ESTestCase; @@ -25,13 +28,11 @@ public class SecuritySettingsTests extends ESTestCase { private static final String TRIBE_T1_SECURITY_ENABLED = "tribe.t1." + Security.enabledSetting(); private static final String TRIBE_T2_SECURITY_ENABLED = "tribe.t2." + Security.enabledSetting(); - public void testSecurityIsMandatoryOnTribes() { + public void testSecurityIsMandatoryOnTribes() throws IOException { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .put("tribe.t2.cluster.name", "non_existing").build(); - Security security = new Security(settings); - - Settings additionalSettings = security.additionalSettings(); + Settings additionalSettings = Security.additionalSettings(settings); assertThat(additionalSettings.getAsArray("tribe.t1.plugin.mandatory", null), arrayContaining(XPackPlugin.NAME)); @@ -42,11 +43,9 @@ public class SecuritySettingsTests extends ESTestCase { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .putArray("tribe.t1.plugin.mandatory", "test_plugin").build(); - Security security = new Security(settings); - //simulate what PluginsService#updatedSettings does to make sure we don't override existing mandatory plugins try { - Settings.builder().put(settings).put(security.additionalSettings()).build(); + Settings.builder().put(settings).put(Security.additionalSettings(settings)).build(); fail("security cannot change the value of a setting that is already defined, so a exception should be thrown"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString(XPackPlugin.NAME)); @@ -58,10 +57,8 @@ public class SecuritySettingsTests extends ESTestCase { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .putArray("tribe.t1.plugin.mandatory", "test_plugin", XPackPlugin.NAME).build(); - Security security = new Security(settings); - //simulate what PluginsService#updatedSettings does to make sure we don't override existing mandatory plugins - Settings finalSettings = Settings.builder().put(settings).put(security.additionalSettings()).build(); + Settings finalSettings = Settings.builder().put(settings).put(Security.additionalSettings(settings)).build(); String[] finalMandatoryPlugins = finalSettings.getAsArray("tribe.t1.plugin.mandatory", null); assertThat(finalMandatoryPlugins, notNullValue()); @@ -74,9 +71,7 @@ public class SecuritySettingsTests extends ESTestCase { Settings settings = Settings.builder().put("tribe.t1.cluster.name", "non_existing") .put("tribe.t2.cluster.name", "non_existing").build(); - Security security = new Security(settings); - - Settings additionalSettings = security.additionalSettings(); + Settings additionalSettings = Security.additionalSettings(settings); assertThat(additionalSettings.getAsBoolean(TRIBE_T1_SECURITY_ENABLED, null), equalTo(true)); assertThat(additionalSettings.getAsBoolean(TRIBE_T2_SECURITY_ENABLED, null), equalTo(true)); @@ -87,10 +82,8 @@ public class SecuritySettingsTests extends ESTestCase { .put(TRIBE_T1_SECURITY_ENABLED, false) .put("tribe.t2.cluster.name", "non_existing").build(); - Security security = new Security(settings); - try { - security.additionalSettings(); + Security.additionalSettings(settings); fail("security cannot change the value of a setting that is already defined, so a exception should be thrown"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString(TRIBE_T1_SECURITY_ENABLED)); @@ -103,10 +96,8 @@ public class SecuritySettingsTests extends ESTestCase { .put("tribe.t2.cluster.name", "non_existing") .putArray("tribe.t1.plugin.mandatory", "test_plugin", XPackPlugin.NAME).build(); - Security security = new Security(settings); - try { - security.additionalSettings(); + Security.additionalSettings(settings); fail("security cannot change the value of a setting that is already defined, so a exception should be thrown"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString(TRIBE_T1_SECURITY_ENABLED)); @@ -122,8 +113,7 @@ public class SecuritySettingsTests extends ESTestCase { .putArray("xpack.security.something.else.here", new String[] { "foo", "bar" }) .build(); - Security security = new Security(settings); - Settings additionalSettings = security.additionalSettings(); + Settings additionalSettings = Security.additionalSettings(settings); assertThat(additionalSettings.get("xpack.security.foo"), nullValue()); assertThat(additionalSettings.get("xpack.security.bar"), nullValue()); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java index 4dadbd0cdf8..2144bc8ba06 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java @@ -39,7 +39,7 @@ import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail.Message; import org.elasticsearch.xpack.security.authc.AuthenticationToken; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport; @@ -99,7 +99,7 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase { @BeforeClass public static void configureBeforeClass() { remoteIndexing = randomBoolean(); - systemKey = InternalCryptoService.generateKey(); + systemKey = CryptoService.generateKey(); if (remoteIndexing == false) { remoteSettings = Settings.EMPTY; } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/InternalCryptoServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/CryptoServiceTests.java similarity index 79% rename from elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/InternalCryptoServiceTests.java rename to elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/CryptoServiceTests.java index 796fcb9b2c7..f7151b1ac2d 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/InternalCryptoServiceTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/CryptoServiceTests.java @@ -22,7 +22,7 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class InternalCryptoServiceTests extends ESTestCase { +public class CryptoServiceTests extends ESTestCase { private Settings settings; private Environment env; private Path keyFile; @@ -30,9 +30,9 @@ public class InternalCryptoServiceTests extends ESTestCase { @Before public void init() throws Exception { keyFile = createTempDir().resolve("system_key"); - Files.write(keyFile, InternalCryptoService.generateKey()); + Files.write(keyFile, CryptoService.generateKey()); settings = Settings.builder() - .put(InternalCryptoService.FILE_SETTING.getKey(), keyFile.toAbsolutePath()) + .put(CryptoService.FILE_SETTING.getKey(), keyFile.toAbsolutePath()) .put("resource.reload.interval.high", "2s") .put("path.home", createTempDir()) .build(); @@ -42,14 +42,14 @@ public class InternalCryptoServiceTests extends ESTestCase { public void testSigned() throws Exception { // randomize whether to use a system key or not Settings settings = randomBoolean() ? this.settings : Settings.EMPTY; - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); String text = randomAsciiOfLength(10); String signed = service.sign(text); assertThat(service.isSigned(signed), is(true)); } public void testSignAndUnsign() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); String text = randomAsciiOfLength(10); String signed = service.sign(text); assertThat(text.equals(signed), is(false)); @@ -58,7 +58,7 @@ public class InternalCryptoServiceTests extends ESTestCase { } public void testSignAndUnsignNoKeyFile() throws Exception { - InternalCryptoService service = new InternalCryptoService(Settings.EMPTY, env); + CryptoService service = new CryptoService(Settings.EMPTY, env); final String text = randomAsciiOfLength(10); String signed = service.sign(text); // we always have some sort of key to sign with @@ -68,7 +68,7 @@ public class InternalCryptoServiceTests extends ESTestCase { } public void testTamperedSignature() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); String text = randomAsciiOfLength(10); String signed = service.sign(text); int i = signed.indexOf("$$", 2); @@ -85,7 +85,7 @@ public class InternalCryptoServiceTests extends ESTestCase { } public void testTamperedSignatureOneChar() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); String text = randomAsciiOfLength(10); String signed = service.sign(text); int i = signed.indexOf("$$", 2); @@ -104,7 +104,7 @@ public class InternalCryptoServiceTests extends ESTestCase { } public void testTamperedSignatureLength() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); String text = randomAsciiOfLength(10); String signed = service.sign(text); int i = signed.indexOf("$$", 2); @@ -131,7 +131,7 @@ public class InternalCryptoServiceTests extends ESTestCase { } public void testEncryptionAndDecryptionChars() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); assertThat(service.isEncryptionEnabled(), is(true)); final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray(); final char[] encrypted = service.encrypt(chars); @@ -143,7 +143,7 @@ public class InternalCryptoServiceTests extends ESTestCase { } public void testEncryptionAndDecryptionCharsWithoutKey() throws Exception { - InternalCryptoService service = new InternalCryptoService(Settings.EMPTY, env); + CryptoService service = new CryptoService(Settings.EMPTY, env); assertThat(service.isEncryptionEnabled(), is(false)); final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray(); final char[] encryptedChars = service.encrypt(chars); @@ -153,34 +153,34 @@ public class InternalCryptoServiceTests extends ESTestCase { } public void testEncryptionEnabledWithKey() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); assertThat(service.isEncryptionEnabled(), is(true)); } public void testEncryptionEnabledWithoutKey() throws Exception { - InternalCryptoService service = new InternalCryptoService(Settings.EMPTY, env); + CryptoService service = new CryptoService(Settings.EMPTY, env); assertThat(service.isEncryptionEnabled(), is(false)); } public void testEncryptedChar() throws Exception { - InternalCryptoService service = new InternalCryptoService(settings, env); + CryptoService service = new CryptoService(settings, env); assertThat(service.isEncryptionEnabled(), is(true)); assertThat(service.isEncrypted((char[]) null), is(false)); assertThat(service.isEncrypted(new char[0]), is(false)); - assertThat(service.isEncrypted(new char[InternalCryptoService.ENCRYPTED_TEXT_PREFIX.length()]), is(false)); - assertThat(service.isEncrypted(InternalCryptoService.ENCRYPTED_TEXT_PREFIX.toCharArray()), is(true)); + assertThat(service.isEncrypted(new char[CryptoService.ENCRYPTED_TEXT_PREFIX.length()]), is(false)); + assertThat(service.isEncrypted(CryptoService.ENCRYPTED_TEXT_PREFIX.toCharArray()), is(true)); assertThat(service.isEncrypted(randomAsciiOfLengthBetween(0, 100).toCharArray()), is(false)); assertThat(service.isEncrypted(service.encrypt(randomAsciiOfLength(10).toCharArray())), is(true)); } public void testSigningKeyCanBeRecomputedConsistently() { - final SecretKey systemKey = new SecretKeySpec(InternalCryptoService.generateKey(), InternalCryptoService.KEY_ALGO); - final SecretKey randomKey = InternalCryptoService.generateSecretKey(InternalCryptoService.RANDOM_KEY_SIZE); + final SecretKey systemKey = new SecretKeySpec(CryptoService.generateKey(), CryptoService.KEY_ALGO); + final SecretKey randomKey = CryptoService.generateSecretKey(CryptoService.RANDOM_KEY_SIZE); int iterations = randomInt(100); - final SecretKey signingKey = InternalCryptoService.createSigningKey(systemKey, randomKey); + final SecretKey signingKey = CryptoService.createSigningKey(systemKey, randomKey); for (int i = 0; i < iterations; i++) { - SecretKey regenerated = InternalCryptoService.createSigningKey(systemKey, randomKey); + SecretKey regenerated = CryptoService.createSigningKey(systemKey, randomKey); assertThat(regenerated, equalTo(signingKey)); } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java index d8e3f3e7e6b..7b9f8483d82 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java @@ -11,7 +11,7 @@ import org.apache.lucene.util.IOUtils; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.CommandTestCase; import org.elasticsearch.common.io.PathUtilsForTesting; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.junit.After; import java.nio.file.FileSystem; @@ -52,7 +52,7 @@ public class SystemKeyToolTests extends CommandTestCase { execute("-Epath.home=" + homeDir, path.toString()); byte[] bytes = Files.readAllBytes(path); // TODO: maybe we should actually check the key is...i dunno...valid? - assertEquals(InternalCryptoService.KEY_SIZE / 8, bytes.length); + assertEquals(CryptoService.KEY_SIZE / 8, bytes.length); Set perms = Files.getPosixFilePermissions(path); assertTrue(perms.toString(), perms.contains(PosixFilePermission.OWNER_READ)); @@ -67,7 +67,7 @@ public class SystemKeyToolTests extends CommandTestCase { Files.createDirectories(path.getParent()); execute("-Epath.home=" + homeDir.toString(), "-Expack.security.system_key.file=" + path.toAbsolutePath().toString()); byte[] bytes = Files.readAllBytes(path); - assertEquals(InternalCryptoService.KEY_SIZE / 8, bytes.length); + assertEquals(CryptoService.KEY_SIZE / 8, bytes.length); } public void testGenerateDefaultPath() throws Exception { @@ -76,7 +76,7 @@ public class SystemKeyToolTests extends CommandTestCase { Files.createDirectories(keyPath.getParent()); execute("-Epath.home=" + homeDir.toString()); byte[] bytes = Files.readAllBytes(keyPath); - assertEquals(InternalCryptoService.KEY_SIZE / 8, bytes.length); + assertEquals(CryptoService.KEY_SIZE / 8, bytes.length); } public void testThatSystemKeyMayOnlyBeReadByOwner() throws Exception { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java index 891392aa162..cb046fdfe4d 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java @@ -17,7 +17,7 @@ import org.elasticsearch.node.Node; import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.transport.Transport; @@ -82,7 +82,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase public void testThatConnectionToServerTypeConnectionWorks() throws IOException { Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class); - String systemKeyFile = InternalCryptoService.FILE_SETTING.get(dataNodeSettings); + String systemKeyFile = CryptoService.FILE_SETTING.get(dataNodeSettings); Transport transport = internalCluster().getDataNodeInstance(Transport.class); TransportAddress transportAddress = transport.boundAddress().publishAddress(); @@ -102,7 +102,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase .put("xpack.security.audit.enabled", false) .put("path.home", createTempDir()) .put(NetworkModule.HTTP_ENABLED.getKey(), false) - .put(InternalCryptoService.FILE_SETTING.getKey(), systemKeyFile) + .put(CryptoService.FILE_SETTING.getKey(), systemKeyFile) .build(); try (Node node = new MockNode(nodeSettings, Collections.singletonList(XPackPlugin.class))) { node.start(); @@ -112,7 +112,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase public void testThatConnectionToClientTypeConnectionIsRejected() throws IOException { Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class); - String systemKeyFile = InternalCryptoService.FILE_SETTING.get(dataNodeSettings); + String systemKeyFile = CryptoService.FILE_SETTING.get(dataNodeSettings); Path folder = createFolder(createTempDir(), getClass().getSimpleName() + "-" + randomAsciiOfLength(10)); @@ -132,7 +132,7 @@ public class ServerTransportFilterIntegrationTests extends SecurityIntegTestCase .put(SecurityNettyTransport.SSL_SETTING.getKey(), sslTransportEnabled()) .put("xpack.security.audit.enabled", false) .put(NetworkModule.HTTP_ENABLED.getKey(), false) - .put(InternalCryptoService.FILE_SETTING.getKey(), systemKeyFile) + .put(CryptoService.FILE_SETTING.getKey(), systemKeyFile) .put("discovery.initial_state_timeout", "2s") .put("path.home", createTempDir()) .put(Node.NODE_MASTER_SETTING.getKey(), false) diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java index 59a375b863d..411bd6d2199 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack; +import java.io.IOException; import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; @@ -109,18 +110,19 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin { protected Graph graph; protected Notification notification; - public XPackPlugin(Settings settings) { + public XPackPlugin(Settings settings) throws IOException { this.settings = settings; this.transportClientMode = transportClientMode(settings); + final Environment env = transportClientMode ? null : new Environment(settings); + this.licensing = new Licensing(settings); - this.security = new Security(settings); + this.security = new Security(settings, env); this.monitoring = new Monitoring(settings); this.watcher = new Watcher(settings); this.graph = new Graph(settings); this.notification = new Notification(settings); // Check if the node is a transport client. if (transportClientMode == false) { - Environment env = new Environment(settings); this.extensionsService = new XPackExtensionsService(settings, resolveXPackExtensionsFile(env), getExtensions()); } else { this.extensionsService = null; diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java index 9159b2a721f..e7824575e09 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/TimeWarpedXPackPlugin.java @@ -10,8 +10,11 @@ import org.elasticsearch.xpack.support.clock.Clock; import org.elasticsearch.xpack.support.clock.ClockMock; import org.elasticsearch.xpack.watcher.test.TimeWarpedWatcher; +import java.io.IOException; + public class TimeWarpedXPackPlugin extends XPackPlugin { - public TimeWarpedXPackPlugin(Settings settings) { + + public TimeWarpedXPackPlugin(Settings settings) throws IOException { super(settings); watcher = new TimeWarpedWatcher(settings); } diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java index b671984c582..af254eb1a09 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java @@ -32,7 +32,7 @@ import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.security.authc.support.Hasher; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; -import org.elasticsearch.xpack.security.crypto.InternalCryptoService; +import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.TestCluster; @@ -700,7 +700,7 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase .put("xpack.security.authc.realms.esusers.files.users", writeFile(folder, "users", USERS)) .put("xpack.security.authc.realms.esusers.files.users_roles", writeFile(folder, "users_roles", USER_ROLES)) .put(FileRolesStore.ROLES_FILE_SETTING.getKey(), writeFile(folder, "roles.yml", ROLES)) - .put(InternalCryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) + .put(CryptoService.FILE_SETTING.getKey(), writeFile(folder, "system_key.yml", systemKey)) .put("xpack.security.authc.sign_user_header", false) .put("xpack.security.audit.enabled", auditLogsEnabled) .build(); @@ -711,7 +711,7 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase static byte[] generateKey() { try { - return InternalCryptoService.generateKey(); + return CryptoService.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java index e40b45629ee..cbfef411e76 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherExecutorServiceBenchmark.java @@ -22,6 +22,7 @@ import org.elasticsearch.xpack.watcher.trigger.ScheduleTriggerEngineMock; import org.elasticsearch.xpack.watcher.trigger.TriggerModule; import org.elasticsearch.xpack.XPackPlugin; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -204,7 +205,7 @@ public class WatcherExecutorServiceBenchmark { public static final class XPackBenchmarkPlugin extends XPackPlugin { - public XPackBenchmarkPlugin(Settings settings) { + public XPackBenchmarkPlugin(Settings settings) throws IOException { super(settings); watcher = new BenchmarkWatcher(settings); }