Skip to content

Commit 4bbe029

Browse files
Defining Keyring interface and implementing RawKeyring.
*Issue #, if available:* #102 *Description of changes:* This change defines the Keyring interface and an implementation of a RawKeyring which supports both AES and RSA. 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 2fe1f0d commit 4bbe029

File tree

9 files changed

+321
-38
lines changed

9 files changed

+321
-38
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<EncryptedDataKey> encryptedDataKeys);
44+
45+
/**
46+
* Constructs a {@link 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 RawKeyring.aes(keyNamespace, keyName, wrappingKey);
56+
}
57+
58+
/**
59+
* Constructs a {@link 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 {@link 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 RawKeyring.rsa(keyNamespace, keyName, publicKey, privateKey, wrappingAlgorithm);
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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.security.PrivateKey;
29+
import java.security.PublicKey;
30+
import java.util.List;
31+
import java.util.logging.Logger;
32+
33+
import static org.apache.commons.lang3.Validate.notBlank;
34+
import static org.apache.commons.lang3.Validate.notNull;
35+
36+
/**
37+
* A keyring supporting local encryption and decryption using either RSA or AES-GCM.
38+
*/
39+
public class RawKeyring implements Keyring {
40+
41+
private final String keyNamespace;
42+
private final String keyName;
43+
private final JceKeyCipher jceKeyCipher;
44+
private static final Charset KEY_NAME_ENCODING = StandardCharsets.UTF_8;
45+
private static final Logger LOGGER = Logger.getLogger(RawKeyring.class.getName());
46+
47+
static Keyring aes(String keyNamespace, String keyName, SecretKey wrappingKey) {
48+
return new RawKeyring(keyNamespace, keyName, JceKeyCipher.aesGcm(wrappingKey));
49+
}
50+
51+
static Keyring rsa(String keyNamespace, String keyName, PublicKey publicKey, PrivateKey privateKey, String transformation) {
52+
return new RawKeyring(keyNamespace, keyName, JceKeyCipher.rsa(publicKey, privateKey, transformation));
53+
}
54+
55+
private RawKeyring(final String keyNamespace, final String keyName, JceKeyCipher jceKeyCipher) {
56+
notBlank(keyNamespace, "keyNamespace is required");
57+
notBlank(keyName, "keyName is required");
58+
notNull(jceKeyCipher, "jceKeyCipher is required");
59+
60+
this.keyNamespace = keyNamespace;
61+
this.keyName = keyName;
62+
this.jceKeyCipher = jceKeyCipher;
63+
}
64+
65+
@Override
66+
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
67+
notNull(encryptionMaterials, "encryptionMaterials are required");
68+
69+
if (encryptionMaterials.getCleartextDataKey() == null) {
70+
generateDataKey(encryptionMaterials);
71+
}
72+
73+
final SecretKey cleartextDataKey = encryptionMaterials.getCleartextDataKey();
74+
75+
if (!cleartextDataKey.getAlgorithm().equalsIgnoreCase(encryptionMaterials.getAlgorithm().getDataKeyAlgo())) {
76+
throw new IllegalArgumentException("Incorrect key algorithm. Expected " + cleartextDataKey.getAlgorithm()
77+
+ " but got " + encryptionMaterials.getAlgorithm().getDataKeyAlgo());
78+
}
79+
80+
final EncryptedDataKey encryptedDataKey = jceKeyCipher.encryptKey(
81+
cleartextDataKey.getEncoded(), keyName, keyNamespace, encryptionMaterials.getEncryptionContext());
82+
encryptionMaterials.getEncryptedDataKeys().add(new KeyBlob(encryptedDataKey));
83+
84+
encryptionMaterials.getKeyringTrace().add(keyNamespace, keyName, KeyringTraceFlag.ENCRYPTED_DATA_KEY);
85+
}
86+
87+
@Override
88+
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<EncryptedDataKey> encryptedDataKeys) {
89+
notNull(decryptionMaterials, "decryptionMaterials are required");
90+
notNull(encryptedDataKeys, "encryptedDataKeys are required");
91+
92+
if (decryptionMaterials.getCleartextDataKey() != null) {
93+
return;
94+
}
95+
96+
final byte[] keyNameBytes = keyName.getBytes(KEY_NAME_ENCODING);
97+
98+
for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) {
99+
if (!keyNamespace.equals(encryptedDataKey.getProviderId())) {
100+
continue;
101+
}
102+
103+
if (!Utils.arrayPrefixEquals(encryptedDataKey.getProviderInformation(), keyNameBytes, keyNameBytes.length)) {
104+
continue;
105+
}
106+
107+
try {
108+
final byte[] decryptedKey = jceKeyCipher.decryptKey(
109+
encryptedDataKey, keyName, decryptionMaterials.getEncryptionContext());
110+
decryptionMaterials.setCleartextDataKey(
111+
new SecretKeySpec(decryptedKey, decryptionMaterials.getAlgorithm().getDataKeyAlgo()));
112+
decryptionMaterials.getKeyringTrace().add(keyNamespace, keyName, KeyringTraceFlag.DECRYPTED_DATA_KEY);
113+
return;
114+
} catch (Exception e) {
115+
LOGGER.info("Could not decrypt key due to: " + e.getMessage());
116+
}
117+
}
118+
119+
LOGGER.warning("Could not decrypt any data keys");
120+
}
121+
122+
private void generateDataKey(EncryptionMaterials encryptionMaterials) {
123+
if (encryptionMaterials.getCleartextDataKey() != null) {
124+
throw new IllegalStateException("Plaintext data key already exists");
125+
}
126+
127+
final byte[] rawKey = new byte[encryptionMaterials.getAlgorithm().getDataKeyLength()];
128+
Utils.getSecureRandom().nextBytes(rawKey);
129+
final SecretKey key = new SecretKeySpec(rawKey, encryptionMaterials.getAlgorithm().getDataKeyAlgo());
130+
131+
encryptionMaterials.setCleartextDataKey(key);
132+
encryptionMaterials.getKeyringTrace().add(keyNamespace, keyName, KeyringTraceFlag.GENERATED_DATA_KEY);
133+
}
134+
}

0 commit comments

Comments
 (0)