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

com.amazonaws.encryptionsdk.jce.JceMasterKey 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.jce;

import com.amazonaws.encryptionsdk.CryptoAlgorithm;
import com.amazonaws.encryptionsdk.DataKey;
import com.amazonaws.encryptionsdk.EncryptedDataKey;
import com.amazonaws.encryptionsdk.MasterKey;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.UnsupportedProviderException;
import com.amazonaws.encryptionsdk.internal.JceKeyCipher;
import com.amazonaws.encryptionsdk.internal.Utils;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Represents a {@link MasterKey} backed by one (or more) JCE {@link Key}s. Instances of this should
 * only be acquired using {@link #getInstance(SecretKey, String, String, String)} or
 * {@link #getInstance(PublicKey, PrivateKey, String, String, String)}.
 */
public class JceMasterKey extends MasterKey {
    private final String providerName_;
    private final String keyId_;
    private final byte[] keyIdBytes_;
    private final JceKeyCipher jceKeyCipher_;

    /**
     * Returns a {@code JceMasterKey} backed by {@code key} using {@code wrappingAlgorithm}.
     * Currently "{@code AES/GCM/NoPadding}" is the only supported value for
     * {@code wrappingAlgorithm}.
     * 
     * @param key
     *            key used to wrap/unwrap (encrypt/decrypt) {@link DataKey}s
     * @param provider
     * @param keyId
     * @param wrappingAlgorithm
     * @return
     */
    public static JceMasterKey getInstance(final SecretKey key, final String provider, final String keyId,
            final String wrappingAlgorithm) {
        switch (wrappingAlgorithm.toUpperCase()) {
            case "AES/GCM/NOPADDING":
                return new JceMasterKey(provider, keyId, JceKeyCipher.aesGcm(key));
            default:
                throw new IllegalArgumentException("Right now only AES/GCM/NoPadding is supported");

        }
    }

    /**
     * Returns a {@code JceMasterKey} backed by {@code unwrappingKey} and {@code wrappingKey} using
     * {@code wrappingAlgorithm}. Currently only RSA algorithms are supported for
     * {@code wrappingAlgorithm}. {@code wrappingAlgorithm}. If {@code unwrappingKey} is
     * {@code null} then the returned {@link JceMasterKey} can only be used for encryption.
     *
     * @param wrappingKey
     *            key used to wrap (encrypt) {@link DataKey}s
     * @param unwrappingKey
     *            (Optional) key used to unwrap (decrypt) {@link DataKey}s.
     */
    public static JceMasterKey getInstance(final PublicKey wrappingKey, final PrivateKey unwrappingKey,
            final String provider, final String keyId,
            final String wrappingAlgorithm) {
        if (wrappingAlgorithm.toUpperCase().startsWith("RSA/ECB/")) {
            return new JceMasterKey(provider, keyId, JceKeyCipher.rsa(wrappingKey, unwrappingKey, wrappingAlgorithm));
        }
        throw new UnsupportedOperationException("Currently only RSA asymmetric algorithms are supported");
    }

    protected JceMasterKey(final String providerName, final String keyId, final JceKeyCipher jceKeyCipher) {
        providerName_ = providerName;
        keyId_ = keyId;
        keyIdBytes_ = keyId_.getBytes(StandardCharsets.UTF_8);
        jceKeyCipher_ = jceKeyCipher;
    }

    @Override
    public String getProviderId() {
        return providerName_;
    }

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

    @Override
    public DataKey generateDataKey(final CryptoAlgorithm algorithm,
            final Map encryptionContext) {
        final byte[] rawKey = new byte[algorithm.getDataKeyLength()];
        Utils.getSecureRandom().nextBytes(rawKey);
        EncryptedDataKey encryptedDataKey = jceKeyCipher_.encryptKey(rawKey, keyId_, providerName_, encryptionContext);
        return new DataKey<>(new SecretKeySpec(rawKey, algorithm.getDataKeyAlgo()),
                encryptedDataKey.getEncryptedDataKey(), encryptedDataKey.getProviderInformation(), this);
    }

    @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("Can only re-encrypt data keys which are in RAW format, not "
                    + dataKey.getKey().getFormat());
        }
        if (!key.getAlgorithm().equalsIgnoreCase(algorithm.getDataKeyAlgo())) {
            throw new IllegalArgumentException("Incorrect key algorithm. Expected " + key.getAlgorithm()
                    + " but got " + algorithm.getKeyAlgo());
        }
        EncryptedDataKey encryptedDataKey = jceKeyCipher_.encryptKey(key.getEncoded(), keyId_, providerName_, encryptionContext);
        return new DataKey<>(key, encryptedDataKey.getEncryptedDataKey(), encryptedDataKey.getProviderInformation(), this);
    }

    @Override
    public DataKey decryptDataKey(final CryptoAlgorithm algorithm,
            final Collection encryptedDataKeys,
            final Map encryptionContext)
            throws UnsupportedProviderException, AwsCryptoException {
        final List exceptions = new ArrayList<>();
        // Find an encrypted key who's provider and info match us
        for (final EncryptedDataKey edk : encryptedDataKeys) {
            try {
                if (edk.getProviderId().equals(getProviderId())
                        && Utils.arrayPrefixEquals(edk.getProviderInformation(), keyIdBytes_, keyIdBytes_.length)) {
                    final byte[] decryptedKey = jceKeyCipher_.decryptKey(edk, keyId_, encryptionContext);

                    // Validate that the decrypted key length is as expected
                    if (decryptedKey.length == algorithm.getDataKeyLength()) {
                        return new DataKey<>(new SecretKeySpec(decryptedKey, algorithm.getDataKeyAlgo()),
                                edk.getEncryptedDataKey(), edk.getProviderInformation(), this);
                    }
                }
            } catch (final Exception ex) {
                exceptions.add(ex);
            }
        }
        throw buildCannotDecryptDksException(exceptions);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy