diff --git a/google-auth-library-java/pom.xml b/google-auth-library-java/pom.xml index 374ec0ed8737..260fe6bfc286 100644 --- a/google-auth-library-java/pom.xml +++ b/google-auth-library-java/pom.xml @@ -80,12 +80,12 @@ 2.0.33 3.0.2 false - 2.48.0 + 2.50.0 4.33.6 0.9.0-proto3 1.15.0 2.0.17 - 2.13.2 + 2.14.0 2.67.0-SNAPSHOT 1.18.0 3.5.2 diff --git a/grpc-gcp-java/pom.xml b/grpc-gcp-java/pom.xml index 17997ee07915..9a6e523f52ec 100644 --- a/grpc-gcp-java/pom.xml +++ b/grpc-gcp-java/pom.xml @@ -64,12 +64,12 @@ grpc-gcp 2.67.0-SNAPSHOT 1.11.0 - 2.48.0 + 2.50.0 2.2.0 - 2.13.2 + 2.14.0 33.6.0-jre 4.33.6 - 1.82.2 + 1.83.0 3.0.2 4.13.2 0.31.1 diff --git a/java-bigtable/pom.xml b/java-bigtable/pom.xml index c6bd75e5f4f7..454eede9bfbd 100644 --- a/java-bigtable/pom.xml +++ b/java-bigtable/pom.xml @@ -144,7 +144,7 @@ github google-cloud-bigtable-parent https://googleapis.dev/java/google-api-grpc/latest - 2.48.0 + 2.50.0 -g diff --git a/java-datastore/pom.xml b/java-datastore/pom.xml index 674bdac579f4..30f3d56b7f25 100644 --- a/java-datastore/pom.xml +++ b/java-datastore/pom.xml @@ -144,7 +144,7 @@ github google-cloud-datastore-parent https://googleapis.dev/java/google-api-grpc/latest - 2.48.0 + 2.50.0 -g diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java index e1f2ed82560d..0228795f3f39 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java @@ -21,6 +21,7 @@ import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.httpjson.HttpJsonConscryptUtils; import com.google.api.gax.httpjson.HttpJsonMetadata; import com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider; @@ -29,6 +30,16 @@ import com.google.showcase.v1beta1.EchoResponse; import com.google.showcase.v1beta1.EchoSettings; import com.google.showcase.v1beta1.it.util.HttpJsonCapturingClientInterceptor; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.ForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; import java.io.File; import java.io.InputStream; import java.nio.file.Files; @@ -266,4 +277,104 @@ private static KeyStore loadCaCert(String certPath) throws Exception { } return trustStore; } + + /** + * Integration test to verify Post-Quantum Cryptography (PQC) TLS negotiation for gRPC clients. + * + *

In gRPC-Java 1.83.0+, the default Netty transport (`grpc-netty-shaded`) bundles BoringSSL + * (`netty-tcnative-boringssl-static`) with built-in PQC hybrid key exchange support (e.g., + * X25519MLKEM768). No custom socket configurator or security provider swapping is needed. + * + *

