All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.encryptionsdk.kms.KmsMasterKey Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/*
 * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
 * in compliance with the License. A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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 com.amazonaws.encryptionsdk.kms;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CryptoAlgorithm;
import com.amazonaws.encryptionsdk.DataKey;
import com.amazonaws.encryptionsdk.EncryptedDataKey;
import com.amazonaws.encryptionsdk.MasterKey;
import com.amazonaws.encryptionsdk.MasterKeyProvider;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
import com.amazonaws.encryptionsdk.internal.VersionInfo;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.model.DecryptRequest;
import com.amazonaws.services.kms.model.DecryptResult;
import com.amazonaws.services.kms.model.EncryptRequest;
import com.amazonaws.services.kms.model.EncryptResult;
import com.amazonaws.services.kms.model.GenerateDataKeyRequest;
import com.amazonaws.services.kms.model.GenerateDataKeyResult;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Represents a single Customer Master Key (CMK) and is used to encrypt/decrypt data with {@link
 * AwsCrypto}.
 */
public final class KmsMasterKey extends MasterKey implements KmsMethods {
  private final Supplier kms_;
  private final MasterKeyProvider sourceProvider_;
  private final String id_;
  private final List grantTokens_ = new ArrayList<>();

  private  T updateUserAgent(T request) {
    request.getRequestClientOptions().appendUserAgent(VersionInfo.USER_AGENT);

    return request;
  }

  /** @deprecated Use a {@link KmsMasterKeyProvider} to obtain {@link KmsMasterKey}s. */
  @Deprecated
  public static KmsMasterKey getInstance(final AWSCredentials creds, final String keyId) {
    return new KmsMasterKeyProvider(creds, keyId).getMasterKey(keyId);
  }

  /** @deprecated Use a {@link KmsMasterKeyProvider} to obtain {@link KmsMasterKey}s. */
  @Deprecated
  public static KmsMasterKey getInstance(final AWSCredentialsProvider creds, final String keyId) {
    return new KmsMasterKeyProvider(creds, keyId).getMasterKey(keyId);
  }

  static KmsMasterKey getInstance(
      final Supplier kms, final String id, final MasterKeyProvider provider) {
    return new KmsMasterKey(kms, id, provider);
  }

  private KmsMasterKey(
      final Supplier kms, final String id, final MasterKeyProvider provider) {
    kms_ = kms;
    id_ = id;
    sourceProvider_ = provider;
  }

  @Override
  public String getProviderId() {
    return sourceProvider_.getDefaultProviderId();
  }

  @Override
  public String getKeyId() {
    return id_;
  }

  @Override
  public DataKey generateDataKey(
      final CryptoAlgorithm algorithm, final Map encryptionContext) {
    final GenerateDataKeyResult gdkResult =
        kms_.get()
            .generateDataKey(
                updateUserAgent(
                    new GenerateDataKeyRequest()
                        .withKeyId(getKeyId())
                        .withNumberOfBytes(algorithm.getDataKeyLength())
                        .withEncryptionContext(encryptionContext)
                        .withGrantTokens(grantTokens_)));
    final byte[] rawKey = new byte[algorithm.getDataKeyLength()];
    gdkResult.getPlaintext().get(rawKey);
    if (gdkResult.getPlaintext().remaining() > 0) {
      throw new IllegalStateException("Recieved an unexpected number of bytes from KMS");
    }
    final byte[] encryptedKey = new byte[gdkResult.getCiphertextBlob().remaining()];
    gdkResult.getCiphertextBlob().get(encryptedKey);

    final SecretKeySpec key = new SecretKeySpec(rawKey, algorithm.getDataKeyAlgo());
    return new DataKey<>(
        key, encryptedKey, gdkResult.getKeyId().getBytes(StandardCharsets.UTF_8), this);
  }

  @Override
  public void setGrantTokens(final List grantTokens) {
    grantTokens_.clear();
    grantTokens_.addAll(grantTokens);
  }

  @Override
  public List getGrantTokens() {
    return grantTokens_;
  }

  @Override
  public void addGrantToken(final String grantToken) {
    grantTokens_.add(grantToken);
  }

  @Override
  public DataKey encryptDataKey(
      final CryptoAlgorithm algorithm,
      final Map encryptionContext,
      final DataKey dataKey) {
    final SecretKey key = dataKey.getKey();
    if (!key.getFormat().equals("RAW")) {
      throw new IllegalArgumentException("Only RAW encoded keys are supported");
    }
    try {
      final EncryptResult encryptResult =
          kms_.get()
              .encrypt(
                  updateUserAgent(
                      new EncryptRequest()
                          .withKeyId(id_)
                          .withPlaintext(ByteBuffer.wrap(key.getEncoded()))
                          .withEncryptionContext(encryptionContext)
                          .withGrantTokens(grantTokens_)));
      final byte[] edk = new byte[encryptResult.getCiphertextBlob().remaining()];
      encryptResult.getCiphertextBlob().get(edk);
      return new DataKey<>(
          dataKey.getKey(), edk, encryptResult.getKeyId().getBytes(StandardCharsets.UTF_8), this);
    } catch (final AmazonServiceException asex) {
      throw new AwsCryptoException(asex);
    }
  }

  @Override
  public DataKey decryptDataKey(
      final CryptoAlgorithm algorithm,
      final Collection encryptedDataKeys,
      final Map encryptionContext)
      throws UnsupportedProviderException, AwsCryptoException {
    final List exceptions = new ArrayList<>();
    for (final EncryptedDataKey edk : encryptedDataKeys) {
      try {
        final String edkKeyId = new String(edk.getProviderInformation(), StandardCharsets.UTF_8);
        if (!edkKeyId.equals(id_)) {
          continue;
        }
        final DecryptResult decryptResult =
            kms_.get()
                .decrypt(
                    updateUserAgent(
                        new DecryptRequest()
                            .withCiphertextBlob(ByteBuffer.wrap(edk.getEncryptedDataKey()))
                            .withEncryptionContext(encryptionContext)
                            .withGrantTokens(grantTokens_)
                            .withKeyId(edkKeyId)));
        if (decryptResult.getKeyId() == null) {
          throw new IllegalStateException("Received an empty keyId from KMS");
        }
        if (decryptResult.getKeyId().equals(id_)) {
          final byte[] rawKey = new byte[algorithm.getDataKeyLength()];
          decryptResult.getPlaintext().get(rawKey);
          if (decryptResult.getPlaintext().remaining() > 0) {
            throw new IllegalStateException("Received an unexpected number of bytes from KMS");
          }
          return new DataKey<>(
              new SecretKeySpec(rawKey, algorithm.getDataKeyAlgo()),
              edk.getEncryptedDataKey(),
              edk.getProviderInformation(),
              this);
        }
      } catch (final AmazonServiceException awsex) {
        exceptions.add(awsex);
      }
    }

    throw buildCannotDecryptDksException(exceptions);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy