From 4205102856771699d6c5cda951eedd023c4bb805 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Fri, 23 Oct 2020 14:48:30 -0700 Subject: [PATCH 01/29] feat: add mtls support --- .../client/googleapis/util/ClientCert.java | 36 +++++++++++++++++++ .../googleapis/util/ClientCertSource.java | 28 +++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java create mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java new file mode 100644 index 000000000..72489a813 --- /dev/null +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.util; + +/** + * Class for mutual TLS channel client certificate. + */ +public final class ClientCert { + /** X.509 certificate string in PEM format for mutual TLS. */ + public String certificate; + + /** Private key string in PEM format for mutual TLS. */ + public String privateKey; + + /** + * Constructor with the given certificate and private key. + * @param certificate X.509 certificate string in PEM format for mutual TLS + * @param privateKey Private key string in PEM format for mutual TLS + */ + public ClientCert(String certificate, String privateKey) { + this.certificate = certificate; + this.privateKey = privateKey; + } +} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java new file mode 100644 index 000000000..e26f5d5b6 --- /dev/null +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.util; + +/** + * Client certificate interface for mutual TLS. + */ +public interface ClientCertSource { + + /** + * Function to produce client certificate for mutual TLS. + * + * @return Client certificate for mutual TLS. + */ + public ClientCert execute(); +} From fa0802054ffbc023152a6357b7fd21a8598b241d Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Tue, 27 Oct 2020 14:31:16 -0700 Subject: [PATCH 02/29] add run cert command --- google-api-client/pom.xml | 5 +++ .../client/googleapis/util/ClientCert.java | 36 ------------------- .../googleapis/util/ClientCertSource.java | 28 --------------- .../api/client/googleapis/util/Utils.java | 30 ++++++++++++++++ .../api/client/googleapis/util/UtilsTest.java | 9 +++++ 5 files changed, 44 insertions(+), 64 deletions(-) delete mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java delete mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java diff --git a/google-api-client/pom.xml b/google-api-client/pom.xml index 0e3bdd0c4..00443e5c0 100644 --- a/google-api-client/pom.xml +++ b/google-api-client/pom.xml @@ -117,6 +117,11 @@ + + com.google.code.gson + gson + 2.8.6 + com.google.oauth-client google-oauth-client diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java deleted file mode 100644 index 72489a813..000000000 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCert.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.googleapis.util; - -/** - * Class for mutual TLS channel client certificate. - */ -public final class ClientCert { - /** X.509 certificate string in PEM format for mutual TLS. */ - public String certificate; - - /** Private key string in PEM format for mutual TLS. */ - public String privateKey; - - /** - * Constructor with the given certificate and private key. - * @param certificate X.509 certificate string in PEM format for mutual TLS - * @param privateKey Private key string in PEM format for mutual TLS - */ - public ClientCert(String certificate, String privateKey) { - this.certificate = certificate; - this.privateKey = privateKey; - } -} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java deleted file mode 100644 index e26f5d5b6..000000000 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ClientCertSource.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.googleapis.util; - -/** - * Client certificate interface for mutual TLS. - */ -public interface ClientCertSource { - - /** - * Function to produce client certificate for mutual TLS. - * - * @return Client certificate for mutual TLS. - */ - public ClientCert execute(); -} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index 97c27e0e1..36e80db6c 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -14,11 +14,21 @@ package com.google.api.client.googleapis.util; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.util.ArrayList; + import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.Beta; +import com.google.gson.GsonBuilder; +import com.google.gson.internal.LinkedTreeMap; /** * {@link Beta}
@@ -28,6 +38,7 @@ */ @Beta public final class Utils { + private static final String CONTEXT_AWARE_METADATA_PATH = System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; /** * Returns a cached default implementation of the JsonFactory interface. @@ -55,6 +66,25 @@ private static class TransportInstanceHolder { static final HttpTransport INSTANCE = new NetHttpTransport(); } + public static boolean hasDefaultCertSource() { + File file = new File(CONTEXT_AWARE_METADATA_PATH); + return file.exists(); + } + + @SuppressWarnings("unchecked") + public static InputStream loadDefaultCert() throws IOException, InterruptedException, GeneralSecurityException { + String json = new String(Files.readAllBytes(Paths.get(CONTEXT_AWARE_METADATA_PATH))); + GsonBuilder builder = new GsonBuilder(); + LinkedTreeMap map = (LinkedTreeMap)builder.create().fromJson(json, Object.class); + ArrayList commands = (ArrayList)map.get("cert_provider_command"); + Process process = new ProcessBuilder(commands).start(); + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new GeneralSecurityException("Failed to execute cert provider command"); + } + return process.getInputStream(); + } + private Utils() { } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index c4326747e..7ae555a54 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -19,6 +19,7 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import java.io.IOException; +import java.io.InputStream; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; @@ -61,4 +62,12 @@ public static Map parseQuery(String query) throws IOException { } return map; } + + public void testHasDefaultCertSource() { + assertTrue("has default cert source", Utils.hasDefaultCertSource()); + } + + public void testLoadDefaultCert() throws Exception { + InputStream certAndKey = Utils.loadDefaultCert(); + } } From b2d88913bf21e8df1154011f08ca9fff70ce690b Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Fri, 30 Oct 2020 12:48:49 -0700 Subject: [PATCH 03/29] javanet --- .../javanet/GoogleNetHttpTransport.java | 32 +++++++++++++++++-- .../api/client/googleapis/util/Utils.java | 20 +++++++++--- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index 47d536eb9..5b1d060c9 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -15,8 +15,12 @@ package com.google.api.client.googleapis.javanet; import com.google.api.client.googleapis.GoogleUtils; +import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.util.SecurityUtils; + import java.io.IOException; +import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -50,9 +54,31 @@ static HttpTransport newProxyTransport() throws GeneralSecurityException, IOExce * */ public static NetHttpTransport newTrustedTransport() - throws GeneralSecurityException, IOException { - return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore()) - .build(); + throws GeneralSecurityException, IOException, InterruptedException { + return newTrustedTransportBuilder(null).build(); + } + + public static NetHttpTransport newTrustedTransport(InputStream clientCertificateSource) + throws GeneralSecurityException, IOException, InterruptedException { + return newTrustedTransportBuilder(clientCertificateSource).build(); + } + + public static NetHttpTransport.Builder newTrustedTransportBuilder(InputStream clientCertificateSource) + throws GeneralSecurityException, IOException, InterruptedException { + if (Utils.useMtlsClientCertificate()) { + InputStream certificateToUse = null; + if (clientCertificateSource != null) { + certificateToUse = clientCertificateSource; + } else { + certificateToUse = Utils.loadDefaultCertificate(); + } + + if (certificateToUse != null) { + KeyStroe mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certificateToUse); + return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, ""); + } + } + return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore()); } private GoogleNetHttpTransport() { diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index 36e80db6c..7ca9631c2 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -39,6 +39,7 @@ @Beta public final class Utils { private static final String CONTEXT_AWARE_METADATA_PATH = System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; + public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; /** * Returns a cached default implementation of the JsonFactory interface. @@ -66,25 +67,34 @@ private static class TransportInstanceHolder { static final HttpTransport INSTANCE = new NetHttpTransport(); } - public static boolean hasDefaultCertSource() { + @SuppressWarnings("unchecked") + public static InputStream loadDefaultCertificate() throws IOException, InterruptedException, GeneralSecurityException { File file = new File(CONTEXT_AWARE_METADATA_PATH); - return file.exists(); - } + if (!file.exists()) { + return null; + } - @SuppressWarnings("unchecked") - public static InputStream loadDefaultCert() throws IOException, InterruptedException, GeneralSecurityException { + // Load the cert provider command from the json file. String json = new String(Files.readAllBytes(Paths.get(CONTEXT_AWARE_METADATA_PATH))); GsonBuilder builder = new GsonBuilder(); LinkedTreeMap map = (LinkedTreeMap)builder.create().fromJson(json, Object.class); ArrayList commands = (ArrayList)map.get("cert_provider_command"); + + // Call the command. Process process = new ProcessBuilder(commands).start(); int exitCode = process.waitFor(); if (exitCode != 0) { throw new GeneralSecurityException("Failed to execute cert provider command"); } + return process.getInputStream(); } + public static boolean useMtlsClientCertificate() { + String env = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); + return "true".equals(env); + } + private Utils() { } } From 79e9ac1edb649b7781e8ab8498c2358c328bf6b4 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Fri, 30 Oct 2020 13:12:15 -0700 Subject: [PATCH 04/29] add apache --- .../apache/v2/GoogleApacheHttpTransport.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index b5f91f6ed..b9b52216e 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -15,9 +15,11 @@ package com.google.api.client.googleapis.apache.v2; import com.google.api.client.googleapis.GoogleUtils; +import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.apache.v2.ApacheHttpTransport; import com.google.api.client.util.SslUtils; import java.io.IOException; +import java.io.InputStream; import java.net.ProxySelector; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -44,6 +46,28 @@ public final class GoogleApacheHttpTransport { */ public static ApacheHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException { + return newTrustedTransport(null); + } + + public static ApacheHttpTransport newTrustedTransport(InputStream clientCertificateSource) throws GeneralSecurityException, + IOException { + InputStream certificateToUse = null; + KeyStore mtlsKeyStore = null; + if (Utils.useMtlsClientCertificate()) { + if (clientCertificateSource != null) { + certificateToUse = clientCertificateSource; + } else { + certificateToUse = Utils.loadDefaultCertificate(); + } + } + if (certificateToUse != null) { + mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certificateToUse); + } + return newTrustedTransport(mtlsKeyStore); + } + + private static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore) throws GeneralSecurityException, + IOException { PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(-1, TimeUnit.MILLISECONDS); @@ -53,7 +77,11 @@ public static ApacheHttpTransport newTrustedTransport() throws GeneralSecurityEx // Use the included trust store KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); SSLContext sslContext = SslUtils.getTlsSslContext(); - SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory()); + if (mtlsKeyStore != null) { + SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory(), mtlsKeyStore, SslUtils.getDefaultKeyManagerFactory()); + } else { + SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory()); + } LayeredConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); HttpClient client = HttpClientBuilder.create() From f8603890e4d11bd6fdbc3aff4e9ee8d6577bfd83 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Fri, 30 Oct 2020 13:17:41 -0700 Subject: [PATCH 05/29] update --- .../apache/v2/GoogleApacheHttpTransport.java | 46 ++++++++++--------- .../javanet/GoogleNetHttpTransport.java | 12 ++--- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index b9b52216e..b9a770b32 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -26,7 +26,6 @@ import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import org.apache.http.client.HttpClient; -import org.apache.http.config.SocketConfig; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; @@ -44,16 +43,16 @@ public final class GoogleApacheHttpTransport { * Returns a new instance of {@link ApacheHttpTransport} that uses * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates. */ - public static ApacheHttpTransport newTrustedTransport() throws GeneralSecurityException, - IOException { + public static ApacheHttpTransport newTrustedTransport() + throws GeneralSecurityException, IOException { return newTrustedTransport(null); } - public static ApacheHttpTransport newTrustedTransport(InputStream clientCertificateSource) throws GeneralSecurityException, - IOException { + public static ApacheHttpTransport newTrustedTransport(InputStream clientCertificateSource) + throws GeneralSecurityException, IOException { InputStream certificateToUse = null; KeyStore mtlsKeyStore = null; - if (Utils.useMtlsClientCertificate()) { + if (Utils.useMtlsClientCertificate()) { if (clientCertificateSource != null) { certificateToUse = clientCertificateSource; } else { @@ -66,8 +65,8 @@ public static ApacheHttpTransport newTrustedTransport(InputStream clientCertific return newTrustedTransport(mtlsKeyStore); } - private static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore) throws GeneralSecurityException, - IOException { + private static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore) + throws GeneralSecurityException, IOException { PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(-1, TimeUnit.MILLISECONDS); @@ -78,25 +77,30 @@ private static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore) th KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); SSLContext sslContext = SslUtils.getTlsSslContext(); if (mtlsKeyStore != null) { - SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory(), mtlsKeyStore, SslUtils.getDefaultKeyManagerFactory()); + SslUtils.initSslContext( + sslContext, + trustStore, + SslUtils.getPkixTrustManagerFactory(), + mtlsKeyStore, + SslUtils.getDefaultKeyManagerFactory()); } else { SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory()); } LayeredConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); - HttpClient client = HttpClientBuilder.create() - .useSystemProperties() - .setSSLSocketFactory(socketFactory) - .setMaxConnTotal(200) - .setMaxConnPerRoute(20) - .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) - .setConnectionManager(connectionManager) - .disableRedirectHandling() - .disableAutomaticRetries() - .build(); + HttpClient client = + HttpClientBuilder.create() + .useSystemProperties() + .setSSLSocketFactory(socketFactory) + .setMaxConnTotal(200) + .setMaxConnPerRoute(20) + .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) + .setConnectionManager(connectionManager) + .disableRedirectHandling() + .disableAutomaticRetries() + .build(); return new ApacheHttpTransport(client); } - private GoogleApacheHttpTransport() { - } + private GoogleApacheHttpTransport() {} } diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index 5b1d060c9..a4ce6dfcc 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -18,7 +18,6 @@ import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.util.SecurityUtils; - import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; @@ -63,7 +62,8 @@ public static NetHttpTransport newTrustedTransport(InputStream clientCertificate return newTrustedTransportBuilder(clientCertificateSource).build(); } - public static NetHttpTransport.Builder newTrustedTransportBuilder(InputStream clientCertificateSource) + public static NetHttpTransport.Builder newTrustedTransportBuilder( + InputStream clientCertificateSource) throws GeneralSecurityException, IOException, InterruptedException { if (Utils.useMtlsClientCertificate()) { InputStream certificateToUse = null; @@ -75,12 +75,12 @@ public static NetHttpTransport.Builder newTrustedTransportBuilder(InputStream cl if (certificateToUse != null) { KeyStroe mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certificateToUse); - return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, ""); + return new NetHttpTransport.Builder() + .trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, ""); } - } + } return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore()); } - private GoogleNetHttpTransport() { - } + private GoogleNetHttpTransport() {} } From 43655ab5b49cf52b0d33a2a800692e5664f0fb8b Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Fri, 30 Oct 2020 13:38:35 -0700 Subject: [PATCH 06/29] fix test --- .../client/googleapis/javanet/GoogleNetHttpTransport.java | 2 +- .../com/google/api/client/googleapis/util/UtilsTest.java | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index a4ce6dfcc..007cd2bed 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -74,7 +74,7 @@ public static NetHttpTransport.Builder newTrustedTransportBuilder( } if (certificateToUse != null) { - KeyStroe mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certificateToUse); + KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certificateToUse); return new NetHttpTransport.Builder() .trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, ""); } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index 7ae555a54..2ec892518 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -63,11 +63,7 @@ public static Map parseQuery(String query) throws IOException { return map; } - public void testHasDefaultCertSource() { - assertTrue("has default cert source", Utils.hasDefaultCertSource()); - } - public void testLoadDefaultCert() throws Exception { - InputStream certAndKey = Utils.loadDefaultCert(); + InputStream certAndKey = Utils.loadDefaultCertificate(); } } From 2849ee7abcf3eea0352f3f83ae5c82c9fd7bf36a Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Fri, 30 Oct 2020 14:33:39 -0700 Subject: [PATCH 07/29] tmp --- .../apache/v2/GoogleApacheHttpTransport.java | 19 ++---------------- .../javanet/GoogleNetHttpTransport.java | 18 +++++------------ .../api/client/googleapis/util/Utils.java | 20 ++++++++++++++++--- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index b9a770b32..8973142ca 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -50,23 +50,6 @@ public static ApacheHttpTransport newTrustedTransport() public static ApacheHttpTransport newTrustedTransport(InputStream clientCertificateSource) throws GeneralSecurityException, IOException { - InputStream certificateToUse = null; - KeyStore mtlsKeyStore = null; - if (Utils.useMtlsClientCertificate()) { - if (clientCertificateSource != null) { - certificateToUse = clientCertificateSource; - } else { - certificateToUse = Utils.loadDefaultCertificate(); - } - } - if (certificateToUse != null) { - mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certificateToUse); - } - return newTrustedTransport(mtlsKeyStore); - } - - private static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore) - throws GeneralSecurityException, IOException { PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(-1, TimeUnit.MILLISECONDS); @@ -76,12 +59,14 @@ private static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore) // Use the included trust store KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); SSLContext sslContext = SslUtils.getTlsSslContext(); + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(clientCertificateSource); if (mtlsKeyStore != null) { SslUtils.initSslContext( sslContext, trustStore, SslUtils.getPkixTrustManagerFactory(), mtlsKeyStore, + "", SslUtils.getDefaultKeyManagerFactory()); } else { SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory()); diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index 007cd2bed..36df49c17 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -54,7 +54,7 @@ static HttpTransport newProxyTransport() throws GeneralSecurityException, IOExce */ public static NetHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException, InterruptedException { - return newTrustedTransportBuilder(null).build(); + return newTrustedTransport(null); } public static NetHttpTransport newTrustedTransport(InputStream clientCertificateSource) @@ -65,19 +65,11 @@ public static NetHttpTransport newTrustedTransport(InputStream clientCertificate public static NetHttpTransport.Builder newTrustedTransportBuilder( InputStream clientCertificateSource) throws GeneralSecurityException, IOException, InterruptedException { - if (Utils.useMtlsClientCertificate()) { - InputStream certificateToUse = null; - if (clientCertificateSource != null) { - certificateToUse = clientCertificateSource; - } else { - certificateToUse = Utils.loadDefaultCertificate(); - } + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(clientCertificateSource); - if (certificateToUse != null) { - KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certificateToUse); - return new NetHttpTransport.Builder() - .trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, ""); - } + if (mtlsKeyStore != null) { + return new NetHttpTransport.Builder() + .trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, ""); } return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore()); } diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index 7ca9631c2..a7a44e7bd 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -20,6 +20,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.security.GeneralSecurityException; +import java.security.KeyStore; import java.util.ArrayList; import com.google.api.client.http.HttpTransport; @@ -27,6 +28,7 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.Beta; +import com.google.api.client.util.SecurityUtils; import com.google.gson.GsonBuilder; import com.google.gson.internal.LinkedTreeMap; @@ -90,9 +92,21 @@ public static InputStream loadDefaultCertificate() throws IOException, Interrupt return process.getInputStream(); } - public static boolean useMtlsClientCertificate() { - String env = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); - return "true".equals(env); + public static KeyStore loadMtlsKeyStore(InputStream clientCertificateSource) throws IOException, InterruptedException, GeneralSecurityException { + String environValue = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); + if ("true".equals(environValue)) { + InputStream certificateToUse = null; + if (clientCertificateSource != null) { + certificateToUse = clientCertificateSource; + } else { + certificateToUse = loadDefaultCertificate(); + } + + if (certificateToUse != null) { + return SecurityUtils.createMtlsKeyStore(certificateToUse); + } + } + return null; } private Utils() { From 46d89739b9fdca1037b1f36a96afdd628e02f793 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Fri, 30 Oct 2020 15:13:38 -0700 Subject: [PATCH 08/29] fix --- .../api/client/googleapis/util/Utils.java | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index a7a44e7bd..04983e3bd 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -41,7 +41,7 @@ @Beta public final class Utils { private static final String CONTEXT_AWARE_METADATA_PATH = System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; - public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; + private static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; /** * Returns a cached default implementation of the JsonFactory interface. @@ -70,7 +70,28 @@ private static class TransportInstanceHolder { } @SuppressWarnings("unchecked") - public static InputStream loadDefaultCertificate() throws IOException, InterruptedException, GeneralSecurityException { + public static ArrayList extractCertificateProviderCommand(String contextAwareMetadataJson) { + GsonBuilder builder = new GsonBuilder(); + LinkedTreeMap map = (LinkedTreeMap)builder.create().fromJson(contextAwareMetadataJson, Object.class); + return (ArrayList)map.get("cert_provider_command"); + } + + public static InputStream runCertificateProviderCommand(ArrayList command) throws IOException, GeneralSecurityException { + Process process = new ProcessBuilder(command).start(); + int exitCode = 0; + try { + exitCode = process.waitFor(); + } catch (InterruptedException exception) { + throw new GeneralSecurityException("Failed to execute cert provider command", exception); + } + if (exitCode != 0) { + throw new GeneralSecurityException("Failed to execute cert provider command"); + } + + return process.getInputStream(); + } + + public static InputStream loadDefaultCertificate() throws IOException, GeneralSecurityException { File file = new File(CONTEXT_AWARE_METADATA_PATH); if (!file.exists()) { return null; @@ -78,21 +99,13 @@ public static InputStream loadDefaultCertificate() throws IOException, Interrupt // Load the cert provider command from the json file. String json = new String(Files.readAllBytes(Paths.get(CONTEXT_AWARE_METADATA_PATH))); - GsonBuilder builder = new GsonBuilder(); - LinkedTreeMap map = (LinkedTreeMap)builder.create().fromJson(json, Object.class); - ArrayList commands = (ArrayList)map.get("cert_provider_command"); + ArrayList command = extractCertificateProviderCommand(json); // Call the command. - Process process = new ProcessBuilder(commands).start(); - int exitCode = process.waitFor(); - if (exitCode != 0) { - throw new GeneralSecurityException("Failed to execute cert provider command"); - } - - return process.getInputStream(); + return runCertificateProviderCommand(command); } - public static KeyStore loadMtlsKeyStore(InputStream clientCertificateSource) throws IOException, InterruptedException, GeneralSecurityException { + public static KeyStore loadMtlsKeyStore(InputStream clientCertificateSource) throws IOException, GeneralSecurityException { String environValue = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); if ("true".equals(environValue)) { InputStream certificateToUse = null; From 5e063b3cc7578827b6d067a4d718c5355bb5eb95 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Sat, 31 Oct 2020 03:46:46 -0700 Subject: [PATCH 09/29] added tests --- google-api-client/pom.xml | 12 ++ .../api/client/googleapis/util/Utils.java | 32 +++--- .../javanet/GoogleNetHttpTransportTest.java | 45 ++++++++ .../api/client/googleapis/util/UtilsTest.java | 108 +++++++++++++++++- .../client/googleapis/util/mtlsCertAndKey.pem | 30 +++++ 5 files changed, 206 insertions(+), 21 deletions(-) create mode 100644 google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java create mode 100644 google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtlsCertAndKey.pem diff --git a/google-api-client/pom.xml b/google-api-client/pom.xml index 8856af6d9..68afc6411 100644 --- a/google-api-client/pom.xml +++ b/google-api-client/pom.xml @@ -159,5 +159,17 @@ com.google.http-client google-http-client-apache-v2
+ + org.powermock + powermock-module-junit4 + 1.6.4 + test + + + org.powermock + powermock-api-mockito + 1.6.4 + test +
diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index 04983e3bd..968cd933e 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -41,7 +41,7 @@ @Beta public final class Utils { private static final String CONTEXT_AWARE_METADATA_PATH = System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; - private static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; + public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; /** * Returns a cached default implementation of the JsonFactory interface. @@ -76,7 +76,17 @@ public static ArrayList extractCertificateProviderCommand(String context return (ArrayList)map.get("cert_provider_command"); } - public static InputStream runCertificateProviderCommand(ArrayList command) throws IOException, GeneralSecurityException { + public static InputStream loadDefaultCertificate() throws IOException, GeneralSecurityException { + File file = new File(CONTEXT_AWARE_METADATA_PATH); + if (!file.exists()) { + return null; + } + + // Load the cert provider command from the json file. + String json = new String(Files.readAllBytes(Paths.get(CONTEXT_AWARE_METADATA_PATH))); + ArrayList command = extractCertificateProviderCommand(json); + + // Call the command. Process process = new ProcessBuilder(command).start(); int exitCode = 0; try { @@ -91,23 +101,9 @@ public static InputStream runCertificateProviderCommand(ArrayList comman return process.getInputStream(); } - public static InputStream loadDefaultCertificate() throws IOException, GeneralSecurityException { - File file = new File(CONTEXT_AWARE_METADATA_PATH); - if (!file.exists()) { - return null; - } - - // Load the cert provider command from the json file. - String json = new String(Files.readAllBytes(Paths.get(CONTEXT_AWARE_METADATA_PATH))); - ArrayList command = extractCertificateProviderCommand(json); - - // Call the command. - return runCertificateProviderCommand(command); - } - public static KeyStore loadMtlsKeyStore(InputStream clientCertificateSource) throws IOException, GeneralSecurityException { - String environValue = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); - if ("true".equals(environValue)) { + String useClientCertificate = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); + if ("true".equals(useClientCertificate)) { InputStream certificateToUse = null; if (clientCertificateSource != null) { certificateToUse = clientCertificateSource; diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java new file mode 100644 index 000000000..60b4c64c7 --- /dev/null +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java @@ -0,0 +1,45 @@ +package com.google.api.client.googleapis.javanet; + +import java.io.InputStream; +import java.security.KeyStore; + +import com.google.api.client.googleapis.util.Utils; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.util.SecurityUtils; + +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import junit.framework.TestCase; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Utils.class, GoogleNetHttpTransport.class}) +@PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) +public class GoogleNetHttpTransportTest extends TestCase { + public InputStream getCertAndKey() throws Exception { + return getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); + } + + // mTLS key store is provided, so mTLS transport is created + public void testWithMtlsKeyStore() throws Exception { + KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(getCertAndKey()); + PowerMockito.mockStatic(Utils.class); + PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(mtlsKeyStore); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); + assertTrue(transport.isMtls()); + } + + // mTLS key store doesn't exist, so transport is not mTLS + public void testWithoutMtlsKeyStore() throws Exception { + PowerMockito.mockStatic(Utils.class); + PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(null); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); + assertFalse(transport.isMtls()); + } +} diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index 2ec892518..5fb07cf97 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -18,9 +18,18 @@ import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; + +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + import java.io.IOException; import java.io.InputStream; import java.net.URLDecoder; +import java.security.KeyStore; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import junit.framework.TestCase; @@ -30,6 +39,9 @@ * * @author Yaniv Inbar */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({System.class, Utils.class}) +@PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) public class UtilsTest extends TestCase { public void testGetDefaultJsonFactory() { @@ -63,7 +75,97 @@ public static Map parseQuery(String query) throws IOException { return map; } - public void testLoadDefaultCert() throws Exception { - InputStream certAndKey = Utils.loadDefaultCertificate(); + public void testExtractCertificateProviderCommand() { + String json = "{\"cert_provider_command\":[\"/opt/google/endpoint-verification/bin/apihelper\",\"--print_certificate\"],\"device_resource_ids\":[\"123\"]}"; + ArrayList command = Utils.extractCertificateProviderCommand(json); + assertEquals(2, command.size()); + assertEquals("/opt/google/endpoint-verification/bin/apihelper", command.get(0)); + assertEquals("--print_certificate", command.get(1)); + } + + public InputStream getMtlsCertificateAndKey() { + return getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); + } + + // Test the case for Utils.loadMtlsKeyStore where: + // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "true" + // - User provided client cert to Utils.loadMtlsKeyStore + // In this case the provided client cert will be used to create a key store. + public void testUseCertWithProvidedCert() throws Exception { + PowerMockito.spy(System.class); + PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("true"); + + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(getMtlsCertificateAndKey()); + assertNotNull(mtlsKeyStore); + } + + // Test the case for Utils.loadMtlsKeyStore where: + // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "true" + // - default client cert exists + // In this case the default client cert will be used to create a key store. + public void testUseCertWithDefaultCert() throws Exception { + PowerMockito.spy(System.class); + PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("true"); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(getMtlsCertificateAndKey()); + + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); + assertNotNull(mtlsKeyStore); + } + + // Test the case for Utils.loadMtlsKeyStore where: + // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "true" + // - no client cert is provided or exists + // In this case key store is null because there is no client cert. + public void testUseCertWithNoCert() throws Exception { + PowerMockito.spy(System.class); + PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("true"); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(null); + + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); + assertNull(mtlsKeyStore); + } + + // Test the case for Utils.loadMtlsKeyStore where: + // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "false" + // - User provided client cert to Utils.loadMtlsKeyStore + // In this case client cert is not used, so no key store is created. + public void testNotUseCertWithProvidedCert() throws Exception { + PowerMockito.spy(System.class); + PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("false"); + + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(getMtlsCertificateAndKey()); + assertNull(mtlsKeyStore); + } + + // Test the case for Utils.loadMtlsKeyStore where: + // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "false" + // - default client cert exists + // In this case client cert is not used, so no key store is created. + public void testNotUseCertWithDefaultCert() throws Exception { + PowerMockito.spy(System.class); + PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("false"); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(getMtlsCertificateAndKey()); + + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); + assertNull(mtlsKeyStore); + } + + // Test the case for Utils.loadMtlsKeyStore where: + // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "false" + // - no client cert is provided or exists + // In this case client cert is not used, so no key store is created. + public void testNotUseCertNoCert() throws Exception { + PowerMockito.spy(System.class); + PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("false"); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(null); + + KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); + assertNull(mtlsKeyStore); } -} +} \ No newline at end of file diff --git a/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtlsCertAndKey.pem b/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtlsCertAndKey.pem new file mode 100644 index 000000000..d6c045125 --- /dev/null +++ b/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtlsCertAndKey.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIICGzCCAYSgAwIBAgIIWrt6xtmHPs4wDQYJKoZIhvcNAQEFBQAwMzExMC8GA1UE +AxMoMTAwOTEyMDcyNjg3OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTAeFw0x +MjEyMDExNjEwNDRaFw0yMjExMjkxNjEwNDRaMDMxMTAvBgNVBAMTKDEwMDkxMjA3 +MjY4NzguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20wgZ8wDQYJKoZIhvcNAQEB +BQADgY0AMIGJAoGBAL1SdY8jTUVU7O4/XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQ +GLW8Iftx9wfXe1zuaehJSgLcyCxazfyJoN3RiONBihBqWY6d3lQKqkgsRTNZkdFJ +Wdzl/6CxhK9sojh2p0r3tydtv9iwq5fuuWIvtODtT98EgphhncQAqkKoF3zVAgMB +AAGjODA2MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM +MAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4GBAD8XQEqzGePa9VrvtEGpf+R4 +fkxKbcYAzqYq202nKu0kfjhIYkYSBj6gi348YaxE64yu60TVl42l5HThmswUheW4 +uQIaq36JvwvsDP5Zoj5BgiNSnDAFQp+jJFBRUA5vooJKgKgMDf/r/DCOsbO6VJF1 +kWwa9n19NFiV0z3m6isj +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL1SdY8jTUVU7O4/ +XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQGLW8Iftx9wfXe1zuaehJSgLcyCxazfyJ +oN3RiONBihBqWY6d3lQKqkgsRTNZkdFJWdzl/6CxhK9sojh2p0r3tydtv9iwq5fu +uWIvtODtT98EgphhncQAqkKoF3zVAgMBAAECgYB51B9cXe4yiGTzJ4pOKpHGySAy +sC1F/IjXt2eeD3PuKv4m/hL4l7kScpLx0+NJuQ4j8U2UK/kQOdrGANapB1ZbMZAK +/q0xmIUzdNIDiGSoTXGN2mEfdsEpQ/Xiv0lyhYBBPC/K4sYIpHccnhSRQUZlWLLY +lE5cFNKC9b7226mNvQJBAPt0hfCNIN0kUYOA9jdLtx7CE4ySGMPf5KPBuzPd8ty1 +fxaFm9PB7B76VZQYmHcWy8rT5XjoLJHrmGW1ZvP+iDsCQQDAvnKoarPOGb5iJfkq +RrA4flf1TOlf+1+uqIOJ94959jkkJeb0gv/TshDnm6/bWn+1kJylQaKygCizwPwB +Z84vAkA0Duur4YvsPJijoQ9YY1SGCagCcjyuUKwFOxaGpmyhRPIKt56LOJqpzyno +fy8ReKa4VyYq4eZYT249oFCwMwIBAkAROPNF2UL3x5UbcAkznd1hLujtIlI4IV4L +XUNjsJtBap7we/KHJq11XRPlniO4lf2TW7iji5neGVWJulTKS1xBAkAerktk4Hsw +ErUaUG1s/d+Sgc8e/KMeBElV+NxGhcWEeZtfHMn/6VOlbzY82JyvC9OKC80A5CAE +VUV6b25kqrcu +-----END PRIVATE KEY----- \ No newline at end of file From dceaacfc20cf63efbb36afa0918e7db27f22d5b7 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Sat, 31 Oct 2020 04:02:42 -0700 Subject: [PATCH 10/29] add apache --- .../apache/v2/GoogleApacheHttpTransport.java | 5 ++- .../v2/GoogleApacheHttpTransportTest.java | 36 ++++++++++++++++ .../javanet/GoogleNetHttpTransportTest.java | 42 +++++++++---------- 3 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index 8973142ca..20345d4b3 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -60,7 +60,8 @@ public static ApacheHttpTransport newTrustedTransport(InputStream clientCertific KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); SSLContext sslContext = SslUtils.getTlsSslContext(); KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(clientCertificateSource); - if (mtlsKeyStore != null) { + boolean isMtls = (mtlsKeyStore != null) && (mtlsKeyStore.size() > 0); + if (isMtls) { SslUtils.initSslContext( sslContext, trustStore, @@ -84,7 +85,7 @@ public static ApacheHttpTransport newTrustedTransport(InputStream clientCertific .disableRedirectHandling() .disableAutomaticRetries() .build(); - return new ApacheHttpTransport(client); + return new ApacheHttpTransport(client, isMtls); } private GoogleApacheHttpTransport() {} diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java new file mode 100644 index 000000000..67721b870 --- /dev/null +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java @@ -0,0 +1,36 @@ +package com.google.api.client.googleapis.apache.v2; + +import java.io.InputStream; +import java.security.KeyStore; + +import com.google.api.client.googleapis.util.Utils; +import com.google.api.client.http.apache.v2.ApacheHttpTransport; +import com.google.api.client.util.SecurityUtils; + +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; + +public class GoogleApacheHttpTransportTest { + public InputStream getCertAndKey() throws Exception { + return getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); + } + + // mTLS key store is provided, so mTLS transport is created + public void testWithMtlsKeyStore() throws Exception { + KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(getCertAndKey()); + PowerMockito.mockStatic(Utils.class); + PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(mtlsKeyStore); + ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(getCertAndKey()); + assertTrue(transport.isMtls()); + } + + // mTLS key store doesn't exist, so transport is not mTLS + public void testWithoutMtlsKeyStore() throws Exception { + PowerMockito.mockStatic(Utils.class); + PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(null); + ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(getCertAndKey()); + assertFalse(transport.isMtls()); + } +} diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java index 60b4c64c7..3332410f0 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java @@ -20,26 +20,26 @@ @PrepareForTest({Utils.class, GoogleNetHttpTransport.class}) @PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) public class GoogleNetHttpTransportTest extends TestCase { - public InputStream getCertAndKey() throws Exception { - return getClass() - .getClassLoader() - .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); - } + public InputStream getCertAndKey() throws Exception { + return getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); + } - // mTLS key store is provided, so mTLS transport is created - public void testWithMtlsKeyStore() throws Exception { - KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(getCertAndKey()); - PowerMockito.mockStatic(Utils.class); - PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(mtlsKeyStore); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); - assertTrue(transport.isMtls()); - } - - // mTLS key store doesn't exist, so transport is not mTLS - public void testWithoutMtlsKeyStore() throws Exception { - PowerMockito.mockStatic(Utils.class); - PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(null); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); - assertFalse(transport.isMtls()); - } + // mTLS key store is provided, so mTLS transport is created + public void testWithMtlsKeyStore() throws Exception { + KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(getCertAndKey()); + PowerMockito.mockStatic(Utils.class); + PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(mtlsKeyStore); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); + assertTrue(transport.isMtls()); + } + + // mTLS key store doesn't exist, so transport is not mTLS + public void testWithoutMtlsKeyStore() throws Exception { + PowerMockito.mockStatic(Utils.class); + PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(null); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); + assertFalse(transport.isMtls()); + } } From 0f4f87879e759619e040fb7afa098773b8efb69b Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Sat, 31 Oct 2020 04:11:31 -0700 Subject: [PATCH 11/29] finished apache --- .../apache/v2/GoogleApacheHttpTransportTest.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java index 67721b870..4f527a119 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java @@ -7,10 +7,19 @@ import com.google.api.client.http.apache.v2.ApacheHttpTransport; import com.google.api.client.util.SecurityUtils; +import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; -public class GoogleApacheHttpTransportTest { +import junit.framework.TestCase; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Utils.class, GoogleApacheHttpTransport.class}) +@PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) +public class GoogleApacheHttpTransportTest extends TestCase { public InputStream getCertAndKey() throws Exception { return getClass() .getClassLoader() From 9e33face4b01d322ab451e4098a4caa82f7f079f Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Sun, 1 Nov 2020 01:36:08 -0700 Subject: [PATCH 12/29] doc string --- .../apache/v2/GoogleApacheHttpTransport.java | 39 +++++++++++++----- .../javanet/GoogleNetHttpTransport.java | 41 +++++++++++++++++-- .../api/client/googleapis/util/Utils.java | 29 +++++++++++++ .../api/client/googleapis/util/UtilsTest.java | 2 +- 4 files changed, 95 insertions(+), 16 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index 20345d4b3..bf6b05985 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -17,6 +17,7 @@ import com.google.api.client.googleapis.GoogleUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.apache.v2.ApacheHttpTransport; +import com.google.api.client.util.Beta; import com.google.api.client.util.SslUtils; import java.io.IOException; import java.io.InputStream; @@ -42,12 +43,29 @@ public final class GoogleApacheHttpTransport { /** * Returns a new instance of {@link ApacheHttpTransport} that uses * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates. + * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", + * and the default client certificate from {@link Utils#loadDefaultCertificate()} + * is not null, then the transport uses the default client certificate and + * is mutual TLS. */ public static ApacheHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException { return newTrustedTransport(null); } + /** + * {@link Beta}
+ * Returns a new instance of {@link ApacheHttpTransport} that uses + * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates. + * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", + * the function looks for user provided client certificate first from + * clientCertificateSource InputStream, if not exists, then the default from + * {@link Utils#loadDefaultCertificate()}. If client certificate exists, + * the transport uses it and is mutual TLS. + * + * @param clientCertificateSource InputStream for mutual TLS client certificate and private key + */ + @Beta public static ApacheHttpTransport newTrustedTransport(InputStream clientCertificateSource) throws GeneralSecurityException, IOException { PoolingHttpClientConnectionManager connectionManager = @@ -74,17 +92,16 @@ public static ApacheHttpTransport newTrustedTransport(InputStream clientCertific } LayeredConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); - HttpClient client = - HttpClientBuilder.create() - .useSystemProperties() - .setSSLSocketFactory(socketFactory) - .setMaxConnTotal(200) - .setMaxConnPerRoute(20) - .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) - .setConnectionManager(connectionManager) - .disableRedirectHandling() - .disableAutomaticRetries() - .build(); + HttpClient client = HttpClientBuilder.create() + .useSystemProperties() + .setSSLSocketFactory(socketFactory) + .setMaxConnTotal(200) + .setMaxConnPerRoute(20) + .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) + .setConnectionManager(connectionManager) + .disableRedirectHandling() + .disableAutomaticRetries() + .build(); return new ApacheHttpTransport(client, isMtls); } diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index 36df49c17..ece8b91e6 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -17,6 +17,7 @@ import com.google.api.client.googleapis.GoogleUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.util.Beta; import com.google.api.client.util.SecurityUtils; import java.io.IOException; import java.io.InputStream; @@ -35,7 +36,10 @@ public class GoogleNetHttpTransport { * Returns a new instance of {@link NetHttpTransport} that uses * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)} - * . + * . If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", + * and the default client certificate from {@link Utils#loadDefaultCertificate()} + * is not null, then the transport uses the default client certificate and + * is mutual TLS. * *

* This helper method doesn't provide for customization of the {@link NetHttpTransport}, such as @@ -53,18 +57,47 @@ static HttpTransport newProxyTransport() throws GeneralSecurityException, IOExce * */ public static NetHttpTransport newTrustedTransport() - throws GeneralSecurityException, IOException, InterruptedException { + throws GeneralSecurityException, IOException { return newTrustedTransport(null); } + /** + * {@link Beta}
+ * Returns a new instance of {@link NetHttpTransport} that uses + * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using + * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)} + * . If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", + * the function looks for user provided client certificate first from + * clientCertificateSource InputStream, if not exists, then the default from + * {@link Utils#loadDefaultCertificate()}. If client certificate exists, + * the transport uses it and is mutual TLS. + * + * @param clientCertificateSource InputStream for mutual TLS client certificate and private key + */ + @Beta public static NetHttpTransport newTrustedTransport(InputStream clientCertificateSource) - throws GeneralSecurityException, IOException, InterruptedException { + throws GeneralSecurityException, IOException { return newTrustedTransportBuilder(clientCertificateSource).build(); } + /** + * {@link Beta}
+ * Returns a new instance of {@link NetHttpTransport.Builder} that uses + * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using + * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)} + * . If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", + * the function looks for user provided client certificate first from + * clientCertificateSource InputStream, if not exists, then the default from + * {@link Utils#loadDefaultCertificate()}. If client certificate exists, + * the transport uses it and is mutual TLS. Note that mutual TLS may not work properly + * if you specify a proxy with the Builder instance. + * + * @param clientCertificateSource InputStream for mutual TLS client certificate and private key + */ + @Beta public static NetHttpTransport.Builder newTrustedTransportBuilder( InputStream clientCertificateSource) - throws GeneralSecurityException, IOException, InterruptedException { + throws GeneralSecurityException, IOException { KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(clientCertificateSource); if (mtlsKeyStore != null) { diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index 968cd933e..bfa0f5f43 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -41,6 +41,8 @@ @Beta public final class Utils { private static final String CONTEXT_AWARE_METADATA_PATH = System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; + + /** GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable */ public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; /** @@ -69,6 +71,13 @@ private static class TransportInstanceHolder { static final HttpTransport INSTANCE = new NetHttpTransport(); } + /** + * Returns the `cert_provider_command` field in context_aware_metadata.json file. + * + * @param contextAwareMetadataJson ~/.secureConnect/context_aware_metadata.json file content. + * @return `cert_provider_command` field + * @since 1.31 + */ @SuppressWarnings("unchecked") public static ArrayList extractCertificateProviderCommand(String contextAwareMetadataJson) { GsonBuilder builder = new GsonBuilder(); @@ -76,6 +85,13 @@ public static ArrayList extractCertificateProviderCommand(String context return (ArrayList)map.get("cert_provider_command"); } + /** + * Returns the default client certificate by running the cert_provider_command commands + * from ~/.secureConnect/context_aware_metadata.json file. + * + * @return The default client certificate input stream + * @since 1.31 + */ public static InputStream loadDefaultCertificate() throws IOException, GeneralSecurityException { File file = new File(CONTEXT_AWARE_METADATA_PATH); if (!file.exists()) { @@ -101,6 +117,19 @@ public static InputStream loadDefaultCertificate() throws IOException, GeneralSe return process.getInputStream(); } + /** + * Returns the KeyStore for mutual TLS. + * + * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", and + * either the client certificate is provided via clientCertificateSource, or the default + * client certificate exists via {@link Utils#loadDefaultCertificate()}, a KeyStore object + * will be created using the client certificate (clientCertificateSource takes precedence). + * Otherwise, this function return null. + * + * @param clientCertificateSource InputStream for mutual TLS client certificate and private key + * @return KeyStore for mutual TLS. + * @since 1.31 + */ public static KeyStore loadMtlsKeyStore(InputStream clientCertificateSource) throws IOException, GeneralSecurityException { String useClientCertificate = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); if ("true".equals(useClientCertificate)) { diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index 5fb07cf97..b8716b366 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -168,4 +168,4 @@ public void testNotUseCertNoCert() throws Exception { KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); assertNull(mtlsKeyStore); } -} \ No newline at end of file +} From 0442025b504fc864060c51bc51ddb077f43db3da Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Wed, 4 Nov 2020 01:19:02 -0800 Subject: [PATCH 13/29] update code --- google-api-client/pom.xml | 5 - .../apache/v2/GoogleApacheHttpTransport.java | 36 +++--- .../javanet/GoogleNetHttpTransport.java | 66 +++++------ .../util/ContextAwareMetadataJson.java | 43 +++++++ .../api/client/googleapis/util/Utils.java | 87 ++++++--------- .../v2/GoogleApacheHttpTransportTest.java | 56 +++++++--- .../javanet/GoogleNetHttpTransportTest.java | 56 +++++++--- .../api/client/googleapis/util/UtilsTest.java | 105 +----------------- 8 files changed, 217 insertions(+), 237 deletions(-) create mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java diff --git a/google-api-client/pom.xml b/google-api-client/pom.xml index 68afc6411..57d78d95a 100644 --- a/google-api-client/pom.xml +++ b/google-api-client/pom.xml @@ -117,11 +117,6 @@ - - com.google.code.gson - gson - 2.8.6 - com.google.oauth-client google-oauth-client diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index bf6b05985..2dafb13d4 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -20,7 +20,6 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.SslUtils; import java.io.IOException; -import java.io.InputStream; import java.net.ProxySelector; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -44,13 +43,13 @@ public final class GoogleApacheHttpTransport { * Returns a new instance of {@link ApacheHttpTransport} that uses * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates. * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * and the default client certificate from {@link Utils#loadDefaultCertificate()} + * and the default client certificate key store from {@link Utils#loadDefaultMtlsKeyStore()} * is not null, then the transport uses the default client certificate and * is mutual TLS. */ public static ApacheHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException { - return newTrustedTransport(null); + return newTrustedTransport(null, ""); } /** @@ -58,15 +57,16 @@ public static ApacheHttpTransport newTrustedTransport() * Returns a new instance of {@link ApacheHttpTransport} that uses * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates. * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * the function looks for user provided client certificate first from - * clientCertificateSource InputStream, if not exists, then the default from - * {@link Utils#loadDefaultCertificate()}. If client certificate exists, - * the transport uses it and is mutual TLS. + * the function uses the provided mtlsKeyStore or the default key store from + * {@link Utils#loadDefaultMtlsKeyStore()} to create the transport. If either key + * store exists, then the created transport is mutual TLS. The provided key store + * takes precedence over the default one. * - * @param clientCertificateSource InputStream for mutual TLS client certificate and private key + * @param mtlsKeyStore KeyStore for mutual TLS client certificate and private key + * @param mtlsKeyStorePassword KeyStore password */ @Beta - public static ApacheHttpTransport newTrustedTransport(InputStream clientCertificateSource) + public static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore, String mtlsKeyStorePassword) throws GeneralSecurityException, IOException { PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(-1, TimeUnit.MILLISECONDS); @@ -77,15 +77,25 @@ public static ApacheHttpTransport newTrustedTransport(InputStream clientCertific // Use the included trust store KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); SSLContext sslContext = SslUtils.getTlsSslContext(); - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(clientCertificateSource); - boolean isMtls = (mtlsKeyStore != null) && (mtlsKeyStore.size() > 0); + + // Figure out if mTLS is needed and what key store to use. + Boolean useMtls = Utils.useMtlsClientCertificate(); + KeyStore mtlsKeyStoreToUse = mtlsKeyStore; + String mtlsKeyStorePasswordToUse = mtlsKeyStorePassword; + if (useMtls && mtlsKeyStoreToUse == null) { + // Use the default mTLS key store if not provided. + mtlsKeyStoreToUse = Utils.loadDefaultMtlsKeyStore(); + mtlsKeyStorePasswordToUse = ""; + } + + Boolean isMtls = useMtls && mtlsKeyStoreToUse != null && mtlsKeyStoreToUse.size() > 0; if (isMtls) { SslUtils.initSslContext( sslContext, trustStore, SslUtils.getPkixTrustManagerFactory(), - mtlsKeyStore, - "", + mtlsKeyStoreToUse, + mtlsKeyStorePasswordToUse, SslUtils.getDefaultKeyManagerFactory()); } else { SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory()); diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index ece8b91e6..a27ea917d 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -18,9 +18,7 @@ import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.util.Beta; -import com.google.api.client.util.SecurityUtils; import java.io.IOException; -import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -35,9 +33,9 @@ public class GoogleNetHttpTransport { /** * Returns a new instance of {@link NetHttpTransport} that uses * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using - * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)} - * . If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * and the default client certificate from {@link Utils#loadDefaultCertificate()} + * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)}. + * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", + * and the default client certificate key store from {@link Utils#loadDefaultMtlsKeyStore()} * is not null, then the transport uses the default client certificate and * is mutual TLS. * @@ -58,53 +56,43 @@ static HttpTransport newProxyTransport() throws GeneralSecurityException, IOExce */ public static NetHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException { - return newTrustedTransport(null); + return newTrustedTransport(null, ""); } /** * {@link Beta}
* Returns a new instance of {@link NetHttpTransport} that uses * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using - * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)} - * . If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * the function looks for user provided client certificate first from - * clientCertificateSource InputStream, if not exists, then the default from - * {@link Utils#loadDefaultCertificate()}. If client certificate exists, - * the transport uses it and is mutual TLS. + * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)}. + * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", + * the function uses the provided mtlsKeyStore or the default key store from + * {@link Utils#loadDefaultMtlsKeyStore()} to create the transport. If either key + * store exists, then the created transport is mutual TLS. The provided key store + * takes precedence over the default one. * - * @param clientCertificateSource InputStream for mutual TLS client certificate and private key + * @param mtlsKeyStore KeyStore for mutual TLS client certificate and private key + * @param mtlsKeyStorePassword KeyStore password + * @since 1.31 */ @Beta - public static NetHttpTransport newTrustedTransport(InputStream clientCertificateSource) + public static NetHttpTransport newTrustedTransport(KeyStore mtlsKeyStore, String mtlsKeyStorePassword) throws GeneralSecurityException, IOException { - return newTrustedTransportBuilder(clientCertificateSource).build(); - } - - /** - * {@link Beta}
- * Returns a new instance of {@link NetHttpTransport.Builder} that uses - * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using - * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)} - * . If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * the function looks for user provided client certificate first from - * clientCertificateSource InputStream, if not exists, then the default from - * {@link Utils#loadDefaultCertificate()}. If client certificate exists, - * the transport uses it and is mutual TLS. Note that mutual TLS may not work properly - * if you specify a proxy with the Builder instance. - * - * @param clientCertificateSource InputStream for mutual TLS client certificate and private key - */ - @Beta - public static NetHttpTransport.Builder newTrustedTransportBuilder( - InputStream clientCertificateSource) - throws GeneralSecurityException, IOException { - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(clientCertificateSource); + // Figure out if mTLS is needed and what key store to use. + Boolean useMtls = Utils.useMtlsClientCertificate(); + KeyStore mtlsKeyStoreToUse = mtlsKeyStore; + String mtlsKeyStorePasswordToUse = mtlsKeyStorePassword; + if (useMtls && mtlsKeyStoreToUse == null) { + // Use the default mTLS key store if not provided. + mtlsKeyStoreToUse = Utils.loadDefaultMtlsKeyStore(); + mtlsKeyStorePasswordToUse = ""; + } - if (mtlsKeyStore != null) { + if (useMtls && mtlsKeyStoreToUse != null && mtlsKeyStoreToUse.size() > 0) { return new NetHttpTransport.Builder() - .trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, ""); + .trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStoreToUse, mtlsKeyStorePasswordToUse) + .build(); } - return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore()); + return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore()).build(); } private GoogleNetHttpTransport() {} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java new file mode 100644 index 000000000..e5733bdfa --- /dev/null +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.util; + +import java.util.List; + +import com.google.api.client.json.GenericJson; +import com.google.api.client.util.Beta; +import com.google.api.client.util.Key; + +/** + * {@link Beta}
+ * Data class representing context_aware_metadata.json file. + * + * @since 1.31 + */ +@Beta +public class ContextAwareMetadataJson extends GenericJson { + /** Cert provider command */ + @Key("cert_provider_command") + private List commands; + + /** + * Returns the cert provider command. + * + * @since 1.31 + */ + public final List getCommands() { + return commands; + } +} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index bfa0f5f43..c1dae7df5 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -15,22 +15,21 @@ package com.google.api.client.googleapis.util; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; +import java.security.AccessControlException; import java.security.GeneralSecurityException; import java.security.KeyStore; -import java.util.ArrayList; +import java.util.List; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.JsonParser; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.Beta; import com.google.api.client.util.SecurityUtils; -import com.google.gson.GsonBuilder; -import com.google.gson.internal.LinkedTreeMap; /** * {@link Beta}
@@ -74,33 +73,50 @@ private static class TransportInstanceHolder { /** * Returns the `cert_provider_command` field in context_aware_metadata.json file. * - * @param contextAwareMetadataJson ~/.secureConnect/context_aware_metadata.json file content. + * @param contextAwareMetadata Input stream for ~/.secureConnect/context_aware_metadata.json file. * @return `cert_provider_command` field * @since 1.31 */ - @SuppressWarnings("unchecked") - public static ArrayList extractCertificateProviderCommand(String contextAwareMetadataJson) { - GsonBuilder builder = new GsonBuilder(); - LinkedTreeMap map = (LinkedTreeMap)builder.create().fromJson(contextAwareMetadataJson, Object.class); - return (ArrayList)map.get("cert_provider_command"); + static List extractCertificateProviderCommand(InputStream contextAwareMetadata) throws IOException { + JsonParser parser = JsonFactoryInstanceHolder.INSTANCE.createJsonParser(contextAwareMetadata); + ContextAwareMetadataJson json = parser.parse(ContextAwareMetadataJson.class); + return json.getCommands(); } /** - * Returns the default client certificate by running the cert_provider_command commands - * from ~/.secureConnect/context_aware_metadata.json file. + * Returns if mTLS client certificate should be used. mTLS client certificate + * is used if and only if "GOOGLE_API_USE_CLIENT_CERTIFICATE" environment variable + * value is "true". * - * @return The default client certificate input stream + * @return If mTLS client certificate should be used * @since 1.31 */ - public static InputStream loadDefaultCertificate() throws IOException, GeneralSecurityException { + public static Boolean useMtlsClientCertificate() { + String useClientCertificate = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); + return "true".equals(useClientCertificate); + } + + /** + * Returns the default KeyStore for mutual TLS. + * + * Default client certificate can be generated by running the commands in + * ~/.secureConnect/context_aware_metadata.json file. If this json exists, + * and the default client certificate exists, this function returns a KeyStore + * object created with the default client certificate and the key store + * password is "". Otherwise, null will be returned. + * + * @return KeyStore for mutual TLS. + * @since 1.31 + */ + public static KeyStore loadDefaultMtlsKeyStore() throws IOException, GeneralSecurityException { File file = new File(CONTEXT_AWARE_METADATA_PATH); if (!file.exists()) { return null; } // Load the cert provider command from the json file. - String json = new String(Files.readAllBytes(Paths.get(CONTEXT_AWARE_METADATA_PATH))); - ArrayList command = extractCertificateProviderCommand(json); + InputStream stream = new FileInputStream(file); + List command = extractCertificateProviderCommand(stream); // Call the command. Process process = new ProcessBuilder(command).start(); @@ -108,43 +124,14 @@ public static InputStream loadDefaultCertificate() throws IOException, GeneralSe try { exitCode = process.waitFor(); } catch (InterruptedException exception) { - throw new GeneralSecurityException("Failed to execute cert provider command", exception); + throw new AccessControlException(exception.toString()); } if (exitCode != 0) { - throw new GeneralSecurityException("Failed to execute cert provider command"); + throw new AccessControlException("Failed to execute cert provider command"); } - return process.getInputStream(); - } - - /** - * Returns the KeyStore for mutual TLS. - * - * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", and - * either the client certificate is provided via clientCertificateSource, or the default - * client certificate exists via {@link Utils#loadDefaultCertificate()}, a KeyStore object - * will be created using the client certificate (clientCertificateSource takes precedence). - * Otherwise, this function return null. - * - * @param clientCertificateSource InputStream for mutual TLS client certificate and private key - * @return KeyStore for mutual TLS. - * @since 1.31 - */ - public static KeyStore loadMtlsKeyStore(InputStream clientCertificateSource) throws IOException, GeneralSecurityException { - String useClientCertificate = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); - if ("true".equals(useClientCertificate)) { - InputStream certificateToUse = null; - if (clientCertificateSource != null) { - certificateToUse = clientCertificateSource; - } else { - certificateToUse = loadDefaultCertificate(); - } - - if (certificateToUse != null) { - return SecurityUtils.createMtlsKeyStore(certificateToUse); - } - } - return null; + InputStream certificateToUse = process.getInputStream(); + return SecurityUtils.createMtlsKeyStore(certificateToUse); } private Utils() { diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java index 4f527a119..7aebcded7 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java @@ -8,7 +8,6 @@ import com.google.api.client.util.SecurityUtils; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -20,26 +19,53 @@ @PrepareForTest({Utils.class, GoogleApacheHttpTransport.class}) @PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) public class GoogleApacheHttpTransportTest extends TestCase { - public InputStream getCertAndKey() throws Exception { - return getClass() + public KeyStore createTestMtlsKeyStore() throws Exception { + InputStream certAndKey = getClass() .getClassLoader() .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); + return SecurityUtils.createMtlsKeyStore(certAndKey); } - // mTLS key store is provided, so mTLS transport is created - public void testWithMtlsKeyStore() throws Exception { - KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(getCertAndKey()); - PowerMockito.mockStatic(Utils.class); - PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(mtlsKeyStore); - ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(getCertAndKey()); + // If client certificate shouldn't be used, then neither the provided mtlsKeyStore + // nor the default mtls key store should be used. + public void testNotUseCertificate() throws Exception { + KeyStore mtlsKeyStore = createTestMtlsKeyStore(); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(false); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); + ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(mtlsKeyStore, ""); + assertFalse(transport.isMtls()); + } + + // If client certificate should be used, and mtlsKeyStore is provided, then the + // provided key store should be used. + public void testUseProvidedCertificate() throws Exception { + KeyStore mtlsKeyStore = createTestMtlsKeyStore(); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); + ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(mtlsKeyStore, ""); assertTrue(transport.isMtls()); } - - // mTLS key store doesn't exist, so transport is not mTLS - public void testWithoutMtlsKeyStore() throws Exception { - PowerMockito.mockStatic(Utils.class); - PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(null); - ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(getCertAndKey()); + + // If client certificate should be used, and mtlsKeyStore is provided, then the + // provided key store should be used. + public void testUseDefaultCertificate() throws Exception { + KeyStore mtlsKeyStore = createTestMtlsKeyStore(); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); + ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(null, ""); + assertTrue(transport.isMtls()); + } + + // If client certificate should be used, but no mtls key store is available, then + // the transport created is not mtls. + public void testNoCertificate() throws Exception { + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); + ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(null, ""); assertFalse(transport.isMtls()); } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java index 3332410f0..505675e2d 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java @@ -8,7 +8,6 @@ import com.google.api.client.util.SecurityUtils; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -20,26 +19,53 @@ @PrepareForTest({Utils.class, GoogleNetHttpTransport.class}) @PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) public class GoogleNetHttpTransportTest extends TestCase { - public InputStream getCertAndKey() throws Exception { - return getClass() + public KeyStore createTestMtlsKeyStore() throws Exception { + InputStream certAndKey = getClass() .getClassLoader() .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); + return SecurityUtils.createMtlsKeyStore(certAndKey); } - // mTLS key store is provided, so mTLS transport is created - public void testWithMtlsKeyStore() throws Exception { - KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(getCertAndKey()); - PowerMockito.mockStatic(Utils.class); - PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(mtlsKeyStore); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); + // If client certificate shouldn't be used, then neither the provided mtlsKeyStore + // nor the default mtls key store should be used. + public void testNotUseCertificate() throws Exception { + KeyStore mtlsKeyStore = createTestMtlsKeyStore(); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(false); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(mtlsKeyStore, ""); + assertFalse(transport.isMtls()); + } + + // If client certificate should be used, and mtlsKeyStore is provided, then the + // provided key store should be used. + public void testUseProvidedCertificate() throws Exception { + KeyStore mtlsKeyStore = createTestMtlsKeyStore(); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(mtlsKeyStore, ""); assertTrue(transport.isMtls()); } - - // mTLS key store doesn't exist, so transport is not mTLS - public void testWithoutMtlsKeyStore() throws Exception { - PowerMockito.mockStatic(Utils.class); - PowerMockito.when(Utils.loadMtlsKeyStore(Mockito.any(InputStream.class))).thenReturn(null); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransportBuilder(getCertAndKey()).build(); + + // If client certificate should be used, and mtlsKeyStore is provided, then the + // provided key store should be used. + public void testUseDefaultCertificate() throws Exception { + KeyStore mtlsKeyStore = createTestMtlsKeyStore(); + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(null, ""); + assertTrue(transport.isMtls()); + } + + // If client certificate should be used, but no mtls key store is available, then + // the transport created is not mtls. + public void testNoCertificate() throws Exception { + PowerMockito.spy(Utils.class); + PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); + PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(null, ""); assertFalse(transport.isMtls()); } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index b8716b366..0bf540681 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -19,18 +19,11 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.net.URLDecoder; -import java.security.KeyStore; -import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import junit.framework.TestCase; @@ -39,9 +32,6 @@ * * @author Yaniv Inbar */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({System.class, Utils.class}) -@PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) public class UtilsTest extends TestCase { public void testGetDefaultJsonFactory() { @@ -75,97 +65,12 @@ public static Map parseQuery(String query) throws IOException { return map; } - public void testExtractCertificateProviderCommand() { + public void testExtractCertificateProviderCommand() throws IOException { String json = "{\"cert_provider_command\":[\"/opt/google/endpoint-verification/bin/apihelper\",\"--print_certificate\"],\"device_resource_ids\":[\"123\"]}"; - ArrayList command = Utils.extractCertificateProviderCommand(json); + ByteArrayInputStream stream = new ByteArrayInputStream(json.getBytes()); + List command = Utils.extractCertificateProviderCommand(stream); assertEquals(2, command.size()); assertEquals("/opt/google/endpoint-verification/bin/apihelper", command.get(0)); assertEquals("--print_certificate", command.get(1)); } - - public InputStream getMtlsCertificateAndKey() { - return getClass() - .getClassLoader() - .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); - } - - // Test the case for Utils.loadMtlsKeyStore where: - // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "true" - // - User provided client cert to Utils.loadMtlsKeyStore - // In this case the provided client cert will be used to create a key store. - public void testUseCertWithProvidedCert() throws Exception { - PowerMockito.spy(System.class); - PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("true"); - - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(getMtlsCertificateAndKey()); - assertNotNull(mtlsKeyStore); - } - - // Test the case for Utils.loadMtlsKeyStore where: - // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "true" - // - default client cert exists - // In this case the default client cert will be used to create a key store. - public void testUseCertWithDefaultCert() throws Exception { - PowerMockito.spy(System.class); - PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("true"); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(getMtlsCertificateAndKey()); - - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); - assertNotNull(mtlsKeyStore); - } - - // Test the case for Utils.loadMtlsKeyStore where: - // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "true" - // - no client cert is provided or exists - // In this case key store is null because there is no client cert. - public void testUseCertWithNoCert() throws Exception { - PowerMockito.spy(System.class); - PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("true"); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(null); - - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); - assertNull(mtlsKeyStore); - } - - // Test the case for Utils.loadMtlsKeyStore where: - // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "false" - // - User provided client cert to Utils.loadMtlsKeyStore - // In this case client cert is not used, so no key store is created. - public void testNotUseCertWithProvidedCert() throws Exception { - PowerMockito.spy(System.class); - PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("false"); - - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(getMtlsCertificateAndKey()); - assertNull(mtlsKeyStore); - } - - // Test the case for Utils.loadMtlsKeyStore where: - // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "false" - // - default client cert exists - // In this case client cert is not used, so no key store is created. - public void testNotUseCertWithDefaultCert() throws Exception { - PowerMockito.spy(System.class); - PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("false"); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(getMtlsCertificateAndKey()); - - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); - assertNull(mtlsKeyStore); - } - - // Test the case for Utils.loadMtlsKeyStore where: - // - GOOGLE_API_USE_CLIENT_CERTIFICATE = "false" - // - no client cert is provided or exists - // In this case client cert is not used, so no key store is created. - public void testNotUseCertNoCert() throws Exception { - PowerMockito.spy(System.class); - PowerMockito.when(System.getenv(Utils.GOOGLE_API_USE_CLIENT_CERTIFICATE)).thenReturn("false"); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.loadDefaultCertificate()).thenReturn(null); - - KeyStore mtlsKeyStore = Utils.loadMtlsKeyStore(null); - assertNull(mtlsKeyStore); - } } From d83c1151219407da7f67e01cf3428c3aa34a4ee9 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Wed, 4 Nov 2020 01:41:42 -0800 Subject: [PATCH 14/29] lint --- .../apache/v2/GoogleApacheHttpTransport.java | 54 ++++++++-------- .../util/ContextAwareMetadataJson.java | 7 +-- .../api/client/googleapis/util/Utils.java | 61 +++++++++---------- 3 files changed, 58 insertions(+), 64 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index 2dafb13d4..32824e0c1 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -40,12 +40,11 @@ public final class GoogleApacheHttpTransport { /** - * Returns a new instance of {@link ApacheHttpTransport} that uses - * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates. - * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * and the default client certificate key store from {@link Utils#loadDefaultMtlsKeyStore()} - * is not null, then the transport uses the default client certificate and - * is mutual TLS. + * Returns a new instance of {@link ApacheHttpTransport} that uses {@link + * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. If + * `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", and the default + * client certificate key store from {@link Utils#loadDefaultMtlsKeyStore()} is not null, then the + * transport uses the default client certificate and is mutual TLS. */ public static ApacheHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException { @@ -53,20 +52,20 @@ public static ApacheHttpTransport newTrustedTransport() } /** - * {@link Beta}
- * Returns a new instance of {@link ApacheHttpTransport} that uses - * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates. - * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * the function uses the provided mtlsKeyStore or the default key store from - * {@link Utils#loadDefaultMtlsKeyStore()} to create the transport. If either key - * store exists, then the created transport is mutual TLS. The provided key store - * takes precedence over the default one. - * + * {@link Beta}
+ * Returns a new instance of {@link ApacheHttpTransport} that uses {@link + * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. If + * `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", the function uses + * the provided mtlsKeyStore or the default key store from {@link Utils#loadDefaultMtlsKeyStore()} + * to create the transport. If either key store exists, then the created transport is mutual TLS. + * The provided key store takes precedence over the default one. + * * @param mtlsKeyStore KeyStore for mutual TLS client certificate and private key - * @param mtlsKeyStorePassword KeyStore password + * @param mtlsKeyStorePassword KeyStore password */ @Beta - public static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore, String mtlsKeyStorePassword) + public static ApacheHttpTransport newTrustedTransport( + KeyStore mtlsKeyStore, String mtlsKeyStorePassword) throws GeneralSecurityException, IOException { PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(-1, TimeUnit.MILLISECONDS); @@ -102,16 +101,17 @@ public static ApacheHttpTransport newTrustedTransport(KeyStore mtlsKeyStore, Str } LayeredConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); - HttpClient client = HttpClientBuilder.create() - .useSystemProperties() - .setSSLSocketFactory(socketFactory) - .setMaxConnTotal(200) - .setMaxConnPerRoute(20) - .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) - .setConnectionManager(connectionManager) - .disableRedirectHandling() - .disableAutomaticRetries() - .build(); + HttpClient client = + HttpClientBuilder.create() + .useSystemProperties() + .setSSLSocketFactory(socketFactory) + .setMaxConnTotal(200) + .setMaxConnPerRoute(20) + .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) + .setConnectionManager(connectionManager) + .disableRedirectHandling() + .disableAutomaticRetries() + .build(); return new ApacheHttpTransport(client, isMtls); } diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java index e5733bdfa..5e408ed4b 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java @@ -14,16 +14,15 @@ package com.google.api.client.googleapis.util; -import java.util.List; - import com.google.api.client.json.GenericJson; import com.google.api.client.util.Beta; import com.google.api.client.util.Key; +import java.util.List; /** * {@link Beta}
* Data class representing context_aware_metadata.json file. - * + * * @since 1.31 */ @Beta @@ -34,7 +33,7 @@ public class ContextAwareMetadataJson extends GenericJson { /** * Returns the cert provider command. - * + * * @since 1.31 */ public final List getCommands() { diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index c1dae7df5..7ae37ce09 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -14,6 +14,13 @@ package com.google.api.client.googleapis.util; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.JsonParser; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.Beta; +import com.google.api.client.util.SecurityUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -23,14 +30,6 @@ import java.security.KeyStore; import java.util.List; -import com.google.api.client.http.HttpTransport; -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.JsonParser; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.google.api.client.util.Beta; -import com.google.api.client.util.SecurityUtils; - /** * {@link Beta}
* Utility class for the Google API Client Library. @@ -39,14 +38,14 @@ */ @Beta public final class Utils { - private static final String CONTEXT_AWARE_METADATA_PATH = System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; - + private static final String CONTEXT_AWARE_METADATA_PATH = + System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; + /** GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable */ - public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; + public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = + "GOOGLE_API_USE_CLIENT_CERTIFICATE"; - /** - * Returns a cached default implementation of the JsonFactory interface. - */ + /** Returns a cached default implementation of the JsonFactory interface. */ public static JsonFactory getDefaultJsonFactory() { return JsonFactoryInstanceHolder.INSTANCE; } @@ -59,9 +58,7 @@ private static class JsonFactoryInstanceHolder { static final JsonFactory INSTANCE = new JacksonFactory(); } - /** - * Returns a cached default implementation of the HttpTransport interface. - */ + /** Returns a cached default implementation of the HttpTransport interface. */ public static HttpTransport getDefaultTransport() { return TransportInstanceHolder.INSTANCE; } @@ -72,22 +69,22 @@ private static class TransportInstanceHolder { /** * Returns the `cert_provider_command` field in context_aware_metadata.json file. - * + * * @param contextAwareMetadata Input stream for ~/.secureConnect/context_aware_metadata.json file. * @return `cert_provider_command` field * @since 1.31 */ - static List extractCertificateProviderCommand(InputStream contextAwareMetadata) throws IOException { + static List extractCertificateProviderCommand(InputStream contextAwareMetadata) + throws IOException { JsonParser parser = JsonFactoryInstanceHolder.INSTANCE.createJsonParser(contextAwareMetadata); ContextAwareMetadataJson json = parser.parse(ContextAwareMetadataJson.class); return json.getCommands(); } /** - * Returns if mTLS client certificate should be used. mTLS client certificate - * is used if and only if "GOOGLE_API_USE_CLIENT_CERTIFICATE" environment variable - * value is "true". - * + * Returns if mTLS client certificate should be used. mTLS client certificate is used if and only + * if "GOOGLE_API_USE_CLIENT_CERTIFICATE" environment variable value is "true". + * * @return If mTLS client certificate should be used * @since 1.31 */ @@ -98,13 +95,12 @@ public static Boolean useMtlsClientCertificate() { /** * Returns the default KeyStore for mutual TLS. - * - * Default client certificate can be generated by running the commands in - * ~/.secureConnect/context_aware_metadata.json file. If this json exists, - * and the default client certificate exists, this function returns a KeyStore - * object created with the default client certificate and the key store - * password is "". Otherwise, null will be returned. - * + * + *

Default client certificate can be generated by running the commands in + * ~/.secureConnect/context_aware_metadata.json file. If this json exists, and the default client + * certificate exists, this function returns a KeyStore object created with the default client + * certificate and the key store password is "". Otherwise, null will be returned. + * * @return KeyStore for mutual TLS. * @since 1.31 */ @@ -117,7 +113,7 @@ public static KeyStore loadDefaultMtlsKeyStore() throws IOException, GeneralSecu // Load the cert provider command from the json file. InputStream stream = new FileInputStream(file); List command = extractCertificateProviderCommand(stream); - + // Call the command. Process process = new ProcessBuilder(command).start(); int exitCode = 0; @@ -134,6 +130,5 @@ public static KeyStore loadDefaultMtlsKeyStore() throws IOException, GeneralSecu return SecurityUtils.createMtlsKeyStore(certificateToUse); } - private Utils() { - } + private Utils() {} } From d92d3b3a67b19ff3ce86cbe4e227596fc0649071 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Thu, 5 Nov 2020 11:25:11 -0800 Subject: [PATCH 15/29] refactor: implement MtlsUtils.MtlsProvider interface with default implementation --- google-api-client/pom.xml | 12 -- .../apache/v2/GoogleApacheHttpTransport.java | 46 ++---- .../javanet/GoogleNetHttpTransport.java | 77 ++++----- .../util/ContextAwareMetadataJson.java | 42 ----- .../api/client/googleapis/util/MtlsUtils.java | 150 ++++++++++++++++++ .../api/client/googleapis/util/Utils.java | 69 -------- .../googleapis/MtlsTransportBaseTest.java | 91 +++++++++++ .../v2/GoogleApacheHttpTransportTest.java | 88 +++------- .../javanet/GoogleNetHttpTransportTest.java | 88 +++------- .../client/googleapis/util/MtlsUtilsTest.java | 119 ++++++++++++++ .../api/client/googleapis/util/UtilsTest.java | 24 --- .../util/mtls_context_aware_metadata.json | 9 ++ ...ls_context_aware_metadata_bad_command.json | 9 ++ 13 files changed, 468 insertions(+), 356 deletions(-) delete mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java create mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/util/MtlsUtils.java create mode 100644 google-api-client/src/test/java/com/google/api/client/googleapis/MtlsTransportBaseTest.java create mode 100644 google-api-client/src/test/java/com/google/api/client/googleapis/util/MtlsUtilsTest.java create mode 100644 google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata.json create mode 100644 google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata_bad_command.json diff --git a/google-api-client/pom.xml b/google-api-client/pom.xml index 57d78d95a..0f3255184 100644 --- a/google-api-client/pom.xml +++ b/google-api-client/pom.xml @@ -154,17 +154,5 @@ com.google.http-client google-http-client-apache-v2 - - org.powermock - powermock-module-junit4 - 1.6.4 - test - - - org.powermock - powermock-api-mockito - 1.6.4 - test - diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index 32824e0c1..c7d008f35 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -15,9 +15,9 @@ package com.google.api.client.googleapis.apache.v2; import com.google.api.client.googleapis.GoogleUtils; +import com.google.api.client.googleapis.util.MtlsUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.apache.v2.ApacheHttpTransport; -import com.google.api.client.util.Beta; import com.google.api.client.util.SslUtils; import java.io.IOException; import java.net.ProxySelector; @@ -48,25 +48,18 @@ public final class GoogleApacheHttpTransport { */ public static ApacheHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException { - return newTrustedTransport(null, ""); + return newTrustedTransport(MtlsUtils.getDefaultMtlsProvider()); } - /** - * {@link Beta}
- * Returns a new instance of {@link ApacheHttpTransport} that uses {@link - * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. If - * `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", the function uses - * the provided mtlsKeyStore or the default key store from {@link Utils#loadDefaultMtlsKeyStore()} - * to create the transport. If either key store exists, then the created transport is mutual TLS. - * The provided key store takes precedence over the default one. - * - * @param mtlsKeyStore KeyStore for mutual TLS client certificate and private key - * @param mtlsKeyStorePassword KeyStore password - */ - @Beta - public static ApacheHttpTransport newTrustedTransport( - KeyStore mtlsKeyStore, String mtlsKeyStorePassword) + static ApacheHttpTransport newTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { + KeyStore mtlsKeyStore = null; + String mtlsKeyStorePassword = null; + if (mtlsProvider.useMtlsClientCertificate()) { + mtlsKeyStore = mtlsProvider.loadDefaultKeyStore(); + mtlsKeyStorePassword = mtlsProvider.getKeyStorePassword(); + } + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(-1, TimeUnit.MILLISECONDS); @@ -77,24 +70,15 @@ public static ApacheHttpTransport newTrustedTransport( KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); SSLContext sslContext = SslUtils.getTlsSslContext(); - // Figure out if mTLS is needed and what key store to use. - Boolean useMtls = Utils.useMtlsClientCertificate(); - KeyStore mtlsKeyStoreToUse = mtlsKeyStore; - String mtlsKeyStorePasswordToUse = mtlsKeyStorePassword; - if (useMtls && mtlsKeyStoreToUse == null) { - // Use the default mTLS key store if not provided. - mtlsKeyStoreToUse = Utils.loadDefaultMtlsKeyStore(); - mtlsKeyStorePasswordToUse = ""; - } - - Boolean isMtls = useMtls && mtlsKeyStoreToUse != null && mtlsKeyStoreToUse.size() > 0; - if (isMtls) { + boolean isMtls = false; + if (mtlsKeyStore != null && mtlsKeyStorePassword != null) { + isMtls = true; SslUtils.initSslContext( sslContext, trustStore, SslUtils.getPkixTrustManagerFactory(), - mtlsKeyStoreToUse, - mtlsKeyStorePasswordToUse, + mtlsKeyStore, + mtlsKeyStorePassword, SslUtils.getDefaultKeyManagerFactory()); } else { SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory()); diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index a27ea917d..9ab8a67c8 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -15,9 +15,9 @@ package com.google.api.client.googleapis.javanet; import com.google.api.client.googleapis.GoogleUtils; +import com.google.api.client.googleapis.util.MtlsUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.util.Beta; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -31,68 +31,49 @@ public class GoogleNetHttpTransport { /** - * Returns a new instance of {@link NetHttpTransport} that uses - * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using - * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)}. - * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * and the default client certificate key store from {@link Utils#loadDefaultMtlsKeyStore()} - * is not null, then the transport uses the default client certificate and - * is mutual TLS. + * Returns a new instance of {@link NetHttpTransport} that uses {@link + * GoogleUtils#getCertificateTrustStore()} for the trusted certificates using {@link + * com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)}. If + * `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", and the default + * client certificate key store from {@link Utils#loadDefaultMtlsKeyStore()} is not null, then the + * transport uses the default client certificate and is mutual TLS. * - *

- * This helper method doesn't provide for customization of the {@link NetHttpTransport}, such as - * the ability to specify a proxy. To do use, use - * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder}, for example: - *

+ *

This helper method doesn't provide for customization of the {@link NetHttpTransport}, such + * as the ability to specify a proxy. To do use, use {@link + * com.google.api.client.http.javanet.NetHttpTransport.Builder}, for example: * *

-  static HttpTransport newProxyTransport() throws GeneralSecurityException, IOException {
-    NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
-    builder.trustCertificates(GoogleUtils.getCertificateTrustStore());
-    builder.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 3128)));
-    return builder.build();
-  }
+   * static HttpTransport newProxyTransport() throws GeneralSecurityException, IOException {
+   * NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
+   * builder.trustCertificates(GoogleUtils.getCertificateTrustStore());
+   * builder.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 3128)));
+   * return builder.build();
+   * }
    * 
