Add CryptoService to support signing and encryption operations

This changes the SignatureService into a CryptoService that now supports encryption
operations in addition to the signing operations. The encryption leverages the existing
system key and uses it with AES.

Closes elastic/elasticsearch#805

Original commit: elastic/x-pack-elasticsearch@a792ed4a54
This commit is contained in:
jaymode 2015-04-15 11:44:03 -04:00
parent 39f587a497
commit bff95d9ca1
21 changed files with 619 additions and 347 deletions

View File

@ -14,7 +14,7 @@ import org.elasticsearch.shield.authc.AuthenticationModule;
import org.elasticsearch.shield.authz.AuthorizationModule; import org.elasticsearch.shield.authz.AuthorizationModule;
import org.elasticsearch.shield.license.LicenseModule; import org.elasticsearch.shield.license.LicenseModule;
import org.elasticsearch.shield.rest.ShieldRestModule; import org.elasticsearch.shield.rest.ShieldRestModule;
import org.elasticsearch.shield.signature.SignatureModule; import org.elasticsearch.shield.crypto.CryptoModule;
import org.elasticsearch.shield.ssl.SSLModule; import org.elasticsearch.shield.ssl.SSLModule;
import org.elasticsearch.shield.support.AbstractShieldModule; import org.elasticsearch.shield.support.AbstractShieldModule;
import org.elasticsearch.shield.transport.ShieldTransportModule; import org.elasticsearch.shield.transport.ShieldTransportModule;
@ -42,13 +42,13 @@ public class ShieldModule extends AbstractShieldModule.Spawn {
return ImmutableList.<Module>of( return ImmutableList.<Module>of(
new LicenseModule(settings), new LicenseModule(settings),
new CryptoModule(settings),
new AuthenticationModule(settings), new AuthenticationModule(settings),
new AuthorizationModule(settings), new AuthorizationModule(settings),
new AuditTrailModule(settings), new AuditTrailModule(settings),
new ShieldRestModule(settings), new ShieldRestModule(settings),
new ShieldActionModule(settings), new ShieldActionModule(settings),
new ShieldTransportModule(settings), new ShieldTransportModule(settings),
new SignatureModule(settings),
new SSLModule(settings)); new SSLModule(settings));
} }

View File

@ -20,7 +20,7 @@ import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken; import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.authz.store.FileRolesStore; import org.elasticsearch.shield.authz.store.FileRolesStore;
import org.elasticsearch.shield.license.LicenseService; import org.elasticsearch.shield.license.LicenseService;
import org.elasticsearch.shield.signature.InternalSignatureService; import org.elasticsearch.shield.crypto.InternalCryptoService;
import org.elasticsearch.shield.transport.filter.IPFilter; import org.elasticsearch.shield.transport.filter.IPFilter;
import java.io.File; import java.io.File;
@ -67,7 +67,7 @@ public class ShieldPlugin extends AbstractPlugin {
@Override @Override
public Collection<Class<? extends LifecycleComponent>> services() { public Collection<Class<? extends LifecycleComponent>> services() {
return enabled && !clientMode ? return enabled && !clientMode ?
ImmutableList.<Class<? extends LifecycleComponent>>of(LicenseService.class, FileRolesStore.class, Realms.class, InternalSignatureService.class, IPFilter.class) : ImmutableList.<Class<? extends LifecycleComponent>>of(LicenseService.class, InternalCryptoService.class, FileRolesStore.class, Realms.class, IPFilter.class) :
ImmutableList.<Class<? extends LifecycleComponent>>of(); ImmutableList.<Class<? extends LifecycleComponent>>of();
} }

View File

@ -26,8 +26,8 @@ import org.elasticsearch.shield.authz.AuthorizationService;
import org.elasticsearch.shield.authz.Privilege; import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.license.LicenseEventsNotifier; import org.elasticsearch.shield.license.LicenseEventsNotifier;
import org.elasticsearch.shield.license.LicenseService; import org.elasticsearch.shield.license.LicenseService;
import org.elasticsearch.shield.signature.SignatureException; import org.elasticsearch.shield.crypto.SignatureException;
import org.elasticsearch.shield.signature.SignatureService; import org.elasticsearch.shield.crypto.CryptoService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -41,19 +41,19 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
private final AuthenticationService authcService; private final AuthenticationService authcService;
private final AuthorizationService authzService; private final AuthorizationService authzService;
private final SignatureService signatureService; private final CryptoService cryptoService;
private final AuditTrail auditTrail; private final AuditTrail auditTrail;
private final ShieldActionMapper actionMapper; private final ShieldActionMapper actionMapper;
private volatile boolean licenseEnabled = true; private volatile boolean licenseEnabled = true;
@Inject @Inject
public ShieldActionFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService, SignatureService signatureService, public ShieldActionFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService, CryptoService cryptoService,
AuditTrail auditTrail, LicenseEventsNotifier licenseEventsNotifier, ShieldActionMapper actionMapper) { AuditTrail auditTrail, LicenseEventsNotifier licenseEventsNotifier, ShieldActionMapper actionMapper) {
super(settings); super(settings);
this.authcService = authcService; this.authcService = authcService;
this.authzService = authzService; this.authzService = authzService;
this.signatureService = signatureService; this.cryptoService = cryptoService;
this.auditTrail = auditTrail; this.auditTrail = auditTrail;
this.actionMapper = actionMapper; this.actionMapper = actionMapper;
licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() { licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() {
@ -122,7 +122,7 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
if (request instanceof SearchScrollRequest) { if (request instanceof SearchScrollRequest) {
SearchScrollRequest scrollRequest = (SearchScrollRequest) request; SearchScrollRequest scrollRequest = (SearchScrollRequest) request;
String scrollId = scrollRequest.scrollId(); String scrollId = scrollRequest.scrollId();
scrollRequest.scrollId(signatureService.unsignAndVerify(scrollId)); scrollRequest.scrollId(cryptoService.unsignAndVerify(scrollId));
return request; return request;
} }
@ -133,7 +133,7 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
List<String> signedIds = clearScrollRequest.scrollIds(); List<String> signedIds = clearScrollRequest.scrollIds();
List<String> unsignedIds = new ArrayList<>(signedIds.size()); List<String> unsignedIds = new ArrayList<>(signedIds.size());
for (String signedId : signedIds) { for (String signedId : signedIds) {
unsignedIds.add(signatureService.unsignAndVerify(signedId)); unsignedIds.add(cryptoService.unsignAndVerify(signedId));
} }
clearScrollRequest.scrollIds(unsignedIds); clearScrollRequest.scrollIds(unsignedIds);
} }
@ -153,8 +153,8 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
if (response instanceof SearchResponse) { if (response instanceof SearchResponse) {
SearchResponse searchResponse = (SearchResponse) response; SearchResponse searchResponse = (SearchResponse) response;
String scrollId = searchResponse.getScrollId(); String scrollId = searchResponse.getScrollId();
if (scrollId != null && !signatureService.signed(scrollId)) { if (scrollId != null && !cryptoService.signed(scrollId)) {
searchResponse.scrollId(signatureService.sign(scrollId)); searchResponse.scrollId(cryptoService.sign(scrollId));
} }
return response; return response;
} }

View File

@ -17,7 +17,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.shield.User; import org.elasticsearch.shield.User;
import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.audit.AuditTrail;
import org.elasticsearch.shield.signature.SignatureService; import org.elasticsearch.shield.crypto.CryptoService;
import org.elasticsearch.transport.TransportMessage; import org.elasticsearch.transport.TransportMessage;
import java.io.IOException; import java.io.IOException;
@ -36,18 +36,18 @@ public class InternalAuthenticationService extends AbstractComponent implements
private final Realms realms; private final Realms realms;
private final AuditTrail auditTrail; private final AuditTrail auditTrail;
private final SignatureService signatureService; private final CryptoService cryptoService;
private final boolean signUserHeader; private final boolean signUserHeader;
@Nullable @Nullable
private final User anonymouseUser; private final User anonymouseUser;
@Inject @Inject
public InternalAuthenticationService(Settings settings, Realms realms, AuditTrail auditTrail, SignatureService signatureService) { public InternalAuthenticationService(Settings settings, Realms realms, AuditTrail auditTrail, CryptoService cryptoService) {
super(settings); super(settings);
this.realms = realms; this.realms = realms;
this.auditTrail = auditTrail; this.auditTrail = auditTrail;
this.signatureService = signatureService; this.cryptoService = cryptoService;
this.signUserHeader = componentSettings.getAsBoolean("sign_user_header", true); this.signUserHeader = componentSettings.getAsBoolean("sign_user_header", true);
anonymouseUser = resolveAnonymouseUser(componentSettings); anonymouseUser = resolveAnonymouseUser(componentSettings);
} }
@ -84,13 +84,13 @@ public class InternalAuthenticationService extends AbstractComponent implements
String header = (String) message.getHeader(USER_KEY); String header = (String) message.getHeader(USER_KEY);
if (header != null) { if (header != null) {
if (signUserHeader) { if (signUserHeader) {
header = signatureService.unsignAndVerify(header); header = cryptoService.unsignAndVerify(header);
} }
user = decodeUser(header); user = decodeUser(header);
} }
if (user == null) { if (user == null) {
user = authenticateWithRealms(action, message, fallbackUser); user = authenticateWithRealms(action, message, fallbackUser);
header = signUserHeader ? signatureService.sign(encodeUser(user, logger)) : encodeUser(user, logger); header = signUserHeader ? cryptoService.sign(encodeUser(user, logger)) : encodeUser(user, logger);
message.putHeader(USER_KEY, header); message.putHeader(USER_KEY, header);
} }
message.putInContext(USER_KEY, user); message.putInContext(USER_KEY, user);
@ -104,13 +104,13 @@ public class InternalAuthenticationService extends AbstractComponent implements
} }
User userFromContext = message.getFromContext(USER_KEY); User userFromContext = message.getFromContext(USER_KEY);
if (userFromContext != null) { if (userFromContext != null) {
String userHeader = signUserHeader ? signatureService.sign(encodeUser(userFromContext, logger)) : encodeUser(userFromContext, logger); String userHeader = signUserHeader ? cryptoService.sign(encodeUser(userFromContext, logger)) : encodeUser(userFromContext, logger);
message.putHeader(USER_KEY, userHeader); message.putHeader(USER_KEY, userHeader);
return; return;
} }
message.putInContext(USER_KEY, user); message.putInContext(USER_KEY, user);
String userHeader = signUserHeader ? signatureService.sign(encodeUser(user, logger)) : encodeUser(user, logger); String userHeader = signUserHeader ? cryptoService.sign(encodeUser(user, logger)) : encodeUser(user, logger);
message.putHeader(USER_KEY, userHeader); message.putHeader(USER_KEY, userHeader);
} }

