1+ package software .amazon .encryption .s3 .legacy .materials ;
2+
3+ import java .security .Key ;
4+ import java .util .List ;
5+ import javax .crypto .Cipher ;
6+ import javax .crypto .SecretKey ;
7+ import software .amazon .encryption .s3 .S3EncryptionClientException ;
8+ import software .amazon .encryption .s3 .materials .DecryptionMaterials ;
9+ import software .amazon .encryption .s3 .materials .EncryptedDataKey ;
10+ import software .amazon .encryption .s3 .materials .EncryptionMaterials ;
11+ import software .amazon .encryption .s3 .materials .Keyring ;
12+
13+ /**
14+ * AesWrapKeyring is a legacy, decrypt-only keyring and will use an AES key to unwrap the data key
15+ * used to encrypt content.
16+ */
17+ public class AesWrapKeyring implements Keyring {
18+
19+ private static final String KEY_ALGORITHM = "AES" ;
20+ private static final String KEY_PROVIDER_ID = "AESWrap" ;
21+ private static final String CIPHER_ALGORITHM = "AESWrap" ;
22+
23+ private final SecretKey _wrappingKey ;
24+ private final Keyring _nonLegacyKeyring ;
25+
26+ private AesWrapKeyring (Builder builder ) {
27+ _wrappingKey = builder ._wrappingKey ;
28+ _nonLegacyKeyring = builder ._nonLegacyKeyring ;
29+ }
30+
31+ public static Builder builder () {
32+ return new Builder ();
33+ }
34+
35+ @ Override
36+ public EncryptionMaterials onEncrypt (EncryptionMaterials materials ) {
37+ return _nonLegacyKeyring .onEncrypt (materials );
38+ }
39+
40+ @ Override
41+ public DecryptionMaterials onDecrypt (DecryptionMaterials materials , List <EncryptedDataKey > encryptedDataKeys ) {
42+ materials = _nonLegacyKeyring .onDecrypt (materials , encryptedDataKeys );
43+
44+ if (materials .plaintextDataKey () != null ) {
45+ return materials ;
46+ }
47+
48+ for (EncryptedDataKey encryptedDataKey : encryptedDataKeys ) {
49+ if (!encryptedDataKey .keyProviderId ().equals (KEY_PROVIDER_ID )) {
50+ continue ;
51+ }
52+
53+ try {
54+ final Cipher cipher = Cipher .getInstance (CIPHER_ALGORITHM );
55+ cipher .init (Cipher .UNWRAP_MODE , _wrappingKey );
56+
57+ Key plaintextKey = cipher .unwrap (encryptedDataKey .ciphertext (), CIPHER_ALGORITHM , Cipher .SECRET_KEY );
58+
59+ return materials .toBuilder ().plaintextDataKey (plaintextKey .getEncoded ()).build ();
60+ } catch (Exception e ) {
61+ throw new S3EncryptionClientException ("Unable to " + KEY_PROVIDER_ID + " unwrap" , e );
62+ }
63+ }
64+
65+ return materials ;
66+ }
67+
68+ public static class Builder {
69+ private SecretKey _wrappingKey ;
70+ private Keyring _nonLegacyKeyring ;
71+
72+ private Builder () {}
73+
74+ public Builder wrappingKey (SecretKey wrappingKey ) {
75+ if (!wrappingKey .getAlgorithm ().equals (KEY_ALGORITHM )) {
76+ throw new S3EncryptionClientException ("Invalid algorithm '" + wrappingKey .getAlgorithm () + "', expecting " + KEY_ALGORITHM );
77+ }
78+ _wrappingKey = wrappingKey ;
79+ return this ;
80+ }
81+
82+ public Builder nonLegacyKeyring (Keyring nonLegacyKeyring ) {
83+ _nonLegacyKeyring = nonLegacyKeyring ;
84+ return this ;
85+ }
86+
87+ public AesWrapKeyring build () {
88+ if (_nonLegacyKeyring == null ) {
89+ // TODO: should we warn or throw an exception if no encryption method is supported?
90+ }
91+ return new AesWrapKeyring (this );
92+ }
93+ }
94+ }
0 commit comments