IGNITE-7054 - S3 IP finder: support client side encryption - Fixes #4555.
authoruday <udaygkale@gmail.com>
Thu, 27 Dec 2018 00:07:09 +0000 (16:07 -0800)
committerValentin Kulichenko <valentin.kulichenko@gmail.com>
Thu, 27 Dec 2018 00:07:09 +0000 (16:07 -0800)
19 files changed:
modules/aws/pom.xml
modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/TcpDiscoveryS3IpFinder.java
modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AsymmetricKeyEncryptionService.java [new file with mode: 0644]
modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AwsKmsEncryptionService.java [new file with mode: 0644]
modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/EncryptionService.java [new file with mode: 0644]
modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/SymmetricKeyEncryptionService.java [new file with mode: 0644]
modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/package-info.java [new file with mode: 0644]
modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/package-info.java
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/TcpDiscoveryS3IpFinderAbstractSelfTest.java
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/TcpDiscoveryS3IpFinderClientSideEncryptionSelfTest.java [new file with mode: 0644]
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AsymmetricKeyEncryptionServiceTest.java [new file with mode: 0644]
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AwsKmsEncryptionServiceTest.java [new file with mode: 0644]
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/MockEncryptionService.java [new file with mode: 0644]
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/MockEncryptionServiceTest.java [new file with mode: 0644]
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/SymmetricKeyEncryptionServiceTest.java [new file with mode: 0644]
modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/package-info.java [new file with mode: 0644]
modules/aws/src/test/java/org/apache/ignite/testsuites/IgniteS3TestSuite.java
modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
parent/pom.xml

index 9168ebc..18d0cf4 100644 (file)
         </dependency>
 
         <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-encryption-sdk-java</artifactId>
+            <version>${aws.encryption.sdk.version}</version>
+        </dependency>
+
+        <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
             <version>2.8.1</version>
index f5b2f51..5eecc02 100644 (file)
 
 package org.apache.ignite.spi.discovery.tcp.ipfinder.s3;
 
+import java.io.ByteArrayInputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
 import com.amazonaws.AmazonClientException;
 import com.amazonaws.ClientConfiguration;
 import com.amazonaws.auth.AWSCredentials;
@@ -26,14 +35,7 @@ import com.amazonaws.services.s3.AmazonS3Client;
 import com.amazonaws.services.s3.model.ObjectListing;
 import com.amazonaws.services.s3.model.ObjectMetadata;
 import com.amazonaws.services.s3.model.S3ObjectSummary;
-import java.io.ByteArrayInputStream;
-import java.net.InetSocketAddress;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.StringTokenizer;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.regex.Pattern;
+import org.apache.commons.codec.binary.Base32;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -45,6 +47,7 @@ import org.apache.ignite.resources.LoggerResource;
 import org.apache.ignite.spi.IgniteSpiConfiguration;
 import org.apache.ignite.spi.IgniteSpiException;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinderAdapter;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.EncryptionService;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -54,31 +57,31 @@ import org.jetbrains.annotations.Nullable;
  * <h1 class="header">Configuration</h1>
  * <h2 class="header">Mandatory</h2>
  * <ul>
