Skip to content

Commit 470d948

Browse files
authored
Merge pull request #1236 from mziccard/storage-encryption-key
Add support for customer-supplied encryption keys
2 parents f9c2471 + b0b6aa0 commit 470d948

File tree

11 files changed

+671
-42
lines changed

11 files changed

+671
-42
lines changed

google-cloud-storage/src/main/java/com/google/cloud/storage/Blob.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@
3636
import com.google.cloud.storage.spi.StorageRpc;
3737
import com.google.cloud.storage.spi.StorageRpc.Tuple;
3838
import com.google.common.base.Function;
39+
import com.google.common.io.BaseEncoding;
3940

4041
import java.io.IOException;
4142
import java.io.ObjectInputStream;
4243
import java.net.URL;
44+
import java.security.Key;
4345
import java.util.Arrays;
4446
import java.util.List;
4547
import java.util.Map;
@@ -81,6 +83,10 @@ private BlobSourceOption(StorageRpc.Option rpcOption) {
8183
super(rpcOption, null);
8284
}
8385

86+
private BlobSourceOption(StorageRpc.Option rpcOption, Object value) {
87+
super(rpcOption, value);
88+
}
89+
8490
private Storage.BlobSourceOption toSourceOptions(BlobInfo blobInfo) {
8591
switch (rpcOption()) {
8692
case IF_GENERATION_MATCH:
@@ -91,6 +97,8 @@ private Storage.BlobSourceOption toSourceOptions(BlobInfo blobInfo) {
9197
return Storage.BlobSourceOption.metagenerationMatch(blobInfo.metageneration());
9298
case IF_METAGENERATION_NOT_MATCH:
9399
return Storage.BlobSourceOption.metagenerationNotMatch(blobInfo.metageneration());
100+
case CUSTOMER_SUPPLIED_KEY:
101+
return Storage.BlobSourceOption.decryptionKey((String) value());
94102
default:
95103
throw new AssertionError("Unexpected enum value");
96104
}
@@ -143,6 +151,25 @@ public static BlobSourceOption metagenerationNotMatch() {
143151
return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH);
144152
}
145153

154+
/**
155+
* Returns an option to set a customer-supplied AES256 key for server-side encryption of the
156+
* blob.
157+
*/
158+
public static BlobSourceOption decryptionKey(Key key) {
159+
String base64Key = BaseEncoding.base64().encode(key.getEncoded());
160+
return new BlobSourceOption(StorageRpc.Option.CUSTOMER_SUPPLIED_KEY, base64Key);
161+
}
162+
163+
/**
164+
* Returns an option to set a customer-supplied AES256 key for server-side encryption of the
165+
* blob.
166+
*
167+
* @param key the AES256 encoded in base64
168+
*/
169+
public static BlobSourceOption decryptionKey(String key) {
170+
return new BlobSourceOption(StorageRpc.Option.CUSTOMER_SUPPLIED_KEY, key);
171+
}
172+
146173
static Storage.BlobSourceOption[] toSourceOptions(BlobInfo blobInfo,
147174
BlobSourceOption... options) {
148175
Storage.BlobSourceOption[] convertedOptions = new Storage.BlobSourceOption[options.length];
@@ -308,6 +335,12 @@ Builder isDirectory(boolean isDirectory) {
308335
return this;
309336
}
310337

338+
@Override
339+
Builder customerEncryption(CustomerEncryption customerEncryption) {
340+
infoBuilder.customerEncryption(customerEncryption);
341+
return this;
342+
}
343+
311344
@Override
312345
public Blob build() {
313346
return new Blob(storage, infoBuilder);

google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public StorageObject apply(BlobInfo blobInfo) {
8080
private final String contentLanguage;
8181
private final Integer componentCount;
8282
private final boolean isDirectory;
83+
private final CustomerEncryption customerEncryption;
8384

8485
/**
8586
* This class is meant for internal use only. Users are discouraged from using this class.
@@ -92,6 +93,69 @@ public Set<Entry<K, V>> entrySet() {
9293
}
9394
}
9495

96+
/**
97+
* Objects of this class hold information on the customer-supplied encryption key, if the blob is
98+
* encrypted using such a key.
99+
*/
100+
public static class CustomerEncryption implements Serializable {
101+
102+
private static final long serialVersionUID = -2133042982786959351L;
103+
104+
private final String encryptionAlgorithm;
105+
private final String keySha256;
106+
107+
CustomerEncryption(String encryptionAlgorithm, String keySha256) {
108+
this.encryptionAlgorithm = encryptionAlgorithm;
109+
this.keySha256 = keySha256;
110+
}
111+
112+
/**
113+
* Returns the algorithm used to encrypt the blob.
114+
*/
115+
public String encryptionAlgorithm() {
116+
return encryptionAlgorithm;
117+
}
118+
119+
/**
120+
* Returns the SHA256 hash of the encryption key.
121+
*/
122+
public String keySha256() {
123+
return keySha256;
124+
}
125+
126+
@Override
127+
public String toString() {
128+
return MoreObjects.toStringHelper(this)
129+
.add("encryptionAlgorithm", encryptionAlgorithm())
130+
.add("keySha256", keySha256())
131+
.toString();
132+
}
133+
134+
@Override
135+
public final int hashCode() {
136+
return Objects.hash(encryptionAlgorithm, keySha256);
137+
}
138+
139+
@Override
140+
public final boolean equals(Object obj) {
141+
return obj == this
142+
|| obj != null
143+
&& obj.getClass().equals(CustomerEncryption.class)
144+
&& Objects.equals(toPb(), ((CustomerEncryption) obj).toPb());
145+
}
146+
147+
StorageObject.CustomerEncryption toPb() {
148+
return new StorageObject.CustomerEncryption()
149+
.setEncryptionAlgorithm(encryptionAlgorithm)
150+
.setKeySha256(keySha256);
151+
}
152+
153+
static CustomerEncryption fromPb(StorageObject.CustomerEncryption customerEncryptionPb) {
154+
return new CustomerEncryption(customerEncryptionPb.getEncryptionAlgorithm(),
155+
customerEncryptionPb.getKeySha256());
156+
}
157+
}
158+
95159
/**
96160
* Builder for {@code BlobInfo}.
97161
*/
@@ -193,6 +257,8 @@ public abstract static class Builder {
193257

194258
abstract Builder isDirectory(boolean isDirectory);
195259

260+
abstract Builder customerEncryption(CustomerEncryption customerEncryption);
261+
196262
/**
197263
* Creates a {@code BlobInfo} object.
198264
*/
@@ -223,6 +289,7 @@ static final class BuilderImpl extends Builder {
223289
private Long updateTime;
224290
private Long createTime;
225291
private Boolean isDirectory;
292+
private CustomerEncryption customerEncryption;
226293

227294
BuilderImpl(BlobId blobId) {
228295
this.blobId = blobId;
@@ -237,6 +304,7 @@ static final class BuilderImpl extends Builder {
237304
contentDisposition = blobInfo.contentDisposition;
238305
contentLanguage = blobInfo.contentLanguage;
239306
componentCount = blobInfo.componentCount;
307+
customerEncryption = blobInfo.customerEncryption;
240308
acl = blobInfo.acl;
241309
owner = blobInfo.owner;
242310
size = blobInfo.size;
@@ -386,6 +454,12 @@ Builder isDirectory(boolean isDirectory) {
386454
return this;
387455
}
388456

457+
@Override
458+
Builder customerEncryption(CustomerEncryption customerEncryption) {
459+
this.customerEncryption = customerEncryption;
460+
return this;
461+
}
462+
389463
@Override
390464
public BlobInfo build() {
391465
checkNotNull(blobId);
@@ -402,6 +476,7 @@ public BlobInfo build() {
402476
contentDisposition = builder.contentDisposition;
403477
contentLanguage = builder.contentLanguage;
404478
componentCount = builder.componentCount;
479+
customerEncryption = builder.customerEncryption;
405480
acl = builder.acl;
406481
owner = builder.owner;
407482
size = builder.size;
@@ -631,6 +706,14 @@ public boolean isDirectory() {
631706
return isDirectory;
632707
}
633708

709+
/**
710+
* Returns information on the customer-supplied encryption key, if the blob is encrypted using
711+
* such a key.
712+
*/
713+
public CustomerEncryption customerEncryption() {
714+
return customerEncryption;
715+
}
716+
634717
/**
635718
* Returns a builder for the current blob.
636719
*/
@@ -696,6 +779,9 @@ public ObjectAccessControl apply(Acl acl) {
696779
firstNonNull(entry.getValue(), Data.<String>nullOf(String.class)));
697780
}
698781
}
782+
if (customerEncryption != null) {
783+
storageObject.setCustomerEncryption(customerEncryption.toPb());
784+
}
699785
storageObject.setMetadata(pbMetadata);
700786
storageObject.setCacheControl(cacheControl);
701787
storageObject.setContentEncoding(contentEncoding);
@@ -815,6 +901,9 @@ public Acl apply(ObjectAccessControl objectAccessControl) {
815901
if (storageObject.containsKey("isDirectory")) {
816902
builder.isDirectory(Boolean.TRUE);
817903
}
904+
if (storageObject.getCustomerEncryption() != null) {
905+
builder.customerEncryption(CustomerEncryption.fromPb(storageObject.getCustomerEncryption()));
906+
}
818907
return builder.build();
819908
}
820909
}

google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@
3131
import com.google.common.collect.ImmutableList;
3232
import com.google.common.collect.Lists;
3333
import com.google.common.collect.Sets;
34+
import com.google.common.io.BaseEncoding;
3435

3536
import java.io.IOException;
3637
import java.io.InputStream;
3738
import java.io.ObjectInputStream;
3839
import java.io.Serializable;
40+
import java.security.Key;
3941
import java.util.Arrays;
4042
import java.util.List;
4143
import java.util.Objects;
@@ -166,6 +168,9 @@ private StorageRpc.Tuple<BlobInfo, Storage.BlobTargetOption> toTargetOption(Blob
166168
case IF_METAGENERATION_NOT_MATCH:
167169
return StorageRpc.Tuple.of(blobInfo.toBuilder().metageneration((Long) value()).build(),
168170
Storage.BlobTargetOption.metagenerationNotMatch());
171+
case CUSTOMER_SUPPLIED_KEY:
172+
return StorageRpc.Tuple.of(blobInfo,
173+
Storage.BlobTargetOption.encryptionKey((String) value()));
169174
default:
170175
throw new AssertionError("Unexpected enum value");
171176
}
@@ -223,6 +228,25 @@ public static BlobTargetOption metagenerationNotMatch(long metageneration) {
223228
return new BlobTargetOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH, metageneration);
224229
}
225230

231+
/**
232+
* Returns an option to set a customer-supplied AES256 key for server-side encryption of the
233+
* blob.
234+
*/
235+
public static BlobTargetOption encryptionKey(Key key) {
236+
String base64Key = BaseEncoding.base64().encode(key.getEncoded());
237+
return new BlobTargetOption(StorageRpc.Option.CUSTOMER_SUPPLIED_KEY, base64Key);
238+
}
239+
240+
/**
241+
* Returns an option to set a customer-supplied AES256 key for server-side encryption of the
242+
* blob.
243+
*
244+
* @param key the AES256 encoded in base64
245+
*/
246+
public static BlobTargetOption encryptionKey(String key) {
247+
return new BlobTargetOption(StorageRpc.Option.CUSTOMER_SUPPLIED_KEY, key);
248+
}
249+
226250
static StorageRpc.Tuple<BlobInfo, Storage.BlobTargetOption[]> toTargetOptions(
227251
BlobInfo info, BlobTargetOption... options) {
228252
Set<StorageRpc.Option> optionSet =
@@ -289,6 +313,9 @@ private StorageRpc.Tuple<BlobInfo, Storage.BlobWriteOption> toWriteOption(BlobIn
289313
case IF_CRC32C_MATCH:
290314
return StorageRpc.Tuple.of(blobInfo.toBuilder().crc32c((String) value).build(),
291315
Storage.BlobWriteOption.crc32cMatch());
316+
case CUSTOMER_SUPPLIED_KEY:
317+
return StorageRpc.Tuple.of(blobInfo,
318+
Storage.BlobWriteOption.encryptionKey((String) value));
292319
default:
293320
throw new AssertionError("Unexpected enum value");
294321
}
@@ -387,6 +414,25 @@ public static BlobWriteOption crc32cMatch(String crc32c) {
387414
return new BlobWriteOption(Storage.BlobWriteOption.Option.IF_CRC32C_MATCH, crc32c);
388415
}
389416

417+
/**
418+
* Returns an option to set a customer-supplied AES256 key for server-side encryption of the
419+
* blob.
420+
*/
421+
public static BlobWriteOption encryptionKey(Key key) {
422+
String base64Key = BaseEncoding.base64().encode(key.getEncoded());
423+
return new BlobWriteOption(Storage.BlobWriteOption.Option.CUSTOMER_SUPPLIED_KEY, base64Key);
424+
}
425+
426+
/**
427+
* Returns an option to set a customer-supplied AES256 key for server-side encryption of the
428+
* blob.
429+
*
430+
* @param key the AES256 encoded in base64
431+
*/
432+
public static BlobWriteOption encryptionKey(String key) {
433+
return new BlobWriteOption(Storage.BlobWriteOption.Option.CUSTOMER_SUPPLIED_KEY, key);
434+
}
435+
390436
static StorageRpc.Tuple<BlobInfo, Storage.BlobWriteOption[]> toWriteOptions(
391437
BlobInfo info, BlobWriteOption... options) {
392438
Set<Storage.BlobWriteOption.Option> optionSet =

0 commit comments

Comments
 (0)