Skip to content

Commit 0a2e63f

Browse files
committed
Add support for customer-supplied encryption keys
1 parent ba74dc3 commit 0a2e63f

File tree

12 files changed

+580
-42
lines changed

12 files changed

+580
-42
lines changed

google-cloud-storage/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<dependency>
2626
<groupId>com.google.apis</groupId>
2727
<artifactId>google-api-services-storage</artifactId>
28-
<version>v1-rev62-1.21.0</version>
28+
<version>v1-rev82-1.22.0</version>
2929
<scope>compile</scope>
3030
<exclusions>
3131
<exclusion>

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
@@ -35,10 +35,12 @@
3535
import com.google.cloud.storage.spi.StorageRpc;
3636
import com.google.cloud.storage.spi.StorageRpc.Tuple;
3737
import com.google.common.base.Function;
38+
import com.google.common.io.BaseEncoding;
3839

3940
import java.io.IOException;
4041
import java.io.ObjectInputStream;
4142
import java.net.URL;
43+
import java.security.Key;
4244
import java.util.Arrays;
4345
import java.util.List;
4446
import java.util.Map;
@@ -80,6 +82,10 @@ private BlobSourceOption(StorageRpc.Option rpcOption) {
8082
super(rpcOption, null);
8183
}
8284

85+
private BlobSourceOption(StorageRpc.Option rpcOption, Object value) {
86+
super(rpcOption, value);
87+
}
88+
8389
private Storage.BlobSourceOption toSourceOptions(BlobInfo blobInfo) {
8490
switch (rpcOption()) {
8591
case IF_GENERATION_MATCH:
@@ -90,6 +96,8 @@ private Storage.BlobSourceOption toSourceOptions(BlobInfo blobInfo) {
9096
return Storage.BlobSourceOption.metagenerationMatch(blobInfo.metageneration());
9197
case IF_METAGENERATION_NOT_MATCH:
9298
return Storage.BlobSourceOption.metagenerationNotMatch(blobInfo.metageneration());
99+
case CUSTOMER_SUPPLIED_KEY:
100+
return Storage.BlobSourceOption.decryptionKey((String) value());
93101
default:
94102
throw new AssertionError("Unexpected enum value");
95103
}
@@ -142,6 +150,25 @@ public static BlobSourceOption metagenerationNotMatch() {
142150
return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH);
143151
}
144152

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

337+
@Override
338+
Builder customerEncryption(CustomerEncryption customerEncryption) {
339+
infoBuilder.customerEncryption(customerEncryption);
340+
return this;
341+
}
342+
310343
@Override
311344
public Blob build() {
312345
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
@@ -30,11 +30,13 @@
3030
import com.google.common.collect.ImmutableList;
3131
import com.google.common.collect.Lists;
3232
import com.google.common.collect.Sets;
33+
import com.google.common.io.BaseEncoding;
3334

3435
import java.io.IOException;
3536
import java.io.InputStream;
3637
import java.io.ObjectInputStream;
3738
import java.io.Serializable;
39+
import java.security.Key;
3840
import java.util.Arrays;
3941
import java.util.List;
4042
import java.util.Objects;
@@ -165,6 +167,9 @@ private StorageRpc.Tuple<BlobInfo, Storage.BlobTargetOption> toTargetOption(Blob
165167
case IF_METAGENERATION_NOT_MATCH:
166168
return StorageRpc.Tuple.of(blobInfo.toBuilder().metageneration((Long) value()).build(),
167169
Storage.BlobTargetOption.metagenerationNotMatch());
170+
case CUSTOMER_SUPPLIED_KEY:
171+
return StorageRpc.Tuple.of(blobInfo,
172+
Storage.BlobTargetOption.encryptionKey((String) value()));
168173
default:
169174
throw new AssertionError("Unexpected enum value");
170175
}
@@ -222,6 +227,25 @@ public static BlobTargetOption metagenerationNotMatch(long metageneration) {
222227
return new BlobTargetOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH, metageneration);
223228
}
224229

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

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

0 commit comments

Comments
 (0)