Skip to content

Commit

Permalink
Defining Keyring interface, RawAesKeyring and RawRsaKeyring.
Browse files Browse the repository at this point in the history
*Issue #, if available:* #102

*Description of changes:*

This change defines the Keyring interface, RawAesKeyring and RawRsaKeyring.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

# Check any applicable:
- [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files.
  • Loading branch information
WesleyRosenblum committed Nov 11, 2019
1 parent 282cd6f commit c2bcffe
Show file tree
Hide file tree
Showing 14 changed files with 871 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,11 @@ public DefaultCryptoMaterialsManager(MasterKeyProvider<?> mkp) {
}

return DecryptionMaterials.newBuilder()
.setDataKey(dataKey)
.setTrailingSignatureKey(pubKey)
.build();
.setAlgorithm(request.getAlgorithm())
.setCleartextDataKey(dataKey.getKey())
.setMasterKey(dataKey.getMasterKey())
.setTrailingSignatureKey(pubKey)
.build();
}

private PublicKey deserializeTrailingKeyFromEc(CryptoAlgorithm algo, String pubKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

import com.amazonaws.encryptionsdk.CryptoAlgorithm;
import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
import com.amazonaws.encryptionsdk.DataKey;
import com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager;
import com.amazonaws.encryptionsdk.MasterKey;
import com.amazonaws.encryptionsdk.MasterKeyProvider;
Expand Down Expand Up @@ -61,7 +60,8 @@ public class DecryptionHandler<K extends MasterKey<K>> implements MessageCryptoH

private CryptoHandler contentCryptoHandler_;

private DataKey<K> dataKey_;
private SecretKey dataKey_;
private K masterKey_;
private SecretKey decryptionKey_;
private CryptoAlgorithm cryptoAlgo_;
private Signature trailingSig_;
Expand Down Expand Up @@ -454,12 +454,13 @@ private void readHeaderFields(final CiphertextHeaders ciphertextHeaders) {

DecryptionMaterials result = materialsManager_.decryptMaterials(request);

dataKey_ = result.getCleartextDataKey();
//noinspection unchecked
dataKey_ = (DataKey<K>)result.getDataKey();
masterKey_ = (K)result.getMasterKey();
PublicKey trailingPublicKey = result.getTrailingSignatureKey();

try {
decryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(dataKey_.getKey(), ciphertextHeaders);
decryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(dataKey_, ciphertextHeaders);
} catch (final InvalidKeyException ex) {
throw new AwsCryptoException(ex);
}
Expand Down Expand Up @@ -536,7 +537,7 @@ public CiphertextHeaders getHeaders() {

@Override
public List<K> getMasterKeys() {
return Collections.singletonList(dataKey_.getMasterKey());
return Collections.singletonList(masterKey_);
}

@Override
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2019 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.keyrings;

import com.amazonaws.encryptionsdk.EncryptedDataKey;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;

import javax.crypto.SecretKey;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.List;

/**
* Keyrings are responsible for the generation, encryption, and decryption of data keys.
*/
public interface Keyring {

/**
* Generate a data key if not present and encrypt it using any available wrapping key
*
* @param encryptionMaterials Materials needed for encryption that the keyring may modify.
*/
void onEncrypt(EncryptionMaterials encryptionMaterials);

/**
* Attempt to decrypt the encrypted data keys
*
* @param decryptionMaterials Materials needed for decryption that the keyring may modify.
* @param encryptedDataKeys List of encrypted data keys.
*/
void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys);

/**
* Constructs a {@code Keyring} which does local AES-GCM encryption
* decryption of data keys using the provided wrapping key.
*
* @param keyNamespace A UTF-8 encoded value that, together with the key name, identifies the wrapping key.
* @param keyName A UTF-8 encoded value that, together with the key namespace, identifies the wrapping key.
* @param wrappingKey The AES key input to AES-GCM to encrypt plaintext data keys.
* @return The {@link Keyring}
*/
static Keyring rawAes(String keyNamespace, String keyName, SecretKey wrappingKey) {
return new RawAesKeyring(keyNamespace, keyName, wrappingKey);
}

/**
* Constructs a {@code Keyring} which does local RSA encryption and decryption of data keys using the
* provided public and private keys. If {@code privateKey} is {@code null} then the returned {@code Keyring}
* can only be used for encryption.
*
* @param keyNamespace A UTF-8 encoded value that, together with the key name, identifies the wrapping key.
* @param keyName A UTF-8 encoded value that, together with the key namespace, identifies the wrapping key.
* @param publicKey The RSA public key used by this keyring to encrypt data keys.
* @param privateKey The RSA private key used by this keyring to decrypt data keys.
* @param wrappingAlgorithm The RSA algorithm to use with this keyring.
* @return The {@link Keyring}
*/
static Keyring rawRsa(String keyNamespace, String keyName, PublicKey publicKey, PrivateKey privateKey, String wrappingAlgorithm) {
return new RawRsaKeyring(keyNamespace, keyName, publicKey, privateKey, wrappingAlgorithm);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2019 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.keyrings;

import com.amazonaws.encryptionsdk.EncryptedDataKey;
import com.amazonaws.encryptionsdk.internal.JceKeyCipher;
import com.amazonaws.encryptionsdk.internal.Utils;

import javax.crypto.SecretKey;

/**
* A {@code Keyring} which does local AES-GCM encryption
* decryption of data keys using the provided wrapping key.
*
* Instantiate by using the {@code Keyring.rawAes(...)} factory method.
*/
class RawAesKeyring extends RawKeyring {

RawAesKeyring(String keyNamespace, String keyName, SecretKey wrappingKey) {
super(keyNamespace, keyName, JceKeyCipher.aesGcm(wrappingKey));
}

@Override
boolean validToDecrypt(EncryptedDataKey encryptedDataKey) {

// the key provider ID of the encrypted data key must
// have a value equal to this keyring's key namespace.
if (!keyNamespace.equals(encryptedDataKey.getProviderId())) {
return false;
}

// the key name obtained from the encrypted data key's key provider
// information must have a value equal to this keyring's key name.
if (!Utils.arrayPrefixEquals(encryptedDataKey.getProviderInformation(), keyNameBytes, keyNameBytes.length)) {
return false;
}

return true;
}

@Override
void traceOnEncrypt(KeyringTrace keyringTrace) {
keyringTrace.add(keyNamespace, keyName,
KeyringTraceFlag.ENCRYPTED_DATA_KEY,
KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT,
KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT);
}

@Override
void traceOnDecrypt(KeyringTrace keyringTrace) {
keyringTrace.add(keyNamespace, keyName,
KeyringTraceFlag.DECRYPTED_DATA_KEY,
KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT,
KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT);
}
}
136 changes: 136 additions & 0 deletions src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright 2019 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.keyrings;

import com.amazonaws.encryptionsdk.EncryptedDataKey;
import com.amazonaws.encryptionsdk.internal.JceKeyCipher;
import com.amazonaws.encryptionsdk.internal.Utils;
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
import com.amazonaws.encryptionsdk.model.KeyBlob;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.logging.Logger;

import static org.apache.commons.lang3.Validate.notBlank;
import static org.apache.commons.lang3.Validate.notNull;

/**
* A keyring supporting local encryption and decryption using either RSA or AES-GCM.
*/
abstract class RawKeyring implements Keyring {

final String keyNamespace;
final String keyName;
final byte[] keyNameBytes;
private final JceKeyCipher jceKeyCipher;
private static final Charset KEY_NAME_ENCODING = StandardCharsets.UTF_8;
private static final Logger LOGGER = Logger.getLogger(RawKeyring.class.getName());

RawKeyring(final String keyNamespace, final String keyName, JceKeyCipher jceKeyCipher) {
notBlank(keyNamespace, "keyNamespace is required");
notBlank(keyName, "keyName is required");
notNull(jceKeyCipher, "jceKeyCipher is required");

this.keyNamespace = keyNamespace;
this.keyName = keyName;
this.keyNameBytes = keyName.getBytes(KEY_NAME_ENCODING);
this.jceKeyCipher = jceKeyCipher;
}

/**
* Returns true if the given encrypted data key may be decrypted with this keyring.
*
* @param encryptedDataKey The encrypted data key.
* @return True if the key may be decrypted, false otherwise.
*/
abstract boolean validToDecrypt(EncryptedDataKey encryptedDataKey);

/**
* Records trace entries for the given keyring upon successful encryption.
*
* @param keyringTrace The keyring trace to record to.
*/
abstract void traceOnEncrypt(KeyringTrace keyringTrace);

/**
* Records trace entries for the given keyring upon successful decryption.
*
* @param keyringTrace The keyring trace to record to.
*/
abstract void traceOnDecrypt(KeyringTrace keyringTrace);

@Override
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
notNull(encryptionMaterials, "encryptionMaterials are required");

if (encryptionMaterials.getCleartextDataKey() == null) {
generateDataKey(encryptionMaterials);
}

final SecretKey cleartextDataKey = encryptionMaterials.getCleartextDataKey();

if (!cleartextDataKey.getAlgorithm().equalsIgnoreCase(encryptionMaterials.getAlgorithm().getDataKeyAlgo())) {
throw new IllegalArgumentException("Incorrect key algorithm. Expected " + cleartextDataKey.getAlgorithm()
+ " but got " + encryptionMaterials.getAlgorithm().getDataKeyAlgo());
}

final EncryptedDataKey encryptedDataKey = jceKeyCipher.encryptKey(
cleartextDataKey.getEncoded(), keyName, keyNamespace, encryptionMaterials.getEncryptionContext());
encryptionMaterials.getEncryptedDataKeys().add(new KeyBlob(encryptedDataKey));

traceOnEncrypt(encryptionMaterials.getKeyringTrace());
}

@Override
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
notNull(decryptionMaterials, "decryptionMaterials are required");
notNull(encryptedDataKeys, "encryptedDataKeys are required");

if (decryptionMaterials.getCleartextDataKey() != null) {
return;
}

for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) {
if (validToDecrypt(encryptedDataKey)) {
try {
final byte[] decryptedKey = jceKeyCipher.decryptKey(
encryptedDataKey, keyName, decryptionMaterials.getEncryptionContext());
decryptionMaterials.setCleartextDataKey(
new SecretKeySpec(decryptedKey, decryptionMaterials.getAlgorithm().getDataKeyAlgo()));
traceOnDecrypt(decryptionMaterials.getKeyringTrace());
return;
} catch (Exception e) {
LOGGER.info("Could not decrypt key due to: " + e.getMessage());
}
}
}

LOGGER.warning("Could not decrypt any data keys");
}

private void generateDataKey(EncryptionMaterials encryptionMaterials) {
final byte[] rawKey = new byte[encryptionMaterials.getAlgorithm().getDataKeyLength()];
Utils.getSecureRandom().nextBytes(rawKey);
final SecretKey key = new SecretKeySpec(rawKey, encryptionMaterials.getAlgorithm().getDataKeyAlgo());

encryptionMaterials.setCleartextDataKey(key);
encryptionMaterials.getKeyringTrace().add(keyNamespace, keyName, KeyringTraceFlag.GENERATED_DATA_KEY);
}
}
Loading

0 comments on commit c2bcffe

Please sign in to comment.