Skip to content

Commit c2bcffe

Browse files
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.
1 parent 282cd6f commit c2bcffe

14 files changed

+871
-39
lines changed

src/main/java/com/amazonaws/encryptionsdk/DefaultCryptoMaterialsManager.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,11 @@ public DefaultCryptoMaterialsManager(MasterKeyProvider<?> mkp) {
131131
}
132132

133133
return DecryptionMaterials.newBuilder()
134-
.setDataKey(dataKey)
135-
.setTrailingSignatureKey(pubKey)
136-
.build();
134+
.setAlgorithm(request.getAlgorithm())
135+
.setCleartextDataKey(dataKey.getKey())
136+
.setMasterKey(dataKey.getMasterKey())
137+
.setTrailingSignatureKey(pubKey)
138+
.build();
137139
}
138140

139141
private PublicKey deserializeTrailingKeyFromEc(CryptoAlgorithm algo, String pubKey) {

src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
import com.amazonaws.encryptionsdk.CryptoAlgorithm;
3030
import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
31-
import com.amazonaws.encryptionsdk.DataKey;
3231
import com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager;
3332
import com.amazonaws.encryptionsdk.MasterKey;
3433
import com.amazonaws.encryptionsdk.MasterKeyProvider;
@@ -61,7 +60,8 @@ public class DecryptionHandler<K extends MasterKey<K>> implements MessageCryptoH
6160

6261
private CryptoHandler contentCryptoHandler_;
6362

64-
private DataKey<K> dataKey_;
63+
private SecretKey dataKey_;
64+
private K masterKey_;
6565
private SecretKey decryptionKey_;
6666
private CryptoAlgorithm cryptoAlgo_;
6767
private Signature trailingSig_;
@@ -454,12 +454,13 @@ private void readHeaderFields(final CiphertextHeaders ciphertextHeaders) {
454454

455455
DecryptionMaterials result = materialsManager_.decryptMaterials(request);
456456

457+
dataKey_ = result.getCleartextDataKey();
457458
//noinspection unchecked
458-
dataKey_ = (DataKey<K>)result.getDataKey();
459+
masterKey_ = (K)result.getMasterKey();
459460
PublicKey trailingPublicKey = result.getTrailingSignatureKey();
460461

461462
try {
462-
decryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(dataKey_.getKey(), ciphertextHeaders);
463+
decryptionKey_ = cryptoAlgo_.getEncryptionKeyFromDataKey(dataKey_, ciphertextHeaders);
463464
} catch (final InvalidKeyException ex) {
464465
throw new AwsCryptoException(ex);
465466
}
@@ -536,7 +537,7 @@ public CiphertextHeaders getHeaders() {
536537

537538
@Override
538539
public List<K> getMasterKeys() {
539-
return Collections.singletonList(dataKey_.getMasterKey());
540+
return Collections.singletonList(masterKey_);
540541
}
541542

542543
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
5+
* in compliance with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package com.amazonaws.encryptionsdk.keyrings;
15+
16+
import com.amazonaws.encryptionsdk.EncryptedDataKey;
17+
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
18+
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
19+
20+
import javax.crypto.SecretKey;
21+
import java.security.PrivateKey;
22+
import java.security.PublicKey;
23+
import java.util.List;
24+
25+
/**
26+
* Keyrings are responsible for the generation, encryption, and decryption of data keys.
27+
*/
28+
public interface Keyring {
29+
30+
/**
31+
* Generate a data key if not present and encrypt it using any available wrapping key
32+
*
33+
* @param encryptionMaterials Materials needed for encryption that the keyring may modify.
34+
*/
35+
void onEncrypt(EncryptionMaterials encryptionMaterials);
36+
37+
/**
38+
* Attempt to decrypt the encrypted data keys
39+
*
40+
* @param decryptionMaterials Materials needed for decryption that the keyring may modify.
41+
* @param encryptedDataKeys List of encrypted data keys.
42+
*/
43+
void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys);
44+
45+
/**
46+
* Constructs a {@code Keyring} which does local AES-GCM encryption
47+
* decryption of data keys using the provided wrapping key.
48+
*
49+
* @param keyNamespace A UTF-8 encoded value that, together with the key name, identifies the wrapping key.
50+
* @param keyName A UTF-8 encoded value that, together with the key namespace, identifies the wrapping key.
51+
* @param wrappingKey The AES key input to AES-GCM to encrypt plaintext data keys.
52+
* @return The {@link Keyring}
53+
*/
54+
static Keyring rawAes(String keyNamespace, String keyName, SecretKey wrappingKey) {
55+
return new RawAesKeyring(keyNamespace, keyName, wrappingKey);
56+
}
57+
58+
/**
59+
* Constructs a {@code Keyring} which does local RSA encryption and decryption of data keys using the
60+
* provided public and private keys. If {@code privateKey} is {@code null} then the returned {@code Keyring}
61+
* can only be used for encryption.
62+
*
63+
* @param keyNamespace A UTF-8 encoded value that, together with the key name, identifies the wrapping key.
64+
* @param keyName A UTF-8 encoded value that, together with the key namespace, identifies the wrapping key.
65+
* @param publicKey The RSA public key used by this keyring to encrypt data keys.
66+
* @param privateKey The RSA private key used by this keyring to decrypt data keys.
67+
* @param wrappingAlgorithm The RSA algorithm to use with this keyring.
68+
* @return The {@link Keyring}
69+
*/
70+
static Keyring rawRsa(String keyNamespace, String keyName, PublicKey publicKey, PrivateKey privateKey, String wrappingAlgorithm) {
71+
return new RawRsaKeyring(keyNamespace, keyName, publicKey, privateKey, wrappingAlgorithm);
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
5+
* in compliance with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package com.amazonaws.encryptionsdk.keyrings;
15+
16+
import com.amazonaws.encryptionsdk.EncryptedDataKey;
17+
import com.amazonaws.encryptionsdk.internal.JceKeyCipher;
18+
import com.amazonaws.encryptionsdk.internal.Utils;
19+
20+
import javax.crypto.SecretKey;
21+
22+
/**
23+
* A {@code Keyring} which does local AES-GCM encryption
24+
* decryption of data keys using the provided wrapping key.
25+
*
26+
* Instantiate by using the {@code Keyring.rawAes(...)} factory method.
27+
*/
28+
class RawAesKeyring extends RawKeyring {
29+
30+
RawAesKeyring(String keyNamespace, String keyName, SecretKey wrappingKey) {
31+
super(keyNamespace, keyName, JceKeyCipher.aesGcm(wrappingKey));
32+
}
33+
34+
@Override
35+
boolean validToDecrypt(EncryptedDataKey encryptedDataKey) {
36+
37+
// the key provider ID of the encrypted data key must
38+
// have a value equal to this keyring's key namespace.
39+
if (!keyNamespace.equals(encryptedDataKey.getProviderId())) {
40+
return false;
41+
}
42+
43+
// the key name obtained from the encrypted data key's key provider
44+
// information must have a value equal to this keyring's key name.
45+
if (!Utils.arrayPrefixEquals(encryptedDataKey.getProviderInformation(), keyNameBytes, keyNameBytes.length)) {
46+
return false;
47+
}
48+
49+
return true;
50+
}
51+
52+
@Override
53+
void traceOnEncrypt(KeyringTrace keyringTrace) {
54+
keyringTrace.add(keyNamespace, keyName,
55+
KeyringTraceFlag.ENCRYPTED_DATA_KEY,
56+
KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT,
57+
KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT);
58+
}
59+
60+
@Override
61+
void traceOnDecrypt(KeyringTrace keyringTrace) {
62+
keyringTrace.add(keyNamespace, keyName,
63+
KeyringTraceFlag.DECRYPTED_DATA_KEY,
64+
KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT,
65+
KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT);
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
5+
* in compliance with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package com.amazonaws.encryptionsdk.keyrings;
15+
16+
import com.amazonaws.encryptionsdk.EncryptedDataKey;
17+
import com.amazonaws.encryptionsdk.internal.JceKeyCipher;
18+
import com.amazonaws.encryptionsdk.internal.Utils;
19+
import com.amazonaws.encryptionsdk.model.DecryptionMaterials;
20+
import com.amazonaws.encryptionsdk.model.EncryptionMaterials;
21+
import com.amazonaws.encryptionsdk.model.KeyBlob;
22+
23+
import javax.crypto.SecretKey;
24+
import javax.crypto.spec.SecretKeySpec;
25+
26+
import java.nio.charset.Charset;
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.List;
29+
import java.util.logging.Logger;
30+
31+
import static org.apache.commons.lang3.Validate.notBlank;
32+
import static org.apache.commons.lang3.Validate.notNull;
33+
34+
/**
35+
* A keyring supporting local encryption and decryption using either RSA or AES-GCM.
36+
*/
37+
abstract class RawKeyring implements Keyring {
38+
39+
final String keyNamespace;
40+
final String keyName;
41+
final byte[] keyNameBytes;
42+
private final JceKeyCipher jceKeyCipher;
43+
private static final Charset KEY_NAME_ENCODING = StandardCharsets.UTF_8;
44+
private static final Logger LOGGER = Logger.getLogger(RawKeyring.class.getName());
45+
46+
RawKeyring(final String keyNamespace, final String keyName, JceKeyCipher jceKeyCipher) {
47+
notBlank(keyNamespace, "keyNamespace is required");
48+
notBlank(keyName, "keyName is required");
49+
notNull(jceKeyCipher, "jceKeyCipher is required");
50+
51+
this.keyNamespace = keyNamespace;
52+
this.keyName = keyName;
53+
this.keyNameBytes = keyName.getBytes(KEY_NAME_ENCODING);
54+
this.jceKeyCipher = jceKeyCipher;
55+
}
56+
57+
/**
58+
* Returns true if the given encrypted data key may be decrypted with this keyring.
59+
*
60+
* @param encryptedDataKey The encrypted data key.
61+
* @return True if the key may be decrypted, false otherwise.
62+
*/
63+
abstract boolean validToDecrypt(EncryptedDataKey encryptedDataKey);
64+
65+
/**
66+
* Records trace entries for the given keyring upon successful encryption.
67+
*
68+
* @param keyringTrace The keyring trace to record to.
69+
*/
70+
abstract void traceOnEncrypt(KeyringTrace keyringTrace);
71+
72+
/**
73+
* Records trace entries for the given keyring upon successful decryption.
74+
*
75+
* @param keyringTrace The keyring trace to record to.
76+
*/
77+
abstract void traceOnDecrypt(KeyringTrace keyringTrace);
78+
79+
@Override
80+
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
81+
notNull(encryptionMaterials, "encryptionMaterials are required");
82+
83+
if (encryptionMaterials.getCleartextDataKey() == null) {
84+
generateDataKey(encryptionMaterials);
85+
}
86+
87+
final SecretKey cleartextDataKey = encryptionMaterials.getCleartextDataKey();
88+
89+
if (!cleartextDataKey.getAlgorithm().equalsIgnoreCase(encryptionMaterials.getAlgorithm().getDataKeyAlgo())) {
90+
throw new IllegalArgumentException("Incorrect key algorithm. Expected " + cleartextDataKey.getAlgorithm()
91+
+ " but got " + encryptionMaterials.getAlgorithm().getDataKeyAlgo());
92+
}
93+
94+
final EncryptedDataKey encryptedDataKey = jceKeyCipher.encryptKey(
95+
cleartextDataKey.getEncoded(), keyName, keyNamespace, encryptionMaterials.getEncryptionContext());
96+
encryptionMaterials.getEncryptedDataKeys().add(new KeyBlob(encryptedDataKey));
97+
98+
traceOnEncrypt(encryptionMaterials.getKeyringTrace());
99+
}
100+
101+
@Override
102+
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
103+
notNull(decryptionMaterials, "decryptionMaterials are required");
104+
notNull(encryptedDataKeys, "encryptedDataKeys are required");
105+
106+
if (decryptionMaterials.getCleartextDataKey() != null) {
107+
return;
108+
}
109+
110+
for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) {
111+
if (validToDecrypt(encryptedDataKey)) {
112+
try {
113+
final byte[] decryptedKey = jceKeyCipher.decryptKey(
114+
encryptedDataKey, keyName, decryptionMaterials.getEncryptionContext());
115+
decryptionMaterials.setCleartextDataKey(
116+
new SecretKeySpec(decryptedKey, decryptionMaterials.getAlgorithm().getDataKeyAlgo()));
117+
traceOnDecrypt(decryptionMaterials.getKeyringTrace());
118+
return;
119+
} catch (Exception e) {
120+
LOGGER.info("Could not decrypt key due to: " + e.getMessage());
121+
}
122+
}
123+
}
124+
125+
LOGGER.warning("Could not decrypt any data keys");
126+
}
127+
128+
private void generateDataKey(EncryptionMaterials encryptionMaterials) {
129+
final byte[] rawKey = new byte[encryptionMaterials.getAlgorithm().getDataKeyLength()];
130+
Utils.getSecureRandom().nextBytes(rawKey);
131+
final SecretKey key = new SecretKeySpec(rawKey, encryptionMaterials.getAlgorithm().getDataKeyAlgo());
132+
133+
encryptionMaterials.setCleartextDataKey(key);
134+
encryptionMaterials.getKeyringTrace().add(keyNamespace, keyName, KeyringTraceFlag.GENERATED_DATA_KEY);
135+
}
136+
}

0 commit comments

Comments
 (0)