/* * Copyright 2014 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.services.dynamodbv2.datamodeling.encryption.providers; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStore.Entry; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.ProtectionParameter; import java.security.KeyStore.SecretKeyEntry; import java.security.KeyStore.TrustedCertificateEntry; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.UnrecoverableEntryException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.AsymmetricRawMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.SymmetricRawMaterials; /** * @author Greg Rubin */ public class KeyStoreMaterialsProvider implements EncryptionMaterialsProvider { private final Map<String, String> description; private final String encryptionAlias; private final String signingAlias; private final ProtectionParameter encryptionProtection; private final ProtectionParameter signingProtection; private final KeyStore keyStore; private final AtomicReference<CurrentMaterials> currMaterials = new AtomicReference<KeyStoreMaterialsProvider.CurrentMaterials>(); public KeyStoreMaterialsProvider(KeyStore keyStore, String encryptionAlias, String signingAlias, Map<String, String> description) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { this(keyStore, encryptionAlias, signingAlias, null, null, description); } public KeyStoreMaterialsProvider(KeyStore keyStore, String encryptionAlias, String signingAlias, ProtectionParameter encryptionProtection, ProtectionParameter signingProtection, Map<String, String> description) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { super(); this.keyStore = keyStore; this.encryptionAlias = encryptionAlias; this.signingAlias = signingAlias; this.encryptionProtection = encryptionProtection; this.signingProtection = signingProtection; this.description = Collections.unmodifiableMap(new HashMap<String, String>(description)); validateKeys(); loadKeys(); } @Override public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) { CurrentMaterials materials = currMaterials.get(); if (context.getMaterialDescription().entrySet().containsAll(description.entrySet())) { if (materials.encryptionEntry instanceof SecretKeyEntry) { return materials.symRawMaterials; } else { try { return makeAsymMaterials(materials, context.getMaterialDescription()); } catch (GeneralSecurityException ex) { throw new DynamoDBMappingException("Unable to decrypt envelope key", ex); } } } else { return null; } } @Override public EncryptionMaterials getEncryptionMaterials(EncryptionContext context) { CurrentMaterials materials = currMaterials.get(); if (materials.encryptionEntry instanceof SecretKeyEntry) { return materials.symRawMaterials; } else { try { return makeAsymMaterials(materials, description); } catch (GeneralSecurityException ex) { throw new DynamoDBMappingException("Unable to encrypt envelope key", ex); } } } private AsymmetricRawMaterials makeAsymMaterials(CurrentMaterials materials, Map<String, String> description) throws GeneralSecurityException { KeyPair encryptionPair = entry2Pair(materials.encryptionEntry); if (materials.signingEntry instanceof SecretKeyEntry) { return new AsymmetricRawMaterials(encryptionPair, ((SecretKeyEntry) materials.signingEntry).getSecretKey(), description); } else { return new AsymmetricRawMaterials(encryptionPair, entry2Pair(materials.signingEntry), description); } } private static KeyPair entry2Pair(Entry entry) { PublicKey pub = null; PrivateKey priv = null; if (entry instanceof PrivateKeyEntry) { PrivateKeyEntry pk = (PrivateKeyEntry) entry; if (pk.getCertificate() != null) { pub = pk.getCertificate().getPublicKey(); } priv = pk.getPrivateKey(); } else if (entry instanceof TrustedCertificateEntry) { TrustedCertificateEntry tc = (TrustedCertificateEntry) entry; pub = tc.getTrustedCertificate().getPublicKey(); } else { throw new IllegalArgumentException( "Only entry types PrivateKeyEntry and TrustedCertificateEntry are supported."); } return new KeyPair(pub, priv); } /** * Reloads the keys from the underlying keystore by calling * {@link KeyStore#getEntry(String, ProtectionParameter)} again for each of them. */ @Override public void refresh() { try { loadKeys(); } catch (GeneralSecurityException ex) { throw new DynamoDBMappingException("Unable to load keys from keystore", ex); } } private void validateKeys() throws KeyStoreException { if (!keyStore.containsAlias(encryptionAlias)) { throw new IllegalArgumentException("Keystore does not contain alias: " + encryptionAlias); } if (!keyStore.containsAlias(signingAlias)) { throw new IllegalArgumentException("Keystore does not contain alias: " + signingAlias); } } private void loadKeys() throws NoSuchAlgorithmException, UnrecoverableEntryException, KeyStoreException { Entry encryptionEntry = keyStore.getEntry(encryptionAlias, encryptionProtection); Entry signingEntry = keyStore.getEntry(signingAlias, signingProtection); CurrentMaterials newMaterials = new CurrentMaterials(encryptionEntry, signingEntry); currMaterials.set(newMaterials); } private class CurrentMaterials { public final Entry encryptionEntry; public final Entry signingEntry; public final SymmetricRawMaterials symRawMaterials; public CurrentMaterials(Entry encryptionEntry, Entry signingEntry) { super(); this.encryptionEntry = encryptionEntry; this.signingEntry = signingEntry; if (encryptionEntry instanceof SecretKeyEntry) { if (signingEntry instanceof SecretKeyEntry) { this.symRawMaterials = new SymmetricRawMaterials( ((SecretKeyEntry) encryptionEntry).getSecretKey(), ((SecretKeyEntry) signingEntry).getSecretKey(), description); } else { this.symRawMaterials = new SymmetricRawMaterials( ((SecretKeyEntry) encryptionEntry).getSecretKey(), entry2Pair(signingEntry), description); } } else { this.symRawMaterials = null; } } } }