Skip to content

Commit

Permalink
feat: Support GRPC-based KeyManagementServiceClient in the GCP-KMS AEAD.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 586704028
Change-Id: I20bd7125d2dea17b91b47b9627512f2b23b8ab37
  • Loading branch information
ise-crypto authored and copybara-github committed Nov 30, 2023
1 parent 533746c commit dbd6867
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 3 deletions.
18 changes: 18 additions & 0 deletions maven/tink-java-gcpkms.pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
<properties>
<java.version>1.8</java.version>
<google-api-client.version>2.2.0</google-api-client.version>
<google-api-gax.version>2.36.0</google-api-gax.version>
<google-api-grpc-proto-cloud-kms-v1.version>0.124.0</google-api-grpc-proto-cloud-kms-v1.version>
<google-api-services-cloudkms.version>v1-rev20221107-2.0.0</google-api-services-cloudkms.version>
<google-cloud-google-cloud-kms.version>2.31.0</google-cloud-google-cloud-kms.version>
<google-auth-library-oauth2-http.version>1.20.0</google-auth-library-oauth2-http.version>
Expand All @@ -94,6 +96,7 @@
<google-http-client.version>1.43.3</google-http-client.version>
<google-http-client-gson.version>1.43.3</google-http-client-gson.version>
<google-oauth-client.version>1.34.1</google-oauth-client.version>
<google-protobuf-java.version>3.24.4</google-protobuf-java.version>
<tink.version>1.11.0</tink.version>
</properties>