View File

@ -16,7 +16,7 @@ import java.util.Arrays;
*/ */
public class CharArrays { public class CharArrays {
static char[] utf8BytesToChars(byte[] utf8Bytes) { public static char[] utf8BytesToChars(byte[] utf8Bytes) {
ByteBuffer byteBuffer = ByteBuffer.wrap(utf8Bytes); ByteBuffer byteBuffer = ByteBuffer.wrap(utf8Bytes);
CharBuffer charBuffer = Charsets.UTF_8.decode(byteBuffer); CharBuffer charBuffer = Charsets.UTF_8.decode(byteBuffer);
char[] chars = Arrays.copyOfRange(charBuffer.array(), charBuffer.position(), charBuffer.limit()); char[] chars = Arrays.copyOfRange(charBuffer.array(), charBuffer.position(), charBuffer.limit());

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.shield.signature; package org.elasticsearch.shield.crypto;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.support.AbstractShieldModule; import org.elasticsearch.shield.support.AbstractShieldModule;
@ -11,15 +11,15 @@ import org.elasticsearch.shield.support.AbstractShieldModule;
/** /**
* *
*/ */
public class SignatureModule extends AbstractShieldModule.Node { public class CryptoModule extends AbstractShieldModule.Node {
public SignatureModule(Settings settings) { public CryptoModule(Settings settings) {
super(settings); super(settings);
} }
@Override @Override
protected void configureNode() { protected void configureNode() {
bind(InternalSignatureService.class).asEagerSingleton(); bind(InternalCryptoService.class).asEagerSingleton();
bind(SignatureService.class).to(InternalSignatureService.class).asEagerSingleton(); bind(CryptoService.class).to(InternalCryptoService.class).asEagerSingleton();
} }
} }

View File

@ -0,0 +1,60 @@
/*
* 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.shield.crypto;
/**
* Service that provides cryptographic methods based on a shared system key
*/
public interface CryptoService {
/**
* Signs the given text and returns the signed text (original text + signature)
*/
String sign(String 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 SignatureException} is thrown.
*/
String unsignAndVerify(String text);
/**
* Checks whether the given text is signed.
*/
boolean signed(String text);
/**
* Encrypts the provided char array and returns the encrypted values in a Base64 encoded char array
* @param chars the characters to encrypt
* @return Base64 character array representing the encrypted data
* @throws UnsupportedOperationException if the system key is not present
*/
char[] encrypt(char[] chars);
/**
* Encrypts the provided byte array and returns the encrypted value
* @param bytes the data to encrypt
* @return encrypted data
* @throws UnsupportedOperationException if the system key is not present
*/
byte[] encrypt(byte[] bytes);
/**
* Decrypts the provided char array and returns the plain-text chars
* @param chars the Base64 encoded data to decrypt
* @return plaintext chars
* @throws UnsupportedOperationException if the system key is not present
*/
char[] decrypt(char[] chars);
/**
* Decrypts the provided byte array and returns the unencrypted bytes
* @param bytes the bytes to decrypt
* @return plaintext bytes
* @throws UnsupportedOperationException if the system key is not present
*/
byte[] decrypt(byte[] bytes);
}

View File

@ -0,0 +1,365 @@
/*
* 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.shield.crypto;
import org.apache.commons.codec.binary.Base64;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.base.Charsets;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ShieldException;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.ShieldSettingsException;
import org.elasticsearch.shield.authc.support.CharArrays;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.regex.Pattern;
import static org.elasticsearch.shield.authc.support.SecuredString.constantTimeEquals;
/**
*
*/
public class InternalCryptoService extends AbstractLifecycleComponent<InternalCryptoService> implements CryptoService {
public static final String FILE_SETTING = "shield.system_key.file";
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 int DEFAULT_KEY_LENGTH = 128;
private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$.+");
private final Environment env;
private final ResourceWatcherService watcherService;
private final Listener listener;
private final SecureRandom secureRandom = new SecureRandom();
private final String encryptionAlgorithm;
private final String keyAlgorithm;
private final int keyLength;
private final int ivLength;
private Path keyFile;
private volatile SecretKey encryptionKey;
private volatile SecretKey systemKey;
@Inject
public InternalCryptoService(Settings settings, Environment env, ResourceWatcherService watcherService) {
this(settings, env, watcherService, Listener.NOOP);
}
InternalCryptoService(Settings settings, Environment env, ResourceWatcherService watcherService, Listener listener) {
super(settings);
this.env = env;
this.watcherService = watcherService;
this.listener = listener;
this.encryptionAlgorithm = settings.get("shield.encryption.algorithm", DEFAULT_ENCRYPTION_ALGORITHM);
this.keyLength = settings.getAsInt("shield.encryption_key.length", DEFAULT_KEY_LENGTH);
if (keyLength % 8 != 0) {
throw new ShieldSettingsException("invalid key length [" + keyLength + "]. value must be a multiple of 8");
}
this.ivLength = keyLength / 8;
this.keyAlgorithm = settings.get("shield.encryption_key.algorithm", DEFAULT_KEY_ALGORITH);
}
@Override
protected void doStart() throws ElasticsearchException {
keyFile = resolveSystemKey(settings, env);
systemKey = readSystemKey(keyFile);
encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm);
FileWatcher watcher = new FileWatcher(keyFile.getParent().toFile());
watcher.addListener(new FileListener(listener));
watcherService.add(watcher, ResourceWatcherService.Frequency.HIGH);
}
@Override
protected void doStop() throws ElasticsearchException {}
@Override
protected void doClose() throws ElasticsearchException {}
public static byte[] generateKey() throws Exception {
KeyGenerator generator = KeyGenerator.getInstance(KEY_ALGO);
generator.init(KEY_SIZE);
return generator.generateKey().getEncoded();
}
public static Path resolveSystemKey(Settings settings, Environment env) {
String location = settings.get(FILE_SETTING);
if (location == null) {
return ShieldPlugin.resolveConfigFile(env, FILE_NAME);
}
return Paths.get(location);
}
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 ShieldException("could not read secret key", e);
}
}
@Override
public String sign(String text) {
SecretKey key = this.systemKey;
if (key == null) {
return text;
}
String sigStr = signInternal(text);
return "$$" + sigStr.length() + "$$" + sigStr + text;
}
@Override
public String unsignAndVerify(String signedText) {
SecretKey key = this.systemKey;
if (key == null) {
return signedText;
}
if (!signedText.startsWith("$$") || signedText.length() < 2) {
throw new SignatureException("tampered signed text");
}
String text;
String receivedSignature;
try {
// $$34$$sigtext
int i = signedText.indexOf("$$", 2);
int length = Integer.parseInt(signedText.substring(2, i));
receivedSignature = signedText.substring(i + 2, i + 2 + length);
text = signedText.substring(i + 2 + length);
} catch (Throwable t) {
logger.error("error occurred while parsing signed text", t);
throw new SignatureException("tampered signed text");
}
try {
String sig = signInternal(text);
if (constantTimeEquals(sig, receivedSignature)) {
return text;
}
} catch (Throwable t) {
logger.error("error occurred while verifying signed text", t);
throw new SignatureException("error while verifying the signed text");
}
throw new SignatureException("tampered signed text");
}
@Override
public boolean signed(String text) {
return SIG_PATTERN.matcher(text).matches();
}
@Override
public char[] encrypt(char[] chars) {
SecretKey key = this.encryptionKey;
if (key == null) {
throw new UnsupportedOperationException("encryption cannot be performed without a system key. please run bin/shield/syskeygen on one node and copy\n"
+ "the file [" + ShieldPlugin.resolveConfigFile(env, FILE_NAME) + "] to all nodes and the key will be loaded automatically.");
}
byte[] charBytes = CharArrays.toUtf8Bytes(chars);
return Base64.encodeBase64String(encryptInternal(charBytes, key)).toCharArray();
}
@Override
public byte[] encrypt(byte[] bytes) {
SecretKey key = this.encryptionKey;
if (key == null) {
throw new UnsupportedOperationException("encryption cannot be performed without a system key. please run bin/shield/syskeygen on one node and copy\n"
+ "the file [" + ShieldPlugin.resolveConfigFile(env, FILE_NAME) + "] to all nodes and the key will be loaded automatically.");
}
return encryptInternal(bytes, key);
}
@Override
public char[] decrypt(char[] chars) {
SecretKey key = this.encryptionKey;
if (key == null) {
throw new UnsupportedOperationException("decryption cannot be performed without a system key. please run bin/shield/syskeygen on one node and copy\n"
+ "the file [" + ShieldPlugin.resolveConfigFile(env, FILE_NAME) + "] to all nodes and the key will be loaded automatically.");
}
byte[] bytes = Base64.decodeBase64(new String(chars));
byte[] decrypted = decryptInternal(bytes, key);
return CharArrays.utf8BytesToChars(decrypted);
}
@Override
public byte[] decrypt(byte[] bytes) {
SecretKey key = this.encryptionKey;
if (key == null) {
throw new UnsupportedOperationException("decryption cannot be performed without a system key. please run bin/shield/syskeygen on one node and copy\n"
+ "the file [" + ShieldPlugin.resolveConfigFile(env, FILE_NAME) + "] to all nodes and the key will be loaded automatically.");
}
return decryptInternal(bytes, key);
}
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 ShieldException("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 ShieldException("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 ShieldException("error decrypting data", e);
}
}
static Mac createMac(SecretKey key) {
try {
Mac mac = Mac.getInstance(HMAC_ALGO);
mac.init(key);
return mac;
} catch (Exception e) {
throw new ElasticsearchException("could not initialize mac", e);
}
}
private String signInternal(String text) {
Mac mac = createMac(systemKey);
byte[] sig = mac.doFinal(text.getBytes(Charsets.UTF_8));
return Base64.encodeBase64URLSafeString(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 ShieldException("error creating cipher", e);
}
}
static SecretKey encryptionKey(SecretKey systemKey, int keyLength, String algorithm) {
if (systemKey == null) {
return null;
}
try {
byte[] bytes = systemKey.getEncoded();
if ((bytes.length * 8) < keyLength) {
throw new ShieldException("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 ShieldException("requested key length is too large");
}
byte[] truncatedDigest = Arrays.copyOfRange(digest, 0, (keyLength / 8));
return new SecretKeySpec(truncatedDigest, algorithm);
} catch (NoSuchAlgorithmException e) {
throw new ShieldException("error getting encryption key", e);
}
}
private class FileListener extends FileChangesListener {
private final Listener listener;
private FileListener(Listener listener) {
this.listener = listener;
}
@Override
public void onFileCreated(File file) {
if (file.equals(keyFile.toFile())) {
systemKey = readSystemKey(file.toPath());
encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm);
logger.info("system key [{}] has been loaded", file.getAbsolutePath());
listener.onKeyRefresh();
}
}
@Override
public void onFileDeleted(File file) {
if (file.equals(keyFile.toFile())) {
logger.error("system key file was removed! as long as the system key file is missing, elasticsearch " +
"won't function as expected for some requests (e.g. scroll/scan) and won't be able to decrypt\n" +
"previously encrypted values without the original key");
systemKey = null;
encryptionKey = null;
}
}
@Override
public void onFileChanged(File file) {
if (file.equals(keyFile.toFile())) {
logger.warn("system key file changed! previously encrypted values cannot be successfully decrypted with a different key");
systemKey = readSystemKey(file.toPath());
encryptionKey = encryptionKey(systemKey, keyLength, keyAlgorithm);
listener.onKeyRefresh();
}
}
}
static interface Listener {
final Listener NOOP = new Listener() {
@Override
public void onKeyRefresh() {
}
};
void onKeyRefresh();
}
}

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.shield.signature; package org.elasticsearch.shield.crypto;
import org.elasticsearch.shield.authz.AuthorizationException; import org.elasticsearch.shield.authz.AuthorizationException;

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.shield.signature.tool; package org.elasticsearch.shield.crypto.tool;
import org.elasticsearch.common.cli.CheckFileCommand; import org.elasticsearch.common.cli.CheckFileCommand;
import org.elasticsearch.common.cli.CliTool; import org.elasticsearch.common.cli.CliTool;
@ -13,7 +13,7 @@ import org.elasticsearch.common.cli.commons.CommandLine;
import org.elasticsearch.common.collect.Sets; import org.elasticsearch.common.collect.Sets;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.signature.InternalSignatureService; import org.elasticsearch.shield.crypto.InternalCryptoService;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -78,7 +78,7 @@ public class SystemKeyTool extends CliTool {
protected Path[] pathsForPermissionsCheck(Settings settings, Environment env) { protected Path[] pathsForPermissionsCheck(Settings settings, Environment env) {
Path path = this.path; Path path = this.path;
if (path == null) { if (path == null) {
path = InternalSignatureService.resolveFile(settings, env); path = InternalCryptoService.resolveSystemKey(settings, env);
} }
return new Path[] { path }; return new Path[] { path };
} }
@ -88,10 +88,10 @@ public class SystemKeyTool extends CliTool {
Path path = this.path; Path path = this.path;
try { try {
if (path == null) { if (path == null) {
path = InternalSignatureService.resolveFile(settings, env); path = InternalCryptoService.resolveSystemKey(settings, env);
} }
terminal.println(Terminal.Verbosity.VERBOSE, "generating..."); terminal.println(Terminal.Verbosity.VERBOSE, "generating...");
byte[] key = InternalSignatureService.generateKey(); byte[] key = InternalCryptoService.generateKey();
terminal.println("Storing generated key in [%s]...", path.toAbsolutePath()); terminal.println("Storing generated key in [%s]...", path.toAbsolutePath());
Files.write(path, key, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); Files.write(path, key, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
} catch (IOException ioe) { } catch (IOException ioe) {

View File

@ -1,220 +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.shield.signature;
import org.apache.commons.codec.binary.Base64;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.base.Charsets;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ShieldException;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import static org.elasticsearch.shield.authc.support.SecuredString.constantTimeEquals;
/**
*
*/
public class InternalSignatureService extends AbstractLifecycleComponent<InternalSignatureService> implements SignatureService {
public static final String FILE_SETTING = "shield.system_key.file";
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";
private static final Pattern SIG_PATTERN = Pattern.compile("^\\$\\$[0-9]+\\$\\$.+");
private final Environment env;
private final ResourceWatcherService watcherService;
private final Listener listener;
private Path keyFile;
private volatile SecretKey key;
@Inject
public InternalSignatureService(Settings settings, Environment env, ResourceWatcherService watcherService) {
this(settings, env, watcherService, Listener.NOOP);
}
InternalSignatureService(Settings settings, Environment env, ResourceWatcherService watcherService, Listener listener) {
super(settings);
this.env = env;
this.watcherService = watcherService;
this.listener = listener;
}
public static byte[] generateKey() throws Exception {
KeyGenerator generator = KeyGenerator.getInstance(KEY_ALGO);
generator.init(KEY_SIZE);
return generator.generateKey().getEncoded();
}
public static Path resolveFile(Settings settings, Environment env) {
String location = settings.get(FILE_SETTING);
if (location == null) {
return ShieldPlugin.resolveConfigFile(env, FILE_NAME);
}
return Paths.get(location);
}
static SecretKey readKey(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 ShieldException("could not read secret key", e);
}
}
@Override
public String sign(String text) {
SecretKey key = this.key;
if (key == null) {
return text;
}
String sigStr = signInternal(text);
return "$$" + sigStr.length() + "$$" + sigStr + text;
}
@Override
public String unsignAndVerify(String signedText) {
SecretKey key = this.key;
if (key == null) {
return signedText;
}
if (!signedText.startsWith("$$") || signedText.length() < 2) {
throw new SignatureException("tampered signed text");
}
String text;
String receivedSignature;
try {
// $$34$$sigtext
int i = signedText.indexOf("$$", 2);
int length = Integer.parseInt(signedText.substring(2, i));
receivedSignature = signedText.substring(i + 2, i + 2 + length);
text = signedText.substring(i + 2 + length);
} catch (Throwable t) {
logger.error("error occurred while parsing signed text", t);
throw new SignatureException("tampered signed text");
}
try {
String sig = signInternal(text);
if (constantTimeEquals(sig, receivedSignature)) {
return text;
}
} catch (Throwable t) {
logger.error("error occurred while verifying signed text", t);
throw new SignatureException("error while verifying the signed text");
}
throw new SignatureException("tampered signed text");
}
@Override
public boolean signed(String text) {
return SIG_PATTERN.matcher(text).matches();
}
static Mac createMac(SecretKey key) {
try {
Mac mac = Mac.getInstance(HMAC_ALGO);
mac.init(key);
return mac;
} catch (Exception e) {
throw new ElasticsearchException("could not initialize mac", e);
}
}
private String signInternal(String text) {
Mac mac = createMac(key);
byte[] sig = mac.doFinal(text.getBytes(Charsets.UTF_8));
return Base64.encodeBase64URLSafeString(sig);
}
@Override
protected void doStart() throws ElasticsearchException {
keyFile = resolveFile(settings, env);
key = readKey(keyFile);
FileWatcher watcher = new FileWatcher(keyFile.getParent().toFile());
watcher.addListener(new FileListener(listener));
watcherService.add(watcher, ResourceWatcherService.Frequency.HIGH);
}
@Override
protected void doStop() throws ElasticsearchException {}
@Override
protected void doClose() throws ElasticsearchException {}
private class FileListener extends FileChangesListener {
private final Listener listener;
private FileListener(Listener listener) {
this.listener = listener;
}
@Override
public void onFileCreated(File file) {
if (file.equals(keyFile.toFile())) {
key = readKey(file.toPath());
listener.onKeyRefresh();
}
}
@Override
public void onFileDeleted(File file) {
if (file.equals(keyFile.toFile())) {
logger.error("system key file was removed! as long as the system key file is missing, elasticsearch " +
"won't function as expected for some requests (e.g. scroll/scan)");
key = null;
}
}
@Override
public void onFileChanged(File file) {
if (file.equals(keyFile.toFile())) {
key = readKey(file.toPath());
listener.onKeyRefresh();
}
}
}
static interface Listener {
final Listener NOOP = new Listener() {
@Override
public void onKeyRefresh() {
}
};
void onKeyRefresh();
}
}

View File

@ -1,29 +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.shield.signature;
/**
*
*/
public interface SignatureService {
/**
* Signs the given text and returns the signed text (original text + signature)
*/
String sign(String 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 SignatureException} is thrown.
*/
String unsignAndVerify(String text);
/**
* Checks whether the given text is signed.
*/
boolean signed(String text);
}

View File

@ -10,10 +10,9 @@ import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.shield.authz.AuthorizationException; import org.elasticsearch.shield.authz.AuthorizationException;
import org.elasticsearch.shield.signature.InternalSignatureService; import org.elasticsearch.shield.crypto.InternalCryptoService;
import org.elasticsearch.shield.signature.SignatureService; import org.elasticsearch.shield.crypto.CryptoService;
import org.elasticsearch.test.ShieldIntegrationTest; import org.elasticsearch.test.ShieldIntegrationTest;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.Locale; import java.util.Locale;
@ -104,8 +103,8 @@ public class ScrollIdSigningTests extends ShieldIntegrationTest {
} }
private void assertSigned(String scrollId) { private void assertSigned(String scrollId) {
SignatureService signatureService = internalCluster().getDataNodeInstance(InternalSignatureService.class); CryptoService cryptoService = internalCluster().getDataNodeInstance(InternalCryptoService.class);
String message = String.format(Locale.ROOT, "Expected scrollId [%s] to be signed, but was not", scrollId); String message = String.format(Locale.ROOT, "Expected scrollId [%s] to be signed, but was not", scrollId);
assertThat(message, signatureService.signed(scrollId), is(true)); assertThat(message, cryptoService.signed(scrollId), is(true));
} }
} }

View File

@ -16,8 +16,8 @@ import org.elasticsearch.shield.authc.AuthenticationService;
import org.elasticsearch.shield.authz.AuthorizationException; import org.elasticsearch.shield.authz.AuthorizationException;
import org.elasticsearch.shield.authz.AuthorizationService; import org.elasticsearch.shield.authz.AuthorizationService;
import org.elasticsearch.shield.license.LicenseEventsNotifier; import org.elasticsearch.shield.license.LicenseEventsNotifier;
import org.elasticsearch.shield.signature.SignatureException; import org.elasticsearch.shield.crypto.SignatureException;
import org.elasticsearch.shield.signature.SignatureService; import org.elasticsearch.shield.crypto.CryptoService;
import org.elasticsearch.test.ElasticsearchTestCase; import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -34,7 +34,7 @@ public class ShieldActionFilterTests extends ElasticsearchTestCase {
private AuthenticationService authcService; private AuthenticationService authcService;
private AuthorizationService authzService; private AuthorizationService authzService;
private SignatureService signatureService; private CryptoService cryptoService;
private AuditTrail auditTrail; private AuditTrail auditTrail;
private LicenseEventsNotifier licenseEventsNotifier; private LicenseEventsNotifier licenseEventsNotifier;
private ShieldActionFilter filter; private ShieldActionFilter filter;
@ -43,10 +43,10 @@ public class ShieldActionFilterTests extends ElasticsearchTestCase {
public void init() throws Exception { public void init() throws Exception {
authcService = mock(AuthenticationService.class); authcService = mock(AuthenticationService.class);
authzService = mock(AuthorizationService.class); authzService = mock(AuthorizationService.class);
signatureService = mock(SignatureService.class); cryptoService = mock(CryptoService.class);
auditTrail = mock(AuditTrail.class); auditTrail = mock(AuditTrail.class);
licenseEventsNotifier = new MockLicenseEventsNotifier(); licenseEventsNotifier = new MockLicenseEventsNotifier();
filter = new ShieldActionFilter(ImmutableSettings.EMPTY, authcService, authzService, signatureService, auditTrail, licenseEventsNotifier, new ShieldActionMapper()); filter = new ShieldActionFilter(ImmutableSettings.EMPTY, authcService, authzService, cryptoService, auditTrail, licenseEventsNotifier, new ShieldActionMapper());
} }
@Test @Test
@ -83,8 +83,8 @@ public class ShieldActionFilterTests extends ElasticsearchTestCase {
ActionFilterChain chain = mock(ActionFilterChain.class); ActionFilterChain chain = mock(ActionFilterChain.class);
User user = mock(User.class); User user = mock(User.class);
when(authcService.authenticate("_action", request, User.SYSTEM)).thenReturn(user); when(authcService.authenticate("_action", request, User.SYSTEM)).thenReturn(user);
when(signatureService.signed("signed_scroll_id")).thenReturn(true); when(cryptoService.signed("signed_scroll_id")).thenReturn(true);
when(signatureService.unsignAndVerify("signed_scroll_id")).thenReturn("scroll_id"); when(cryptoService.unsignAndVerify("signed_scroll_id")).thenReturn("scroll_id");
filter.apply("_action", request, listener, chain); filter.apply("_action", request, listener, chain);
assertThat(request.scrollId(), equalTo("scroll_id")); assertThat(request.scrollId(), equalTo("scroll_id"));
verify(authzService).authorize(user, "_action", request); verify(authzService).authorize(user, "_action", request);
@ -99,8 +99,8 @@ public class ShieldActionFilterTests extends ElasticsearchTestCase {
SignatureException sigException = new SignatureException("bad bad boy"); SignatureException sigException = new SignatureException("bad bad boy");
User user = mock(User.class); User user = mock(User.class);
when(authcService.authenticate("_action", request, User.SYSTEM)).thenReturn(user); when(authcService.authenticate("_action", request, User.SYSTEM)).thenReturn(user);
when(signatureService.signed("scroll_id")).thenReturn(true); when(cryptoService.signed("scroll_id")).thenReturn(true);
doThrow(sigException).when(signatureService).unsignAndVerify("scroll_id"); doThrow(sigException).when(cryptoService).unsignAndVerify("scroll_id");
filter.apply("_action", request, listener, chain); filter.apply("_action", request, listener, chain);
verify(listener).onFailure(isA(AuthorizationException.class)); verify(listener).onFailure(isA(AuthorizationException.class));
verify(auditTrail).tamperedRequest(user, "_action", request); verify(auditTrail).tamperedRequest(user, "_action", request);

View File

@ -18,7 +18,7 @@ import org.elasticsearch.shield.User;
import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.audit.AuditTrail;
import org.elasticsearch.shield.authc.support.SecuredString; import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken; import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.signature.SignatureService; import org.elasticsearch.shield.crypto.CryptoService;
import org.elasticsearch.test.ElasticsearchTestCase; import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.transport.TransportMessage; import org.elasticsearch.transport.TransportMessage;
import org.junit.Before; import org.junit.Before;
@ -49,7 +49,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
Realm secondRealm; Realm secondRealm;
AuditTrail auditTrail; AuditTrail auditTrail;
AuthenticationToken token; AuthenticationToken token;
SignatureService signatureService; CryptoService cryptoService;
@Before @Before
public void init() throws Exception { public void init() throws Exception {
@ -67,10 +67,10 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
} }
}; };
realms.start(); realms.start();
signatureService = mock(SignatureService.class); cryptoService = mock(CryptoService.class);
auditTrail = mock(AuditTrail.class); auditTrail = mock(AuditTrail.class);
service = new InternalAuthenticationService(ImmutableSettings.EMPTY, realms, auditTrail, signatureService); service = new InternalAuthenticationService(ImmutableSettings.EMPTY, realms, auditTrail, cryptoService);
} }
@Test @SuppressWarnings("unchecked") @Test @SuppressWarnings("unchecked")
@ -116,7 +116,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
service = spy(service); service = spy(service);
doReturn(token).when(service).token("_action", message); doReturn(token).when(service).token("_action", message);
when(signatureService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_encoded_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_encoded_user");
User result = service.authenticate("_action", message, null); User result = service.authenticate("_action", message, null);
assertThat(result, notNullValue()); assertThat(result, notNullValue());
@ -137,7 +137,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
service = spy(service); service = spy(service);
doReturn(token).when(service).token("_action", message); doReturn(token).when(service).token("_action", message);
when(signatureService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_encoded_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_encoded_user");
User result = service.authenticate("_action", message, null); User result = service.authenticate("_action", message, null);
assertThat(result, notNullValue()); assertThat(result, notNullValue());
@ -159,7 +159,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
verifyZeroInteractions(auditTrail); verifyZeroInteractions(auditTrail);
verifyZeroInteractions(firstRealm); verifyZeroInteractions(firstRealm);
verifyZeroInteractions(secondRealm); verifyZeroInteractions(secondRealm);
verifyZeroInteractions(signatureService); verifyZeroInteractions(cryptoService);
assertThat(message.getContext().get(InternalAuthenticationService.USER_KEY), notNullValue()); assertThat(message.getContext().get(InternalAuthenticationService.USER_KEY), notNullValue());
assertThat(message.getContext().get(InternalAuthenticationService.USER_KEY), is((Object) user)); assertThat(message.getContext().get(InternalAuthenticationService.USER_KEY), is((Object) user));
} }
@ -211,7 +211,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
when(firstRealm.token(message)).thenReturn(token); when(firstRealm.token(message)).thenReturn(token);
when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.supports(token)).thenReturn(true);
when(firstRealm.authenticate(token)).thenReturn(user); when(firstRealm.authenticate(token)).thenReturn(user);
when(signatureService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user");
service = spy(service); service = spy(service);
doReturn(token).when(service).token("_action", message); doReturn(token).when(service).token("_action", message);
User result = service.authenticate("_action", message, null); User result = service.authenticate("_action", message, null);
@ -253,7 +253,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
when(firstRealm.token(message)).thenReturn(null); when(firstRealm.token(message)).thenReturn(null);
when(secondRealm.token(message)).thenReturn(null); when(secondRealm.token(message)).thenReturn(null);
User.Simple user1 = new User.Simple("username", "r1", "r2"); User.Simple user1 = new User.Simple("username", "r1", "r2");
when(signatureService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user");
User user2 = service.authenticate("_action", message, user1); User user2 = service.authenticate("_action", message, user1);
assertThat(user1, sameInstance(user2)); assertThat(user1, sameInstance(user2));
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2)); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2));
@ -266,7 +266,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
when(firstRealm.token(message)).thenReturn(token); when(firstRealm.token(message)).thenReturn(token);
when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.supports(token)).thenReturn(true);
when(firstRealm.authenticate(token)).thenReturn(user1); when(firstRealm.authenticate(token)).thenReturn(user1);
when(signatureService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user");
User user2 = service.authenticate("_action", message, null); User user2 = service.authenticate("_action", message, null);
assertThat(user1, sameInstance(user2)); assertThat(user1, sameInstance(user2));
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2)); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2));
@ -279,7 +279,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
when(firstRealm.token(message)).thenReturn(token); when(firstRealm.token(message)).thenReturn(token);
when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.supports(token)).thenReturn(true);
when(firstRealm.authenticate(token)).thenReturn(user1); when(firstRealm.authenticate(token)).thenReturn(user1);
when(signatureService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user");
User user2 = service.authenticate("_action", message, User.SYSTEM); User user2 = service.authenticate("_action", message, User.SYSTEM);
assertThat(user1, sameInstance(user2)); assertThat(user1, sameInstance(user2));
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2)); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2));
@ -303,7 +303,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
when(firstRealm.token(message)).thenReturn(token); when(firstRealm.token(message)).thenReturn(token);
when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.supports(token)).thenReturn(true);
when(firstRealm.authenticate(token)).thenReturn(user1); when(firstRealm.authenticate(token)).thenReturn(user1);
when(signatureService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user");
User user2 = service.authenticate("_action", message, User.SYSTEM); User user2 = service.authenticate("_action", message, User.SYSTEM);
assertThat(user1, sameInstance(user2)); assertThat(user1, sameInstance(user2));
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2)); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user2));
@ -321,7 +321,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
// checking authentication from the user header // checking authentication from the user header
message1.putHeader(InternalAuthenticationService.USER_KEY, message.getHeader(InternalAuthenticationService.USER_KEY)); message1.putHeader(InternalAuthenticationService.USER_KEY, message.getHeader(InternalAuthenticationService.USER_KEY));
when(signatureService.unsignAndVerify("_signed_user")).thenReturn(InternalAuthenticationService.encodeUser(user1, null)); when(cryptoService.unsignAndVerify("_signed_user")).thenReturn(InternalAuthenticationService.encodeUser(user1, null));
BytesStreamOutput output = new BytesStreamOutput(); BytesStreamOutput output = new BytesStreamOutput();
message1.writeTo(output); message1.writeTo(output);
BytesStreamInput input = new BytesStreamInput(output.bytes()); BytesStreamInput input = new BytesStreamInput(output.bytes());
@ -335,7 +335,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
@Test @Test
public void testAutheticate_Transport_ContextAndHeader_NoSigning() throws Exception { public void testAutheticate_Transport_ContextAndHeader_NoSigning() throws Exception {
Settings settings = ImmutableSettings.builder().put("shield.authc.sign_user_header", false).build(); Settings settings = ImmutableSettings.builder().put("shield.authc.sign_user_header", false).build();
service = new InternalAuthenticationService(settings, realms, auditTrail, signatureService); service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService);
User user1 = new User.Simple("username", "r1", "r2"); User user1 = new User.Simple("username", "r1", "r2");
when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.supports(token)).thenReturn(true);
@ -367,7 +367,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
assertThat(user, equalTo(user1)); assertThat(user, equalTo(user1));
verifyZeroInteractions(firstRealm); verifyZeroInteractions(firstRealm);
verifyZeroInteractions(signatureService); verifyZeroInteractions(cryptoService);
} }
@Test @Test
@ -375,7 +375,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
User user = new User.Simple("username", "r1", "r2"); User user = new User.Simple("username", "r1", "r2");
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), nullValue()); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), nullValue());
assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), nullValue()); assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), nullValue());
when(signatureService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user");
service.attachUserHeaderIfMissing(message, user); service.attachUserHeaderIfMissing(message, user);
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user)); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user));
assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user")); assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user"));
@ -384,7 +384,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
message = new InternalMessage(); message = new InternalMessage();
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), nullValue()); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), nullValue());
assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), nullValue()); assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), nullValue());
when(signatureService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user"); when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user");
service.attachUserHeaderIfMissing(message, user); service.attachUserHeaderIfMissing(message, user);
assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user)); assertThat(message.getFromContext(InternalAuthenticationService.USER_KEY), sameInstance((Object) user));
assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user")); assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user"));
@ -437,7 +437,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
if (username != InternalAuthenticationService.ANONYMOUS_USERNAME) { if (username != InternalAuthenticationService.ANONYMOUS_USERNAME) {
builder.put("shield.authc.anonymous.username", username); builder.put("shield.authc.anonymous.username", username);
} }
service = new InternalAuthenticationService(builder.build(), realms, auditTrail, signatureService); service = new InternalAuthenticationService(builder.build(), realms, auditTrail, cryptoService);
RestRequest request = new FakeRestRequest(); RestRequest request = new FakeRestRequest();
@ -454,7 +454,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
Settings settings = ImmutableSettings.builder() Settings settings = ImmutableSettings.builder()
.putArray("shield.authc.anonymous.roles", "r1", "r2", "r3") .putArray("shield.authc.anonymous.roles", "r1", "r2", "r3")
.build(); .build();
service = new InternalAuthenticationService(settings, realms, auditTrail, signatureService); service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService);
InternalMessage message = new InternalMessage(); InternalMessage message = new InternalMessage();
@ -469,7 +469,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
Settings settings = ImmutableSettings.builder() Settings settings = ImmutableSettings.builder()
.putArray("shield.authc.anonymous.roles", "r1", "r2", "r3") .putArray("shield.authc.anonymous.roles", "r1", "r2", "r3")
.build(); .build();
service = new InternalAuthenticationService(settings, realms, auditTrail, signatureService); service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService);
InternalMessage message = new InternalMessage(); InternalMessage message = new InternalMessage();

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.shield.signature; package org.elasticsearch.shield.crypto;
import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
@ -17,17 +17,17 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
/** /**
* *
*/ */
public class InternalSignatureServiceTests extends ElasticsearchTestCase { public class InternalCryptoServiceTests extends ElasticsearchTestCase {
private ResourceWatcherService watcherService; private ResourceWatcherService watcherService;
private Settings settings; private Settings settings;
@ -38,7 +38,7 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
@Before @Before
public void init() throws Exception { public void init() throws Exception {
keyFile = new File(newTempDir(), "system_key"); keyFile = new File(newTempDir(), "system_key");
Streams.copy(InternalSignatureService.generateKey(), keyFile); Streams.copy(InternalCryptoService.generateKey(), keyFile);
settings = ImmutableSettings.builder() settings = ImmutableSettings.builder()
.put("shield.system_key.file", keyFile.getAbsolutePath()) .put("shield.system_key.file", keyFile.getAbsolutePath())
.put("watcher.interval.high", "2s") .put("watcher.interval.high", "2s")
@ -51,12 +51,13 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
@After @After
public void shutdown() throws InterruptedException { public void shutdown() throws InterruptedException {
watcherService.stop();
terminate(threadPool); terminate(threadPool);
} }
@Test @Test
public void testSigned() throws Exception { public void testSigned() throws Exception {
InternalSignatureService service = new InternalSignatureService(settings, env, watcherService).start(); InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
String text = randomAsciiOfLength(10); String text = randomAsciiOfLength(10);
String signed = service.sign(text); String signed = service.sign(text);
assertThat(service.signed(signed), is(true)); assertThat(service.signed(signed), is(true));
@ -64,7 +65,7 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
@Test @Test
public void testSignAndUnsign() throws Exception { public void testSignAndUnsign() throws Exception {
InternalSignatureService service = new InternalSignatureService(settings, env, watcherService).start(); InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
String text = randomAsciiOfLength(10); String text = randomAsciiOfLength(10);
String signed = service.sign(text); String signed = service.sign(text);
assertThat(text.equals(signed), is(false)); assertThat(text.equals(signed), is(false));
@ -74,7 +75,7 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
@Test @Test
public void testSignAndUnsign_NoKeyFile() throws Exception { public void testSignAndUnsign_NoKeyFile() throws Exception {
InternalSignatureService service = new InternalSignatureService(ImmutableSettings.EMPTY, env, watcherService).start(); InternalCryptoService service = new InternalCryptoService(ImmutableSettings.EMPTY, env, watcherService).start();
String text = randomAsciiOfLength(10); String text = randomAsciiOfLength(10);
String signed = service.sign(text); String signed = service.sign(text);
assertThat(text, equalTo(signed)); assertThat(text, equalTo(signed));
@ -84,7 +85,7 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
@Test @Test
public void testTamperedSignature() throws Exception { public void testTamperedSignature() throws Exception {
InternalSignatureService service = new InternalSignatureService(settings, env, watcherService).start(); InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
String text = randomAsciiOfLength(10); String text = randomAsciiOfLength(10);
String signed = service.sign(text); String signed = service.sign(text);
int i = signed.indexOf("$$", 2); int i = signed.indexOf("$$", 2);
@ -102,7 +103,7 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
@Test @Test
public void testTamperedSignatureOneChar() throws Exception { public void testTamperedSignatureOneChar() throws Exception {
InternalSignatureService service = new InternalSignatureService(settings, env, watcherService).start(); InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
String text = randomAsciiOfLength(10); String text = randomAsciiOfLength(10);
String signed = service.sign(text); String signed = service.sign(text);
int i = signed.indexOf("$$", 2); int i = signed.indexOf("$$", 2);
@ -122,7 +123,7 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
@Test @Test
public void testTamperedSignatureLength() throws Exception { public void testTamperedSignatureLength() throws Exception {
InternalSignatureService service = new InternalSignatureService(settings, env, watcherService).start(); InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
String text = randomAsciiOfLength(10); String text = randomAsciiOfLength(10);
String signed = service.sign(text); String signed = service.sign(text);
int i = signed.indexOf("$$", 2); int i = signed.indexOf("$$", 2);
@ -148,10 +149,88 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
} }
} }
@Test
public void testEncryptionAndDecryptionChars() {
InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray();
final char[] encrypted = service.encrypt(chars);
assertThat(encrypted, notNullValue());
assertThat(Arrays.equals(encrypted, chars), is(false));
final char[] decrypted = service.decrypt(encrypted);
assertThat(Arrays.equals(chars, decrypted), is(true));
}
@Test
public void testEncryptionAndDecryptionBytes() {
InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
final byte[] bytes = randomByteArray();
final byte[] encrypted = service.encrypt(bytes);
assertThat(encrypted, notNullValue());
assertThat(Arrays.equals(encrypted, bytes), is(false));
final byte[] decrypted = service.decrypt(encrypted);
assertThat(Arrays.equals(bytes, decrypted), is(true));
}
@Test
public void testEncryptionAndDecryptionCharsWithoutKey() {
InternalCryptoService service = new InternalCryptoService(ImmutableSettings.EMPTY, env, watcherService).start();
final char[] chars = randomAsciiOfLengthBetween(0, 1000).toCharArray();
try {
service.encrypt(chars);
fail("exception should have been thrown");
} catch (Exception e) {
assertThat(e, instanceOf(UnsupportedOperationException.class));
assertThat(e.getMessage(), containsString("system_key"));
}
try {
service.decrypt(chars);
} catch (Exception e) {
assertThat(e, instanceOf(UnsupportedOperationException.class));
assertThat(e.getMessage(), containsString("system_key"));
}
}
@Test
public void testEncryptionAndDecryptionBytesWithoutKey() {
InternalCryptoService service = new InternalCryptoService(ImmutableSettings.EMPTY, env, watcherService).start();
final byte[] bytes = randomByteArray();
try {
service.encrypt(bytes);
fail("exception should have been thrown");
} catch (Exception e) {
assertThat(e, instanceOf(UnsupportedOperationException.class));
assertThat(e.getMessage(), containsString("system_key"));
}
try {
service.decrypt(bytes);
} catch (Exception e) {
assertThat(e, instanceOf(UnsupportedOperationException.class));
assertThat(e.getMessage(), containsString("system_key"));
}
}
@Test
public void testChangingAByte() {
InternalCryptoService service = new InternalCryptoService(settings, env, watcherService).start();
final byte[] bytes = randomByteArray();
final byte[] encrypted = service.encrypt(bytes);
assertThat(encrypted, notNullValue());
assertThat(Arrays.equals(encrypted, bytes), is(false));
int tamperedIndex = randomIntBetween(0, encrypted.length - 1);
encrypted[tamperedIndex] = randomByte();
final byte[] decrypted = service.decrypt(encrypted);
assertThat(Arrays.equals(bytes, decrypted), is(false));
}
@Test @Test
public void testReloadKey() throws Exception { public void testReloadKey() throws Exception {
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
InternalSignatureService service = new InternalSignatureService(settings, env, watcherService, new InternalSignatureService.Listener() { InternalCryptoService service = new InternalCryptoService(settings, env, watcherService, new InternalCryptoService.Listener() {
@Override @Override
public void onKeyRefresh() { public void onKeyRefresh() {
latch.countDown(); latch.countDown();
@ -160,16 +239,34 @@ public class InternalSignatureServiceTests extends ElasticsearchTestCase {
String text = randomAsciiOfLength(10); String text = randomAsciiOfLength(10);
String signed = service.sign(text); String signed = service.sign(text);
char[] textChars = text.toCharArray();
char[] encrypted = service.encrypt(textChars);
// we need to sleep so to ensure the timestamp of the file will definitely change // we need to sleep so to ensure the timestamp of the file will definitely change
// and so the resource watcher will pick up the change. // and so the resource watcher will pick up the change.
sleep(1000); sleep(1000);
Streams.copy(InternalSignatureService.generateKey(), keyFile); Streams.copy(InternalCryptoService.generateKey(), keyFile);
if (!latch.await(10, TimeUnit.SECONDS)) { if (!latch.await(10, TimeUnit.SECONDS)) {
fail("waiting too long for test to complete. Expected callback is not called"); fail("waiting too long for test to complete. Expected callback is not called");
} }
String signed2 = service.sign(text); String signed2 = service.sign(text);
assertThat(signed.equals(signed2), is(false)); assertThat(signed.equals(signed2), is(false));
char[] encrypted2 = service.encrypt(textChars);
char[] decrypted = service.decrypt(encrypted);
char[] decrypted2 = service.decrypt(encrypted2);
assertThat(Arrays.equals(textChars, decrypted), is(false));
assertThat(Arrays.equals(textChars, decrypted2), is(true));
}
private static byte[] randomByteArray() {
int count = randomIntBetween(0, 1000);
byte[] bytes = new byte[count];
for (int i = 0; i < count; i++) {
bytes[i] = randomByte();
}
return bytes;
} }
} }

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.shield.signature.tool; package org.elasticsearch.shield.crypto.tool;
import org.elasticsearch.common.cli.CliTool; import org.elasticsearch.common.cli.CliTool;
import org.elasticsearch.common.cli.CliToolTestCase; import org.elasticsearch.common.cli.CliToolTestCase;
@ -13,7 +13,7 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ShieldPlugin; import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.signature.InternalSignatureService; import org.elasticsearch.shield.crypto.InternalCryptoService;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -24,7 +24,7 @@ import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermission;
import java.util.Set; import java.util.Set;
import static org.elasticsearch.shield.signature.tool.SystemKeyTool.Generate; import static org.elasticsearch.shield.crypto.tool.SystemKeyTool.Generate;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -67,7 +67,7 @@ public class SystemKeyToolTests extends CliToolTestCase {
CliTool.ExitStatus status = generate.execute(ImmutableSettings.EMPTY, env); CliTool.ExitStatus status = generate.execute(ImmutableSettings.EMPTY, env);
assertThat(status, is(CliTool.ExitStatus.OK)); assertThat(status, is(CliTool.ExitStatus.OK));
byte[] bytes = Streams.copyToByteArray(path.toFile()); byte[] bytes = Streams.copyToByteArray(path.toFile());
assertThat(bytes.length, is(InternalSignatureService.KEY_SIZE / 8)); assertThat(bytes.length, is(InternalCryptoService.KEY_SIZE / 8));
} }
@Test @Test
@ -80,7 +80,7 @@ public class SystemKeyToolTests extends CliToolTestCase {
CliTool.ExitStatus status = generate.execute(settings, env); CliTool.ExitStatus status = generate.execute(settings, env);
assertThat(status, is(CliTool.ExitStatus.OK)); assertThat(status, is(CliTool.ExitStatus.OK));
byte[] bytes = Streams.copyToByteArray(path.toFile()); byte[] bytes = Streams.copyToByteArray(path.toFile());
assertThat(bytes.length, is(InternalSignatureService.KEY_SIZE / 8)); assertThat(bytes.length, is(InternalCryptoService.KEY_SIZE / 8));
} }
@Test @Test
@ -94,7 +94,7 @@ public class SystemKeyToolTests extends CliToolTestCase {
CliTool.ExitStatus status = generate.execute(ImmutableSettings.EMPTY, env); CliTool.ExitStatus status = generate.execute(ImmutableSettings.EMPTY, env);
assertThat(status, is(CliTool.ExitStatus.OK)); assertThat(status, is(CliTool.ExitStatus.OK));
byte[] bytes = Streams.copyToByteArray(path.toFile()); byte[] bytes = Streams.copyToByteArray(path.toFile());
assertThat(bytes.length, is(InternalSignatureService.KEY_SIZE / 8)); assertThat(bytes.length, is(InternalCryptoService.KEY_SIZE / 8));
} }
@Test @Test

View File

@ -15,7 +15,7 @@ import org.elasticsearch.discovery.MasterNotDiscoveredException;
import org.elasticsearch.node.Node; import org.elasticsearch.node.Node;
import org.elasticsearch.node.internal.InternalNode; import org.elasticsearch.node.internal.InternalNode;
import org.elasticsearch.shield.authc.esusers.ESUsersRealm; import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
import org.elasticsearch.shield.signature.InternalSignatureService; import org.elasticsearch.shield.crypto.InternalCryptoService;
import org.elasticsearch.test.ShieldIntegrationTest; import org.elasticsearch.test.ShieldIntegrationTest;
import org.elasticsearch.test.ShieldSettingsSource; import org.elasticsearch.test.ShieldSettingsSource;
import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.Transport;
@ -78,7 +78,7 @@ public class ServerTransportFilterIntegrationTests extends ShieldIntegrationTest
@Test @Test
public void testThatConnectionToServerTypeConnectionWorks() { public void testThatConnectionToServerTypeConnectionWorks() {
Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class); Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class);
String systemKeyFile = dataNodeSettings.get(InternalSignatureService.FILE_SETTING); String systemKeyFile = dataNodeSettings.get(InternalCryptoService.FILE_SETTING);
Transport transport = internalCluster().getDataNodeInstance(Transport.class); Transport transport = internalCluster().getDataNodeInstance(Transport.class);
TransportAddress transportAddress = transport.boundAddress().publishAddress(); TransportAddress transportAddress = transport.boundAddress().publishAddress();
@ -98,7 +98,7 @@ public class ServerTransportFilterIntegrationTests extends ShieldIntegrationTest
.put("shield.transport.ssl", sslTransportEnabled()) .put("shield.transport.ssl", sslTransportEnabled())
.put("shield.audit.enabled", false) .put("shield.audit.enabled", false)
.put(InternalNode.HTTP_ENABLED, false) .put(InternalNode.HTTP_ENABLED, false)
.put(InternalSignatureService.FILE_SETTING, systemKeyFile) .put(InternalCryptoService.FILE_SETTING, systemKeyFile)
.build(); .build();
try (Node node = nodeBuilder().client(true).settings(nodeSettings).node()) { try (Node node = nodeBuilder().client(true).settings(nodeSettings).node()) {
assertGreenClusterState(node.client()); assertGreenClusterState(node.client());
@ -108,7 +108,7 @@ public class ServerTransportFilterIntegrationTests extends ShieldIntegrationTest
@Test @Test
public void testThatConnectionToClientTypeConnectionIsRejected() { public void testThatConnectionToClientTypeConnectionIsRejected() {
Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class); Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class);
String systemKeyFile = dataNodeSettings.get(InternalSignatureService.FILE_SETTING); String systemKeyFile = dataNodeSettings.get(InternalCryptoService.FILE_SETTING);
File folder = createFolder(globalTempDir(), getClass().getSimpleName() + "-" + randomAsciiOfLength(10)); File folder = createFolder(globalTempDir(), getClass().getSimpleName() + "-" + randomAsciiOfLength(10));
@ -129,7 +129,7 @@ public class ServerTransportFilterIntegrationTests extends ShieldIntegrationTest
.put("shield.transport.ssl", sslTransportEnabled()) .put("shield.transport.ssl", sslTransportEnabled())
.put("shield.audit.enabled", false) .put("shield.audit.enabled", false)
.put(InternalNode.HTTP_ENABLED, false) .put(InternalNode.HTTP_ENABLED, false)
.put(InternalSignatureService.FILE_SETTING, systemKeyFile) .put(InternalCryptoService.FILE_SETTING, systemKeyFile)
.put("discovery.initial_state_timeout", "2s") .put("discovery.initial_state_timeout", "2s")
.build(); .build();
try (Node node = nodeBuilder().client(true).settings(nodeSettings).build()) { try (Node node = nodeBuilder().client(true).settings(nodeSettings).build()) {

View File

@ -16,7 +16,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken; import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.signature.InternalSignatureService; import org.elasticsearch.shield.crypto.InternalCryptoService;
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport; import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.test.ShieldIntegrationTest; import org.elasticsearch.test.ShieldIntegrationTest;
@ -57,7 +57,7 @@ public class TribeTests extends ShieldIntegrationTest {
final boolean sslTransportEnabled = globalClusterSettings.getAsBoolean("shield.transport.ssl", null); final boolean sslTransportEnabled = globalClusterSettings.getAsBoolean("shield.transport.ssl", null);
//we need to make sure that all clusters and the tribe node use the same system key, we just point to the same file on all clusters //we need to make sure that all clusters and the tribe node use the same system key, we just point to the same file on all clusters
byte[] systemKey = Files.readAllBytes(Paths.get(globalClusterSettings.get(InternalSignatureService.FILE_SETTING))); byte[] systemKey = Files.readAllBytes(Paths.get(globalClusterSettings.get(InternalCryptoService.FILE_SETTING)));
//we run this part in @Before instead of beforeClass because we need to have the current cluster already assigned to global //we run this part in @Before instead of beforeClass because we need to have the current cluster already assigned to global
//so that we can retrieve its settings and apply some of them the the second cluster (and tribe node too) //so that we can retrieve its settings and apply some of them the the second cluster (and tribe node too)
@ -124,7 +124,7 @@ public class TribeTests extends ShieldIntegrationTest {
return true; return true;
} }
//forward the system key to the tribe clients, same file will be used //forward the system key to the tribe clients, same file will be used
if (settingKey.equals(InternalSignatureService.FILE_SETTING)) { if (settingKey.equals(InternalCryptoService.FILE_SETTING)) {
return true; return true;
} }
//forward ssl settings to the tribe clients, same certificates will be used //forward ssl settings to the tribe clients, same certificates will be used

View File

@ -15,7 +15,7 @@ import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.esusers.ESUsersRealm; import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
import org.elasticsearch.shield.authc.support.SecuredString; import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken; import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.signature.InternalSignatureService; import org.elasticsearch.shield.crypto.InternalCryptoService;
import org.elasticsearch.shield.test.ShieldTestUtils; import org.elasticsearch.shield.test.ShieldTestUtils;
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport; import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport; import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
@ -111,7 +111,7 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
ImmutableSettings.Builder builder = ImmutableSettings.builder().put(super.node(nodeOrdinal)) ImmutableSettings.Builder builder = ImmutableSettings.builder().put(super.node(nodeOrdinal))
.put("plugin.types", ShieldPlugin.class.getName() + "," + licensePluginClass().getName()) .put("plugin.types", ShieldPlugin.class.getName() + "," + licensePluginClass().getName())
.put("shield.audit.enabled", randomBoolean()) .put("shield.audit.enabled", randomBoolean())
.put(InternalSignatureService.FILE_SETTING, writeFile(folder, "system_key", systemKey)) .put(InternalCryptoService.FILE_SETTING, writeFile(folder, "system_key", systemKey))
.put("shield.authc.realms.esusers.type", ESUsersRealm.TYPE) .put("shield.authc.realms.esusers.type", ESUsersRealm.TYPE)
.put("shield.authc.realms.esusers.order", 0) .put("shield.authc.realms.esusers.order", 0)
.put("shield.authc.realms.esusers.files.users", writeFile(folder, "users", configUsers())) .put("shield.authc.realms.esusers.files.users", writeFile(folder, "users", configUsers()))
@ -180,7 +180,7 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
private static byte[] generateKey() { private static byte[] generateKey() {
try { try {
return InternalSignatureService.generateKey(); return InternalCryptoService.generateKey();
} catch (Exception e) { } catch (Exception e) {
throw new ElasticsearchException("exception while generating the system key", e); throw new ElasticsearchException("exception while generating the system key", e);
} }