From 1b648a7ab39f3e2e6f350b135760ca7513a1832e Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 20 Aug 2019 10:02:06 +0300 Subject: [PATCH 01/19] POC --- .../repositories/EncryptedRepository.java | 75 +++++++++++++++++++ .../blobstore/BlobStoreRepository.java | 7 ++ 2 files changed, 82 insertions(+) create mode 100644 server/src/main/java/org/elasticsearch/repositories/EncryptedRepository.java diff --git a/server/src/main/java/org/elasticsearch/repositories/EncryptedRepository.java b/server/src/main/java/org/elasticsearch/repositories/EncryptedRepository.java new file mode 100644 index 0000000000000..4612130c5022b --- /dev/null +++ b/server/src/main/java/org/elasticsearch/repositories/EncryptedRepository.java @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.repositories; + +import org.elasticsearch.cluster.metadata.RepositoryMetaData; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.blobstore.BlobStore; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.repositories.blobstore.BlobStoreRepository; + +import java.util.function.Function; + +public class EncryptedRepository extends BlobStoreRepository { + + private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), Setting.Property + .NodeScope); + + private BlobStoreRepository delegatedRepository; + + protected EncryptedRepository(BlobStoreRepository delegatedRepository) { + super(delegatedRepository); + this.delegatedRepository = delegatedRepository; + } + + @Override + protected BlobStore createBlobStore() throws Exception { + // TODO + return null; + } + + /** + * Returns a new source only repository factory + */ + public static Repository.Factory newRepositoryFactory() { + return new Repository.Factory() { + + @Override + public Repository create(RepositoryMetaData metadata) { + throw new UnsupportedOperationException(); + } + + @Override + public Repository create(RepositoryMetaData metaData, Function typeLookup) throws Exception { + String delegateType = DELEGATE_TYPE.get(metaData.settings()); + if (Strings.hasLength(delegateType) == false) { + throw new IllegalArgumentException(DELEGATE_TYPE.getKey() + " must be set"); + } + Repository.Factory factory = typeLookup.apply(delegateType); + Repository delegatedRepository = factory.create(new RepositoryMetaData(metaData.name(), + delegateType, metaData.settings())); + if (false == (delegatedRepository instanceof BlobStoreRepository)) { + throw new IllegalArgumentException("Unsupported type " + DELEGATE_TYPE.getKey()); + } + return new EncryptedRepository((BlobStoreRepository)delegatedRepository); + } + }; + } +} diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 947fdd4dfa952..844fe5f946de6 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -182,6 +182,8 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp protected final ChecksumBlobStoreFormat snapshotFormat; + private final NamedXContentRegistry namedXContentRegistry; + private final boolean readOnly; private final ChecksumBlobStoreFormat indexShardSnapshotFormat; @@ -196,6 +198,10 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp private final BlobPath basePath; + protected BlobStoreRepository(BlobStoreRepository other) { + this(other.metadata, other.settings, other.namedXContentRegistry, other.threadPool, other.basePath); + } + /** * Constructs new BlobStoreRepository * @param metadata The metadata for this repository including name and settings @@ -212,6 +218,7 @@ protected BlobStoreRepository(RepositoryMetaData metadata, Settings settings, Na restoreRateLimiter = getRateLimiter(metadata.settings(), "max_restore_bytes_per_sec", new ByteSizeValue(40, ByteSizeUnit.MB)); readOnly = metadata.settings().getAsBoolean("readonly", false); this.basePath = basePath; + this.namedXContentRegistry = namedXContentRegistry; indexShardSnapshotFormat = new ChecksumBlobStoreFormat<>(SNAPSHOT_CODEC, SNAPSHOT_NAME_FORMAT, BlobStoreIndexShardSnapshot::fromXContent, namedXContentRegistry, compress); From e48c5683070d65cad4462d1d46b0205711d1e614 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 20 Aug 2019 14:27:20 +0300 Subject: [PATCH 02/19] EncryptedRepository lifecycle --- .../snapshots}/EncryptedRepository.java | 53 ++++++++++++++++--- .../elasticsearch/xpack/core/XPackPlugin.java | 5 +- 2 files changed, 50 insertions(+), 8 deletions(-) rename {server/src/main/java/org/elasticsearch/repositories => x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots}/EncryptedRepository.java (67%) diff --git a/server/src/main/java/org/elasticsearch/repositories/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java similarity index 67% rename from server/src/main/java/org/elasticsearch/repositories/EncryptedRepository.java rename to x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 4612130c5022b..d39038ca51bb0 100644 --- a/server/src/main/java/org/elasticsearch/repositories/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -17,20 +17,24 @@ * under the License. */ -package org.elasticsearch.repositories; +package org.elasticsearch.snapshots; import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.blobstore.BlobContainer; +import org.elasticsearch.common.blobstore.BlobPath; import org.elasticsearch.common.blobstore.BlobStore; import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.blobstore.BlobStoreRepository; +import java.io.IOException; import java.util.function.Function; public class EncryptedRepository extends BlobStoreRepository { - private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), Setting.Property - .NodeScope); + private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), + Setting.Property.NodeScope); private BlobStoreRepository delegatedRepository; @@ -41,12 +45,29 @@ protected EncryptedRepository(BlobStoreRepository delegatedRepository) { @Override protected BlobStore createBlobStore() throws Exception { - // TODO - return null; + return new EncryptedBlobStoreDecorator(this.delegatedRepository.blobStore()); + } + + @Override + protected void doStart() { + this.delegatedRepository.start(); + super.doStart(); + } + + @Override + protected void doStop() { + super.doStop(); + this.delegatedRepository.stop(); + } + + @Override + protected void doClose() { + super.doClose(); + this.delegatedRepository.close(); } /** - * Returns a new source only repository factory + * Returns a new encrypted repository factory */ public static Repository.Factory newRepositoryFactory() { return new Repository.Factory() { @@ -72,4 +93,24 @@ public Repository create(RepositoryMetaData metaData, Function getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry, ThreadPool threadPool) { - return Collections.singletonMap("source", SourceOnlySnapshotRepository.newRepositoryFactory()); + return Map.of("source", SourceOnlySnapshotRepository.newRepositoryFactory(), + "encrypted", EncryptedRepository.newRepositoryFactory()); } @Override From 61e4f2a11342cbbf7d27394fc1d4a6cfb95207e4 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 20 Aug 2019 15:33:51 +0300 Subject: [PATCH 03/19] encryptionMetadataBlobPath --- .../snapshots/EncryptedRepository.java | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index d39038ca51bb0..c3c485e522905 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.blobstore.BlobContainer; +import org.elasticsearch.common.blobstore.BlobMetaData; import org.elasticsearch.common.blobstore.BlobPath; import org.elasticsearch.common.blobstore.BlobStore; import org.elasticsearch.common.settings.Setting; @@ -29,12 +30,15 @@ import org.elasticsearch.repositories.blobstore.BlobStoreRepository; import java.io.IOException; +import java.io.InputStream; +import java.util.Map; import java.util.function.Function; public class EncryptedRepository extends BlobStoreRepository { private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), Setting.Property.NodeScope); + private static final String ENCRYPTION_METADATA_PREFIX = "encryption-metadata-"; private BlobStoreRepository delegatedRepository; @@ -109,8 +113,75 @@ public void close() throws IOException { @Override public BlobContainer blobContainer(BlobPath path) { - return this.delegatedBlobStore.blobContainer(path); + BlobPath encryptionMetadataBlobPath = BlobPath.cleanPath(); + encryptionMetadataBlobPath = encryptionMetadataBlobPath.add(ENCRYPTION_METADATA_PREFIX + ""); + for (String pathComponent : path) { + encryptionMetadataBlobPath = encryptionMetadataBlobPath.add(pathComponent); + } + return new EncryptedBlobContainerDecorator(this.delegatedBlobStore.blobContainer(path), + this.delegatedBlobStore.blobContainer(encryptionMetadataBlobPath)); + } + } + + private static class EncryptedBlobContainerDecorator implements BlobContainer { + + private final BlobContainer delegatedBlobContainer; + private final BlobContainer metadataBlobContainer; + + EncryptedBlobContainerDecorator(BlobContainer delegatedBlobContainer, BlobContainer metadataBlobContainer) { + this.delegatedBlobContainer = delegatedBlobContainer; + this.metadataBlobContainer = metadataBlobContainer; + } + + @Override + public BlobPath path() { + return this.delegatedBlobContainer.path(); + } + + @Override + public InputStream readBlob(String blobName) throws IOException { + // TODO + return this.delegatedBlobContainer.readBlob(blobName); + } + + @Override + public void writeBlob(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException { + // TODO + this.delegatedBlobContainer.writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists); + } + + @Override + public void writeBlobAtomic(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) + throws IOException { + // TODO + this.delegatedBlobContainer.writeBlobAtomic(blobName, inputStream, blobSize, failIfAlreadyExists); + } + + @Override + public void deleteBlob(String blobName) throws IOException { + // TODO + this.delegatedBlobContainer.deleteBlob(blobName); + } + + @Override + public void delete() throws IOException { + // TODO + this.delegatedBlobContainer.delete(); } + @Override + public Map listBlobs() throws IOException { + return this.delegatedBlobContainer.listBlobs(); + } + + @Override + public Map children() throws IOException { + return this.delegatedBlobContainer.children(); + } + + @Override + public Map listBlobsByPrefix(String blobNamePrefix) throws IOException { + return this.delegatedBlobContainer.listBlobsByPrefix(blobNamePrefix); + } } } From 5b7f5ead2b51f4e88114d6578107b84efd1dd7b3 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 21 Aug 2019 08:28:21 +0300 Subject: [PATCH 04/19] Almost... --- .../snapshots/EncryptedRepository.java | 94 ++++++++++++++++--- 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index c3c485e522905..0d08741f1b801 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.blobstore.BlobContainer; import org.elasticsearch.common.blobstore.BlobMetaData; import org.elasticsearch.common.blobstore.BlobPath; @@ -31,25 +32,42 @@ import java.io.IOException; import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; import java.util.Map; import java.util.function.Function; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + public class EncryptedRepository extends BlobStoreRepository { private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), Setting.Property.NodeScope); + private static final Setting PASSWORD = new Setting<>("password", "", Function.identity(), + Setting.Property.NodeScope); private static final String ENCRYPTION_METADATA_PREFIX = "encryption-metadata-"; - private BlobStoreRepository delegatedRepository; + private final BlobStoreRepository delegatedRepository; + private final SecretKey masterSecretKey; - protected EncryptedRepository(BlobStoreRepository delegatedRepository) { + protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey) { super(delegatedRepository); this.delegatedRepository = delegatedRepository; + this.masterSecretKey = masterSecretKey; } @Override protected BlobStore createBlobStore() throws Exception { - return new EncryptedBlobStoreDecorator(this.delegatedRepository.blobStore()); + return new EncryptedBlobStoreDecorator(this.delegatedRepository.blobStore(), this.masterSecretKey); } @Override @@ -70,6 +88,36 @@ protected void doClose() { this.delegatedRepository.close(); } + private static SecretKey generateSecretKeyFromPassword(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { + SecureRandom sr = SecureRandom.getInstanceStrong(); + byte[] salt = new byte[16]; + sr.nextBytes(salt); + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128 * 8); + return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec); + } + + private static SecretKey generateRandomSecretKey() throws NoSuchAlgorithmException { + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(128); + return keyGen.generateKey(); + } + + private static String wrapKey(SecretKey toWrap, SecretKey keyWrappingKey) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance("AESWrap"); + cipher.init(Cipher.WRAP_MODE, keyWrappingKey); + byte[] encodedKey = cipher.wrap(toWrap); + return Base64.getEncoder().encodeToString(encodedKey); + } + + private static SecretKey unwrapKey(String toUnwrap, SecretKey keyEncryptionKey) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { + Cipher cipher = Cipher.getInstance("AESWrap"); + cipher.init(Cipher.UNWRAP_MODE, keyEncryptionKey); + byte[] encodedKey = Base64.getDecoder().decode(toUnwrap); + return (SecretKey) cipher.unwrap(encodedKey, "AES", Cipher.SECRET_KEY); + } + /** * Returns a new encrypted repository factory */ @@ -87,13 +135,18 @@ public Repository create(RepositoryMetaData metaData, Function Date: Thu, 22 Aug 2019 10:56:55 +0300 Subject: [PATCH 05/19] Done??? --- .../snapshots/EncryptedRepository.java | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 0d08741f1b801..accdd6f5c2bfb 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -21,26 +21,29 @@ import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.blobstore.BlobContainer; import org.elasticsearch.common.blobstore.BlobMetaData; import org.elasticsearch.common.blobstore.BlobPath; import org.elasticsearch.common.blobstore.BlobStore; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.blobstore.BlobStoreRepository; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; -import java.util.Base64; import java.util.Map; import java.util.function.Function; import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; @@ -102,20 +105,18 @@ private static SecretKey generateRandomSecretKey() throws NoSuchAlgorithmExcepti return keyGen.generateKey(); } - private static String wrapKey(SecretKey toWrap, SecretKey keyWrappingKey) + private static byte[] wrapKey(SecretKey toWrap, SecretKey keyWrappingKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AESWrap"); cipher.init(Cipher.WRAP_MODE, keyWrappingKey); - byte[] encodedKey = cipher.wrap(toWrap); - return Base64.getEncoder().encodeToString(encodedKey); + return cipher.wrap(toWrap); } - private static SecretKey unwrapKey(String toUnwrap, SecretKey keyEncryptionKey) + private static SecretKey unwrapKey(byte[] toUnwrap, SecretKey keyEncryptionKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { Cipher cipher = Cipher.getInstance("AESWrap"); cipher.init(Cipher.UNWRAP_MODE, keyEncryptionKey); - byte[] encodedKey = Base64.getDecoder().decode(toUnwrap); - return (SecretKey) cipher.unwrap(encodedKey, "AES", Cipher.SECRET_KEY); + return (SecretKey) cipher.unwrap(toUnwrap, "AES", Cipher.SECRET_KEY); } /** @@ -198,22 +199,31 @@ public BlobPath path() { @Override public InputStream readBlob(String blobName) throws IOException { - // TODO - return this.delegatedBlobContainer.readBlob(blobName); + final BytesReference dataDecryptionKeyBytes = Streams.readFully(this.encryptionMetadataBlobContainer.readBlob(blobName)); + try { + SecretKey dataDecryptionKey = unwrapKey(BytesReference.toBytes(dataDecryptionKeyBytes), this.masterSecretKey); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, dataDecryptionKey); + return new CipherInputStream(this.delegatedBlobContainer.readBlob(blobName), cipher); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IOException(e); + } } @Override public void writeBlob(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException { try { - SecretKey dataEncryptionSecretKey = generateSecretKeyFromPassword(UUIDs.randomBase64UUID()); + SecretKey dataEncryptionKey = generateRandomSecretKey(); + byte[] wrappedDataEncryptionKey = wrapKey(dataEncryptionKey, this.masterSecretKey); + try (InputStream stream = new ByteArrayInputStream(wrappedDataEncryptionKey)) { + this.encryptionMetadataBlobContainer.writeBlob(blobName, stream, wrappedDataEncryptionKey.length, failIfAlreadyExists); + } Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.WRAP_MODE, this.masterSecretKey); - byte[] encodedKey = cipher.wrap(dataEncryptionSecretKey); - String encodedDataEncryptionKey = Base64.getEncoder().encodeToString(encodedKey); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + cipher.init(Cipher.ENCRYPT_MODE, dataEncryptionKey); + this.delegatedBlobContainer.writeBlob(blobName, new CipherInputStream(inputStream, cipher), blobSize, failIfAlreadyExists); + } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException e) { throw new IOException(e); } - this.delegatedBlobContainer.writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists); } @Override From f7ac3edabbc6982c8829b8263e92068c9c6f2e07 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 23 Aug 2019 01:12:49 +0300 Subject: [PATCH 06/19] Nit --- .../org/elasticsearch/snapshots/EncryptedRepository.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index accdd6f5c2bfb..842a8a3577945 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.blobstore.BlobMetaData; import org.elasticsearch.common.blobstore.BlobPath; import org.elasticsearch.common.blobstore.BlobStore; +import org.elasticsearch.common.blobstore.DeleteResult; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.settings.Setting; @@ -43,7 +44,6 @@ import javax.crypto.Cipher; import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; @@ -240,9 +240,10 @@ public void deleteBlob(String blobName) throws IOException { } @Override - public void delete() throws IOException { - this.delegatedBlobContainer.delete(); + public DeleteResult delete() throws IOException { + DeleteResult result = this.delegatedBlobContainer.delete(); this.encryptionMetadataBlobContainer.delete(); + return result; } @Override From d892c2c0271e2b4b8f26f29830f48ef1eb600a83 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 23 Aug 2019 16:04:39 +0300 Subject: [PATCH 07/19] WORKS! --- .../snapshots/EncryptedRepository.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 842a8a3577945..359407e54793e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.blobstore.BlobStore; import org.elasticsearch.common.blobstore.DeleteResult; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.hash.MessageDigests; import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.repositories.Repository; @@ -35,9 +36,12 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.security.SecureRandom; +import java.security.Security; import java.security.spec.InvalidKeySpecException; import java.util.Map; import java.util.function.Function; @@ -49,7 +53,9 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; public class EncryptedRepository extends BlobStoreRepository { @@ -58,10 +64,13 @@ public class EncryptedRepository extends BlobStoreRepository { private static final Setting PASSWORD = new Setting<>("password", "", Function.identity(), Setting.Property.NodeScope); private static final String ENCRYPTION_METADATA_PREFIX = "encryption-metadata-"; + // always the same IV because the key is generated everytime (Key-IV pair never repeats) + private static final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); private final BlobStoreRepository delegatedRepository; private final SecretKey masterSecretKey; + protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey) { super(delegatedRepository); this.delegatedRepository = delegatedRepository; @@ -95,13 +104,18 @@ private static SecretKey generateSecretKeyFromPassword(String password) throws N SecureRandom sr = SecureRandom.getInstanceStrong(); byte[] salt = new byte[16]; sr.nextBytes(salt); - PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128 * 8); - return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec); + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256); + SecretKey tmp = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(spec); + return new SecretKeySpec(tmp.getEncoded(), "AES"); + } + + private static String keyId(SecretKey secretKey) { + return MessageDigests.toHexString(MessageDigests.sha256().digest(secretKey.getEncoded())); } private static SecretKey generateRandomSecretKey() throws NoSuchAlgorithmException { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(128); + keyGen.init(256); return keyGen.generateKey(); } @@ -170,7 +184,7 @@ public void close() throws IOException { @Override public BlobContainer blobContainer(BlobPath path) { BlobPath encryptionMetadataBlobPath = BlobPath.cleanPath(); - encryptionMetadataBlobPath = encryptionMetadataBlobPath.add(ENCRYPTION_METADATA_PREFIX + ""); + encryptionMetadataBlobPath = encryptionMetadataBlobPath.add(ENCRYPTION_METADATA_PREFIX + keyId(this.masterSecretKey)); for (String pathComponent : path) { encryptionMetadataBlobPath = encryptionMetadataBlobPath.add(pathComponent); } @@ -203,9 +217,9 @@ public InputStream readBlob(String blobName) throws IOException { try { SecretKey dataDecryptionKey = unwrapKey(BytesReference.toBytes(dataDecryptionKeyBytes), this.masterSecretKey); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.DECRYPT_MODE, dataDecryptionKey); + cipher.init(Cipher.DECRYPT_MODE, dataDecryptionKey, gcmParameterSpec); return new CipherInputStream(this.delegatedBlobContainer.readBlob(blobName), cipher); - } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { throw new IOException(e); } } @@ -219,9 +233,10 @@ public void writeBlob(String blobName, InputStream inputStream, long blobSize, b this.encryptionMetadataBlobContainer.writeBlob(blobName, stream, wrappedDataEncryptionKey.length, failIfAlreadyExists); } Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, dataEncryptionKey); + cipher.init(Cipher.ENCRYPT_MODE, dataEncryptionKey, gcmParameterSpec); this.delegatedBlobContainer.writeBlob(blobName, new CipherInputStream(inputStream, cipher), blobSize, failIfAlreadyExists); - } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException e) { + } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException + | InvalidAlgorithmParameterException e) { throw new IOException(e); } } From f1a44de24322faf012719f3c043ed9589b4c4a78 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 26 Aug 2019 18:58:14 +0300 Subject: [PATCH 08/19] Chunk size --- .../org/elasticsearch/snapshots/EncryptedRepository.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 359407e54793e..61ceb01e2de15 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -30,6 +30,7 @@ import org.elasticsearch.common.hash.MessageDigests; import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.blobstore.BlobStoreRepository; @@ -39,9 +40,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.Provider; import java.security.SecureRandom; -import java.security.Security; import java.security.spec.InvalidKeySpecException; import java.util.Map; import java.util.function.Function; @@ -64,7 +63,7 @@ public class EncryptedRepository extends BlobStoreRepository { private static final Setting PASSWORD = new Setting<>("password", "", Function.identity(), Setting.Property.NodeScope); private static final String ENCRYPTION_METADATA_PREFIX = "encryption-metadata-"; - // always the same IV because the key is generated everytime (Key-IV pair never repeats) + // always the same IV because the key is randomly generated anew (Key-IV pair is never repeated) private static final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); private final BlobStoreRepository delegatedRepository; @@ -100,6 +99,10 @@ protected void doClose() { this.delegatedRepository.close(); } + protected ByteSizeValue chunkSize() { + return ByteSizeValue.parseBytesSizeValue("32gb", "encrypted blob store repository max chunk size"); + } + private static SecretKey generateSecretKeyFromPassword(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { SecureRandom sr = SecureRandom.getInstanceStrong(); byte[] salt = new byte[16]; From 7b3eb4d37eb21ada0f5d5eee78e89752c0d67cfd Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 30 Aug 2019 01:52:17 +0300 Subject: [PATCH 09/19] SunJCE mrrrr --- .../org/elasticsearch/snapshots/EncryptedRepository.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 61ceb01e2de15..c5440b95cbc23 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -40,7 +40,6 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.util.Map; import java.util.function.Function; @@ -100,13 +99,11 @@ protected void doClose() { } protected ByteSizeValue chunkSize() { - return ByteSizeValue.parseBytesSizeValue("32gb", "encrypted blob store repository max chunk size"); + return ByteSizeValue.parseBytesSizeValue("16mb", "encrypted blob store repository max chunk size"); } private static SecretKey generateSecretKeyFromPassword(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { - SecureRandom sr = SecureRandom.getInstanceStrong(); - byte[] salt = new byte[16]; - sr.nextBytes(salt); + byte[] salt = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // same salt for 1:1 password to key PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256); SecretKey tmp = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(spec); return new SecretKeySpec(tmp.getEncoded(), "AES"); From 43087e59796d171889f7bcb61f8c704e1a0d39a1 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 2 Sep 2019 12:24:16 +0300 Subject: [PATCH 10/19] Always failIfExists for encryption metadata --- .../repositories/blobstore/BlobStoreRepository.java | 2 +- .../java/org/elasticsearch/snapshots/EncryptedRepository.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 7e042cc7da251..9f33d5889de16 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -200,7 +200,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp private final BlobPath basePath; protected BlobStoreRepository(BlobStoreRepository other) { - this(other.metadata, other.settings, other.namedXContentRegistry, other.threadPool, other.basePath); + this(other.metadata, other.namedXContentRegistry, other.threadPool, other.basePath); } /** diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index c5440b95cbc23..382bd80687473 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -230,7 +230,8 @@ public void writeBlob(String blobName, InputStream inputStream, long blobSize, b SecretKey dataEncryptionKey = generateRandomSecretKey(); byte[] wrappedDataEncryptionKey = wrapKey(dataEncryptionKey, this.masterSecretKey); try (InputStream stream = new ByteArrayInputStream(wrappedDataEncryptionKey)) { - this.encryptionMetadataBlobContainer.writeBlob(blobName, stream, wrappedDataEncryptionKey.length, failIfAlreadyExists); + // failIfAlreadyExists=true for encryption metadata because overwriting metadata is disastrous + this.encryptionMetadataBlobContainer.writeBlob(blobName, stream, wrappedDataEncryptionKey.length, true); } Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, dataEncryptionKey, gcmParameterSpec); From c160245d5b139bf306a429c89fedf59cb6be22a7 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 2 Sep 2019 13:49:36 +0300 Subject: [PATCH 11/19] Parameterize for provider and chunk size --- .../snapshots/EncryptedRepository.java | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 382bd80687473..62a5c5e54709a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -40,6 +40,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.spec.InvalidKeySpecException; import java.util.Map; import java.util.function.Function; @@ -59,20 +60,26 @@ public class EncryptedRepository extends BlobStoreRepository { private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), Setting.Property.NodeScope); - private static final Setting PASSWORD = new Setting<>("password", "", Function.identity(), - Setting.Property.NodeScope); private static final String ENCRYPTION_METADATA_PREFIX = "encryption-metadata-"; // always the same IV because the key is randomly generated anew (Key-IV pair is never repeated) private static final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); + private static final Setting PASSWORD = new Setting<>("password", "", Function.identity()); + private static final Setting CHUNK_SIZE = new Setting<>("chunk_size", "16mb", Function.identity()); + private static final Setting PROVIDER = new Setting<>("provider", "SunJCE", Function.identity()); + private final BlobStoreRepository delegatedRepository; private final SecretKey masterSecretKey; + private final ByteSizeValue chunkSize; + private final String provider; - protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey) { + protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey, String chunkSize, String provider) { super(delegatedRepository); this.delegatedRepository = delegatedRepository; this.masterSecretKey = masterSecretKey; + this.chunkSize = ByteSizeValue.parseBytesSizeValue(chunkSize, "encrypted blob store repository max chunk size"); + this.provider = provider; } @Override @@ -99,7 +106,7 @@ protected void doClose() { } protected ByteSizeValue chunkSize() { - return ByteSizeValue.parseBytesSizeValue("16mb", "encrypted blob store repository max chunk size"); + return this.chunkSize; } private static SecretKey generateSecretKeyFromPassword(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { @@ -161,7 +168,9 @@ public Repository create(RepositoryMetaData metaData, Function Date: Mon, 2 Sep 2019 13:53:03 +0300 Subject: [PATCH 12/19] compile oversight --- .../java/org/elasticsearch/snapshots/EncryptedRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 62a5c5e54709a..00b1fe82bebcf 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -84,7 +84,7 @@ protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey @Override protected BlobStore createBlobStore() throws Exception { - return new EncryptedBlobStoreDecorator(this.delegatedRepository.blobStore(), this.masterSecretKey); + return new EncryptedBlobStoreDecorator(this.delegatedRepository.blobStore(), this.masterSecretKey, this.provider); } @Override From 7345c9b9097fe5dea6d233adb1a8a6c02dc35f1d Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 2 Sep 2019 14:22:54 +0300 Subject: [PATCH 13/19] License --- .../snapshots/EncryptedRepository.java | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index 00b1fe82bebcf..c62075a1f180f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -1,20 +1,7 @@ /* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * 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.snapshots; From 5fd4e618f3c4922faf7e69876b52f7f09079039f Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 2 Sep 2019 18:59:44 +0300 Subject: [PATCH 14/19] Adjust sizes --- .../snapshots/EncryptedRepository.java | 59 +++++++++++-------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java index c62075a1f180f..071f176e850fd 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java @@ -29,6 +29,7 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.spec.InvalidKeySpecException; +import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -39,7 +40,7 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; @@ -47,31 +48,29 @@ public class EncryptedRepository extends BlobStoreRepository { private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), Setting.Property.NodeScope); + private static final int GCM_TAG_BYTES_LENGTH = 16; private static final String ENCRYPTION_METADATA_PREFIX = "encryption-metadata-"; // always the same IV because the key is randomly generated anew (Key-IV pair is never repeated) - private static final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); + //private static final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, new byte[] {0,1,2,3,4,5,6,7,8,9,10,11 }); + private static final IvParameterSpec ivParameterSpec = new IvParameterSpec(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); private static final Setting PASSWORD = new Setting<>("password", "", Function.identity()); - private static final Setting CHUNK_SIZE = new Setting<>("chunk_size", "16mb", Function.identity()); - private static final Setting PROVIDER = new Setting<>("provider", "SunJCE", Function.identity()); + private static final Setting CHUNK_SIZE = new Setting<>("chunk_size", "32gb", Function.identity()); private final BlobStoreRepository delegatedRepository; private final SecretKey masterSecretKey; - private final ByteSizeValue chunkSize; - private final String provider; - protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey, String chunkSize, String provider) { + protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey, String chunkSize) { super(delegatedRepository); this.delegatedRepository = delegatedRepository; this.masterSecretKey = masterSecretKey; this.chunkSize = ByteSizeValue.parseBytesSizeValue(chunkSize, "encrypted blob store repository max chunk size"); - this.provider = provider; } @Override protected BlobStore createBlobStore() throws Exception { - return new EncryptedBlobStoreDecorator(this.delegatedRepository.blobStore(), this.masterSecretKey, this.provider); + return new EncryptedBlobStoreDecorator(this.delegatedRepository.blobStore(), this.masterSecretKey); } @Override @@ -156,8 +155,7 @@ public Repository create(RepositoryMetaData metaData, Function children() throws IOException { @Override public Map listBlobsByPrefix(String blobNamePrefix) throws IOException { - return this.delegatedBlobContainer.listBlobsByPrefix(blobNamePrefix); + Map delegatedBlobs = this.delegatedBlobContainer.listBlobsByPrefix(blobNamePrefix); + Map delegatedBlobsWithPlainSize = new HashMap<>(delegatedBlobs.size()); + for (Map.Entry entry : delegatedBlobs.entrySet()) { + delegatedBlobsWithPlainSize.put(entry.getKey(), new BlobMetaData() { + + @Override + public String name() { + return entry.getValue().name(); + } + + @Override + public long length() { + return entry.getValue().length() - GCM_TAG_BYTES_LENGTH; + } + }); + } + return delegatedBlobsWithPlainSize; } } } From 24378fc22bf6b759330af783be2b066faf2b5017 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 4 Sep 2019 00:21:28 +0300 Subject: [PATCH 15/19] Refactoring in a new plugin WIP --- .../repositories/azure/AzureRepository.java | 2 +- plugins/repository-encrypted/build.gradle | 27 ++++ .../encrypted}/EncryptedRepository.java | 121 ++++++++++-------- .../encrypted/EncryptedRepositoryPlugin.java | 58 +++++++++ .../plugin-metadata/plugin-security.policy | 22 ++++ .../test/repository_encrypted/10_basic.yml | 16 +++ .../gcs/GoogleCloudStorageRepository.java | 2 +- .../repositories/s3/S3Repository.java | 2 +- .../blobstore/BlobStoreRepository.java | 2 +- .../repositories/fs/FsRepository.java | 2 +- .../elasticsearch/xpack/core/XPackPlugin.java | 4 +- 11 files changed, 197 insertions(+), 61 deletions(-) create mode 100644 plugins/repository-encrypted/build.gradle rename {x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots => plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted}/EncryptedRepository.java (82%) create mode 100644 plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepositoryPlugin.java create mode 100644 plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy create mode 100644 plugins/repository-encrypted/src/test/resources/rest-api-spec/test/repository_encrypted/10_basic.yml diff --git a/plugins/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java b/plugins/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java index b5c6ed70ad0d2..3bbe5db68353b 100644 --- a/plugins/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java +++ b/plugins/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java @@ -124,7 +124,7 @@ protected AzureBlobStore createBlobStore() { } @Override - protected ByteSizeValue chunkSize() { + public ByteSizeValue chunkSize() { return chunkSize; } diff --git a/plugins/repository-encrypted/build.gradle b/plugins/repository-encrypted/build.gradle new file mode 100644 index 0000000000000..2765b7f00641c --- /dev/null +++ b/plugins/repository-encrypted/build.gradle @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +esplugin { + description 'The encrypted repository plugin adds support for client-side AES-GCM encrypted repositories.' + classname 'org.elasticsearch.repositories.encrypted.EncryptedRepositoryPlugin' +} + +dependencies { + compile 'org.bouncycastle:bcprov-jdk15on:1.62' +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java b/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java similarity index 82% rename from x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java rename to plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java index 071f176e850fd..3fb46d601870b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/snapshots/EncryptedRepository.java +++ b/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.snapshots; +package org.elasticsearch.repositories.encrypted; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.blobstore.BlobContainer; @@ -16,7 +17,11 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.hash.MessageDigests; import org.elasticsearch.common.io.Streams; +import org.elasticsearch.common.settings.SecureSetting; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.blobstore.BlobStoreRepository; @@ -46,26 +51,28 @@ public class EncryptedRepository extends BlobStoreRepository { - private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity(), - Setting.Property.NodeScope); + static final Setting.AffixSetting ENCRYPTION_PASSWORD_SETTING = Setting.affixKeySetting("repository.encrypted.", + "password", key -> SecureSetting.secureString(key, null)); + + private static final Setting DELEGATE_TYPE = new Setting<>("delegate_type", "", Function.identity()); private static final int GCM_TAG_BYTES_LENGTH = 16; + private static final String ENCRYPTION_MODE = "AES/GCM/NoPadding"; private static final String ENCRYPTION_METADATA_PREFIX = "encryption-metadata-"; // always the same IV because the key is randomly generated anew (Key-IV pair is never repeated) //private static final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, new byte[] {0,1,2,3,4,5,6,7,8,9,10,11 }); private static final IvParameterSpec ivParameterSpec = new IvParameterSpec(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); + // given the mode, the IV and the tag length, the maximum "chunk" size is ~64GB, we set it to 32GB to err on the safe side + public static final ByteSizeValue MAX_CHUNK_SIZE = new ByteSizeValue(32, ByteSizeUnit.GB); - private static final Setting PASSWORD = new Setting<>("password", "", Function.identity()); - private static final Setting CHUNK_SIZE = new Setting<>("chunk_size", "32gb", Function.identity()); + private static final BouncyCastleProvider BC_PROV = new BouncyCastleProvider(); private final BlobStoreRepository delegatedRepository; private final SecretKey masterSecretKey; - private final ByteSizeValue chunkSize; - protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey, String chunkSize) { + protected EncryptedRepository(BlobStoreRepository delegatedRepository, SecretKey masterSecretKey) { super(delegatedRepository); this.delegatedRepository = delegatedRepository; this.masterSecretKey = masterSecretKey; - this.chunkSize = ByteSizeValue.parseBytesSizeValue(chunkSize, "encrypted blob store repository max chunk size"); } @Override @@ -91,45 +98,20 @@ protected void doClose() { this.delegatedRepository.close(); } - protected ByteSizeValue chunkSize() { - return this.chunkSize; - } - - private static SecretKey generateSecretKeyFromPassword(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { - byte[] salt = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // same salt for 1:1 password to key - PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256); - SecretKey tmp = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(spec); - return new SecretKeySpec(tmp.getEncoded(), "AES"); - } - - private static String keyId(SecretKey secretKey) { - return MessageDigests.toHexString(MessageDigests.sha256().digest(secretKey.getEncoded())); - } - - private static SecretKey generateRandomSecretKey() throws NoSuchAlgorithmException { - KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(256); - return keyGen.generateKey(); - } - - private static byte[] wrapKey(SecretKey toWrap, SecretKey keyWrappingKey) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException { - Cipher cipher = Cipher.getInstance("AESWrap"); - cipher.init(Cipher.WRAP_MODE, keyWrappingKey); - return cipher.wrap(toWrap); - } - - private static SecretKey unwrapKey(byte[] toUnwrap, SecretKey keyEncryptionKey) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { - Cipher cipher = Cipher.getInstance("AESWrap"); - cipher.init(Cipher.UNWRAP_MODE, keyEncryptionKey); - return (SecretKey) cipher.unwrap(toUnwrap, "AES", Cipher.SECRET_KEY); + @Override + public ByteSizeValue chunkSize() { + ByteSizeValue delegatedChunkSize = this.delegatedRepository.chunkSize(); + if (delegatedChunkSize == null || delegatedChunkSize.compareTo(MAX_CHUNK_SIZE) > 0) { + return MAX_CHUNK_SIZE; + } else { + return delegatedChunkSize; + } } /** * Returns a new encrypted repository factory */ - public static Repository.Factory newRepositoryFactory() { + public static Repository.Factory newRepositoryFactory(final Settings settings) { return new Repository.Factory() { @Override @@ -143,19 +125,22 @@ public Repository create(RepositoryMetaData metaData, Function encryptionPasswordSetting = ENCRYPTION_PASSWORD_SETTING + .getConcreteSettingForNamespace(metaData.name()); + final SecretKey secretKey; + try (SecureString encryptionPassword = encryptionPasswordSetting.get(settings)) { + if (encryptionPassword.length() == 0) { + throw new IllegalArgumentException(encryptionPasswordSetting.getKey() + " must be set"); + } + secretKey = generateSecretKeyFromPassword(encryptionPassword.getChars()); } - SecretKey secretKey = generateSecretKeyFromPassword(password); Repository.Factory factory = typeLookup.apply(delegateType); Repository delegatedRepository = factory.create(new RepositoryMetaData(metaData.name(), delegateType, metaData.settings())); if (false == (delegatedRepository instanceof BlobStoreRepository)) { throw new IllegalArgumentException("Unsupported type " + DELEGATE_TYPE.getKey()); } - String chunkSize = CHUNK_SIZE.get(metaData.settings()); - return new EncryptedRepository((BlobStoreRepository)delegatedRepository, secretKey, chunkSize); + return new EncryptedRepository((BlobStoreRepository)delegatedRepository, secretKey); } }; } @@ -210,11 +195,10 @@ public InputStream readBlob(String blobName) throws IOException { final BytesReference dataDecryptionKeyBytes = Streams.readFully(this.encryptionMetadataBlobContainer.readBlob(blobName)); try { SecretKey dataDecryptionKey = unwrapKey(BytesReference.toBytes(dataDecryptionKeyBytes), this.masterSecretKey); - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); + Cipher cipher = Cipher.getInstance(ENCRYPTION_MODE, BC_PROV); cipher.init(Cipher.DECRYPT_MODE, dataDecryptionKey, ivParameterSpec); return new CipherInputStream(this.delegatedBlobContainer.readBlob(blobName), cipher); - } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException - | NoSuchProviderException e) { + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { throw new IOException(e); } } @@ -227,12 +211,12 @@ public void writeBlob(String blobName, InputStream inputStream, long blobSize, b try (InputStream stream = new ByteArrayInputStream(wrappedDataEncryptionKey)) { this.encryptionMetadataBlobContainer.writeBlob(blobName, stream, wrappedDataEncryptionKey.length, failIfAlreadyExists); } - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); + Cipher cipher = Cipher.getInstance(ENCRYPTION_MODE, BC_PROV); cipher.init(Cipher.ENCRYPT_MODE, dataEncryptionKey, ivParameterSpec); this.delegatedBlobContainer.writeBlob(blobName, new CipherInputStream(inputStream, cipher), blobSize + GCM_TAG_BYTES_LENGTH, failIfAlreadyExists); } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException - | InvalidAlgorithmParameterException | NoSuchProviderException e) { + | InvalidAlgorithmParameterException e) { throw new IOException(e); } } @@ -288,4 +272,35 @@ public long length() { return delegatedBlobsWithPlainSize; } } + + private static SecretKey generateSecretKeyFromPassword(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException { + byte[] salt = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // same salt for 1:1 password to key + PBEKeySpec spec = new PBEKeySpec(password, salt, 65536, 256); + SecretKey tmp = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(spec); + return new SecretKeySpec(tmp.getEncoded(), "AES"); + } + + private static String keyId(SecretKey secretKey) { + return MessageDigests.toHexString(MessageDigests.sha256().digest(secretKey.getEncoded())); + } + + private static SecretKey generateRandomSecretKey() throws NoSuchAlgorithmException { + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + return keyGen.generateKey(); + } + + private static byte[] wrapKey(SecretKey toWrap, SecretKey keyWrappingKey) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance("AESWrap"); + cipher.init(Cipher.WRAP_MODE, keyWrappingKey); + return cipher.wrap(toWrap); + } + + private static SecretKey unwrapKey(byte[] toUnwrap, SecretKey keyEncryptionKey) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { + Cipher cipher = Cipher.getInstance("AESWrap"); + cipher.init(Cipher.UNWRAP_MODE, keyEncryptionKey); + return (SecretKey) cipher.unwrap(toUnwrap, "AES", Cipher.SECRET_KEY); + } } diff --git a/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepositoryPlugin.java b/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepositoryPlugin.java new file mode 100644 index 0000000000000..8d189cda9ca44 --- /dev/null +++ b/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepositoryPlugin.java @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.repositories.encrypted; + +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.env.Environment; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.ReloadablePlugin; +import org.elasticsearch.plugins.RepositoryPlugin; +import org.elasticsearch.repositories.Repository; +import org.elasticsearch.threadpool.ThreadPool; + +import java.util.List; +import java.util.Map; + +public class EncryptedRepositoryPlugin extends Plugin implements RepositoryPlugin, ReloadablePlugin { + + private final Repository.Factory encryptedRepositoryFactory; + + public EncryptedRepositoryPlugin(final Settings settings) { + encryptedRepositoryFactory = EncryptedRepository.newRepositoryFactory(settings); + } + + @Override + public Map getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry, + ThreadPool threadPool) { + return Map.of("encrypted", encryptedRepositoryFactory); + } + + @Override + public List> getSettings() { + return List.of(EncryptedRepository.ENCRYPTION_PASSWORD_SETTING); + } + + @Override + public void reload(Settings settings) { + // Secure settings should be readable inside this method. + } +} diff --git a/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy b/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy new file mode 100644 index 0000000000000..795c2cd8ef812 --- /dev/null +++ b/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +grant { + permission java.security.SecurityPermission "putProviderProperty.BC" +}; diff --git a/plugins/repository-encrypted/src/test/resources/rest-api-spec/test/repository_encrypted/10_basic.yml b/plugins/repository-encrypted/src/test/resources/rest-api-spec/test/repository_encrypted/10_basic.yml new file mode 100644 index 0000000000000..858ba3e21e3ae --- /dev/null +++ b/plugins/repository-encrypted/src/test/resources/rest-api-spec/test/repository_encrypted/10_basic.yml @@ -0,0 +1,16 @@ +# Integration tests for repository-encrypted +# +"Plugin repository-encrypted is loaded": + - skip: + reason: "contains is a newly added assertion" + features: contains + - do: + cluster.state: {} + + # Get master node id + - set: { master_node: master } + + - do: + nodes.info: {} + + - contains: { nodes.$master.plugins: { name: repository-encrypted } } diff --git a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java index 4b17fd6bef3ea..e5b33b8d5fdf6 100644 --- a/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java +++ b/plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java @@ -94,7 +94,7 @@ protected GoogleCloudStorageBlobStore createBlobStore() { } @Override - protected ByteSizeValue chunkSize() { + public ByteSizeValue chunkSize() { return chunkSize; } diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java index a164900b635e7..b77f0c7d7569d 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java @@ -207,7 +207,7 @@ protected BlobStore getBlobStore() { } @Override - protected ByteSizeValue chunkSize() { + public ByteSizeValue chunkSize() { return chunkSize; } } diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 9f33d5889de16..fc2eb3f02b4ab 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -353,7 +353,7 @@ protected final boolean isCompress() { * * @return chunk size */ - protected ByteSizeValue chunkSize() { + public ByteSizeValue chunkSize() { return null; } diff --git a/server/src/main/java/org/elasticsearch/repositories/fs/FsRepository.java b/server/src/main/java/org/elasticsearch/repositories/fs/FsRepository.java index 61558e4f42efa..c177de9afc6f1 100644 --- a/server/src/main/java/org/elasticsearch/repositories/fs/FsRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/fs/FsRepository.java @@ -109,7 +109,7 @@ protected BlobStore createBlobStore() throws Exception { } @Override - protected ByteSizeValue chunkSize() { + public ByteSizeValue chunkSize() { return chunkSize; } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java index 564cc1f43e07d..099e06c205301 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java @@ -50,7 +50,6 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.snapshots.EncryptedRepository; import org.elasticsearch.snapshots.SourceOnlySnapshotRepository; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -313,8 +312,7 @@ public static Path resolveConfigFile(Environment env, String name) { @Override public Map getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry, ThreadPool threadPool) { - return Map.of("source", SourceOnlySnapshotRepository.newRepositoryFactory(), - "encrypted", EncryptedRepository.newRepositoryFactory()); + return Map.of("source", SourceOnlySnapshotRepository.newRepositoryFactory()); } @Override From c1649c8f209902cd9a11e12699d058e30a31d0a1 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 4 Sep 2019 04:03:21 +0300 Subject: [PATCH 16/19] Works! --- .../encrypted/EncryptedRepository.java | 20 ++++++++++--------- .../plugin-metadata/plugin-security.policy | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java b/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java index 3fb46d601870b..73c01dd7d2f05 100644 --- a/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java +++ b/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java @@ -32,7 +32,6 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.security.spec.InvalidKeySpecException; import java.util.HashMap; import java.util.Map; @@ -112,6 +111,13 @@ public ByteSizeValue chunkSize() { * Returns a new encrypted repository factory */ public static Repository.Factory newRepositoryFactory(final Settings settings) { + final Map cachedRepositoryPasswords = new HashMap<>(); + for (String repositoryName : ENCRYPTION_PASSWORD_SETTING.getNamespaces(settings)) { + Setting encryptionPasswordSetting = ENCRYPTION_PASSWORD_SETTING + .getConcreteSettingForNamespace(repositoryName); + SecureString encryptionPassword = encryptionPasswordSetting.get(settings); + cachedRepositoryPasswords.put(repositoryName, encryptionPassword.getChars()); + } return new Repository.Factory() { @Override @@ -125,15 +131,11 @@ public Repository create(RepositoryMetaData metaData, Function encryptionPasswordSetting = ENCRYPTION_PASSWORD_SETTING - .getConcreteSettingForNamespace(metaData.name()); - final SecretKey secretKey; - try (SecureString encryptionPassword = encryptionPasswordSetting.get(settings)) { - if (encryptionPassword.length() == 0) { - throw new IllegalArgumentException(encryptionPasswordSetting.getKey() + " must be set"); - } - secretKey = generateSecretKeyFromPassword(encryptionPassword.getChars()); + if (false == cachedRepositoryPasswords.containsKey(metaData.name())) { + throw new IllegalArgumentException( + ENCRYPTION_PASSWORD_SETTING.getConcreteSettingForNamespace(metaData.name()).getKey() + " must be set"); } + SecretKey secretKey = generateSecretKeyFromPassword(cachedRepositoryPasswords.get(metaData.name())); Repository.Factory factory = typeLookup.apply(delegateType); Repository delegatedRepository = factory.create(new RepositoryMetaData(metaData.name(), delegateType, metaData.settings())); diff --git a/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy b/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy index 795c2cd8ef812..72b1dfbff537e 100644 --- a/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy +++ b/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy @@ -18,5 +18,5 @@ */ grant { - permission java.security.SecurityPermission "putProviderProperty.BC" + permission java.security.SecurityPermission "putProviderProperty.BC"; }; From ab8d6c64a4571829820caa23d303e4c9681ada66 Mon Sep 17 00:00:00 2001 From: Yogesh Gaikwad Date: Fri, 6 Sep 2019 13:19:43 +1000 Subject: [PATCH 17/19] Changes to move encrypted snapshots code to x-pack module --- plugins/repository-encrypted/build.gradle | 27 ------------------- .../plugin-metadata/plugin-security.policy | 22 --------------- .../repositories/hdfs/HdfsRepository.java | 2 +- .../plugin/repository-encrypted/build.gradle | 15 +++++++++++ .../licenses/bcprov-jdk15on-1.62.jar.sha1 | 1 + .../licenses/bcprov-jdk15on-LICENSE.txt | 12 +++++++++ .../licenses/bcprov-jdk15on-NOTICE.txt | 0 .../encrypted/EncryptedRepository.java | 1 + .../encrypted/EncryptedRepositoryPlugin.java | 19 +++---------- .../plugin-metadata/plugin-security.policy | 9 +++++++ .../encrypted/EncryptedRepositoryTests.java | 14 ++++++++++ .../test/repository_encrypted/10_basic.yml | 0 12 files changed, 56 insertions(+), 66 deletions(-) delete mode 100644 plugins/repository-encrypted/build.gradle delete mode 100644 plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy create mode 100644 x-pack/plugin/repository-encrypted/build.gradle create mode 100644 x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 create mode 100644 x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-LICENSE.txt create mode 100644 x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-NOTICE.txt rename {plugins => x-pack/plugin}/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java (99%) rename {plugins => x-pack/plugin}/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepositoryPlugin.java (64%) create mode 100644 x-pack/plugin/repository-encrypted/src/main/plugin-metadata/plugin-security.policy create mode 100644 x-pack/plugin/repository-encrypted/src/test/java/org/elasticsearch/repositories/encrypted/EncryptedRepositoryTests.java rename {plugins => x-pack/plugin}/repository-encrypted/src/test/resources/rest-api-spec/test/repository_encrypted/10_basic.yml (100%) diff --git a/plugins/repository-encrypted/build.gradle b/plugins/repository-encrypted/build.gradle deleted file mode 100644 index 2765b7f00641c..0000000000000 --- a/plugins/repository-encrypted/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -esplugin { - description 'The encrypted repository plugin adds support for client-side AES-GCM encrypted repositories.' - classname 'org.elasticsearch.repositories.encrypted.EncryptedRepositoryPlugin' -} - -dependencies { - compile 'org.bouncycastle:bcprov-jdk15on:1.62' -} diff --git a/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy b/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy deleted file mode 100644 index 72b1dfbff537e..0000000000000 --- a/plugins/repository-encrypted/src/main/plugin-metadata/plugin-security.policy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -grant { - permission java.security.SecurityPermission "putProviderProperty.BC"; -}; diff --git a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java index 72430bcd36631..dcc3076f03893 100644 --- a/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java +++ b/plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java @@ -233,7 +233,7 @@ protected HdfsBlobStore createBlobStore() { } @Override - protected ByteSizeValue chunkSize() { + public ByteSizeValue chunkSize() { return chunkSize; } } diff --git a/x-pack/plugin/repository-encrypted/build.gradle b/x-pack/plugin/repository-encrypted/build.gradle new file mode 100644 index 0000000000000..c2064ee9e596a --- /dev/null +++ b/x-pack/plugin/repository-encrypted/build.gradle @@ -0,0 +1,15 @@ +evaluationDependsOn(xpackModule('core')) + +apply plugin: 'elasticsearch.esplugin' +esplugin { + name 'repository-encrypted' + description 'Elasticsearch Expanded Pack Plugin - client-side AES-GCM encrypted repositories.' + classname 'org.elasticsearch.repositories.encrypted.EncryptedRepositoryPlugin' + extendedPlugins = ['x-pack-core'] +} + +dependencies { + compile 'org.bouncycastle:bcprov-jdk15on:1.62' +} + +integTest.enabled = false diff --git a/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 b/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 new file mode 100644 index 0000000000000..1ff21fbc8be00 --- /dev/null +++ b/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 @@ -0,0 +1 @@ +633b6739ef8f07f2e71f8eebd1c6f25b17a4ec7d \ No newline at end of file diff --git a/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-LICENSE.txt b/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-LICENSE.txt new file mode 100644 index 0000000000000..e94fe212ff725 --- /dev/null +++ b/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-NOTICE.txt b/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java b/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java similarity index 99% rename from plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java rename to x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java index 73c01dd7d2f05..981614e593e58 100644 --- a/plugins/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java +++ b/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java @@ -131,6 +131,7 @@ public Repository create(RepositoryMetaData metaData, Function Date: Mon, 7 Oct 2019 19:37:20 +0300 Subject: [PATCH 18/19] FIPS libs --- .../plugin/repository-encrypted/build.gradle | 5 ++-- .../licenses/bc-fips-1.0.1.jar.sha1 | 1 + ...dk15on-LICENSE.txt => bc-fips-LICENSE.txt} | 0 ...-jdk15on-NOTICE.txt => bc-fips-NOTICE.txt} | 0 .../licenses/bcpkix-fips-1.0.3.jar.sha1 | 1 + .../licenses/bcpkix-fips-LICENSE.txt | 12 +++++++++ .../licenses/bcpkix-fips-NOTICE.txt | 0 .../licenses/bcprov-jdk15on-1.62.jar.sha1 | 1 - .../encrypted/EncryptedRepository.java | 25 +++++++++---------- 9 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 x-pack/plugin/repository-encrypted/licenses/bc-fips-1.0.1.jar.sha1 rename x-pack/plugin/repository-encrypted/licenses/{bcprov-jdk15on-LICENSE.txt => bc-fips-LICENSE.txt} (100%) rename x-pack/plugin/repository-encrypted/licenses/{bcprov-jdk15on-NOTICE.txt => bc-fips-NOTICE.txt} (100%) create mode 100644 x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-1.0.3.jar.sha1 create mode 100644 x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-LICENSE.txt create mode 100644 x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-NOTICE.txt delete mode 100644 x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 diff --git a/x-pack/plugin/repository-encrypted/build.gradle b/x-pack/plugin/repository-encrypted/build.gradle index c2064ee9e596a..7e0014987d934 100644 --- a/x-pack/plugin/repository-encrypted/build.gradle +++ b/x-pack/plugin/repository-encrypted/build.gradle @@ -3,13 +3,14 @@ evaluationDependsOn(xpackModule('core')) apply plugin: 'elasticsearch.esplugin' esplugin { name 'repository-encrypted' - description 'Elasticsearch Expanded Pack Plugin - client-side AES-GCM encrypted repositories.' + description 'Elasticsearch Expanded Pack Plugin - client-side encrypted repositories.' classname 'org.elasticsearch.repositories.encrypted.EncryptedRepositoryPlugin' extendedPlugins = ['x-pack-core'] } dependencies { - compile 'org.bouncycastle:bcprov-jdk15on:1.62' + compile "org.bouncycastle:bc-fips:1.0.1" + compile "org.bouncycastle:bcpkix-fips:1.0.3" } integTest.enabled = false diff --git a/x-pack/plugin/repository-encrypted/licenses/bc-fips-1.0.1.jar.sha1 b/x-pack/plugin/repository-encrypted/licenses/bc-fips-1.0.1.jar.sha1 new file mode 100644 index 0000000000000..2e4bb227b43bc --- /dev/null +++ b/x-pack/plugin/repository-encrypted/licenses/bc-fips-1.0.1.jar.sha1 @@ -0,0 +1 @@ +ed8dd3144761eaa33b9c56f5e2bef85f1b731d6f \ No newline at end of file diff --git a/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-LICENSE.txt b/x-pack/plugin/repository-encrypted/licenses/bc-fips-LICENSE.txt similarity index 100% rename from x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-LICENSE.txt rename to x-pack/plugin/repository-encrypted/licenses/bc-fips-LICENSE.txt diff --git a/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-NOTICE.txt b/x-pack/plugin/repository-encrypted/licenses/bc-fips-NOTICE.txt similarity index 100% rename from x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-NOTICE.txt rename to x-pack/plugin/repository-encrypted/licenses/bc-fips-NOTICE.txt diff --git a/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-1.0.3.jar.sha1 b/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-1.0.3.jar.sha1 new file mode 100644 index 0000000000000..3262bdf0f3d03 --- /dev/null +++ b/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-1.0.3.jar.sha1 @@ -0,0 +1 @@ +33c47b105777c9dcc8a08188186bd35401366bd1 \ No newline at end of file diff --git a/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-LICENSE.txt b/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-LICENSE.txt new file mode 100644 index 0000000000000..e94fe212ff725 --- /dev/null +++ b/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-NOTICE.txt b/x-pack/plugin/repository-encrypted/licenses/bcpkix-fips-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 b/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 deleted file mode 100644 index 1ff21fbc8be00..0000000000000 --- a/x-pack/plugin/repository-encrypted/licenses/bcprov-jdk15on-1.62.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -633b6739ef8f07f2e71f8eebd1c6f25b17a4ec7d \ No newline at end of file diff --git a/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java b/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java index 981614e593e58..2f0a54dbf09eb 100644 --- a/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java +++ b/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java @@ -6,7 +6,7 @@ package org.elasticsearch.repositories.encrypted; -import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.blobstore.BlobContainer; @@ -26,17 +26,6 @@ import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.blobstore.BlobStoreRepository; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.IllegalBlockSizeException; @@ -47,6 +36,16 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; public class EncryptedRepository extends BlobStoreRepository { @@ -63,7 +62,7 @@ public class EncryptedRepository extends BlobStoreRepository { // given the mode, the IV and the tag length, the maximum "chunk" size is ~64GB, we set it to 32GB to err on the safe side public static final ByteSizeValue MAX_CHUNK_SIZE = new ByteSizeValue(32, ByteSizeUnit.GB); - private static final BouncyCastleProvider BC_PROV = new BouncyCastleProvider(); + private static final BouncyCastleFipsProvider BC_PROV = new BouncyCastleFipsProvider(); private final BlobStoreRepository delegatedRepository; private final SecretKey masterSecretKey; From b483b57989e9d7790e3c6afa0637040f3a805f5e Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 10 Oct 2019 02:43:47 +0300 Subject: [PATCH 19/19] Straight GCM but with the bc-fips lib --- .../repositories/encrypted/EncryptedRepository.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java b/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java index 2f0a54dbf09eb..bb1317d73260d 100644 --- a/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java +++ b/x-pack/plugin/repository-encrypted/src/main/java/org/elasticsearch/repositories/encrypted/EncryptedRepository.java @@ -62,7 +62,7 @@ public class EncryptedRepository extends BlobStoreRepository { // given the mode, the IV and the tag length, the maximum "chunk" size is ~64GB, we set it to 32GB to err on the safe side public static final ByteSizeValue MAX_CHUNK_SIZE = new ByteSizeValue(32, ByteSizeUnit.GB); - private static final BouncyCastleFipsProvider BC_PROV = new BouncyCastleFipsProvider(); + private static final BouncyCastleFipsProvider BC_FIPS_PROV = new BouncyCastleFipsProvider(); private final BlobStoreRepository delegatedRepository; private final SecretKey masterSecretKey; @@ -197,7 +197,7 @@ public InputStream readBlob(String blobName) throws IOException { final BytesReference dataDecryptionKeyBytes = Streams.readFully(this.encryptionMetadataBlobContainer.readBlob(blobName)); try { SecretKey dataDecryptionKey = unwrapKey(BytesReference.toBytes(dataDecryptionKeyBytes), this.masterSecretKey); - Cipher cipher = Cipher.getInstance(ENCRYPTION_MODE, BC_PROV); + Cipher cipher = Cipher.getInstance(ENCRYPTION_MODE, BC_FIPS_PROV); cipher.init(Cipher.DECRYPT_MODE, dataDecryptionKey, ivParameterSpec); return new CipherInputStream(this.delegatedBlobContainer.readBlob(blobName), cipher); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { @@ -213,7 +213,7 @@ public void writeBlob(String blobName, InputStream inputStream, long blobSize, b try (InputStream stream = new ByteArrayInputStream(wrappedDataEncryptionKey)) { this.encryptionMetadataBlobContainer.writeBlob(blobName, stream, wrappedDataEncryptionKey.length, failIfAlreadyExists); } - Cipher cipher = Cipher.getInstance(ENCRYPTION_MODE, BC_PROV); + Cipher cipher = Cipher.getInstance(ENCRYPTION_MODE, BC_FIPS_PROV); cipher.init(Cipher.ENCRYPT_MODE, dataEncryptionKey, ivParameterSpec); this.delegatedBlobContainer.writeBlob(blobName, new CipherInputStream(inputStream, cipher), blobSize + GCM_TAG_BYTES_LENGTH, failIfAlreadyExists); @@ -287,7 +287,7 @@ private static String keyId(SecretKey secretKey) { } private static SecretKey generateRandomSecretKey() throws NoSuchAlgorithmException { - KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + KeyGenerator keyGen = KeyGenerator.getInstance("AES", BC_FIPS_PROV); keyGen.init(256); return keyGen.generateKey(); }