-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Defining Keyring interface, RawAesKeyring and RawRsaKeyring.
*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
1 parent
282cd6f
commit c2bcffe
Showing
14 changed files
with
871 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
src/main/java/com/amazonaws/encryptionsdk/keyrings/Keyring.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
136
src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.