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

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

/*
 * 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 javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
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 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;

/**
 * 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;
    }

    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