Expand Down Expand Up @@ -148,6 +151,21 @@
<artifactId>google-oauth-client</artifactId>
<version>${google-oauth-client.version}</version>
</dependency>
<dependency>
<groupId>com.google.api</groupId>
<artifactId>gax</artifactId>
<version>${google-api-gax.version}</version>
</dependency>
<dependency>
<groupId>com.google.api.grpc</groupId>
<artifactId>proto-google-cloud-kms-v1</artifactId>
<version>${google-api-grpc-proto-cloud-kms-v1.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${google-protobuf-java.version}</version>
</dependency>
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ java_library(
srcs = ["GcpKmsAead.java"],
deps = [
"@tink_java//src/main/java/com/google/crypto/tink:aead",
"@maven//:com_google_api_gax",
"@maven//:com_google_api_grpc_proto_google_cloud_kms_v1",
"@maven//:com_google_apis_google_api_services_cloudkms",
"@maven//:com_google_cloud_google_cloud_kms",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_protobuf_protobuf_java",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,31 @@

package com.google.crypto.tink.integration.gcpkms;

import com.google.api.gax.rpc.ApiException;
import com.google.api.services.cloudkms.v1.CloudKMS;
import com.google.api.services.cloudkms.v1.model.DecryptRequest;
import com.google.api.services.cloudkms.v1.model.DecryptResponse;
import com.google.api.services.cloudkms.v1.model.EncryptRequest;
import com.google.api.services.cloudkms.v1.model.EncryptResponse;
import com.google.cloud.kms.v1.KeyManagementServiceClient;
import com.google.crypto.tink.Aead;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.regex.Pattern;
import javax.annotation.Nullable;

/**
* A {@link Aead} that forwards encryption/decryption requests to a key in <a
* An {@link Aead} that forwards encryption/decryption requests to a key in <a
* href="https://cloud.google.com/kms/">Google Cloud KMS</a>.
*
* <p>As of August 2017, Google Cloud KMS supports only AES-256-GCM keys.
*
* @since 1.0.0
*/
public final class GcpKmsAead implements Aead {

/** This client knows how to talk to Google Cloud KMS. */
/** An HTTP-based client to communicate with Google Cloud KMS. */
private final CloudKMS kmsClient;

// The location of a CryptoKey in Google Cloud KMS.
Expand Down Expand Up @@ -101,4 +106,136 @@ private static byte[] toNonNullableByteArray(byte[] data) {
return data;
}
}

/**
* An {@link Aead} that forwards encryption/decryption requests to a key in <a
* href="https://cloud.google.com/kms/">Google Cloud KMS</a> using GRPC.
*/
private static final class GcpKmsAeadGrpc implements Aead {

/** A GRPC-based client to communicate with Google Cloud KMS. */
private final KeyManagementServiceClient kmsClient;

// The location of a CryptoKey in Google Cloud KMS.
// Valid values have this format: projects/*/locations/*/keyRings/*/cryptoKeys/*.
// See https://cloud.google.com/kms/docs/object-hierarchy.
private final String keyName;

private GcpKmsAeadGrpc(KeyManagementServiceClient kmsClient, String keyName) {
this.kmsClient = kmsClient;
this.keyName = keyName;
}

@Override
public byte[] encrypt(final byte[] plaintext, final byte[] associatedData)
throws GeneralSecurityException {
try {
com.google.cloud.kms.v1.EncryptRequest encryptRequest =
com.google.cloud.kms.v1.EncryptRequest.newBuilder()
.setName(keyName)
.setPlaintext(ByteString.copyFrom(plaintext))
.setAdditionalAuthenticatedData(ByteString.copyFrom(associatedData))
.build();

com.google.cloud.kms.v1.EncryptResponse encResponse = kmsClient.encrypt(encryptRequest);
return encResponse.getCiphertext().toByteArray();
} catch (ApiException e) {
throw new GeneralSecurityException("encryption failed", e);
}
}

@Override
public byte[] decrypt(final byte[] ciphertext, final byte[] associatedData)
throws GeneralSecurityException {
try {
com.google.cloud.kms.v1.DecryptRequest decryptRequest =
com.google.cloud.kms.v1.DecryptRequest.newBuilder()
.setName(keyName)
.setCiphertext(ByteString.copyFrom(ciphertext))
.setAdditionalAuthenticatedData(ByteString.copyFrom(associatedData))
.build();

com.google.cloud.kms.v1.DecryptResponse decResponse = kmsClient.decrypt(decryptRequest);
return decResponse.getPlaintext().toByteArray();
} catch (ApiException e) {
throw new GeneralSecurityException("decryption failed", e);
}
}
}

/**
* A Builder to create an Aead backed by GCP Cloud KMS.
*
* <p>If {@link #setKeyManagementServiceClient} is used, the Aead will communicate with Cloud KMS
* via gRPC given a {@link KeyManagementServiceClient} instance. If {@link #setCloudKms} is used,
* the Aead will communicate with Cloud KMS via HTTP given a {@link CloudKMS} instance.
*
* <p>For new users we recommend using {@link #setKeyManagementServiceClient}.
*/
public static final class Builder {
@Nullable private String keyName = null;
@Nullable private CloudKMS kmsClientHttp = null;
@Nullable private KeyManagementServiceClient kmsClientGrpc = null;
private static final String KEY_NAME_PATTERN =
"projects/([^/]+)/locations/([a-zA-Z0-9_-]{1,63})/keyRings/"
+ "[a-zA-Z0-9_-]{1,63}/cryptoKeys/[a-zA-Z0-9_-]{1,63}";
private static final Pattern KEY_NAME_MATCHER = Pattern.compile(KEY_NAME_PATTERN);

private Builder() {}

/** Set the ResourceName of the KMS key. */
@CanIgnoreReturnValue
public Builder setKeyName(String keyName) {
this.keyName = keyName;
return this;
}

/** Set the CloudKms object. */
@CanIgnoreReturnValue
public Builder setCloudKms(CloudKMS cloudKms) {
this.kmsClientHttp = cloudKms;
return this;
}

/** Set the KeyManagementServiceClient object. */
@CanIgnoreReturnValue
public Builder setKeyManagementServiceClient(KeyManagementServiceClient kmsClient) {
this.kmsClientGrpc = kmsClient;
return this;
}

public Aead build() throws GeneralSecurityException {
if (keyName == null) {
throw new GeneralSecurityException("The keyName is null.");
}

if (keyName.isEmpty()) {
throw new GeneralSecurityException("The keyName is empty.");
}

if (!KEY_NAME_MATCHER.matcher(keyName).matches()) {
throw new GeneralSecurityException("The keyName must follow " + KEY_NAME_PATTERN);
}

if (kmsClientGrpc == null && kmsClientHttp == null) {
throw new GeneralSecurityException(
"Either the CloudKMS or the KeyManagementServiceClient object must be provided.");
}

if (kmsClientGrpc != null && kmsClientHttp != null) {
throw new GeneralSecurityException(
"Either the CloudKMS or the KeyManagementServiceClient object must be provided.");
}

if (kmsClientHttp != null) {
return new GcpKmsAead(kmsClientHttp, keyName);
}

return new GcpKmsAeadGrpc(kmsClientGrpc, keyName);
}
}

public static Builder builder() {
return new Builder();
}
}
3 changes: 3 additions & 0 deletions tink_java_gcpkms_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ TINK_JAVA_GCPKMS_MAVEN_TOOLS_ARTIFACTS = [
]

TINK_JAVA_GCPKMS_MAVEN_ARTIFACTS = [
"com.google.api:gax:2.36.0",
"com.google.api.grpc:proto-google-cloud-kms-v1:0.124.0",
"com.google.api-client:google-api-client:2.2.0",
"com.google.apis:google-api-services-cloudkms:v1-rev20221107-2.0.0",
"com.google.auth:google-auth-library-oauth2-http:1.20.0",
Expand All @@ -26,6 +28,7 @@ TINK_JAVA_GCPKMS_MAVEN_ARTIFACTS = [
"com.google.http-client:google-http-client-gson:1.43.3",
"com.google.http-client:google-http-client:1.43.3",
"com.google.oauth-client:google-oauth-client:1.34.1",
"com.google.protobuf:protobuf-java:3.24.4",
]

def tink_java_gcpkms_deps():
Expand Down

0 comments on commit dbd6867

Please sign in to comment.