

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# AWS-Verschlüsselungs-SDK for Java
<a name="java"></a>

In diesem Thema wird erklärt, wie das AWS-Verschlüsselungs-SDK for Java installiert und verwendet wird. Einzelheiten zur Programmierung mit dem AWS-Verschlüsselungs-SDK for Java finden Sie im [aws-encryption-sdk-java](https://github.com/aws/aws-encryption-sdk-java/)Repository unter GitHub. Eine API-Dokumentation finden Sie im [Javadoc](https://aws.github.io/aws-encryption-sdk-java/) für das AWS-Verschlüsselungs-SDK for Java.

**Topics**
+ [

## Voraussetzungen
](#java-prerequisites)
+ [

## Installation
](#java-installation)
+ [Beispiele](java-example-code.md)

## Voraussetzungen
<a name="java-prerequisites"></a>

Stellen Sie vor der Installation von sicher AWS-Verschlüsselungs-SDK for Java, dass Sie die folgenden Voraussetzungen erfüllen.

**Eine Java-Entwicklungsumgebung**  
Sie benötigen Java 8 oder höher. Klicken Sie auf der Oracle-Website auf [Java SE Downloads](https://www.oracle.com/java/technologies/downloads/) und laden und installieren Sie anschließend das Java SE Development Kit (JDK).  
Wenn Sie das Oracle JDK verwenden, müssen Sie auch die [Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files](http://www.oracle.com/java/technologies/javase-jce8-downloads.html) herunterladen und installieren.

**Bouncy Castle**  
Das AWS-Verschlüsselungs-SDK for Java erfordert [Bouncy Castle](https://www.bouncycastle.org/download/bouncy-castle-java/).   
+ AWS-Verschlüsselungs-SDK for Java Versionen 1.6.1 und höher verwenden Bouncy Castle, um kryptografische Objekte zu serialisieren und zu deserialisieren. Sie können Bouncy Castle oder [Bouncy Castle FIPS](https://www.bouncycastle.org/about/bouncy-castle-fips-faq/) verwenden, um diese Anforderung zu erfüllen. ****Hilfe zur Installation und Konfiguration von Bouncy Castle FIPS finden Sie in der [BC FIPS-Dokumentation](https://www.bouncycastle.org/documentation/), insbesondere in den Benutzerhandbüchern und den Sicherheitsrichtlinien.**** PDFs
+ Frühere Versionen von AWS-Verschlüsselungs-SDK for Java verwenden die Kryptografie-API von Bouncy Castle für Java. Diese Anforderung wird nur von Nicht-FIPS Bouncy Castle erfüllt.
Wenn Sie Bouncy Castle nicht haben, gehen Sie zu [Bouncy Castle für Java herunterladen](https://bouncycastle.org/download/bouncy-castle-java/), um die Anbieterdatei herunterzuladen, die Ihrem JDK entspricht. [Sie können auch [Apache Maven](https://maven.apache.org/) verwenden, um das Artefakt für den Standard-Bouncy Castle-Anbieter ([bcprov-ext-jdk15on](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-ext-jdk15on)) oder das Artefakt für Bouncy Castle FIPS (bc-fips) abzurufen.](https://mvnrepository.com/artifact/org.bouncycastle/bc-fips)

**AWS SDK für Java**  
Version 3. *x* der AWS-Verschlüsselungs-SDK for Java erfordert das AWS SDK for Java 2.x, auch wenn Sie keine AWS KMS Schlüsselringe verwenden.  
Ausführung 2. *x* oder früher von benötigt AWS-Verschlüsselungs-SDK for Java das nicht AWS SDK für Java. Die AWS SDK für Java ist jedoch erforderlich, um [AWS Key Management Service](https://aws.amazon.com/kms/)(AWS KMS) als Hauptschlüsselanbieter zu verwenden. Ab AWS-Verschlüsselungs-SDK for Java Version 2.4.0 AWS-Verschlüsselungs-SDK for Java unterstützt der sowohl Version 1.x als auch 2.x von. AWS SDK für Java AWS Encryption SDK Der Code für AWS SDK für Java 1.x und 2.x ist interoperabel. Sie können beispielsweise Daten mit AWS Encryption SDK Code verschlüsseln, der 1.x unterstützt, und sie mit Code entschlüsseln, der AWS SDK für Java 1.x unterstützt AWS SDK for Java 2.x (oder umgekehrt). Versionen vor 2.4.0 AWS-Verschlüsselungs-SDK for Java unterstützen nur 1.x. AWS SDK für Java Hinweise zur Aktualisierung Ihrer Version von finden Sie unter AWS Encryption SDK. [Migrieren Sie Ihre AWS Encryption SDK](migration.md)  
Wenn Sie Ihren AWS-Verschlüsselungs-SDK for Java Code von AWS SDK für Java 1.x auf aktualisieren AWS SDK for Java 2.x, ersetzen Sie Verweise auf die [`AWSKMS`Schnittstelle](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/kms/package-summary.html) in AWS SDK für Java 1.x durch Verweise auf die [`KmsClient`Schnittstelle](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kms/package-summary.html) in. AWS SDK for Java 2.x[Das AWS-Verschlüsselungs-SDK for Java unterstützt die Schnittstelle nicht. `KmsAsyncClient`](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kms/KmsAsyncClient.html) Aktualisieren Sie außerdem Ihren Code, sodass die AWS KMS zugehörigen Objekte im `kmssdkv2` Namespace statt im `kms` Namespace verwendet werden.   
Verwenden Sie Apache Maven AWS SDK für Java, um das zu installieren.   
+ Um [das gesamte AWS SDK für Java](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup-project-maven.html#build-the-entire-sdk-into-your-project) als Abhängigkeit zu importieren, deklarieren Sie es in Ihrer `pom.xml`-Datei.
+ Um eine Abhängigkeit nur für das AWS KMS Modul in AWS SDK für Java 1.x zu erstellen, folgen Sie den Anweisungen zur [Angabe bestimmter Module](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/setup-project-maven.html#modules-dependencies) und setzen Sie den `artifactId` Wert auf. `aws-java-sdk-kms`
+ Um eine Abhängigkeit nur für das AWS KMS Modul in AWS SDK für Java 2.x zu erstellen, folgen Sie den Anweisungen zur [Angabe bestimmter](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup-project-maven.html#modules-dependencies) Module. Stellen Sie „`groupId`bis“ `software.amazon.awssdk` und „`artifactId`Bis`kms`“ ein.
Weitere Änderungen finden Sie unter [Was ist der Unterschied zwischen AWS SDK für Java 1.x und 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/migration-whats-different.html) im AWS SDK for Java 2.x Entwicklerhandbuch.  
In den Java-Beispielen im AWS Encryption SDK Developer Guide wird der verwendet. AWS SDK for Java 2.x

## Installation
<a name="java-installation"></a>

Installieren Sie die neueste Version der AWS-Verschlüsselungs-SDK for Java.

**Anmerkung**  
Alle AWS-Verschlüsselungs-SDK for Java Versionen vor 2.0.0 befinden sich in der [end-of-supportPhase](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html#version-life-cycle).  
Sie können sicher von Version 2.0 aus aktualisieren. *x* und höher auf die neueste Version von AWS-Verschlüsselungs-SDK for Java ohne Code- oder Datenänderungen. In Version 2.0 wurden jedoch [neue Sicherheitsfunktionen](about-versions.md#version-2) eingeführt. *x* sind nicht abwärtskompatibel. Um von Versionen vor 1.7 zu aktualisieren. *x* auf Version 2.0. *x* und höher, Sie müssen zuerst auf die neueste Version 1 aktualisieren. *x-Version* von AWS Encryption SDK. Details hierzu finden Sie unter [Migrieren Sie Ihre AWS Encryption SDK](migration.md).

Sie können das AWS-Verschlüsselungs-SDK for Java auf folgende Weise installieren.

**manuell**  
Um das [aws-encryption-sdk-java](https://github.com/aws/aws-encryption-sdk-java/) GitHubRepository zu installieren AWS-Verschlüsselungs-SDK for Java, klonen oder laden Sie es herunter.

**Verwenden von Apache Maven**  
Das AWS-Verschlüsselungs-SDK for Java ist über [Apache Maven](https://maven.apache.org/) mit der folgenden Abhängigkeitsdefinition verfügbar.  

```
<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-encryption-sdk-java</artifactId>
  <version>3.0.0</version>
</dependency>
```

Nachdem Sie das SDK installiert haben, schauen Sie sich zunächst den [Java-Beispielcode](java-example-code.md) in diesem Handbuch und das [Javadoc](https://aws.github.io/aws-encryption-sdk-java/) an. GitHub

# AWS-Verschlüsselungs-SDK for Java Beispiele
<a name="java-example-code"></a>

Die folgenden Beispiele zeigen Ihnen, wie Sie mit AWS-Verschlüsselungs-SDK for Java dem Daten ver- und entschlüsseln können. Diese Beispiele zeigen, wie Version 3 verwendet wird. *x* und später von AWS-Verschlüsselungs-SDK for Java. Version 3. *x* von AWS-Verschlüsselungs-SDK for Java benötigt die AWS SDK for Java 2.x. Version 3. *x* von AWS-Verschlüsselungs-SDK for Java ersetzt die [Hauptschlüsselanbieter](concepts.md#master-key-provider) durch [Schlüsselringe](concepts.md#keyring). Beispiele, die frühere Versionen verwenden, finden Sie in der [Release-Liste](https://github.com/aws/aws-encryption-sdk-java/releases) des [aws-encryption-sdk-java](https://github.com/aws/aws-encryption-sdk-java/)Repositorys unter GitHub.

**Topics**
+ [Zeichenfolgen](#java-example-strings)
+ [Byte-Streams](#java-example-streams)
+ [Bytestreams mit mehreren Master-Key-Anbietern](#java-example-multiple-providers)

## Verschlüsseln und Entschlüsseln von Zeichenfolgen
<a name="java-example-strings"></a>

Das folgende Beispiel zeigt Ihnen, wie Sie Version 3 verwenden. *x* der AWS-Verschlüsselungs-SDK for Java zum Verschlüsseln und Entschlüsseln von Zeichenketten. Bevor Sie die Zeichenfolge verwenden, konvertieren Sie sie in ein Byte-Array.

[In diesem Beispiel wird ein AWS KMS Schlüsselbund verwendet.](use-kms-keyring.md) Wenn Sie mit einem AWS KMS Schlüsselbund verschlüsseln, können Sie eine Schlüssel-ID, einen Schlüssel-ARN, einen Aliasnamen oder einen Alias-ARN verwenden, um die KMS-Schlüssel zu identifizieren. Beim Entschlüsseln müssen Sie einen Schlüssel-ARN verwenden, um KMS-Schlüssel zu identifizieren.

Wenn Sie die `encryptData()`-Methode aufrufen, wird eine [verschlüsselte Nachricht](concepts.md#message) (`CryptoResult`) zurückgegeben, die den Verschlüsselungstext, die verschlüsselten Datenschlüssel und den Verschlüsselungskontext enthält. Wenn Sie `CryptoResult` auf dem `getResult`-Objekt aufrufen, gibt es eine Base-64-codierte Zeichenfolgenversion der [verschlüsselten Nachricht](message-format.md) zurück, die Sie an die `decryptData()`-Methode übergeben können.

In ähnlicher Weise enthält das `decryptData()` zurückgegebene `CryptoResult` Objekt beim Aufrufen die Klartextnachricht und eine AWS KMS key ID. Bevor Ihre Anwendung den Klartext zurückgibt, stellen Sie sicher, dass die AWS KMS key ID und der Verschlüsselungskontext in der verschlüsselten Nachricht den Erwartungen entsprechen.

```
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazonaws.crypto.keyrings;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CommitmentPolicy;
import com.amazonaws.encryptionsdk.CryptoResult;
import software.amazon.cryptography.materialproviders.IKeyring;
import software.amazon.cryptography.materialproviders.MaterialProviders;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsMultiKeyringInput;
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

/**
 * Encrypts and then decrypts data using an AWS KMS Keyring.
 *
 * <p>Arguments:
 *
 * <ol>
 *   <li>Key ARN: For help finding the Amazon Resource Name (ARN) of your AWS KMS customer master
 *       key (CMK), see 'Viewing Keys' at
 *       http://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html
 * </ol>
 */
public class BasicEncryptionKeyringExample {

  private static final byte[] EXAMPLE_DATA = "Hello World".getBytes(StandardCharsets.UTF_8);

  public static void main(final String[] args) {
    final String keyArn = args[0];

    encryptAndDecryptWithKeyring(keyArn);
  }

  public static void encryptAndDecryptWithKeyring(final String keyArn) {
    // 1. Instantiate the SDK
    // This builds the AwsCrypto client with the RequireEncryptRequireDecrypt commitment policy,
    // which means this client only encrypts using committing algorithm suites and enforces
    // that the client will only decrypt encrypted messages that were created with a committing
    // algorithm suite.
    // This is the default commitment policy if you build the client with
    // `AwsCrypto.builder().build()`
    // or `AwsCrypto.standard()`.
    final AwsCrypto crypto =
        AwsCrypto.builder()
            .withCommitmentPolicy(CommitmentPolicy.RequireEncryptRequireDecrypt)
            .build();

    // 2. Create the AWS KMS keyring.
    // This example creates a multi keyring, which automatically creates the KMS client.
    final MaterialProviders materialProviders =
        MaterialProviders.builder()
            .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
            .build();
    final CreateAwsKmsMultiKeyringInput keyringInput =
        CreateAwsKmsMultiKeyringInput.builder().generator(keyArn).build();
    final IKeyring kmsKeyring = materialProviders.CreateAwsKmsMultiKeyring(keyringInput);

    // 3. Create an encryption context
    // We recommend using an encryption context whenever possible
    // to protect integrity. This sample uses placeholder values.
    // For more information see:
    // blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management
    final Map<String, String> encryptionContext =
        Collections.singletonMap("ExampleContextKey", "ExampleContextValue");

    // 4. Encrypt the data
    final CryptoResult<byte[], ?> encryptResult =
        crypto.encryptData(kmsKeyring, EXAMPLE_DATA, encryptionContext);
    final byte[] ciphertext = encryptResult.getResult();

    // 5. Decrypt the data
    final CryptoResult<byte[], ?> decryptResult =
        crypto.decryptData(
            kmsKeyring,
            ciphertext,
            // Verify that the encryption context in the result contains the
            // encryption context supplied to the encryptData method
            encryptionContext);

    // 6. Verify that the decrypted plaintext matches the original plaintext
    assert Arrays.equals(decryptResult.getResult(), EXAMPLE_DATA);
  }
}
```

## Verschlüsseln und Entschlüsseln von Byte-Streams
<a name="java-example-streams"></a>

Das folgende Beispiel zeigt Ihnen, wie Sie AWS Encryption SDK Bytestreams verschlüsseln und entschlüsseln können.

In diesem Beispiel wird ein [Raw AES-Schlüsselbund](use-raw-aes-keyring.md) verwendet.

Bei der Verschlüsselung verwendet dieses Beispiel die `AwsCrypto.builder() .withEncryptionAlgorithm()` Methode, um eine Algorithmussuite ohne [digitale](concepts.md#digital-sigs) Signaturen anzugeben. Bei der Entschlüsselung wird in diesem Beispiel die Methode verwendet, um sicherzustellen, dass der Chiffretext nicht signiert ist. `createUnsignedMessageDecryptingStream()` Die `createUnsignedMessageDecryptingStream()` Methode schlägt fehl, wenn sie auf einen Chiffretext mit einer digitalen Signatur trifft. 

Wenn Sie mit der Standard-Algorithmus-Suite verschlüsseln, die digitale Signaturen enthält, verwenden Sie stattdessen die `createDecryptingStream()` Methode, wie im nächsten Beispiel gezeigt.

```
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazonaws.crypto.keyrings;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CommitmentPolicy;
import com.amazonaws.encryptionsdk.CryptoAlgorithm;
import com.amazonaws.encryptionsdk.CryptoInputStream;
import com.amazonaws.encryptionsdk.jce.JceMasterKey;
import com.amazonaws.util.IOUtils;
import software.amazon.cryptography.materialproviders.IKeyring;
import software.amazon.cryptography.materialproviders.MaterialProviders;
import software.amazon.cryptography.materialproviders.model.AesWrappingAlg;
import software.amazon.cryptography.materialproviders.model.CreateRawAesKeyringInput;
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Map;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;


/**
 * <p>
 * Encrypts and then decrypts a file under a random key.
 *
 * <p>
 * Arguments:
 * <ol>
 * <li>Name of file containing plaintext data to encrypt
 * </ol>
 *
 * <p>
 * This program demonstrates using a standard Java {@link SecretKey} object as a {@link IKeyring} to
 * encrypt and decrypt streaming data.
 */
public class FileStreamingKeyringExample {
    private static String srcFile;

    public static void main(String[] args) throws IOException {
        srcFile = args[0];

        // In this example, we generate a random key. In practice, 
        // you would get a key from an existing store
        SecretKey cryptoKey = retrieveEncryptionKey();

        // Create a Raw Aes Keyring using the random key and an AES-GCM encryption algorithm
        final MaterialProviders materialProviders = MaterialProviders.builder()
                .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
                .build();
        final CreateRawAesKeyringInput keyringInput = CreateRawAesKeyringInput.builder()
                .wrappingKey(ByteBuffer.wrap(cryptoKey.getEncoded()))
                .keyNamespace("Example")
                .keyName("RandomKey")
                .wrappingAlg(AesWrappingAlg.ALG_AES128_GCM_IV12_TAG16)
                .build();
        IKeyring keyring = materialProviders.CreateRawAesKeyring(keyringInput);

        // Instantiate the SDK.
        // This builds the AwsCrypto client with the RequireEncryptRequireDecrypt commitment policy,
        // which means this client only encrypts using committing algorithm suites and enforces
        // that the client will only decrypt encrypted messages that were created with a committing
        // algorithm suite.
        // This is the default commitment policy if you build the client with
        // `AwsCrypto.builder().build()`
        // or `AwsCrypto.standard()`.
        // This example encrypts with an algorithm suite that doesn't include signing for faster decryption,
        // since this use case assumes that the contexts that encrypt and decrypt are equally trusted.
        final AwsCrypto crypto = AwsCrypto.builder()
                .withCommitmentPolicy(CommitmentPolicy.RequireEncryptRequireDecrypt)
                .withEncryptionAlgorithm(CryptoAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY)
                .build();

        // Create an encryption context to identify the ciphertext
        Map<String, String> context = Collections.singletonMap("Example", "FileStreaming");

        // Because the file might be too large to load into memory, we stream the data, instead of 
        //loading it all at once.
        FileInputStream in = new FileInputStream(srcFile);
        CryptoInputStream<JceMasterKey> encryptingStream = crypto.createEncryptingStream(keyring, in, context);

        FileOutputStream out = new FileOutputStream(srcFile + ".encrypted");
        IOUtils.copy(encryptingStream, out);
        encryptingStream.close();
        out.close();

        // Decrypt the file. Verify the encryption context before returning the plaintext.
        // Since the data was encrypted using an unsigned algorithm suite, use the recommended
        // createUnsignedMessageDecryptingStream method, which only accepts unsigned messages.
        in = new FileInputStream(srcFile + ".encrypted");
        CryptoInputStream<JceMasterKey> decryptingStream = crypto.createUnsignedMessageDecryptingStream(keyring, in);
        // Does it contain the expected encryption context?
        if (!"FileStreaming".equals(decryptingStream.getCryptoResult().getEncryptionContext().get("Example"))) {
            throw new IllegalStateException("Bad encryption context");
        }

        // Write the plaintext data to disk.
        out = new FileOutputStream(srcFile + ".decrypted");
        IOUtils.copy(decryptingStream, out);
        decryptingStream.close();
        out.close();
    }

    /**
     * In practice, this key would be saved in a secure location.
     * For this demo, we generate a new random key for each operation.
     */
    private static SecretKey retrieveEncryptionKey() {
        SecureRandom rnd = new SecureRandom();
        byte[] rawKey = new byte[16]; // 128 bits
        rnd.nextBytes(rawKey);
        return new SecretKeySpec(rawKey, "AES");
    }
}
```

## Verschlüsseln und Entschlüsseln von Bytestreams mit einem Mehrfachschlüsselbund
<a name="java-example-multiple-providers"></a>

Das folgende Beispiel zeigt Ihnen, wie Sie den AWS Encryption SDK mit einem [Mehrfachschlüsselbund](use-multi-keyring.md) verwenden. Wenn Sie einen Multi-Schlüsselbund verwenden, um Daten zu verschlüsseln, können alle Umhüllungsschlüssel in einem seiner Schlüsselbunde diese Daten entschlüsseln. In diesem Beispiel werden ein [AWS KMS Schlüsselbund](use-kms-keyring.md) und ein [Raw RSA-Schlüsselbund als untergeordnete Schlüsselanhänger](use-raw-rsa-keyring.md) verwendet.

[In diesem Beispiel wird mit der [Standard-Algorithmussuite](supported-algorithms.md) verschlüsselt, die eine digitale Signatur enthält.](concepts.md#digital-sigs) Beim Streaming AWS Encryption SDK gibt der Klartext nach Integritätsprüfungen, aber bevor die digitale Signatur verifiziert wurde, frei. Um zu vermeiden, dass der Klartext verwendet wird, bis die Signatur verifiziert ist, puffert dieses Beispiel den Klartext und schreibt ihn erst auf die Festplatte, wenn die Entschlüsselung und Überprüfung abgeschlossen sind. 

```
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazonaws.crypto.keyrings;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CommitmentPolicy;
import com.amazonaws.encryptionsdk.CryptoOutputStream;
import com.amazonaws.util.IOUtils;
import software.amazon.cryptography.materialproviders.IKeyring;
import software.amazon.cryptography.materialproviders.MaterialProviders;
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsMultiKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateMultiKeyringInput;
import software.amazon.cryptography.materialproviders.model.CreateRawRsaKeyringInput;
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;
import software.amazon.cryptography.materialproviders.model.PaddingScheme;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Collections;

/**
 * <p>
 * Encrypts a file using both AWS KMS Key and an asymmetric key pair.
 *
 * <p>
 * Arguments:
 * <ol>
 * <li>Key ARN: For help finding the Amazon Resource Name (ARN) of your AWS KMS key,
 *   see 'Viewing Keys' at http://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html
 *
 * <li>Name of file containing plaintext data to encrypt
 * </ol>
 * <p>
 * You might use AWS Key Management Service (AWS KMS) for most encryption and decryption operations, but
 * still want the option of decrypting your data offline independently of AWS KMS. This sample
 * demonstrates one way to do this.
 * <p>
 * The sample encrypts data under both an AWS KMS key and an "escrowed" RSA key pair
 * so that either key alone can decrypt it. You might commonly use the AWS KMS key for decryption. However,
 * at any time, you can use the private RSA key to decrypt the ciphertext independent of AWS KMS.
 * <p>
 * This sample uses the RawRsaKeyring to generate a RSA public-private key pair
 * and saves the key pair in memory. In practice, you would store the private key in a secure offline
 * location, such as an offline HSM, and distribute the public key to your development team.
 */
public class EscrowedEncryptKeyringExample {
    private static ByteBuffer publicEscrowKey;
    private static ByteBuffer privateEscrowKey;

    public static void main(final String[] args) throws Exception {
        // This sample generates a new random key for each operation.
        // In practice, you would distribute the public key and save the private key in secure
        // storage.
        generateEscrowKeyPair();

        final String kmsArn = args[0];
        final String fileName = args[1];

        standardEncrypt(kmsArn, fileName);
        standardDecrypt(kmsArn, fileName);

        escrowDecrypt(fileName);
    }

    private static void standardEncrypt(final String kmsArn, final String fileName) throws Exception {
        // Encrypt with the KMS key and the escrowed public key
        // 1. Instantiate the SDK
        // This builds the AwsCrypto client with the RequireEncryptRequireDecrypt commitment policy,
        // which means this client only encrypts using committing algorithm suites and enforces
        // that the client will only decrypt encrypted messages that were created with a committing
        // algorithm suite.
        // This is the default commitment policy if you build the client with
        // `AwsCrypto.builder().build()`
        // or `AwsCrypto.standard()`.
        final AwsCrypto crypto = AwsCrypto.builder()
                .withCommitmentPolicy(CommitmentPolicy.RequireEncryptRequireDecrypt)
                .build();

        // 2. Create the AWS KMS keyring.
        // This example creates a multi keyring, which automatically creates the KMS client.
        final MaterialProviders matProv = MaterialProviders.builder()
                .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
                .build();
        final CreateAwsKmsMultiKeyringInput keyringInput = CreateAwsKmsMultiKeyringInput.builder()
                .generator(kmsArn)
                .build();
        IKeyring kmsKeyring = matProv.CreateAwsKmsMultiKeyring(keyringInput);

        // 3. Create the Raw Rsa Keyring with Public Key.
        final CreateRawRsaKeyringInput encryptingKeyringInput = CreateRawRsaKeyringInput.builder()
                .keyName("Escrow")
                .keyNamespace("Escrow")
                .paddingScheme(PaddingScheme.OAEP_SHA512_MGF1)
                .publicKey(publicEscrowKey)
                .build();
        IKeyring rsaPublicKeyring = matProv.CreateRawRsaKeyring(encryptingKeyringInput);

        // 4. Create the multi-keyring.
        final CreateMultiKeyringInput createMultiKeyringInput = CreateMultiKeyringInput.builder()
                .generator(kmsKeyring)
                .childKeyrings(Collections.singletonList(rsaPublicKeyring))
                .build();
        IKeyring multiKeyring = matProv.CreateMultiKeyring(createMultiKeyringInput);

        // 5. Encrypt the file
        // To simplify this code example, we omit the encryption context. Production code should always 
        // use an encryption context. 
        final FileInputStream in = new FileInputStream(fileName);
        final FileOutputStream out = new FileOutputStream(fileName + ".encrypted");
        final CryptoOutputStream<?> encryptingStream = crypto.createEncryptingStream(multiKeyring, out);

        IOUtils.copy(in, encryptingStream);
        in.close();
        encryptingStream.close();
    }

    private static void standardDecrypt(final String kmsArn, final String fileName) throws Exception {
        // Decrypt with the AWS KMS key and the escrow public key. 

        // 1. Instantiate the SDK.
        // This builds the AwsCrypto client with the RequireEncryptRequireDecrypt commitment policy,
        // which means this client only encrypts using committing algorithm suites and enforces
        // that the client will only decrypt encrypted messages that were created with a committing
        // algorithm suite.
        // This is the default commitment policy if you build the client with
        // `AwsCrypto.builder().build()`
        // or `AwsCrypto.standard()`.
        final AwsCrypto crypto = AwsCrypto.builder()
                .withCommitmentPolicy(CommitmentPolicy.RequireEncryptRequireDecrypt)
                .build();

        // 2. Create the AWS KMS keyring.
        // This example creates a multi keyring, which automatically creates the KMS client.
        final MaterialProviders matProv = MaterialProviders.builder()
                .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
                .build();
        final CreateAwsKmsMultiKeyringInput keyringInput = CreateAwsKmsMultiKeyringInput.builder()
                .generator(kmsArn)
                .build();
        IKeyring kmsKeyring = matProv.CreateAwsKmsMultiKeyring(keyringInput);

        // 3. Create the Raw Rsa Keyring with Public Key.
        final CreateRawRsaKeyringInput encryptingKeyringInput = CreateRawRsaKeyringInput.builder()
                .keyName("Escrow")
                .keyNamespace("Escrow")
                .paddingScheme(PaddingScheme.OAEP_SHA512_MGF1)
                .publicKey(publicEscrowKey)
                .build();
        IKeyring rsaPublicKeyring = matProv.CreateRawRsaKeyring(encryptingKeyringInput);

        // 4. Create the multi-keyring.
        final CreateMultiKeyringInput createMultiKeyringInput = CreateMultiKeyringInput.builder()
                .generator(kmsKeyring)
                .childKeyrings(Collections.singletonList(rsaPublicKeyring))
                .build();
        IKeyring multiKeyring = matProv.CreateMultiKeyring(createMultiKeyringInput);

        // 5. Decrypt the file
        // To simplify this code example, we omit the encryption context. Production code should always 
        // use an encryption context. 
        final FileInputStream in = new FileInputStream(fileName + ".encrypted");
        final FileOutputStream out = new FileOutputStream(fileName + ".decrypted");
        // Since we are using a signing algorithm suite, we avoid streaming decryption directly to the output file,
        // to ensure that the trailing signature is verified before writing any untrusted plaintext to disk.
        final ByteArrayOutputStream plaintextBuffer = new ByteArrayOutputStream();
        final CryptoOutputStream<?> decryptingStream = crypto.createDecryptingStream(multiKeyring, plaintextBuffer);
        IOUtils.copy(in, decryptingStream);
        in.close();
        decryptingStream.close();
        final ByteArrayInputStream plaintextReader = new ByteArrayInputStream(plaintextBuffer.toByteArray());
        IOUtils.copy(plaintextReader, out);
        out.close();
    }

    private static void escrowDecrypt(final String fileName) throws Exception {
        // You can decrypt the stream using only the private key.
        // This method does not call AWS KMS.

        // 1. Instantiate the SDK
        final AwsCrypto crypto = AwsCrypto.standard();

        // 2. Create the Raw Rsa Keyring with Private Key.
        final MaterialProviders matProv = MaterialProviders.builder()
                .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
                .build();
        final CreateRawRsaKeyringInput encryptingKeyringInput = CreateRawRsaKeyringInput.builder()
                .keyName("Escrow")
                .keyNamespace("Escrow")
                .paddingScheme(PaddingScheme.OAEP_SHA512_MGF1)
                .publicKey(publicEscrowKey)
                .privateKey(privateEscrowKey)
                .build();
        IKeyring escrowPrivateKeyring = matProv.CreateRawRsaKeyring(encryptingKeyringInput);


        // 3. Decrypt the file
        // To simplify this code example, we omit the encryption context. Production code should always 
        // use an encryption context. 
        final FileInputStream in = new FileInputStream(fileName + ".encrypted");
        final FileOutputStream out = new FileOutputStream(fileName + ".deescrowed");
        final CryptoOutputStream<?> decryptingStream = crypto.createDecryptingStream(escrowPrivateKeyring, out);
        IOUtils.copy(in, decryptingStream);
        in.close();
        decryptingStream.close();

    }

    private static void generateEscrowKeyPair() throws GeneralSecurityException {
        final KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
        kg.initialize(4096); // Escrow keys should be very strong
        final KeyPair keyPair = kg.generateKeyPair();
        publicEscrowKey = RawRsaKeyringExample.getPEMPublicKey(keyPair.getPublic());
        privateEscrowKey = RawRsaKeyringExample.getPEMPrivateKey(keyPair.getPrivate());

    }
}
```