- *      <li>AWS credentials (see {@link #setAwsCredentials(AWSCredentials)} and
- *      {@link #setAwsCredentialsProvider(AWSCredentialsProvider)}</li>
- *      <li>Bucket name (see {@link #setBucketName(String)})</li>
+ * <li>AWS credentials (see {@link #setAwsCredentials(AWSCredentials)} and
+ * {@link #setAwsCredentialsProvider(AWSCredentialsProvider)}</li>
+ * <li>Bucket name (see {@link #setBucketName(String)})</li>
  * </ul>
  * <h2 class="header">Optional</h2>
  * <ul>
- *      <li>Client configuration (see {@link #setClientConfiguration(ClientConfiguration)})</li>
- *      <li>Shared flag (see {@link #setShared(boolean)})</li>
- *      <li>Bucket endpoint (see {@link #setBucketEndpoint(String)})</li>
- *      <li>Server side encryption algorithm (see {@link #setSSEAlgorithm(String)})</li>
- *      <li>Server side encryption algorithm (see {@link #setKeyPrefix(String)})</li>
+ * <li>Client configuration (see {@link #setClientConfiguration(ClientConfiguration)})</li>
+ * <li>Shared flag (see {@link #setShared(boolean)})</li>
+ * <li>Bucket endpoint (see {@link #setBucketEndpoint(String)})</li>
+ * <li>Server side encryption algorithm (see {@link #setSSEAlgorithm(String)})</li>
+ * <li>Key prefix for the node addresses (see {@link #setKeyPrefix(String)})</li>
+ * <li>Client side encryption service (see {@link #setEncryptionService(EncryptionService)})</li>
  * </ul>
  * <p>
- * The finder will create S3 bucket with configured name. The bucket will contain entries named
- * like the following: {@code 192.168.1.136#1001}.
+ * The finder will create S3 bucket with configured name. The bucket will contain entries named like the following:
+ * {@code 192.168.1.136#1001}.
  * <p>
- * Note that storing data in AWS S3 service will result in charges to your AWS account.
- * Choose another implementation of {@link org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder} for local
- * or home network tests.
+ * Note that storing data in AWS S3 service will result in charges to your AWS account. Choose another implementation of
+ * {@link org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder} for local or home network tests.
  * <p>
  * Note that this finder is shared by default (see {@link org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder#isShared()}.
  */
 public class TcpDiscoveryS3IpFinder extends TcpDiscoveryIpFinderAdapter {
     /** Delimiter to use in S3 entries name. */
-    public static final String DELIM = "#";
+    private static final String DELIM = "#";
 
     /** Entry content. */
     private static final byte[] ENTRY_CONTENT = new byte[] {1};
@@ -107,6 +110,9 @@ public class TcpDiscoveryS3IpFinder extends TcpDiscoveryIpFinderAdapter {
     /** Sub-folder name to write node addresses. */
     @Nullable private String keyPrefix;
 
+    /** Encryption service. **/
+    @Nullable private EncryptionService encryptionSvc;
+
     /** Init guard. */
     @GridToStringExclude
     private final AtomicBoolean initGuard = new AtomicBoolean();
@@ -140,46 +146,14 @@ public class TcpDiscoveryS3IpFinder extends TcpDiscoveryIpFinderAdapter {
         Collection<InetSocketAddress> addrs = new LinkedList<>();
 
         try {
-            ObjectListing list;
-
-            if (keyPrefix == null)
-                list = s3.listObjects(bucketName);
-            else
-                list = s3.listObjects(bucketName, keyPrefix);
+            ObjectListing list = keyPrefix == null ? s3.listObjects(bucketName) : s3.listObjects(bucketName, keyPrefix);
 
             while (true) {
                 for (S3ObjectSummary sum : list.getObjectSummaries()) {
-                    String key = sum.getKey();
-                    String addr = key;
-
-                    if (keyPrefix != null)
-                        addr = key.replaceFirst(Pattern.quote(keyPrefix), "");
-
-                    StringTokenizer st = new StringTokenizer(addr, DELIM);
-
-                    if (st.countTokens() != 2)
-                        U.error(log, "Failed to parse S3 entry due to invalid format: " + addr);
-                    else {
-                        String addrStr = st.nextToken();
-                        String portStr = st.nextToken();
-
-                        int port = -1;
+                    InetSocketAddress addr = addr(sum);
 
-                        try {
-                            port = Integer.parseInt(portStr);
-                        }
-                        catch (NumberFormatException e) {
-                            U.error(log, "Failed to parse port for S3 entry: " + addr, e);
-                        }
-
-                        if (port != -1)
-                            try {
-                                addrs.add(new InetSocketAddress(addrStr, port));
-                            }
-                            catch (IllegalArgumentException e) {
-                                U.error(log, "Failed to parse port for S3 entry: " + addr, e);
-                            }
-                    }
+                    if (addr != null)
+                        addrs.add(addr);
                 }
 
                 if (list.isTruncated())
@@ -195,6 +169,53 @@ public class TcpDiscoveryS3IpFinder extends TcpDiscoveryIpFinderAdapter {
         return addrs;
     }
 
+    /**
+     * Parses the S3 key to return the ip and addresses.
+     *
+     * @param sum S3 Object summary.
+     */
+    private InetSocketAddress addr(S3ObjectSummary sum) {
+        String key = sum.getKey();
+        String addr = key;
+
+        if (keyPrefix != null)
+            addr = key.replaceFirst(Pattern.quote(keyPrefix), "");
+
+        if (encryptionSvc != null) {
+            byte[] encBytes = new Base32().decode(addr.getBytes(StandardCharsets.UTF_8));
+            byte[] decBytes = encryptionSvc.decrypt(encBytes);
+            addr = new String(decBytes, StandardCharsets.UTF_8).replaceAll("=", "");
+        }
+
+        StringTokenizer st = new StringTokenizer(addr, DELIM);
+
+        if (st.countTokens() != 2)
+            U.error(log, "Failed to parse S3 entry due to invalid format: " + addr);
+        else {
+            String addrStr = st.nextToken();
+            String portStr = st.nextToken();
+
+            int port = -1;
+
+            try {
+                port = Integer.parseInt(portStr);
+            }
+            catch (NumberFormatException e) {
+                U.error(log, "Failed to parse port for S3 entry: " + addr, e);
+            }
+
+            if (port != -1)
+                try {
+                    return new InetSocketAddress(addrStr, port);
+                }
+                catch (IllegalArgumentException e) {
+                    U.error(log, "Failed to parse port for S3 entry: " + addr, e);
+                }
+        }
+
+        return null;
+    }
+
     /** {@inheritDoc} */
     @Override public void registerAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
         assert !F.isEmpty(addrs);
@@ -244,14 +265,27 @@ public class TcpDiscoveryS3IpFinder extends TcpDiscoveryIpFinderAdapter {
 
         SB sb = new SB();
 
-        String addrStr = addr.getAddress().getHostAddress();
-
         if (keyPrefix != null)
             sb.a(keyPrefix);
 
-        sb.a(addrStr)
-            .a(DELIM)
-            .a(addr.getPort());
+        String addrStr = addr.getAddress().getHostAddress();
+
+        if (encryptionSvc != null) {
+            String addrPort = new SB()
+                .a(addrStr)
+                .a(DELIM)
+                .a(addr.getPort()).toString();
+
+            byte[] encBytes = encryptionSvc.encrypt(addrPort.getBytes(StandardCharsets.UTF_8));
+            byte[] base32Bytes = new Base32().encode(encBytes);
+            String encStr = new String(base32Bytes, StandardCharsets.UTF_8).replaceAll("=", "");
+
+            sb.a(encStr);
+        }
+        else
+            sb.a(addrStr)
+                .a(DELIM)
+                .a(addr.getPort());
 
         return sb.toString();
     }
@@ -395,6 +429,19 @@ public class TcpDiscoveryS3IpFinder extends TcpDiscoveryIpFinderAdapter {
     }
 
     /**
+     * Sets encryption service for client side node address encryption.
+     *
+     * @param encryptionSvc Encryption service .
+     * @return {@code this} for chaining.
+     */
+    @IgniteSpiConfiguration(optional = true)
+    public TcpDiscoveryS3IpFinder setEncryptionService(EncryptionService encryptionSvc) {
+        this.encryptionSvc = encryptionSvc;
+
+        return this;
+    }
+
+    /**
      * Sets AWS credentials. Either use {@link #setAwsCredentialsProvider(AWSCredentialsProvider)} or this one.
      * <p>
      * For details refer to Amazon S3 API reference.
diff --git a/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AsymmetricKeyEncryptionService.java b/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AsymmetricKeyEncryptionService.java
new file mode 100644 (file)
index 0000000..f85a906
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.security.Key;
+import java.security.KeyPair;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Provides an implementation of asymmetric encryption to encrypt/decrypt the data.
+ */
+public class AsymmetricKeyEncryptionService implements EncryptionService {
+    /** Public key. */
+    private Key publicKey;
+
+    /** Private key. */
+    private Key privateKey;
+
+    /** Encryption service. */
+    private Cipher encCipher;
+
+    /** Decryption service. */
+    private Cipher decCipher;
+
+    /**
+     * Set the public private key pair.
+     *
+     * @param keyPair Key pair of Public and Private key.
+     */
+    public void setKeyPair(KeyPair keyPair) {
+        if (keyPair.getPublic() == null)
+            throw new IgniteException("Public key was not set / was set to null.");
+
+        if (keyPair.getPrivate() == null)
+            throw new IgniteException("Private key was not set / was set to null.");
+
+        publicKey = keyPair.getPublic();
+        privateKey = keyPair.getPrivate();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void init() throws IgniteException {
+        if (privateKey == null)
+            throw new IgniteException("Private key was not set / was set to null.");
+
+        if (publicKey == null)
+            throw new IgniteException("Public key was not set / was set to null.");
+
+        encCipher = IgniteUtils.createCipher(privateKey, Cipher.ENCRYPT_MODE);
+        decCipher = IgniteUtils.createCipher(publicKey, Cipher.DECRYPT_MODE);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] encrypt(byte[] data) {
+        if (data == null)
+            throw new IgniteException("Parameter data cannot be null");
+
+        if (encCipher == null)
+            throw new IgniteException("The init() method was not called.");
+
+        try {
+            return encCipher.doFinal(data);
+        }
+        catch (IllegalBlockSizeException | BadPaddingException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] decrypt(byte[] data) {
+        if (data == null)
+            throw new IgniteException("Parameter data cannot be null");
+
+        if (decCipher == null)
+            throw new IgniteException("The init() method was not called.");
+
+        try {
+            return decCipher.doFinal(data);
+        }
+        catch (IllegalBlockSizeException | BadPaddingException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(AsymmetricKeyEncryptionService.class, this, "super", super.toString());
+    }
+}
diff --git a/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AwsKmsEncryptionService.java b/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AwsKmsEncryptionService.java
new file mode 100644 (file)
index 0000000..aa878b4
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.util.List;
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.CryptoResult;
+import com.amazonaws.encryptionsdk.kms.KmsMasterKey;
+import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider;
+import com.amazonaws.regions.Region;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Provides an implementation of AWS KMS to encrypt/decrypt the data.
+ */
+public class AwsKmsEncryptionService implements EncryptionService {
+    /** KMS Key id. */
+    private String keyId;
+
+    /** AWS Region. */
+    private Region region;
+
+    /** AWS Credentials to access the key. */
+    private AWSCredentials creds;
+
+    /** AWS Client conf. */
+    private ClientConfiguration clientConf = new ClientConfiguration();
+
+    /** Kms master key provider. */
+    private KmsMasterKeyProvider prov;
+
+    /** Aws crypto. */
+    private AwsCrypto crypto;
+
+    /**
+     * Set the KMS key id used to encrypt/decrypt the data.
+     *
+     * @param keyId Key id.
+     * @return {@code this} for chaining.
+     */
+    public AwsKmsEncryptionService setKeyId(String keyId) {
+        this.keyId = keyId;
+
+        return this;
+    }
+
+    /**
+     * AWS region.
+     *
+     * @param region Region.
+     * @return {@code this} for chaining.
+     */
+    public AwsKmsEncryptionService setRegion(Region region) {
+        this.region = region;
+
+        return this;
+    }
+
+    /**
+     * AWS credentials.
+     *
+     * @param creds Aws Credentials.
+     * @return {@code this} for chaining.
+     */
+    public AwsKmsEncryptionService setCredentials(AWSCredentials creds) {
+        this.creds = creds;
+
+        return this;
+    }
+
+    /**
+     * AWS client configuration.
+     *
+     * @param clientConf Client conf.
+     * @return {@code this} for chaining.
+     */
+    public AwsKmsEncryptionService setClientConf(ClientConfiguration clientConf) {
+        this.clientConf = clientConf;
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void init() {
+        if (creds == null || region == null || keyId == null || keyId.trim().isEmpty())
+            throw new IgniteException(String.format("At-least one of the required parameters " +
+                "[creds = %s, region = %s, keyId = %s] is invalid.", creds, region, keyId));
+
+        crypto = createClient();
+
+        prov = createKmsMasterKeyProvider();
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] encrypt(byte[] data) {
+        if (crypto == null || prov == null)
+            throw new IgniteException("The init() method was not called.");
+
+        return crypto.encryptData(prov, data).getResult();
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] decrypt(byte[] data) {
+        if (crypto == null || prov == null)
+            throw new IgniteException("The init() method was not called.");
+
+        CryptoResult<byte[], KmsMasterKey> decryptRes = crypto.decryptData(prov, data);
+
+        List<String> keyIds = decryptRes.getMasterKeyIds();
+
+        if (keyIds != null && !keyIds.contains(keyId))
+            throw new IgniteException("Wrong KMS key ID!");
+
+        return decryptRes.getResult();
+    }
+
+    /**
+     * @return An instance of {@link AwsCrypto}.
+     */
+    AwsCrypto createClient() {
+        return crypto = new AwsCrypto();
+    }
+
+    /**
+     * @return An instance of {@link KmsMasterKeyProvider}.
+     */
+    KmsMasterKeyProvider createKmsMasterKeyProvider() {
+        return new KmsMasterKeyProvider(new AWSStaticCredentialsProvider(creds), region, clientConf, keyId);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(AwsKmsEncryptionService.class, this, "super", super.toString());
+    }
+}
diff --git a/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/EncryptionService.java b/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/EncryptionService.java
new file mode 100644 (file)
index 0000000..296dc34
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+/**
+ * A implementation of this interface should provide feature to encrypt/decrypt the data.
+ */
+public interface EncryptionService {
+    /**
+     * For initialization operations. Must be called before the {@link EncryptionService#encrypt(byte[])} and {@link
+     * EncryptionService#decrypt(byte[])} are used.
+     */
+    public void init();
+
+    /**
+     * Encrypt the input data.
+     *
+     * @param data Data. bytes to be encrypted.
+     * @return The encrypted data bytes.
+     * @throws IllegalArgumentException If the parameter data is null.
+     */
+    public byte[] encrypt(byte[] data);
+
+    /**
+     * Decrypt the input data.
+     *
+     * @param data Encrypted data.
+     * @return Decrypted result.
+     * @throws IllegalArgumentException If the parameter data is null.
+     */
+    public byte[] decrypt(byte[] data);
+}
diff --git a/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/SymmetricKeyEncryptionService.java b/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/SymmetricKeyEncryptionService.java
new file mode 100644 (file)
index 0000000..89f0402
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.security.Key;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Service to encrypt data using symmetric key encryption.
+ */
+public class SymmetricKeyEncryptionService implements EncryptionService {
+    /** Secret key. */
+    private Key secretKey;
+
+    /** Cipher, to be used for encryption. */
+    private Cipher encCipher;
+
+    /** Cipher, to be used for decryption. */
+    private Cipher decCipher;
+
+    /**
+     * The key used to encrypt and decrypt the data.
+     *
+     * @param secretKey Secret key.
+     * @return {@code this} for chaining.
+     */
+    public SymmetricKeyEncryptionService setSecretKey(Key secretKey) {
+        this.secretKey = secretKey;
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void init() throws IgniteException {
+        if (secretKey == null)
+            throw new IgniteException("Secret key was not set / was set to null.");
+
+        encCipher = IgniteUtils.createCipher(secretKey, Cipher.ENCRYPT_MODE);
+        decCipher = IgniteUtils.createCipher(secretKey, Cipher.DECRYPT_MODE);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] encrypt(byte[] data) {
+        if (data == null)
+            throw new IgniteException("Parameter [data] cannot be null");
+
+        if (encCipher == null)
+            throw new IgniteException("The init() method was not called.");
+
+        try {
+            return encCipher.doFinal(data);
+        }
+        catch (IllegalBlockSizeException | BadPaddingException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] decrypt(byte[] data) {
+        if (data == null)
+            throw new IgniteException("Parameter [data] cannot be null");
+
+        if (decCipher == null)
+            throw new IgniteException("The init() method was not called.");
+
+        try {
+            return decCipher.doFinal(data);
+        }
+        catch (BadPaddingException | IllegalBlockSizeException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(SymmetricKeyEncryptionService.class, this, "super", super.toString());
+    }
+}
diff --git a/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/package-info.java b/modules/aws/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/package-info.java
new file mode 100644 (file)
index 0000000..ec64be2
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 description. --> Contains Encryption services.
+ */
+package org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
index 66022d8..347c5fd 100644 (file)
@@ -16,7 +16,6 @@
  */
 
 /**
- * <!-- Package description. -->
  * Contains AWS S3-based IP finder.
  */
-package org.apache.ignite.spi.discovery.tcp.ipfinder.s3;
\ No newline at end of file
+package org.apache.ignite.spi.discovery.tcp.ipfinder.s3;
index 37a0acc..612f84e 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.concurrent.ThreadLocalRandom;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinderAbstractSelfTest;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.EncryptionService;
 import org.apache.ignite.testsuites.IgniteIgnore;
 import org.apache.ignite.testsuites.IgniteS3TestSuite;
 import org.jetbrains.annotations.Nullable;
@@ -35,7 +36,7 @@ import org.junit.runners.JUnit4;
  * Abstract TcpDiscoveryS3IpFinder to test with different ways of setting AWS credentials.
  */
 @RunWith(JUnit4.class)
-abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
+public abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
     extends TcpDiscoveryIpFinderAbstractSelfTest<TcpDiscoveryS3IpFinder> {
     /** Bucket endpoint */
     @Nullable protected String bucketEndpoint;
@@ -46,6 +47,9 @@ abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
     /** Key prefix of the address. */
     @Nullable protected String keyPrefix;
 
+    /** Encryption service. */
+    @Nullable protected EncryptionService encryptionSvc;
+
     /**
      * Constructor.
      *
@@ -67,6 +71,7 @@ abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
         setBucketName(finder);
         setSSEAlgorithm(finder);
         setKeyPrefix(finder);
+        setEncryptionService(finder);
 
         for (int i = 0; i < 5; i++) {
             Collection<InetSocketAddress> addrs = finder.getRegisteredAddresses();
@@ -94,12 +99,14 @@ abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
 
     /**
      * Set AWS credentials into the provided {@code finder}.
+     *
      * @param finder finder credentials to set into
      */
     protected abstract void setAwsCredentials(TcpDiscoveryS3IpFinder finder);
 
     /**
      * Set Bucket endpoint into the provided {@code finder}.
+     *
      * @param finder finder endpoint to set into.
      */
     private void setBucketEndpoint(TcpDiscoveryS3IpFinder finder) {
@@ -108,6 +115,7 @@ abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
 
     /**
      * Set server-side encryption algorithm for Amazon S3-managed encryption keys into the provided {@code finder}.
+     *
      * @param finder finder encryption algorithm to set into.
      */
     private void setSSEAlgorithm(TcpDiscoveryS3IpFinder finder) {
@@ -116,6 +124,7 @@ abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
 
     /**
      * Set Bucket endpoint into the provided {@code finder}.
+     *
      * @param finder finder endpoint to set into.
      */
     protected void setBucketName(TcpDiscoveryS3IpFinder finder) {
@@ -124,6 +133,7 @@ abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
 
     /**
      * Set the ip address key prefix into the provided {@code finder}.
+     *
      * @param finder finder encryption algorithm to set into.
      */
     protected void setKeyPrefix(TcpDiscoveryS3IpFinder finder) {
@@ -131,9 +141,17 @@ abstract class TcpDiscoveryS3IpFinderAbstractSelfTest
     }
 
     /**
-     * Gets Bucket name.
-     * Bucket name should be unique for the host to parallel test run on one bucket.
-     * Please note that the final bucket name should not exceed 63 chars.
+     * Set encryption service into the provided {@code finder}.
+     *
+     * @param finder finder encryption service to set into.
+     */
+    protected void setEncryptionService(TcpDiscoveryS3IpFinder finder) {
+        finder.setEncryptionService(encryptionSvc);
+    }
+
+    /**
+     * Gets Bucket name. Bucket name should be unique for the host to parallel test run on one bucket. Please note that
+     * the final bucket name should not exceed 63 chars.
      *
      * @return Bucket name.
      */
diff --git a/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/TcpDiscoveryS3IpFinderClientSideEncryptionSelfTest.java b/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/TcpDiscoveryS3IpFinderClientSideEncryptionSelfTest.java
new file mode 100644 (file)
index 0000000..73cc3fc
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3;
+
+import com.amazonaws.auth.BasicAWSCredentials;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.client.DummyS3Client;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.EncryptionService;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.MockEncryptionService;
+import org.mockito.Mockito;
+
+/**
+ * TcpDiscoveryS3IpFinder tests client side encryption for S3 IP finder.
+ */
+public class TcpDiscoveryS3IpFinderClientSideEncryptionSelfTest extends TcpDiscoveryS3IpFinderAbstractSelfTest {
+    /**
+     * Constructor.
+     *
+     * @throws Exception If any error occurs.
+     */
+    public TcpDiscoveryS3IpFinderClientSideEncryptionSelfTest() throws Exception {
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void setAwsCredentials(TcpDiscoveryS3IpFinder finder) {
+        finder.setAwsCredentials(new BasicAWSCredentials("dummy", "dummy"));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void setEncryptionService(TcpDiscoveryS3IpFinder finder) {
+        EncryptionService encryptionSvc = MockEncryptionService.instance();
+        encryptionSvc.init();
+        finder.setEncryptionService(encryptionSvc);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected TcpDiscoveryS3IpFinder ipFinder() {
+        TcpDiscoveryS3IpFinder ipFinder = Mockito.spy(new TcpDiscoveryS3IpFinder());
+
+        Mockito.doReturn(new DummyS3Client()).when(ipFinder).createAmazonS3Client();
+
+        setAwsCredentials(ipFinder);
+        setBucketName(ipFinder);
+        setKeyPrefix(ipFinder);
+        setEncryptionService(ipFinder);
+
+        return ipFinder;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void testIpFinder() throws Exception {
+        injectLogger(finder);
+
+        assert finder.isShared() : "Ip finder should be shared by default.";
+
+        super.testIpFinder();
+    }
+}
diff --git a/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AsymmetricKeyEncryptionServiceTest.java b/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AsymmetricKeyEncryptionServiceTest.java
new file mode 100644 (file)
index 0000000..f781f45
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Contains tests for {@link AsymmetricKeyEncryptionService}.
+ */
+@RunWith(JUnit4.class)
+public class AsymmetricKeyEncryptionServiceTest extends GridCommonAbstractTest {
+    /** Asymmetric key encryption service. */
+    private AsymmetricKeyEncryptionService encryptionSvc;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() {
+        try {
+            String algo = "RSA";
+            // Public and private key pair is generated using 'openssl'
+            String publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbuQ7RcOtsHf2oGQ" +
+                "b//cMgfN9kS8tsn21BOAnXwkBN0LwpVXdw1SAfN6fhdJqr4Z585IgF" +
+                "EDOlimoDZ2pXHZ6NfmAot4xkioXlsX+lsSir3gMtPfJhtTFvvnvzgr" +
+                "ZGWVxu0eLBCiuhlUpYNTHlFaiD8C/Qj7eRY+tUagZRskug8QIDAQAB";
+
+            String privateKeyStr = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJu5D" +
+                "tFw62wd/agZBv/9wyB832RLy2yfbUE4CdfCQE3QvClVd3DVIB83p+" +
+                "F0mqvhnnzkiAUQM6WKagNnalcdno1+YCi3jGSKheWxf6WxKKveAy0" +
+                "98mG1MW++e/OCtkZZXG7R4sEKK6GVSlg1MeUVqIPwL9CPt5Fj61Rq" +
+                "BlGyS6DxAgMBAAECgYEAj+lILnqitvpIb08hzvYfnCiK8s+xIaN8f" +
+                "qdhQUo9zyw2mCRqC5aK5w6yUYNHZc1OgLFamwNMF5KBQsAR4Ix492" +
+                "1K8ch4fmqtnaD4wlx3euyH1+ZjmagzutlFHKxKOnFuoaWeWJj0RN2" +
+                "f2S3dci2Kh1hkde3PylOgOfKXmz0MfAECQQDMjqEr4KdWnAUwBFgP" +
+                "+48wQufpfWzTt2rR7lDxfWoeoo0BlIPVEgvrjmr3mwcX2/kyZK1tD" +
+                "Hf9BSTI65a9zl4hAkEAwuJ7mmd/emqXCqgIs8qsLaaNnZUfTTyzb4" +
+                "iHgFyh/FEyXeuPN/hyg3Hch2/uA+ZFW+Bc46GSSmzWK4RTJGfI0QJ" +
+                "BAI3tHBhUe+ZUxCinqu4T7SpgEYZoNrzCkwPrJRAYoyt0Pv9sqveH" +
+                "2Otr2f3H+2jrgAAd6FI0B4BvNDGPe/xfleECQHkopP+RaMeKjOyrG" +
+                "v3r+q9G5LQbiaJTIpssnlFHRc3ADTgmwpthcpAVsaziAW+bMXO1QQ" +
+                "qj4Hc0wtG7KpVvkIECQBm72Wh6od+BFeWq2iN7XiXIAgXRRvfVTuD" +
+                "KFM3vYQlszEsTI2YKcCg2Lg1oFoHn/tuRjOajNs6eWz/0BWzfuHY=";
+
+            PublicKey publicKey = KeyFactory.getInstance(algo)
+                .generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr)));
+
+            PrivateKey privateKey = KeyFactory.getInstance(algo)
+                .generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)));
+
+            KeyPair keyPair = new KeyPair(publicKey, privateKey);
+
+            encryptionSvc = new AsymmetricKeyEncryptionService();
+            encryptionSvc.setKeyPair(keyPair);
+            encryptionSvc.init();
+        }
+        catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+            Assert.fail();
+        }
+    }
+
+    /**
+     * Test encryption and decryption.
+     */
+    @Test
+    public void testEncryptDecrypt() {
+        byte[] testData = "This is some test data.".getBytes(StandardCharsets.UTF_8);
+
+        byte[] encData = encryptionSvc.encrypt(testData);
+        byte[] decData = encryptionSvc.decrypt(encData);
+
+        Assert.assertArrayEquals(testData, decData);
+    }
+}
diff --git a/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AwsKmsEncryptionServiceTest.java b/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/AwsKmsEncryptionServiceTest.java
new file mode 100644 (file)
index 0000000..83c28b2
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.encryptionsdk.AwsCrypto;
+import com.amazonaws.encryptionsdk.CryptoResult;
+import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider;
+import com.amazonaws.regions.Region;
+import com.amazonaws.regions.Regions;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+/**
+ * Class to test {@link AwsKmsEncryptionService}.
+ */
+@RunWith(JUnit4.class)
+public class AwsKmsEncryptionServiceTest extends GridCommonAbstractTest {
+    /**
+     * Test encryption and decryption.
+     */
+    @Test
+    public void testEncryptDecrypt() {
+        String encKey = "12345";
+        byte[] testData = "test string".getBytes(StandardCharsets.UTF_8);
+        byte[] encTestData = "enc test string".getBytes(StandardCharsets.UTF_8);
+
+        AwsKmsEncryptionService awsKmsEncryptionSvc = Mockito.spy(new AwsKmsEncryptionService());
+        awsKmsEncryptionSvc.setKeyId(encKey)
+            .setCredentials(new BasicAWSCredentials("dummy", "dummy"))
+            .setRegion(Region.getRegion(Regions.AP_SOUTH_1));
+
+        AwsCrypto awsCrypto = Mockito.mock(AwsCrypto.class);
+        KmsMasterKeyProvider prov = Mockito.mock(KmsMasterKeyProvider.class);
+        CryptoResult encCryptoRes = Mockito.mock(CryptoResult.class);
+        CryptoResult decCryptoRes = Mockito.mock(CryptoResult.class);
+
+        Mockito.doReturn(awsCrypto).when(awsKmsEncryptionSvc).createClient();
+        Mockito.doReturn(prov).when(awsKmsEncryptionSvc).createKmsMasterKeyProvider();
+
+        awsKmsEncryptionSvc.init();
+
+        Mockito.doReturn(encCryptoRes).when(awsCrypto).encryptData(prov, testData);
+        Mockito.doReturn(encTestData).when(encCryptoRes).getResult();
+
+        Mockito.doReturn(decCryptoRes).when(awsCrypto).decryptData(prov, encTestData);
+        Mockito.doReturn(Arrays.asList(encKey)).when(decCryptoRes).getMasterKeyIds();
+        Mockito.doReturn(testData).when(decCryptoRes).getResult();
+
+        byte[] encData = awsKmsEncryptionSvc.encrypt(testData);
+        byte[] actualOutput = awsKmsEncryptionSvc.decrypt(encData);
+
+        Assert.assertArrayEquals(testData, actualOutput);
+    }
+}
diff --git a/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/MockEncryptionService.java b/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/MockEncryptionService.java
new file mode 100644 (file)
index 0000000..d5dbe44
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.nio.charset.StandardCharsets;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Class to provide a mock implementation of {@link EncryptionService}.
+ */
+public class MockEncryptionService implements EncryptionService {
+    /** Encryption service. */
+    private final EncryptionService encryptionSvc;
+
+    /**
+     * Constructor
+     *
+     * @param encryptionSvc Encryption service.
+     */
+    private MockEncryptionService(EncryptionService encryptionSvc) {
+        this.encryptionSvc = encryptionSvc;
+    }
+
+    /**
+     * @return An instance of this class.
+     */
+    public static MockEncryptionService instance() {
+        SecretKey secretKey = new SecretKeySpec("0000000000000000".getBytes(StandardCharsets.UTF_8), "AES");
+        EncryptionService encryptionSvc = new SymmetricKeyEncryptionService().setSecretKey(secretKey);
+
+        encryptionSvc.init();
+
+        return new MockEncryptionService(encryptionSvc);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void init() {
+        // Nothing to do
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] encrypt(byte[] payload) {
+        return encryptionSvc.encrypt(payload);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] decrypt(byte[] payload) {
+        return encryptionSvc.decrypt(payload);
+    }
+}
diff --git a/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/MockEncryptionServiceTest.java b/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/MockEncryptionServiceTest.java
new file mode 100644 (file)
index 0000000..2ea1650
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.nio.charset.StandardCharsets;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Class to test {@link MockEncryptionService}.
+ */
+@RunWith(JUnit4.class)
+public class MockEncryptionServiceTest extends GridCommonAbstractTest {
+    /** Mock encryption service. */
+    private MockEncryptionService mockEncryptionSvc;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() {
+        mockEncryptionSvc = MockEncryptionService.instance();
+    }
+
+    /**
+     * Test if the service correctly encrypts and decrypts data.
+     */
+    @Test
+    public void testEncryptDecrypt() {
+        byte[] testStr = "test string".getBytes(StandardCharsets.UTF_8);
+
+        byte[] encData = mockEncryptionSvc.encrypt(testStr);
+        byte[] decData = mockEncryptionSvc.decrypt(encData);
+
+        Assert.assertArrayEquals(testStr, decData);
+    }
+}
diff --git a/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/SymmetricKeyEncryptionServiceTest.java b/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/SymmetricKeyEncryptionServiceTest.java
new file mode 100644 (file)
index 0000000..50ec6ee
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
+
+import java.nio.charset.StandardCharsets;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Class to test {@link SymmetricKeyEncryptionService}.
+ */
+@RunWith(JUnit4.class)
+public class SymmetricKeyEncryptionServiceTest extends GridCommonAbstractTest {
+    /** Symmetric key encryption service. */
+    private SymmetricKeyEncryptionService encryptionSvc;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() {
+        byte[] key = "0000000000000000".getBytes(StandardCharsets.UTF_8);
+        SecretKey secretKey = new SecretKeySpec(key, "AES");
+
+        encryptionSvc = new SymmetricKeyEncryptionService().setSecretKey(secretKey);
+        encryptionSvc.init();
+    }
+
+    /**
+     * Test whether encryption and decryption.
+     */
+    @Test
+    public void testEncryptDecrypt() {
+        byte[] testData = "test string".getBytes(StandardCharsets.UTF_8);
+        byte[] encData = encryptionSvc.encrypt(testData);
+        byte[] decData = encryptionSvc.decrypt(encData);
+
+        Assert.assertArrayEquals(testData, decData);
+    }
+}
diff --git a/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/package-info.java b/modules/aws/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/s3/encrypt/package-info.java
new file mode 100644 (file)
index 0000000..a2cb726
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 description. --> Contains internal tests or test related classes and interfaces.
+ */
+package org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt;
index 1b28376..9e03db4 100644 (file)
@@ -29,10 +29,15 @@ import org.apache.ignite.spi.checkpoint.s3.S3SessionCheckpointSelfTest;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinderAwsCredentialsProviderSelfTest;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinderAwsCredentialsSelfTest;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinderBucketEndpointSelfTest;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinderClientSideEncryptionSelfTest;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinderKeyPrefixSelfTest;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinderSSEAlgorithmSelfTest;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.client.DummyObjectListingTest;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.client.DummyS3ClientTest;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.AsymmetricKeyEncryptionServiceTest;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.AwsKmsEncryptionServiceTest;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.MockEncryptionServiceTest;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.s3.encrypt.SymmetricKeyEncryptionServiceTest;
 import org.apache.ignite.testframework.IgniteTestSuite;
 import org.junit.runner.RunWith;
 import org.junit.runners.AllTests;
@@ -57,6 +62,12 @@ public class IgniteS3TestSuite {
         suite.addTest(new JUnit4TestAdapter(S3CheckpointSpiStartStopBucketEndpointSelfTest.class));
         suite.addTest(new JUnit4TestAdapter(S3CheckpointSpiStartStopSSEAlgorithmSelfTest.class));
 
+        // S3 Encryption tests.
+        suite.addTest(new JUnit4TestAdapter(MockEncryptionServiceTest.class));
+        suite.addTest(new JUnit4TestAdapter(AwsKmsEncryptionServiceTest.class));
+        suite.addTest(new JUnit4TestAdapter(SymmetricKeyEncryptionServiceTest.class));
+        suite.addTest(new JUnit4TestAdapter(AsymmetricKeyEncryptionServiceTest.class));
+
         // S3 IP finder.
         suite.addTest(new JUnit4TestAdapter(DummyS3ClientTest.class));
         suite.addTest(new JUnit4TestAdapter(DummyObjectListingTest.class));
@@ -65,6 +76,7 @@ public class IgniteS3TestSuite {
         suite.addTest(new JUnit4TestAdapter(TcpDiscoveryS3IpFinderBucketEndpointSelfTest.class));
         suite.addTest(new JUnit4TestAdapter(TcpDiscoveryS3IpFinderSSEAlgorithmSelfTest.class));
         suite.addTest(new JUnit4TestAdapter(TcpDiscoveryS3IpFinderKeyPrefixSelfTest.class));
+        suite.addTest(new JUnit4TestAdapter(TcpDiscoveryS3IpFinderClientSideEncryptionSelfTest.class));
 
         return suite;
     }
@@ -83,12 +95,20 @@ public class IgniteS3TestSuite {
         return getRequiredEnvVar("test.amazon.secret.key");
     }
 
-    public static String getBucketName(final String defaultBucketName) {
-        String value = System.getenv("test.s3.bucket.name");
+    /**
+     * @param dfltBucketName Default bucket name.
+     * @return Bucket name.
+     */
+    public static String getBucketName(final String dfltBucketName) {
+        String val = System.getenv("test.s3.bucket.name");
 
-        return value == null ? defaultBucketName : value;
+        return val == null ? dfltBucketName : val;
     }
 
+    /**
+     * @param name Name of environment.
+     * @return Environment variable value.
+     */
     private static String getRequiredEnvVar(String name) {
         String key = System.getenv(name);
 
index 1d9a0ad..13aeafa 100755 (executable)
 
 package org.apache.ignite.internal.util;
 
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.IgniteClientDisconnectedException;
-import org.apache.ignite.IgniteDeploymentException;
-import org.apache.ignite.IgniteException;
-import org.apache.ignite.IgniteIllegalStateException;
-import org.apache.ignite.IgniteInterruptedException;
-import org.apache.ignite.IgniteLogger;
-import org.apache.ignite.IgniteSystemProperties;
-import org.apache.ignite.binary.BinaryRawReader;
-import org.apache.ignite.binary.BinaryRawWriter;
-import org.apache.ignite.cluster.ClusterGroupEmptyException;
-import org.apache.ignite.cluster.ClusterMetrics;
-import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.cluster.ClusterTopologyException;
-import org.apache.ignite.compute.ComputeTask;
-import org.apache.ignite.compute.ComputeTaskCancelledException;
-import org.apache.ignite.compute.ComputeTaskName;
-import org.apache.ignite.compute.ComputeTaskTimeoutException;
-import org.apache.ignite.configuration.AddressResolver;
-import org.apache.ignite.configuration.DataRegionConfiguration;
-import org.apache.ignite.configuration.DataStorageConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.events.EventType;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException;
-import org.apache.ignite.internal.IgniteDeploymentCheckedException;
-import org.apache.ignite.internal.IgniteFutureCancelledCheckedException;
-import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException;
-import org.apache.ignite.internal.IgniteInternalFuture;
-import org.apache.ignite.internal.IgniteInterruptedCheckedException;
-import org.apache.ignite.internal.IgniteNodeAttributes;
-import org.apache.ignite.internal.cluster.ClusterGroupEmptyCheckedException;
-import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
-import org.apache.ignite.internal.compute.ComputeTaskCancelledCheckedException;
-import org.apache.ignite.internal.compute.ComputeTaskTimeoutCheckedException;
-import org.apache.ignite.internal.events.DiscoveryCustomEvent;
-import org.apache.ignite.internal.managers.communication.GridIoManager;
-import org.apache.ignite.internal.managers.communication.GridIoPolicy;
-import org.apache.ignite.internal.managers.deployment.GridDeploymentInfo;
-import org.apache.ignite.internal.mxbean.IgniteStandardMXBean;
-import org.apache.ignite.internal.processors.cache.CacheClassLoaderMarker;
-import org.apache.ignite.internal.processors.cache.GridCacheAttributes;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
-import org.apache.ignite.internal.processors.cluster.BaselineTopology;
-import org.apache.ignite.internal.transactions.IgniteTxHeuristicCheckedException;
-import org.apache.ignite.internal.transactions.IgniteTxOptimisticCheckedException;
-import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
-import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
-import org.apache.ignite.internal.util.future.GridFutureAdapter;
-import org.apache.ignite.internal.util.future.IgniteFutureImpl;
-import org.apache.ignite.internal.util.io.GridFilenameUtils;
-import org.apache.ignite.internal.util.ipc.shmem.IpcSharedMemoryNativeLoader;
-import org.apache.ignite.internal.util.lang.GridClosureException;
-import org.apache.ignite.internal.util.lang.GridPeerDeployAware;
-import org.apache.ignite.internal.util.lang.GridTuple;
-import org.apache.ignite.internal.util.lang.IgniteThrowableConsumer;
-import org.apache.ignite.internal.util.typedef.C1;
-import org.apache.ignite.internal.util.typedef.CI1;
-import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.G;
-import org.apache.ignite.internal.util.typedef.P1;
-import org.apache.ignite.internal.util.typedef.X;
-import org.apache.ignite.internal.util.typedef.internal.A;
-import org.apache.ignite.internal.util.typedef.internal.SB;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.internal.util.worker.GridWorker;
-import org.apache.ignite.lang.IgniteBiTuple;
-import org.apache.ignite.lang.IgniteClosure;
-import org.apache.ignite.lang.IgniteFutureCancelledException;
-import org.apache.ignite.lang.IgniteFutureTimeoutException;
-import org.apache.ignite.lang.IgniteOutClosure;
-import org.apache.ignite.lang.IgnitePredicate;
-import org.apache.ignite.lang.IgniteProductVersion;
-import org.apache.ignite.lang.IgniteUuid;
-import org.apache.ignite.lifecycle.LifecycleAware;
-import org.apache.ignite.marshaller.Marshaller;
-import org.apache.ignite.plugin.PluginProvider;
-import org.apache.ignite.plugin.extensions.communication.Message;
-import org.apache.ignite.plugin.extensions.communication.MessageWriter;
-import org.apache.ignite.spi.IgniteSpi;
-import org.apache.ignite.spi.IgniteSpiException;
-import org.apache.ignite.spi.discovery.DiscoverySpi;
-import org.apache.ignite.spi.discovery.DiscoverySpiOrderSupport;
-import org.apache.ignite.transactions.TransactionDeadlockException;
-import org.apache.ignite.transactions.TransactionHeuristicException;
-import org.apache.ignite.transactions.TransactionOptimisticException;
-import org.apache.ignite.transactions.TransactionRollbackException;
-import org.apache.ignite.transactions.TransactionTimeoutException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import sun.misc.Unsafe;
-
-import javax.management.DynamicMBean;
-import javax.management.JMException;
-import javax.management.MBeanRegistrationException;
-import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-import javax.naming.Context;
-import javax.naming.NamingException;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -192,6 +84,8 @@ import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.security.AccessController;
+import java.security.InvalidKeyException;
+import java.security.Key;
 import java.security.KeyManagementException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -253,6 +147,115 @@ import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.management.DynamicMBean;
+import javax.management.JMException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteClientDisconnectedException;
+import org.apache.ignite.IgniteDeploymentException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteIllegalStateException;
+import org.apache.ignite.IgniteInterruptedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.cluster.ClusterGroupEmptyException;
+import org.apache.ignite.cluster.ClusterMetrics;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.cluster.ClusterTopologyException;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.compute.ComputeTaskCancelledException;
+import org.apache.ignite.compute.ComputeTaskName;
+import org.apache.ignite.compute.ComputeTaskTimeoutException;
+import org.apache.ignite.configuration.AddressResolver;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.events.EventType;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException;
+import org.apache.ignite.internal.IgniteDeploymentCheckedException;
+import org.apache.ignite.internal.IgniteFutureCancelledCheckedException;
+import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.IgniteNodeAttributes;
+import org.apache.ignite.internal.cluster.ClusterGroupEmptyCheckedException;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.compute.ComputeTaskCancelledCheckedException;
+import org.apache.ignite.internal.compute.ComputeTaskTimeoutCheckedException;
+import org.apache.ignite.internal.events.DiscoveryCustomEvent;
+import org.apache.ignite.internal.managers.communication.GridIoManager;
+import org.apache.ignite.internal.managers.communication.GridIoPolicy;
+import org.apache.ignite.internal.managers.deployment.GridDeploymentInfo;
+import org.apache.ignite.internal.mxbean.IgniteStandardMXBean;
+import org.apache.ignite.internal.processors.cache.CacheClassLoaderMarker;
+import org.apache.ignite.internal.processors.cache.GridCacheAttributes;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cluster.BaselineTopology;
+import org.apache.ignite.internal.transactions.IgniteTxHeuristicCheckedException;
+import org.apache.ignite.internal.transactions.IgniteTxOptimisticCheckedException;
+import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
+import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.future.IgniteFutureImpl;
+import org.apache.ignite.internal.util.io.GridFilenameUtils;
+import org.apache.ignite.internal.util.ipc.shmem.IpcSharedMemoryNativeLoader;
+import org.apache.ignite.internal.util.lang.GridClosureException;
+import org.apache.ignite.internal.util.lang.GridPeerDeployAware;
+import org.apache.ignite.internal.util.lang.GridTuple;
+import org.apache.ignite.internal.util.lang.IgniteThrowableConsumer;
+import org.apache.ignite.internal.util.typedef.C1;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.P1;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.A;
+import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.util.worker.GridWorker;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgniteFutureCancelledException;
+import org.apache.ignite.lang.IgniteFutureTimeoutException;
+import org.apache.ignite.lang.IgniteOutClosure;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.lang.IgniteProductVersion;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.lifecycle.LifecycleAware;
+import org.apache.ignite.marshaller.Marshaller;
+import org.apache.ignite.plugin.PluginProvider;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import org.apache.ignite.spi.IgniteSpi;
+import org.apache.ignite.spi.IgniteSpiException;
+import org.apache.ignite.spi.discovery.DiscoverySpi;
+import org.apache.ignite.spi.discovery.DiscoverySpiOrderSupport;
+import org.apache.ignite.transactions.TransactionDeadlockException;
+import org.apache.ignite.transactions.TransactionHeuristicException;
+import org.apache.ignite.transactions.TransactionOptimisticException;
+import org.apache.ignite.transactions.TransactionRollbackException;
+import org.apache.ignite.transactions.TransactionTimeoutException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import sun.misc.Unsafe;
 
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_DISABLE_HOSTNAME_VERIFIER;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_HOME;
@@ -11100,4 +11103,24 @@ public abstract class IgniteUtils {
             return cnt.get();
         }
     }
+
+    /**
+     * @param key Cipher Key.
+     * @param encMode Enc mode see {@link Cipher#ENCRYPT_MODE}, {@link Cipher#DECRYPT_MODE}, etc.
+     */
+    public static Cipher createCipher(Key key, int encMode) {
+        if (key == null)
+            throw new IgniteException("Cipher Key cannot be null");
+
+        try {
+            Cipher cipher = Cipher.getInstance(key.getAlgorithm());
+
+            cipher.init(encMode, key);
+
+            return cipher;
+        }
+        catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
+            throw new IgniteException(e);
+        }
+    }
 }
index 00e5634..ce3cff6 100644 (file)
@@ -54,6 +54,7 @@
         <aws.sdk.bundle.version>1.10.12_1</aws.sdk.bundle.version>
         <aws.sdk.version>1.11.75</aws.sdk.version>
         <camel.version>2.22.0</camel.version>
+        <aws.encryption.sdk.version>1.3.2</aws.encryption.sdk.version>
         <commons.beanutils.bundle.version>1.9.2_1</commons.beanutils.bundle.version>
         <commons.beanutils.version>1.9.3</commons.beanutils.version>
         <commons.codec.version>1.11</commons.codec.version>