Because the local Showcase test server uses a self-signed CA certificate (written to {@link + * #DEFAULT_CA_CERT_PATH}), we configure the gRPC transport channel builder directly to trust this + * certificate via {@link GrpcSslContexts#forClient()}. This avoids mutating global JVM system + * properties in {@code setUp()} and ensures HTTP/JSON tests remain completely isolated. + */ + @Test + void testGrpcPqc_withTls() throws Exception { + GrpcTlsCapturingClientInterceptor interceptor = new GrpcTlsCapturingClientInterceptor(); + + InstantiatingGrpcChannelProvider transportChannelProvider = + EchoSettings.defaultGrpcTransportProviderBuilder() + .setEndpoint(SECURE_ENDPOINT) + .setInterceptorProvider(() -> Collections.singletonList(interceptor)) + .setChannelConfigurator( + managedChannelBuilder -> { + if (managedChannelBuilder instanceof NettyChannelBuilder) { + try { + // Explicitly trust the self-signed CA certificate created by the local + // Showcase TLS connection without altering JVM-wide SSL trust stores. + ((NettyChannelBuilder) managedChannelBuilder) + .sslContext( + GrpcSslContexts.forClient() + .trustManager(new File(DEFAULT_CA_CERT_PATH)) + .build()); + } catch (Exception e) { + throw new RuntimeException("Failed to configure gRPC SSL context", e); + } + } + return managedChannelBuilder; + }) + .build(); + + EchoSettings settings = + EchoSettings.newBuilder() + .setCredentialsProvider(NoCredentialsProvider.create()) + .setTransportChannelProvider(transportChannelProvider) + .build(); + + try (EchoClient client = EchoClient.create(settings)) { + EchoResponse response = + client.echo(EchoRequest.newBuilder().setContent("pqc-grpc-tls-test").build()); + assertThat(response.getContent()).isEqualTo("pqc-grpc-tls-test"); + + Metadata capturedHeaders = interceptor.capturedMetadata; + assertThat(capturedHeaders).isNotNull(); + + // Verify that TLS 1.3 key exchange negotiated the expected PQC hybrid group (X25519MLKEM768). + String negotiatedGroup = getGrpcSingleHeaderString(capturedHeaders, TLS_GROUP_HEADER); + assertThat(negotiatedGroup).isEqualTo(EXPECTED_PQC_GROUP); + } + } + + /** + * Private gRPC client interceptor to capture the response headers from the Showcase server to + * verify the PQC algorithm. + */ + private static class GrpcTlsCapturingClientInterceptor implements ClientInterceptor { + final Metadata capturedMetadata = new Metadata(); + + @Override + public ClientCall interceptCall( + MethodDescriptor method, CallOptions callOptions, Channel next) { + return new ForwardingClientCall.SimpleForwardingClientCall( + next.newCall(method, callOptions)) { + @Override + public void start(Listener responseListener, Metadata headers) { + super.start( + new ForwardingClientCallListener.SimpleForwardingClientCallListener( + responseListener) { + @Override + public void onHeaders(Metadata headers) { + capturedMetadata.merge(headers); + super.onHeaders(headers); + } + }, + headers); + } + }; + } + } + + /** + * Private helper method required to extract a single string header from gRPC {@link Metadata}. + * + * @param metadata the captured gRPC response metadata + * @param name the case-insensitive HTTP/2 header name + * @return the string header value, or {@code null} if not present + */ + private static String getGrpcSingleHeaderString(Metadata metadata, String name) { + Metadata.Key key = Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER); + return metadata.get(key); + } } diff --git a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedReadableByteChannel.java b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedReadableByteChannel.java index 781c449369cb..25757921ba54 100644 --- a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedReadableByteChannel.java +++ b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedReadableByteChannel.java @@ -41,14 +41,11 @@ import com.google.common.hash.HashingInputStream; import com.google.common.io.BaseEncoding; import com.google.common.primitives.Ints; -import com.google.gson.Gson; -import com.google.gson.stream.JsonReader; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.io.StringReader; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.channels.Channels; @@ -381,7 +378,6 @@ private static String getHeaderValue(@NonNull HttpHeaders headers, @NonNull Stri @Immutable static final class ApiaryReadRequest implements Serializable { private static final long serialVersionUID = -4059435314115374448L; - private static final Gson gson = new Gson(); @NonNull private transient StorageObject object; @NonNull private final Map options; @NonNull private final ByteRangeSpec byteRangeSpec; @@ -450,7 +446,7 @@ private String getObjectJson() { if (objectJson == null) { synchronized (this) { if (objectJson == null) { - objectJson = gson.toJson(object); + objectJson = JsonUtils.objectToJson(object); } } } @@ -464,8 +460,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); - JsonReader jsonReader = gson.newJsonReader(new StringReader(this.objectJson)); - this.object = gson.fromJson(jsonReader, StorageObject.class); + this.object = JsonUtils.jsonToObject(this.objectJson, StorageObject.class); } } } diff --git a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java index b2347c47f3cf..3fef2dcbb9dc 100644 --- a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java +++ b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java @@ -22,13 +22,10 @@ import com.google.cloud.storage.spi.v1.StorageRpc; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.stream.JsonReader; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.io.StringReader; import java.util.Map; import java.util.Objects; import org.checkerframework.checker.lock.qual.GuardedBy; @@ -37,8 +34,6 @@ final class JsonResumableWrite implements Serializable { private static final long serialVersionUID = 7934407897802252292L; - private static final Gson gson = new Gson(); - @MonotonicNonNull private transient StorageObject object; @MonotonicNonNull private transient Hasher hasher; @MonotonicNonNull private transient Crc32cValue cumulativeCrc32c; @@ -152,7 +147,7 @@ private String getObjectJson() { if (objectJson == null) { synchronized (this) { if (objectJson == null) { - objectJson = gson.toJson(object); + objectJson = JsonUtils.objectToJson(object); base64CumulativeCrc32c = Utils.crc32cCodec.encode(cumulativeCrc32c.getValue()); } } @@ -167,8 +162,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); - JsonReader jsonReader = gson.newJsonReader(new StringReader(this.objectJson)); - this.object = gson.fromJson(jsonReader, StorageObject.class); + this.object = JsonUtils.jsonToObject(this.objectJson, StorageObject.class); if (base64CumulativeCrc32c != null) { Integer decode = Utils.crc32cCodec.decode(base64CumulativeCrc32c); if (decode == 0) { diff --git a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java index f5297ee837c0..6bc8d27d6e44 100644 --- a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java +++ b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonUtils.java @@ -53,6 +53,28 @@ final class JsonUtils { private JsonUtils() {} + static String objectToJson(T object) { + if (object == null) { + return null; + } + try { + return jop.getJsonFactory().toString(object); + } catch (IOException e) { + throw StorageException.coalesce(e); + } + } + + static T jsonToObject(String json, Class clazz) { + if (json == null) { + return null; + } + try { + return jop.parseAndClose(new StringReader(json), clazz); + } catch (IOException e) { + throw StorageException.coalesce(e); + } + } + /** * Given a GenericJson src, and a list of {@code fieldsForOutput} create a new GenericJson where * every field specified in {@code fieldsForOutput} is present. If a field exists in {@code src} diff --git a/librarian.yaml b/librarian.yaml index fe4c6889f47f..0fd0b4b58840 100644 --- a/librarian.yaml +++ b/librarian.yaml @@ -30,7 +30,7 @@ tools: classifier: all-deps packaging: jar - name: protoc-gen-java_grpc - version: 1.82.2 + version: 1.83.0 group_id: io.grpc artifact_id: protoc-gen-grpc-java classifier: linux-x86_64 diff --git a/sdk-platform-java/gapic-generator-java-pom-parent/pom.xml b/sdk-platform-java/gapic-generator-java-pom-parent/pom.xml index 4b7b477219c5..c810ed161d02 100644 --- a/sdk-platform-java/gapic-generator-java-pom-parent/pom.xml +++ b/sdk-platform-java/gapic-generator-java-pom-parent/pom.xml @@ -27,13 +27,13 @@ 1.3.2 - 1.82.2 + 1.83.0 2.2.0 - 2.13.2 + 2.14.0 33.6.0-jre 4.33.6 1.62.0 - 2.48.0 + 2.50.0 1.0.0 3.1 1.7.0