*/ public static NetHttpTransport newTrustedTransport() throws GeneralSecurityException, IOException { - return newTrustedTransport(null, ""); + return newTrustedTransport(MtlsUtils.getDefaultMtlsProvider()); } - /** - * {@link Beta}
- * Returns a new instance of {@link NetHttpTransport} that uses - * {@link GoogleUtils#getCertificateTrustStore()} for the trusted certificates using - * {@link com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)}. - * If `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", - * the function uses the provided mtlsKeyStore or the default key store from - * {@link Utils#loadDefaultMtlsKeyStore()} to create the transport. If either key - * store exists, then the created transport is mutual TLS. The provided key store - * takes precedence over the default one. - * - * @param mtlsKeyStore KeyStore for mutual TLS client certificate and private key - * @param mtlsKeyStorePassword KeyStore password - * @since 1.31 - */ - @Beta - public static NetHttpTransport newTrustedTransport(KeyStore mtlsKeyStore, String mtlsKeyStorePassword) + static NetHttpTransport newTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { - // Figure out if mTLS is needed and what key store to use. - Boolean useMtls = Utils.useMtlsClientCertificate(); - KeyStore mtlsKeyStoreToUse = mtlsKeyStore; - String mtlsKeyStorePasswordToUse = mtlsKeyStorePassword; - if (useMtls && mtlsKeyStoreToUse == null) { - // Use the default mTLS key store if not provided. - mtlsKeyStoreToUse = Utils.loadDefaultMtlsKeyStore(); - mtlsKeyStorePasswordToUse = ""; + KeyStore mtlsKeyStore = null; + String mtlsKeyStorePassword = null; + if (mtlsProvider.useMtlsClientCertificate()) { + mtlsKeyStore = mtlsProvider.loadDefaultKeyStore(); + mtlsKeyStorePassword = mtlsProvider.getKeyStorePassword(); } - if (useMtls && mtlsKeyStoreToUse != null && mtlsKeyStoreToUse.size() > 0) { + if (mtlsKeyStore != null && mtlsKeyStorePassword != null) { return new NetHttpTransport.Builder() - .trustCertificates(GoogleUtils.getCertificateTrustStore(), mtlsKeyStoreToUse, mtlsKeyStorePasswordToUse) + .trustCertificates( + GoogleUtils.getCertificateTrustStore(), mtlsKeyStore, mtlsKeyStorePassword) .build(); } - return new NetHttpTransport.Builder().trustCertificates(GoogleUtils.getCertificateTrustStore()).build(); + return new NetHttpTransport.Builder() + .trustCertificates(GoogleUtils.getCertificateTrustStore()) + .build(); } private GoogleNetHttpTransport() {} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java deleted file mode 100644 index 5e408ed4b..000000000 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/ContextAwareMetadataJson.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.googleapis.util; - -import com.google.api.client.json.GenericJson; -import com.google.api.client.util.Beta; -import com.google.api.client.util.Key; -import java.util.List; - -/** - * {@link Beta}
- * Data class representing context_aware_metadata.json file. - * - * @since 1.31 - */ -@Beta -public class ContextAwareMetadataJson extends GenericJson { - /** Cert provider command */ - @Key("cert_provider_command") - private List commands; - - /** - * Returns the cert provider command. - * - * @since 1.31 - */ - public final List getCommands() { - return commands; - } -} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/MtlsUtils.java new file mode 100644 index 000000000..52c5fbf44 --- /dev/null +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/MtlsUtils.java @@ -0,0 +1,150 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.util; + +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonParser; +import com.google.api.client.util.Beta; +import com.google.api.client.util.Key; +import com.google.api.client.util.SecurityUtils; +import com.google.common.annotations.VisibleForTesting; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.List; + +public class MtlsUtils { + public interface MtlsProvider { + boolean useMtlsClientCertificate(); + + String getKeyStorePassword(); + + KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException; + } + + /** + * {@link Beta}
+ * Data class representing context_aware_metadata.json file. + * + * @since 1.31 + */ + @Beta + public static class ContextAwareMetadataJson extends GenericJson { + /** Cert provider command */ + @Key("cert_provider_command") + private List commands; + + /** + * Returns the cert provider command. + * + * @since 1.31 + */ + public final List getCommands() { + return commands; + } + } + + @VisibleForTesting + static class DefaultMtlsProvider implements MtlsProvider { + private static final String DEFAULT_CONTEXT_AWARE_METADATA_PATH = + System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; + + /** GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable */ + public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = + "GOOGLE_API_USE_CLIENT_CERTIFICATE"; + + interface EnvironmentProvider { + String getenv(String name); + } + + static class SystemEnvironmentProvider implements EnvironmentProvider { + @Override + public String getenv(String name) { + return System.getenv(name); + } + } + + DefaultMtlsProvider() { + this(new SystemEnvironmentProvider(), DEFAULT_CONTEXT_AWARE_METADATA_PATH); + } + + private EnvironmentProvider envProvider; + private String metadataPath; + + @VisibleForTesting + DefaultMtlsProvider(EnvironmentProvider envProvider, String metadataPath) { + this.envProvider = envProvider; + this.metadataPath = metadataPath; + } + + @Override + public boolean useMtlsClientCertificate() { + String useClientCertificate = envProvider.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); + return "true".equals(useClientCertificate); + } + + @Override + public String getKeyStorePassword() { + return ""; + } + + @Override + public KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException { + // Load the cert provider command from the json file. + InputStream stream; + try { + stream = new FileInputStream(metadataPath); + } catch (FileNotFoundException ignored) { + // file doesn't exist + return null; + } + + List command = extractCertificateProviderCommand(stream); + + // Call the command. + Process process = new ProcessBuilder(command).start(); + int exitCode = -1; + try { + exitCode = process.waitFor(); + } catch (InterruptedException e) { + throw new IOException("Interrupted executing certificate provider command", e); + } + if (exitCode != 0) { + throw new IOException( + String.format("Failed to execute cert provider command with exit code: %d", exitCode)); + } + + // Parse input certificates from shell command + return SecurityUtils.createMtlsKeyStore(process.getInputStream()); + } + + @VisibleForTesting + static List extractCertificateProviderCommand(InputStream contextAwareMetadata) + throws IOException { + JsonParser parser = Utils.getDefaultJsonFactory().createJsonParser(contextAwareMetadata); + ContextAwareMetadataJson json = parser.parse(ContextAwareMetadataJson.class); + return json.getCommands(); + } + } + + private static final MtlsProvider MTLS_PROVIDER = new DefaultMtlsProvider(); + + public static MtlsProvider getDefaultMtlsProvider() { + return MTLS_PROVIDER; + } +} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index 7ae37ce09..ab5ac8580 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -38,12 +38,6 @@ */ @Beta public final class Utils { - private static final String CONTEXT_AWARE_METADATA_PATH = - System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; - - /** GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable */ - public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = - "GOOGLE_API_USE_CLIENT_CERTIFICATE"; /** Returns a cached default implementation of the JsonFactory interface. */ public static JsonFactory getDefaultJsonFactory() { @@ -67,68 +61,5 @@ private static class TransportInstanceHolder { static final HttpTransport INSTANCE = new NetHttpTransport(); } - /** - * Returns the `cert_provider_command` field in context_aware_metadata.json file. - * - * @param contextAwareMetadata Input stream for ~/.secureConnect/context_aware_metadata.json file. - * @return `cert_provider_command` field - * @since 1.31 - */ - static List extractCertificateProviderCommand(InputStream contextAwareMetadata) - throws IOException { - JsonParser parser = JsonFactoryInstanceHolder.INSTANCE.createJsonParser(contextAwareMetadata); - ContextAwareMetadataJson json = parser.parse(ContextAwareMetadataJson.class); - return json.getCommands(); - } - - /** - * Returns if mTLS client certificate should be used. mTLS client certificate is used if and only - * if "GOOGLE_API_USE_CLIENT_CERTIFICATE" environment variable value is "true". - * - * @return If mTLS client certificate should be used - * @since 1.31 - */ - public static Boolean useMtlsClientCertificate() { - String useClientCertificate = System.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE); - return "true".equals(useClientCertificate); - } - - /** - * Returns the default KeyStore for mutual TLS. - * - *

Default client certificate can be generated by running the commands in - * ~/.secureConnect/context_aware_metadata.json file. If this json exists, and the default client - * certificate exists, this function returns a KeyStore object created with the default client - * certificate and the key store password is "". Otherwise, null will be returned. - * - * @return KeyStore for mutual TLS. - * @since 1.31 - */ - public static KeyStore loadDefaultMtlsKeyStore() throws IOException, GeneralSecurityException { - File file = new File(CONTEXT_AWARE_METADATA_PATH); - if (!file.exists()) { - return null; - } - - // Load the cert provider command from the json file. - InputStream stream = new FileInputStream(file); - List command = extractCertificateProviderCommand(stream); - - // Call the command. - Process process = new ProcessBuilder(command).start(); - int exitCode = 0; - try { - exitCode = process.waitFor(); - } catch (InterruptedException exception) { - throw new AccessControlException(exception.toString()); - } - if (exitCode != 0) { - throw new AccessControlException("Failed to execute cert provider command"); - } - - InputStream certificateToUse = process.getInputStream(); - return SecurityUtils.createMtlsKeyStore(certificateToUse); - } - private Utils() {} } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/MtlsTransportBaseTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/MtlsTransportBaseTest.java new file mode 100644 index 000000000..eb3e37fe9 --- /dev/null +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/MtlsTransportBaseTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis; + +import com.google.api.client.googleapis.util.MtlsUtils; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.util.SecurityUtils; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public abstract class MtlsTransportBaseTest { + protected KeyStore createTestMtlsKeyStore() throws Exception { + InputStream certAndKey = getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); + return SecurityUtils.createMtlsKeyStore(certAndKey); + } + + protected static class TestMtlsProvider implements MtlsUtils.MtlsProvider { + private boolean useClientCertificate; + private KeyStore keyStore; + private String keyStorePassword; + TestMtlsProvider(boolean useClientCertificate, KeyStore keystore, String keyStorePassword) { + this.useClientCertificate = useClientCertificate; + this.keyStore = keystore; + this.keyStorePassword = keyStorePassword; + } + @Override + public boolean useMtlsClientCertificate() { + return useClientCertificate; + } + + @Override + public String getKeyStorePassword() { + return keyStorePassword; + } + + @Override + public KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException { + return keyStore; + } + } + + abstract protected HttpTransport buildTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws IOException, GeneralSecurityException; + + // If client certificate shouldn't be used, then neither the provided mtlsKeyStore + // nor the default mtls key store should be used. + @Test + public void testNotUseCertificate() throws Exception { + MtlsUtils.MtlsProvider mtlsProvider = new TestMtlsProvider(false, createTestMtlsKeyStore(), ""); + HttpTransport transport = buildTrustedTransport(mtlsProvider); + assertFalse(transport.isMtls()); + } + + // If client certificate should be used, and mtlsKeyStore is provided, then the + // provided key store should be used. + @Test + public void testUseProvidedCertificate() throws Exception { + MtlsUtils.MtlsProvider mtlsProvider = new TestMtlsProvider(true, createTestMtlsKeyStore(), ""); + HttpTransport transport = buildTrustedTransport(mtlsProvider); + assertTrue(transport.isMtls()); + } + + // If client certificate should be used, but no mtls key store is available, then + // the transport created is not mtls. + @Test + public void testNoCertificate() throws Exception { + MtlsUtils.MtlsProvider mtlsProvider = new TestMtlsProvider(true, null, ""); + HttpTransport transport = buildTrustedTransport(mtlsProvider); + assertFalse(transport.isMtls()); + } +} diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java index 7aebcded7..ae4c8c35d 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java @@ -1,71 +1,29 @@ -package com.google.api.client.googleapis.apache.v2; - -import java.io.InputStream; -import java.security.KeyStore; - -import com.google.api.client.googleapis.util.Utils; -import com.google.api.client.http.apache.v2.ApacheHttpTransport; -import com.google.api.client.util.SecurityUtils; +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import junit.framework.TestCase; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({Utils.class, GoogleApacheHttpTransport.class}) -@PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) -public class GoogleApacheHttpTransportTest extends TestCase { - public KeyStore createTestMtlsKeyStore() throws Exception { - InputStream certAndKey = getClass() - .getClassLoader() - .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); - return SecurityUtils.createMtlsKeyStore(certAndKey); - } +package com.google.api.client.googleapis.apache.v2; - // If client certificate shouldn't be used, then neither the provided mtlsKeyStore - // nor the default mtls key store should be used. - public void testNotUseCertificate() throws Exception { - KeyStore mtlsKeyStore = createTestMtlsKeyStore(); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(false); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); - ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(mtlsKeyStore, ""); - assertFalse(transport.isMtls()); - } +import java.io.IOException; +import java.security.GeneralSecurityException; - // If client certificate should be used, and mtlsKeyStore is provided, then the - // provided key store should be used. - public void testUseProvidedCertificate() throws Exception { - KeyStore mtlsKeyStore = createTestMtlsKeyStore(); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); - ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(mtlsKeyStore, ""); - assertTrue(transport.isMtls()); - } - - // If client certificate should be used, and mtlsKeyStore is provided, then the - // provided key store should be used. - public void testUseDefaultCertificate() throws Exception { - KeyStore mtlsKeyStore = createTestMtlsKeyStore(); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); - ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(null, ""); - assertTrue(transport.isMtls()); - } +import com.google.api.client.googleapis.MtlsTransportBaseTest; +import com.google.api.client.googleapis.util.MtlsUtils; +import com.google.api.client.http.HttpTransport; - // If client certificate should be used, but no mtls key store is available, then - // the transport created is not mtls. - public void testNoCertificate() throws Exception { - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); - ApacheHttpTransport transport = GoogleApacheHttpTransport.newTrustedTransport(null, ""); - assertFalse(transport.isMtls()); +public class GoogleApacheHttpTransportTest extends MtlsTransportBaseTest { + @Override + protected HttpTransport buildTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { + return GoogleApacheHttpTransport.newTrustedTransport(mtlsProvider); } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java index 505675e2d..37763df85 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java @@ -1,71 +1,29 @@ -package com.google.api.client.googleapis.javanet; - -import java.io.InputStream; -import java.security.KeyStore; - -import com.google.api.client.googleapis.util.Utils; -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.util.SecurityUtils; +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import junit.framework.TestCase; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({Utils.class, GoogleNetHttpTransport.class}) -@PowerMockIgnore({"jdk.internal.reflect.*", "javax.net.ssl.*"}) -public class GoogleNetHttpTransportTest extends TestCase { - public KeyStore createTestMtlsKeyStore() throws Exception { - InputStream certAndKey = getClass() - .getClassLoader() - .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); - return SecurityUtils.createMtlsKeyStore(certAndKey); - } +package com.google.api.client.googleapis.javanet; - // If client certificate shouldn't be used, then neither the provided mtlsKeyStore - // nor the default mtls key store should be used. - public void testNotUseCertificate() throws Exception { - KeyStore mtlsKeyStore = createTestMtlsKeyStore(); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(false); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(mtlsKeyStore, ""); - assertFalse(transport.isMtls()); - } +import java.io.IOException; +import java.security.GeneralSecurityException; - // If client certificate should be used, and mtlsKeyStore is provided, then the - // provided key store should be used. - public void testUseProvidedCertificate() throws Exception { - KeyStore mtlsKeyStore = createTestMtlsKeyStore(); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(mtlsKeyStore, ""); - assertTrue(transport.isMtls()); - } - - // If client certificate should be used, and mtlsKeyStore is provided, then the - // provided key store should be used. - public void testUseDefaultCertificate() throws Exception { - KeyStore mtlsKeyStore = createTestMtlsKeyStore(); - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(mtlsKeyStore); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(null, ""); - assertTrue(transport.isMtls()); - } +import com.google.api.client.googleapis.MtlsTransportBaseTest; +import com.google.api.client.googleapis.util.MtlsUtils; +import com.google.api.client.http.HttpTransport; - // If client certificate should be used, but no mtls key store is available, then - // the transport created is not mtls. - public void testNoCertificate() throws Exception { - PowerMockito.spy(Utils.class); - PowerMockito.when(Utils.useMtlsClientCertificate()).thenReturn(true); - PowerMockito.when(Utils.loadDefaultMtlsKeyStore()).thenReturn(null); - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(null, ""); - assertFalse(transport.isMtls()); +public class GoogleNetHttpTransportTest extends MtlsTransportBaseTest { + @Override + protected HttpTransport buildTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { + return GoogleNetHttpTransport.newTrustedTransport(mtlsProvider); } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/MtlsUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/MtlsUtilsTest.java new file mode 100644 index 000000000..6fb6987d1 --- /dev/null +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/MtlsUtilsTest.java @@ -0,0 +1,119 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.List; +import org.junit.Test; + +public class MtlsUtilsTest { + static class TestEnvironmentProvider + implements MtlsUtils.DefaultMtlsProvider.EnvironmentProvider { + private final String value; + + TestEnvironmentProvider(String value) { + this.value = value; + } + + @Override + public String getenv(String name) { + return value; + } + } + + @Test + public void testUseMtlsClientCertificateEmpty() { + MtlsUtils.MtlsProvider mtlsProvider = + new MtlsUtils.DefaultMtlsProvider(new TestEnvironmentProvider(""), "/path/to/missing/file"); + assertFalse(mtlsProvider.useMtlsClientCertificate()); + } + + @Test + public void testUseMtlsClientCertificateNull() { + MtlsUtils.MtlsProvider mtlsProvider = + new MtlsUtils.DefaultMtlsProvider( + new TestEnvironmentProvider(null), "/path/to/missing/file"); + assertFalse(mtlsProvider.useMtlsClientCertificate()); + } + + @Test + public void testUseMtlsClientCertificateTrue() { + MtlsUtils.MtlsProvider mtlsProvider = + new MtlsUtils.DefaultMtlsProvider( + new TestEnvironmentProvider("true"), "/path/to/missing/file"); + assertTrue(mtlsProvider.useMtlsClientCertificate()); + } + + @Test + public void testLoadDefaultKeyStoreMissingFile() + throws InterruptedException, GeneralSecurityException, IOException { + MtlsUtils.MtlsProvider mtlsProvider = + new MtlsUtils.DefaultMtlsProvider( + new TestEnvironmentProvider("true"), "/path/to/missing/file"); + KeyStore keyStore = mtlsProvider.loadDefaultKeyStore(); + assertNull(keyStore); + } + + @Test + public void testLoadDefaultKeyStore() + throws InterruptedException, GeneralSecurityException, IOException { + MtlsUtils.MtlsProvider mtlsProvider = + new MtlsUtils.DefaultMtlsProvider( + new TestEnvironmentProvider("true"), + "src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata.json"); + KeyStore keyStore = mtlsProvider.loadDefaultKeyStore(); + assertNotNull(keyStore); + } + + @Test + public void testLoadDefaultKeyStoreBadCertificate() + throws InterruptedException, GeneralSecurityException, IOException { + MtlsUtils.MtlsProvider mtlsProvider = + new MtlsUtils.DefaultMtlsProvider( + new TestEnvironmentProvider("true"), + "src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata_bad_command.json"); + try { + KeyStore keyStore = mtlsProvider.loadDefaultKeyStore(); + fail("should throw and exception"); + } catch (IllegalArgumentException e) { + assertTrue( + "expected to fail with certificate is missing", + e.getMessage().contains("certificate is missing")); + } + } + + @Test + public void testExtractCertificateProviderCommand() throws IOException { + InputStream inputStream = + this.getClass().getResourceAsStream("mtls_context_aware_metadata.json"); + List command = + MtlsUtils.DefaultMtlsProvider.extractCertificateProviderCommand(inputStream); + assertEquals(2, command.size()); + assertEquals("cat", command.get(0)); + assertEquals( + "src/test/resources/com/google/api/client/googleapis/util/mtlsCertAndKey.pem", + command.get(1)); + } +} diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index 0bf540681..e9c6ca7b9 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -49,28 +49,4 @@ public void testGetDefaultTransport() { HttpTransport secondCall = Utils.getDefaultTransport(); assertSame(transport, secondCall); } - - public static Map parseQuery(String query) throws IOException { - Map map = new HashMap(); - String[] entries = query.split("&"); - for (String entry : entries) { - String[] sides = entry.split("="); - if (sides.length != 2) { - throw new IOException("Invalid Query String"); - } - String key = URLDecoder.decode(sides[0], "UTF-8"); - String value = URLDecoder.decode(sides[1], "UTF-8"); - map.put(key, value); - } - return map; - } - - public void testExtractCertificateProviderCommand() throws IOException { - String json = "{\"cert_provider_command\":[\"/opt/google/endpoint-verification/bin/apihelper\",\"--print_certificate\"],\"device_resource_ids\":[\"123\"]}"; - ByteArrayInputStream stream = new ByteArrayInputStream(json.getBytes()); - List command = Utils.extractCertificateProviderCommand(stream); - assertEquals(2, command.size()); - assertEquals("/opt/google/endpoint-verification/bin/apihelper", command.get(0)); - assertEquals("--print_certificate", command.get(1)); - } } diff --git a/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata.json b/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata.json new file mode 100644 index 000000000..c7729cf1b --- /dev/null +++ b/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata.json @@ -0,0 +1,9 @@ +{ + "cert_provider_command": [ + "cat", + "src/test/resources/com/google/api/client/googleapis/util/mtlsCertAndKey.pem" + ], + "device_resource_ids": [ + "123" + ] +} \ No newline at end of file diff --git a/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata_bad_command.json b/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata_bad_command.json new file mode 100644 index 000000000..69367d719 --- /dev/null +++ b/google-api-client/src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata_bad_command.json @@ -0,0 +1,9 @@ +{ + "cert_provider_command": [ + "echo", + "\"foo\"" + ], + "device_resource_ids": [ + "123" + ] +} \ No newline at end of file From 05e052cf39ac5b515cfca01579345b1d8f10ef19 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 14:40:36 -0800 Subject: [PATCH 16/29] refactor mtls to a module --- .../apache/v2/GoogleApacheHttpTransport.java | 5 ++- .../javanet/GoogleNetHttpTransport.java | 5 ++- .../mtls/ContextAwareMetadataJson.java | 43 +++++++++++++++++++ .../client/googleapis/mtls/MtlsProvider.java | 36 ++++++++++++++++ .../googleapis/{util => mtls}/MtlsUtils.java | 33 +------------- .../client/googleapis/mtls/package-info.java | 21 +++++++++ .../api/client/googleapis/util/Utils.java | 10 ----- .../v2/GoogleApacheHttpTransportTest.java | 6 +-- .../javanet/GoogleNetHttpTransportTest.java | 6 +-- .../{ => mtls}/MtlsTransportBaseTest.java | 13 +++--- .../{util => mtls}/MtlsUtilsTest.java | 16 +++---- .../api/client/googleapis/util/UtilsTest.java | 6 --- 12 files changed, 128 insertions(+), 72 deletions(-) create mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/mtls/ContextAwareMetadataJson.java create mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java rename google-api-client/src/main/java/com/google/api/client/googleapis/{util => mtls}/MtlsUtils.java (85%) create mode 100644 google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java rename google-api-client/src/test/java/com/google/api/client/googleapis/{ => mtls}/MtlsTransportBaseTest.java (82%) rename google-api-client/src/test/java/com/google/api/client/googleapis/{util => mtls}/MtlsUtilsTest.java (91%) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index c7d008f35..a501dd698 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -15,7 +15,8 @@ package com.google.api.client.googleapis.apache.v2; import com.google.api.client.googleapis.GoogleUtils; -import com.google.api.client.googleapis.util.MtlsUtils; +import com.google.api.client.googleapis.mtls.MtlsProvider; +import com.google.api.client.googleapis.mtls.MtlsUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.apache.v2.ApacheHttpTransport; import com.google.api.client.util.SslUtils; @@ -51,7 +52,7 @@ public static ApacheHttpTransport newTrustedTransport() return newTrustedTransport(MtlsUtils.getDefaultMtlsProvider()); } - static ApacheHttpTransport newTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) + static ApacheHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { KeyStore mtlsKeyStore = null; String mtlsKeyStorePassword = null; diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index 9ab8a67c8..0efe08f33 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -15,7 +15,8 @@ package com.google.api.client.googleapis.javanet; import com.google.api.client.googleapis.GoogleUtils; -import com.google.api.client.googleapis.util.MtlsUtils; +import com.google.api.client.googleapis.mtls.MtlsProvider; +import com.google.api.client.googleapis.mtls.MtlsUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; import java.io.IOException; @@ -56,7 +57,7 @@ public static NetHttpTransport newTrustedTransport() return newTrustedTransport(MtlsUtils.getDefaultMtlsProvider()); } - static NetHttpTransport newTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) + static NetHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { KeyStore mtlsKeyStore = null; String mtlsKeyStorePassword = null; diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/ContextAwareMetadataJson.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/ContextAwareMetadataJson.java new file mode 100644 index 000000000..ce3ad0036 --- /dev/null +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/ContextAwareMetadataJson.java @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.mtls; + +import java.util.List; + +import com.google.api.client.json.GenericJson; +import com.google.api.client.util.Beta; +import com.google.api.client.util.Key; + +/** + * {@link Beta}
+ * Data class representing context_aware_metadata.json file. + * + * @since 1.31 + */ +@Beta +public class ContextAwareMetadataJson extends GenericJson { + /** Cert provider command */ + @Key("cert_provider_command") + private List commands; + + /** + * Returns the cert provider command. + * + * @since 1.31 + */ + public final List getCommands() { + return commands; + } +} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java new file mode 100644 index 000000000..bad125ed5 --- /dev/null +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.googleapis.mtls; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +import com.google.api.client.util.Beta; + +/** + * {@link Beta}
+ * Provider interface for mutual TLS. + * + * @since 1.31 + */ +@Beta +public interface MtlsProvider { + boolean useMtlsClientCertificate(); + + String getKeyStorePassword(); + + KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException; +} diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java similarity index 85% rename from google-api-client/src/main/java/com/google/api/client/googleapis/util/MtlsUtils.java rename to google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java index 52c5fbf44..5f87cbe24 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/MtlsUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java @@ -12,8 +12,9 @@ * the License. */ -package com.google.api.client.googleapis.util; +package com.google.api.client.googleapis.mtls; +import com.google.api.client.googleapis.util.Utils; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonParser; import com.google.api.client.util.Beta; @@ -29,36 +30,6 @@ import java.util.List; public class MtlsUtils { - public interface MtlsProvider { - boolean useMtlsClientCertificate(); - - String getKeyStorePassword(); - - KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException; - } - - /** - * {@link Beta}
- * Data class representing context_aware_metadata.json file. - * - * @since 1.31 - */ - @Beta - public static class ContextAwareMetadataJson extends GenericJson { - /** Cert provider command */ - @Key("cert_provider_command") - private List commands; - - /** - * Returns the cert provider command. - * - * @since 1.31 - */ - public final List getCommands() { - return commands; - } - } - @VisibleForTesting static class DefaultMtlsProvider implements MtlsProvider { private static final String DEFAULT_CONTEXT_AWARE_METADATA_PATH = diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java new file mode 100644 index 000000000..bb6fbd0e6 --- /dev/null +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +/** + * Mutual TLS utilities for the Google API Client Library. + * + * @since 1.31 + */ +package com.google.api.client.googleapis.mtls; + diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java index ab5ac8580..115da760e 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/util/Utils.java @@ -17,18 +17,8 @@ import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.JsonParser; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.Beta; -import com.google.api.client.util.SecurityUtils; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.AccessControlException; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.util.List; /** * {@link Beta}
diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java index ae4c8c35d..8963b532b 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransportTest.java @@ -17,13 +17,13 @@ import java.io.IOException; import java.security.GeneralSecurityException; -import com.google.api.client.googleapis.MtlsTransportBaseTest; -import com.google.api.client.googleapis.util.MtlsUtils; +import com.google.api.client.googleapis.mtls.MtlsTransportBaseTest; +import com.google.api.client.googleapis.mtls.MtlsProvider; import com.google.api.client.http.HttpTransport; public class GoogleApacheHttpTransportTest extends MtlsTransportBaseTest { @Override - protected HttpTransport buildTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { + protected HttpTransport buildTrustedTransport(MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { return GoogleApacheHttpTransport.newTrustedTransport(mtlsProvider); } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java index 37763df85..1dd970d24 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransportTest.java @@ -17,13 +17,13 @@ import java.io.IOException; import java.security.GeneralSecurityException; -import com.google.api.client.googleapis.MtlsTransportBaseTest; -import com.google.api.client.googleapis.util.MtlsUtils; +import com.google.api.client.googleapis.mtls.MtlsTransportBaseTest; +import com.google.api.client.googleapis.mtls.MtlsProvider; import com.google.api.client.http.HttpTransport; public class GoogleNetHttpTransportTest extends MtlsTransportBaseTest { @Override - protected HttpTransport buildTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { + protected HttpTransport buildTrustedTransport(MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { return GoogleNetHttpTransport.newTrustedTransport(mtlsProvider); } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/MtlsTransportBaseTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java similarity index 82% rename from google-api-client/src/test/java/com/google/api/client/googleapis/MtlsTransportBaseTest.java rename to google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java index eb3e37fe9..9fc3b0800 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/MtlsTransportBaseTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java @@ -12,9 +12,8 @@ * the License. */ -package com.google.api.client.googleapis; +package com.google.api.client.googleapis.mtls; -import com.google.api.client.googleapis.util.MtlsUtils; import com.google.api.client.http.HttpTransport; import com.google.api.client.util.SecurityUtils; import org.junit.Test; @@ -35,7 +34,7 @@ protected KeyStore createTestMtlsKeyStore() throws Exception { return SecurityUtils.createMtlsKeyStore(certAndKey); } - protected static class TestMtlsProvider implements MtlsUtils.MtlsProvider { + protected static class TestMtlsProvider implements MtlsProvider { private boolean useClientCertificate; private KeyStore keyStore; private String keyStorePassword; @@ -60,13 +59,13 @@ public KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityExcepti } } - abstract protected HttpTransport buildTrustedTransport(MtlsUtils.MtlsProvider mtlsProvider) throws IOException, GeneralSecurityException; + abstract protected HttpTransport buildTrustedTransport(MtlsProvider mtlsProvider) throws IOException, GeneralSecurityException; // If client certificate shouldn't be used, then neither the provided mtlsKeyStore // nor the default mtls key store should be used. @Test public void testNotUseCertificate() throws Exception { - MtlsUtils.MtlsProvider mtlsProvider = new TestMtlsProvider(false, createTestMtlsKeyStore(), ""); + MtlsProvider mtlsProvider = new TestMtlsProvider(false, createTestMtlsKeyStore(), ""); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertFalse(transport.isMtls()); } @@ -75,7 +74,7 @@ public void testNotUseCertificate() throws Exception { // provided key store should be used. @Test public void testUseProvidedCertificate() throws Exception { - MtlsUtils.MtlsProvider mtlsProvider = new TestMtlsProvider(true, createTestMtlsKeyStore(), ""); + MtlsProvider mtlsProvider = new TestMtlsProvider(true, createTestMtlsKeyStore(), ""); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertTrue(transport.isMtls()); } @@ -84,7 +83,7 @@ public void testUseProvidedCertificate() throws Exception { // the transport created is not mtls. @Test public void testNoCertificate() throws Exception { - MtlsUtils.MtlsProvider mtlsProvider = new TestMtlsProvider(true, null, ""); + MtlsProvider mtlsProvider = new TestMtlsProvider(true, null, ""); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertFalse(transport.isMtls()); } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/MtlsUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java similarity index 91% rename from google-api-client/src/test/java/com/google/api/client/googleapis/util/MtlsUtilsTest.java rename to google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java index 6fb6987d1..e5391ac22 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/MtlsUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java @@ -12,7 +12,7 @@ * the License. */ -package com.google.api.client.googleapis.util; +package com.google.api.client.googleapis.mtls; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -45,14 +45,14 @@ public String getenv(String name) { @Test public void testUseMtlsClientCertificateEmpty() { - MtlsUtils.MtlsProvider mtlsProvider = + MtlsProvider mtlsProvider = new MtlsUtils.DefaultMtlsProvider(new TestEnvironmentProvider(""), "/path/to/missing/file"); assertFalse(mtlsProvider.useMtlsClientCertificate()); } @Test public void testUseMtlsClientCertificateNull() { - MtlsUtils.MtlsProvider mtlsProvider = + MtlsProvider mtlsProvider = new MtlsUtils.DefaultMtlsProvider( new TestEnvironmentProvider(null), "/path/to/missing/file"); assertFalse(mtlsProvider.useMtlsClientCertificate()); @@ -60,7 +60,7 @@ public void testUseMtlsClientCertificateNull() { @Test public void testUseMtlsClientCertificateTrue() { - MtlsUtils.MtlsProvider mtlsProvider = + MtlsProvider mtlsProvider = new MtlsUtils.DefaultMtlsProvider( new TestEnvironmentProvider("true"), "/path/to/missing/file"); assertTrue(mtlsProvider.useMtlsClientCertificate()); @@ -69,7 +69,7 @@ public void testUseMtlsClientCertificateTrue() { @Test public void testLoadDefaultKeyStoreMissingFile() throws InterruptedException, GeneralSecurityException, IOException { - MtlsUtils.MtlsProvider mtlsProvider = + MtlsProvider mtlsProvider = new MtlsUtils.DefaultMtlsProvider( new TestEnvironmentProvider("true"), "/path/to/missing/file"); KeyStore keyStore = mtlsProvider.loadDefaultKeyStore(); @@ -79,7 +79,7 @@ public void testLoadDefaultKeyStoreMissingFile() @Test public void testLoadDefaultKeyStore() throws InterruptedException, GeneralSecurityException, IOException { - MtlsUtils.MtlsProvider mtlsProvider = + MtlsProvider mtlsProvider = new MtlsUtils.DefaultMtlsProvider( new TestEnvironmentProvider("true"), "src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata.json"); @@ -90,7 +90,7 @@ public void testLoadDefaultKeyStore() @Test public void testLoadDefaultKeyStoreBadCertificate() throws InterruptedException, GeneralSecurityException, IOException { - MtlsUtils.MtlsProvider mtlsProvider = + MtlsProvider mtlsProvider = new MtlsUtils.DefaultMtlsProvider( new TestEnvironmentProvider("true"), "src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata_bad_command.json"); @@ -107,7 +107,7 @@ public void testLoadDefaultKeyStoreBadCertificate() @Test public void testExtractCertificateProviderCommand() throws IOException { InputStream inputStream = - this.getClass().getResourceAsStream("mtls_context_aware_metadata.json"); + this.getClass().getClassLoader().getResourceAsStream("com/google/api/client/googleapis/util/mtls_context_aware_metadata.json"); List command = MtlsUtils.DefaultMtlsProvider.extractCertificateProviderCommand(inputStream); assertEquals(2, command.size()); diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index e9c6ca7b9..5aa13da9b 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -19,12 +19,6 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import junit.framework.TestCase; /** From 312e73a2e2c7c244b5da73aace176327cd530483 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 15:09:07 -0800 Subject: [PATCH 17/29] rename --- .../apache/v2/GoogleApacheHttpTransport.java | 2 +- .../javanet/GoogleNetHttpTransport.java | 2 +- .../api/client/googleapis/mtls/MtlsProvider.java | 2 +- .../api/client/googleapis/mtls/MtlsUtils.java | 16 +++++++++++++--- .../googleapis/mtls/MtlsTransportBaseTest.java | 2 +- .../client/googleapis/mtls/MtlsUtilsTest.java | 6 +++--- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index a501dd698..9f840efee 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -57,7 +57,7 @@ static ApacheHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) KeyStore mtlsKeyStore = null; String mtlsKeyStorePassword = null; if (mtlsProvider.useMtlsClientCertificate()) { - mtlsKeyStore = mtlsProvider.loadDefaultKeyStore(); + mtlsKeyStore = mtlsProvider.getKeyStore(); mtlsKeyStorePassword = mtlsProvider.getKeyStorePassword(); } diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index 0efe08f33..42a789f71 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -62,7 +62,7 @@ static NetHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) KeyStore mtlsKeyStore = null; String mtlsKeyStorePassword = null; if (mtlsProvider.useMtlsClientCertificate()) { - mtlsKeyStore = mtlsProvider.loadDefaultKeyStore(); + mtlsKeyStore = mtlsProvider.getKeyStore(); mtlsKeyStorePassword = mtlsProvider.getKeyStorePassword(); } diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java index bad125ed5..5fba2f2d5 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java @@ -32,5 +32,5 @@ public interface MtlsProvider { String getKeyStorePassword(); - KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException; + KeyStore getKeyStore() throws IOException, GeneralSecurityException; } diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java index 5f87cbe24..61f94e634 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java @@ -15,10 +15,8 @@ package com.google.api.client.googleapis.mtls; import com.google.api.client.googleapis.util.Utils; -import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonParser; import com.google.api.client.util.Beta; -import com.google.api.client.util.Key; import com.google.api.client.util.SecurityUtils; import com.google.common.annotations.VisibleForTesting; import java.io.FileInputStream; @@ -29,6 +27,13 @@ import java.security.KeyStore; import java.util.List; +/** + * {@link Beta}
+ * Utilities for mutual TLS. + * + * @since 1.31 + */ +@Beta public class MtlsUtils { @VisibleForTesting static class DefaultMtlsProvider implements MtlsProvider { @@ -75,7 +80,7 @@ public String getKeyStorePassword() { } @Override - public KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException { + public KeyStore getKeyStore() throws IOException, GeneralSecurityException { // Load the cert provider command from the json file. InputStream stream; try { @@ -115,6 +120,11 @@ static List extractCertificateProviderCommand(InputStream contextAwareMe private static final MtlsProvider MTLS_PROVIDER = new DefaultMtlsProvider(); + /** + * Returns the default MtlsProvider instance. + * + * @return The default MtlsProvider instance. + */ public static MtlsProvider getDefaultMtlsProvider() { return MTLS_PROVIDER; } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java index 9fc3b0800..55b4beaee 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java @@ -54,7 +54,7 @@ public String getKeyStorePassword() { } @Override - public KeyStore loadDefaultKeyStore() throws IOException, GeneralSecurityException { + public KeyStore getKeyStore() throws IOException, GeneralSecurityException { return keyStore; } } diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java index e5391ac22..340099a17 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java @@ -72,7 +72,7 @@ public void testLoadDefaultKeyStoreMissingFile() MtlsProvider mtlsProvider = new MtlsUtils.DefaultMtlsProvider( new TestEnvironmentProvider("true"), "/path/to/missing/file"); - KeyStore keyStore = mtlsProvider.loadDefaultKeyStore(); + KeyStore keyStore = mtlsProvider.getKeyStore(); assertNull(keyStore); } @@ -83,7 +83,7 @@ public void testLoadDefaultKeyStore() new MtlsUtils.DefaultMtlsProvider( new TestEnvironmentProvider("true"), "src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata.json"); - KeyStore keyStore = mtlsProvider.loadDefaultKeyStore(); + KeyStore keyStore = mtlsProvider.getKeyStore(); assertNotNull(keyStore); } @@ -95,7 +95,7 @@ public void testLoadDefaultKeyStoreBadCertificate() new TestEnvironmentProvider("true"), "src/test/resources/com/google/api/client/googleapis/util/mtls_context_aware_metadata_bad_command.json"); try { - KeyStore keyStore = mtlsProvider.loadDefaultKeyStore(); + mtlsProvider.getKeyStore(); fail("should throw and exception"); } catch (IllegalArgumentException e) { assertTrue( From 5209becfdddcd61d186b4ea34f9dec79987e44e6 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 15:16:14 -0800 Subject: [PATCH 18/29] fix exceptions --- .../api/client/googleapis/mtls/MtlsTransportBaseTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java index 55b4beaee..259f8263a 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java @@ -27,7 +27,7 @@ import static org.junit.Assert.assertTrue; public abstract class MtlsTransportBaseTest { - protected KeyStore createTestMtlsKeyStore() throws Exception { + protected KeyStore createTestMtlsKeyStore() throws IOException, GeneralSecurityException { InputStream certAndKey = getClass() .getClassLoader() .getResourceAsStream("com/google/api/client/googleapis/util/mtlsCertAndKey.pem"); @@ -64,7 +64,7 @@ public KeyStore getKeyStore() throws IOException, GeneralSecurityException { // If client certificate shouldn't be used, then neither the provided mtlsKeyStore // nor the default mtls key store should be used. @Test - public void testNotUseCertificate() throws Exception { + public void testNotUseCertificate() throws IOException, GeneralSecurityException { MtlsProvider mtlsProvider = new TestMtlsProvider(false, createTestMtlsKeyStore(), ""); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertFalse(transport.isMtls()); @@ -73,7 +73,7 @@ public void testNotUseCertificate() throws Exception { // If client certificate should be used, and mtlsKeyStore is provided, then the // provided key store should be used. @Test - public void testUseProvidedCertificate() throws Exception { + public void testUseProvidedCertificate() throws IOException, GeneralSecurityException { MtlsProvider mtlsProvider = new TestMtlsProvider(true, createTestMtlsKeyStore(), ""); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertTrue(transport.isMtls()); @@ -82,7 +82,7 @@ public void testUseProvidedCertificate() throws Exception { // If client certificate should be used, but no mtls key store is available, then // the transport created is not mtls. @Test - public void testNoCertificate() throws Exception { + public void testNoCertificate() throws IOException, GeneralSecurityException { MtlsProvider mtlsProvider = new TestMtlsProvider(true, null, ""); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertFalse(transport.isMtls()); From 1edf0609740f4e4eb48f08b287b6c5f279e93d05 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 17:03:47 -0800 Subject: [PATCH 19/29] add timeout --- .../api/client/googleapis/mtls/MtlsUtils.java | 32 +++++++++- .../client/googleapis/mtls/MtlsUtilsTest.java | 63 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java index 61f94e634..11d5d437f 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java @@ -96,7 +96,8 @@ public KeyStore getKeyStore() throws IOException, GeneralSecurityException { Process process = new ProcessBuilder(command).start(); int exitCode = -1; try { - exitCode = process.waitFor(); + // Run the command and timeout after 1000 milliseconds. + exitCode = runCertificateProviderCommand(process, 1000); } catch (InterruptedException e) { throw new IOException("Interrupted executing certificate provider command", e); } @@ -116,6 +117,35 @@ static List extractCertificateProviderCommand(InputStream contextAwareMe ContextAwareMetadataJson json = parser.parse(ContextAwareMetadataJson.class); return json.getCommands(); } + + @VisibleForTesting + static int runCertificateProviderCommand(Process commandProcess, long timeoutMilliseconds) throws IOException, InterruptedException { + long startTime = System.currentTimeMillis(); + long remainTime = timeoutMilliseconds; + boolean terminated = false; + + do { + try { + // Check if process is terminated by pooling the exitValue, which throws + // IllegalThreadStateException if not terminated. + commandProcess.exitValue(); + terminated = true; + break; + } catch(IllegalThreadStateException ex) { + if (remainTime > 0) { + Thread.sleep(Math.min(remainTime + 1, 100)); + } + } + remainTime = remainTime - (System.currentTimeMillis() - startTime); + } while (remainTime > 0); + + if (!terminated) { + commandProcess.destroy(); + throw new IOException("cert provider command timed out"); + } + + return commandProcess.exitValue(); + } } private static final MtlsProvider MTLS_PROVIDER = new DefaultMtlsProvider(); diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java index 340099a17..7cc85a2ac 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsUtilsTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.List; @@ -116,4 +117,66 @@ public void testExtractCertificateProviderCommand() throws IOException { "src/test/resources/com/google/api/client/googleapis/util/mtlsCertAndKey.pem", command.get(1)); } + + static class TestCertProviderCommandProcess extends Process { + private boolean runForever; + private int exitValue; + + public TestCertProviderCommandProcess(int exitValue, boolean runForever) { + this.runForever = runForever; + this.exitValue = exitValue; + } + + @Override + public OutputStream getOutputStream() { + return null; + } + + @Override + public InputStream getInputStream() { + return null; + } + + @Override + public InputStream getErrorStream() { + return null; + } + + @Override + public int waitFor() throws InterruptedException { + return 0; + } + + @Override + public int exitValue() { + if (runForever) { + throw new IllegalThreadStateException(); + } + return exitValue; + } + + @Override + public void destroy() {} + } + + @Test + public void testRunCertificateProviderCommandSuccess() throws IOException, InterruptedException { + Process certCommandProcess = new TestCertProviderCommandProcess(0, false); + int exitValue = + MtlsUtils.DefaultMtlsProvider.runCertificateProviderCommand(certCommandProcess, 100); + assertEquals(0, exitValue); + } + + @Test + public void testRunCertificateProviderCommandTimeout() throws InterruptedException { + Process certCommandProcess = new TestCertProviderCommandProcess(0, true); + try { + MtlsUtils.DefaultMtlsProvider.runCertificateProviderCommand(certCommandProcess, 100); + fail("should throw and exception"); + } catch (IOException e) { + assertTrue( + "expected to fail with timeout", + e.getMessage().contains("cert provider command timed out")); + } + } } From daeb967036a9f8ef2de0c36ff6b10e8aac959afe Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 17:15:56 -0800 Subject: [PATCH 20/29] fix util test --- .../api/client/googleapis/util/UtilsTest.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java index 5aa13da9b..c4326747e 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/util/UtilsTest.java @@ -18,7 +18,10 @@ import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; - +import java.io.IOException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; import junit.framework.TestCase; /** @@ -43,4 +46,19 @@ public void testGetDefaultTransport() { HttpTransport secondCall = Utils.getDefaultTransport(); assertSame(transport, secondCall); } + + public static Map parseQuery(String query) throws IOException { + Map map = new HashMap(); + String[] entries = query.split("&"); + for (String entry : entries) { + String[] sides = entry.split("="); + if (sides.length != 2) { + throw new IOException("Invalid Query String"); + } + String key = URLDecoder.decode(sides[0], "UTF-8"); + String value = URLDecoder.decode(sides[1], "UTF-8"); + map.put(key, value); + } + return map; + } } From 377f94460c5ec5e5b16f739ee8cc8aabed8af529 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 17:36:18 -0800 Subject: [PATCH 21/29] add tests --- .../mtls/MtlsTransportBaseTest.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java index 259f8263a..60dae51da 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; public abstract class MtlsTransportBaseTest { protected KeyStore createTestMtlsKeyStore() throws IOException, GeneralSecurityException { @@ -38,11 +39,15 @@ protected static class TestMtlsProvider implements MtlsProvider { private boolean useClientCertificate; private KeyStore keyStore; private String keyStorePassword; - TestMtlsProvider(boolean useClientCertificate, KeyStore keystore, String keyStorePassword) { + private boolean throwExceptionForGetKeyStore; + + TestMtlsProvider(boolean useClientCertificate, KeyStore keystore, String keyStorePassword, boolean throwExceptionForGetKeyStore) { this.useClientCertificate = useClientCertificate; this.keyStore = keystore; this.keyStorePassword = keyStorePassword; + this.throwExceptionForGetKeyStore = throwExceptionForGetKeyStore; } + @Override public boolean useMtlsClientCertificate() { return useClientCertificate; @@ -55,6 +60,9 @@ public String getKeyStorePassword() { @Override public KeyStore getKeyStore() throws IOException, GeneralSecurityException { + if (throwExceptionForGetKeyStore) { + throw new IOException("getKeyStore throws exception"); + } return keyStore; } } @@ -65,7 +73,7 @@ public KeyStore getKeyStore() throws IOException, GeneralSecurityException { // nor the default mtls key store should be used. @Test public void testNotUseCertificate() throws IOException, GeneralSecurityException { - MtlsProvider mtlsProvider = new TestMtlsProvider(false, createTestMtlsKeyStore(), ""); + MtlsProvider mtlsProvider = new TestMtlsProvider(false, createTestMtlsKeyStore(), "", false); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertFalse(transport.isMtls()); } @@ -74,7 +82,7 @@ public void testNotUseCertificate() throws IOException, GeneralSecurityException // provided key store should be used. @Test public void testUseProvidedCertificate() throws IOException, GeneralSecurityException { - MtlsProvider mtlsProvider = new TestMtlsProvider(true, createTestMtlsKeyStore(), ""); + MtlsProvider mtlsProvider = new TestMtlsProvider(true, createTestMtlsKeyStore(), "", false); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertTrue(transport.isMtls()); } @@ -83,8 +91,22 @@ public void testUseProvidedCertificate() throws IOException, GeneralSecurityExce // the transport created is not mtls. @Test public void testNoCertificate() throws IOException, GeneralSecurityException { - MtlsProvider mtlsProvider = new TestMtlsProvider(true, null, ""); + MtlsProvider mtlsProvider = new TestMtlsProvider(true, null, "", false); HttpTransport transport = buildTrustedTransport(mtlsProvider); assertFalse(transport.isMtls()); } + + // Test the case where mtlsProvider.getKeyStore() throws. + @Test + public void testGetKeyStoreThrows() throws GeneralSecurityException { + MtlsProvider mtlsProvider = new TestMtlsProvider(true, null, "", true); + try { + buildTrustedTransport(mtlsProvider); + fail("should throw and exception"); + } catch (IOException e) { + assertTrue( + "expected to fail with exception", + e.getMessage().contains("getKeyStore throws exception")); + } + } } From 533c76ec9044a76aaf326f9dc66f33a50f41e597 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 17:39:13 -0800 Subject: [PATCH 22/29] lint --- .../client/googleapis/mtls/MtlsProvider.java | 3 +- .../api/client/googleapis/mtls/MtlsUtils.java | 39 ++++++++++--------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java index 5fba2f2d5..30e6b453e 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java @@ -14,12 +14,11 @@ package com.google.api.client.googleapis.mtls; +import com.google.api.client.util.Beta; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; -import com.google.api.client.util.Beta; - /** * {@link Beta}
* Provider interface for mutual TLS. diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java index 11d5d437f..d9aaecdb9 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java @@ -96,7 +96,7 @@ public KeyStore getKeyStore() throws IOException, GeneralSecurityException { Process process = new ProcessBuilder(command).start(); int exitCode = -1; try { - // Run the command and timeout after 1000 milliseconds. + // Run the command and timeout after 1000 milliseconds. exitCode = runCertificateProviderCommand(process, 1000); } catch (InterruptedException e) { throw new IOException("Interrupted executing certificate provider command", e); @@ -117,42 +117,43 @@ static List extractCertificateProviderCommand(InputStream contextAwareMe ContextAwareMetadataJson json = parser.parse(ContextAwareMetadataJson.class); return json.getCommands(); } - + @VisibleForTesting - static int runCertificateProviderCommand(Process commandProcess, long timeoutMilliseconds) throws IOException, InterruptedException { + static int runCertificateProviderCommand(Process commandProcess, long timeoutMilliseconds) + throws IOException, InterruptedException { long startTime = System.currentTimeMillis(); long remainTime = timeoutMilliseconds; boolean terminated = false; - + do { - try { - // Check if process is terminated by pooling the exitValue, which throws - // IllegalThreadStateException if not terminated. - commandProcess.exitValue(); - terminated = true; - break; - } catch(IllegalThreadStateException ex) { - if (remainTime > 0) { - Thread.sleep(Math.min(remainTime + 1, 100)); - } + try { + // Check if process is terminated by pooling the exitValue, which throws + // IllegalThreadStateException if not terminated. + commandProcess.exitValue(); + terminated = true; + break; + } catch (IllegalThreadStateException ex) { + if (remainTime > 0) { + Thread.sleep(Math.min(remainTime + 1, 100)); } - remainTime = remainTime - (System.currentTimeMillis() - startTime); + } + remainTime = remainTime - (System.currentTimeMillis() - startTime); } while (remainTime > 0); - + if (!terminated) { commandProcess.destroy(); throw new IOException("cert provider command timed out"); } - + return commandProcess.exitValue(); - } + } } private static final MtlsProvider MTLS_PROVIDER = new DefaultMtlsProvider(); /** * Returns the default MtlsProvider instance. - * + * * @return The default MtlsProvider instance. */ public static MtlsProvider getDefaultMtlsProvider() { From 9ad39a5b05e0a17f120e6777e762f07d4225bc4d Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 22:33:57 -0800 Subject: [PATCH 23/29] make constructor public --- .../apache/v2/GoogleApacheHttpTransport.java | 10 +++++++++ .../javanet/GoogleNetHttpTransport.java | 22 ++++++++++++++----- .../client/googleapis/mtls/MtlsProvider.java | 12 +++++++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index 9f840efee..c3a4c4a54 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -19,6 +19,7 @@ import com.google.api.client.googleapis.mtls.MtlsUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.apache.v2.ApacheHttpTransport; +import com.google.api.client.util.Beta; import com.google.api.client.util.SslUtils; import java.io.IOException; import java.net.ProxySelector; @@ -52,6 +53,15 @@ public static ApacheHttpTransport newTrustedTransport() return newTrustedTransport(MtlsUtils.getDefaultMtlsProvider()); } + /** + * {@link Beta}
+ * Returns a new instance of {@link ApacheHttpTransport} that uses {@link + * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. + * mtlsProvider can be used to configure mutual TLS for the transport. + * + * @param mtlsProvider MtlsProvider to configure mutual TLS for the transport + */ + @Beta static ApacheHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { KeyStore mtlsKeyStore = null; diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index 42a789f71..cc0529f57 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -19,6 +19,8 @@ import com.google.api.client.googleapis.mtls.MtlsUtils; import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.util.Beta; + import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -45,10 +47,10 @@ public class GoogleNetHttpTransport { * *

    * static HttpTransport newProxyTransport() throws GeneralSecurityException, IOException {
-   * NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
-   * builder.trustCertificates(GoogleUtils.getCertificateTrustStore());
-   * builder.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 3128)));
-   * return builder.build();
+   *   NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
+   *   builder.trustCertificates(GoogleUtils.getCertificateTrustStore());
+   *   builder.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 3128)));
+   *   return builder.build();
    * }
    * 
*/ @@ -57,7 +59,17 @@ public static NetHttpTransport newTrustedTransport() return newTrustedTransport(MtlsUtils.getDefaultMtlsProvider()); } - static NetHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) + /** + * {@link Beta}
+ * Returns a new instance of {@link NetHttpTransport} that uses {@link + * GoogleUtils#getCertificateTrustStore()} for the trusted certificates using {@link + * com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)}. + * mtlsProvider can be used to configure mutual TLS for the transport. + * + * @param mtlsProvider MtlsProvider to configure mutual TLS for the transport + */ + @Beta + public static NetHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { KeyStore mtlsKeyStore = null; String mtlsKeyStorePassword = null; diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java index 30e6b453e..83f94828b 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java @@ -21,15 +21,25 @@ /** * {@link Beta}
- * Provider interface for mutual TLS. + * Provider interface for mutual TLS. It is used in + * {@link GoogleApacheHttpTransport#newTrustedTransport(MtlsProvider)} and + * {@link GoogleNetHttpTransport#newTrustedTransport(MtlsProvider)} to configure the mutual TLS + * in the transport. * * @since 1.31 */ @Beta public interface MtlsProvider { + /** + * Returns if mutual TLS client certificate should be used. If the value is true, the key store + * from {@link #getKeyStore()} and key store password from {@link #getKeyStorePassword()} will + * be used to configure mutual TLS transport. + */ boolean useMtlsClientCertificate(); + /** The key store to use for mutual TLS. */ String getKeyStorePassword(); + /** The password for mutual TLS key store. */ KeyStore getKeyStore() throws IOException, GeneralSecurityException; } From 47f8c0788722408799b8e896c1259abb4c58c57e Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 22:37:28 -0800 Subject: [PATCH 24/29] lint --- .../apache/v2/GoogleApacheHttpTransport.java | 6 +++--- .../googleapis/javanet/GoogleNetHttpTransport.java | 3 +-- .../api/client/googleapis/mtls/MtlsProvider.java | 14 +++++++------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index c3a4c4a54..21a108538 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -56,9 +56,9 @@ public static ApacheHttpTransport newTrustedTransport() /** * {@link Beta}
* Returns a new instance of {@link ApacheHttpTransport} that uses {@link - * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. - * mtlsProvider can be used to configure mutual TLS for the transport. - * + * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. mtlsProvider can be used + * to configure mutual TLS for the transport. + * * @param mtlsProvider MtlsProvider to configure mutual TLS for the transport */ @Beta diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java index cc0529f57..e6099014e 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/javanet/GoogleNetHttpTransport.java @@ -20,7 +20,6 @@ import com.google.api.client.googleapis.util.Utils; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.util.Beta; - import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -65,7 +64,7 @@ public static NetHttpTransport newTrustedTransport() * GoogleUtils#getCertificateTrustStore()} for the trusted certificates using {@link * com.google.api.client.http.javanet.NetHttpTransport.Builder#trustCertificates(KeyStore)}. * mtlsProvider can be used to configure mutual TLS for the transport. - * + * * @param mtlsProvider MtlsProvider to configure mutual TLS for the transport */ @Beta diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java index 83f94828b..cb0813b45 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java @@ -21,19 +21,19 @@ /** * {@link Beta}
- * Provider interface for mutual TLS. It is used in - * {@link GoogleApacheHttpTransport#newTrustedTransport(MtlsProvider)} and - * {@link GoogleNetHttpTransport#newTrustedTransport(MtlsProvider)} to configure the mutual TLS - * in the transport. + * Provider interface for mutual TLS. It is used in {@link + * GoogleApacheHttpTransport#newTrustedTransport(MtlsProvider)} and {@link + * GoogleNetHttpTransport#newTrustedTransport(MtlsProvider)} to configure the mutual TLS in the + * transport. * * @since 1.31 */ @Beta public interface MtlsProvider { - /** + /** * Returns if mutual TLS client certificate should be used. If the value is true, the key store - * from {@link #getKeyStore()} and key store password from {@link #getKeyStorePassword()} will - * be used to configure mutual TLS transport. + * from {@link #getKeyStore()} and key store password from {@link #getKeyStorePassword()} will be + * used to configure mutual TLS transport. */ boolean useMtlsClientCertificate(); From 7bf8d8c70ce53d8261aa4427bbd40beb24c5dd19 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Thu, 5 Nov 2020 22:39:05 -0800 Subject: [PATCH 25/29] make constructor public --- .../client/googleapis/apache/v2/GoogleApacheHttpTransport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java index 21a108538..f5f41fddf 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/apache/v2/GoogleApacheHttpTransport.java @@ -62,7 +62,7 @@ public static ApacheHttpTransport newTrustedTransport() * @param mtlsProvider MtlsProvider to configure mutual TLS for the transport */ @Beta - static ApacheHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) + public static ApacheHttpTransport newTrustedTransport(MtlsProvider mtlsProvider) throws GeneralSecurityException, IOException { KeyStore mtlsKeyStore = null; String mtlsKeyStorePassword = null; From 55d4eb5a8ce6701d912a98c85fceea9984f117e3 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Mon, 9 Nov 2020 10:22:22 -0800 Subject: [PATCH 26/29] update --- .../java/com/google/api/client/googleapis/mtls/MtlsUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java index d9aaecdb9..9cf1ef95b 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java @@ -127,7 +127,7 @@ static int runCertificateProviderCommand(Process commandProcess, long timeoutMil do { try { - // Check if process is terminated by pooling the exitValue, which throws + // Check if process is terminated by polling the exitValue, which throws // IllegalThreadStateException if not terminated. commandProcess.exitValue(); terminated = true; @@ -154,7 +154,7 @@ static int runCertificateProviderCommand(Process commandProcess, long timeoutMil /** * Returns the default MtlsProvider instance. * - * @return The default MtlsProvider instance. + * @return The default MtlsProvider instance */ public static MtlsProvider getDefaultMtlsProvider() { return MTLS_PROVIDER; From 46dd6b81eee249eab1f6b0cf0baf9e50301156a7 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Mon, 9 Nov 2020 11:22:28 -0800 Subject: [PATCH 27/29] use bigger try catch block --- .../api/client/googleapis/mtls/MtlsUtils.java | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java index 9cf1ef95b..22960ecc2 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java @@ -81,33 +81,26 @@ public String getKeyStorePassword() { @Override public KeyStore getKeyStore() throws IOException, GeneralSecurityException { - // Load the cert provider command from the json file. - InputStream stream; try { - stream = new FileInputStream(metadataPath); + // Load the cert provider command from the json file. + InputStream stream = new FileInputStream(metadataPath); + List command = extractCertificateProviderCommand(stream); + + // Run the command and timeout after 1000 milliseconds. + Process process = new ProcessBuilder(command).start(); + int exitCode = runCertificateProviderCommand(process, 1000); + if (exitCode != 0) { + throw new IOException(String.format("Failed to execute cert provider command with exit code: %d", exitCode)); + } + + // Create mTLS key store with the input certificates from shell command. + return SecurityUtils.createMtlsKeyStore(process.getInputStream()); } catch (FileNotFoundException ignored) { // file doesn't exist return null; - } - - List command = extractCertificateProviderCommand(stream); - - // Call the command. - Process process = new ProcessBuilder(command).start(); - int exitCode = -1; - try { - // Run the command and timeout after 1000 milliseconds. - exitCode = runCertificateProviderCommand(process, 1000); } catch (InterruptedException e) { throw new IOException("Interrupted executing certificate provider command", e); } - if (exitCode != 0) { - throw new IOException( - String.format("Failed to execute cert provider command with exit code: %d", exitCode)); - } - - // Parse input certificates from shell command - return SecurityUtils.createMtlsKeyStore(process.getInputStream()); } @VisibleForTesting From 9eee91927a2433967d00324b50733697c08f0bf4 Mon Sep 17 00:00:00 2001 From: arithmetic1728 Date: Mon, 9 Nov 2020 22:12:55 -0800 Subject: [PATCH 28/29] update code --- .../java/com/google/api/client/googleapis/mtls/MtlsUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java index 22960ecc2..f08913e7f 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsUtils.java @@ -40,7 +40,7 @@ static class DefaultMtlsProvider implements MtlsProvider { private static final String DEFAULT_CONTEXT_AWARE_METADATA_PATH = System.getProperty("user.home") + "/.secureConnect/context_aware_metadata.json"; - /** GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable */ + /** GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable. */ public static final String GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"; @@ -90,7 +90,7 @@ public KeyStore getKeyStore() throws IOException, GeneralSecurityException { Process process = new ProcessBuilder(command).start(); int exitCode = runCertificateProviderCommand(process, 1000); if (exitCode != 0) { - throw new IOException(String.format("Failed to execute cert provider command with exit code: %d", exitCode)); + throw new IOException("Cert provider command failed with exit code: " + exitCode); } // Create mTLS key store with the input certificates from shell command. From 5593a9026e9957e0db54647e896f01e92074c297 Mon Sep 17 00:00:00 2001 From: arithmetic1728 <58957152+arithmetic1728@users.noreply.github.com> Date: Tue, 10 Nov 2020 13:00:41 -0800 Subject: [PATCH 29/29] Update google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java Co-authored-by: Jeff Ching --- .../com/google/api/client/googleapis/mtls/package-info.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java index bb6fbd0e6..fce7f4e26 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -18,4 +18,3 @@ * @since 1.31 */ package com.google.api.client.googleapis.mtls; -