From 2ec8ed9a231316afb0ec254fea76a9ef890d8643 Mon Sep 17 00:00:00 2001 From: ldetmer Date: Mon, 9 Feb 2026 12:05:14 -0500 Subject: [PATCH 01/13] chore: update GoogleUtils#getCertificateTrustStore to first attempt to load JSDK default certs --- docs/oauth-2.0.md | 14 ++-- .../api/client/googleapis/GoogleUtils.java | 71 +++++++++++++++++-- .../javanet/GoogleNetHttpTransport.java | 3 + .../client/googleapis/GoogleUtilsTest.java | 37 +++++++--- 4 files changed, 105 insertions(+), 20 deletions(-) diff --git a/docs/oauth-2.0.md b/docs/oauth-2.0.md index 56a72b165..247750b32 100644 --- a/docs/oauth-2.0.md +++ b/docs/oauth-2.0.md @@ -47,7 +47,7 @@ For instructions on setting up your credentials properly, see the already have an access token, you can make a request in the following way: ```java -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.books.Books; import com.google.auth.http.HttpCredentialsAdapter; @@ -59,7 +59,7 @@ GoogleCredentials credentials = Books books = new Books.Builder( - GoogleNetHttpTransport.newTrustedTransport(), + Gnew NetHttpTransport(), GsonFactory.getDefaultInstance(), new HttpCredentialsAdapter(credentials)) .setApplicationName("BooksExample/1.0") @@ -79,7 +79,7 @@ App Engine takes care of all of the details. You only specify the OAuth 2.0 scope you need. ```java -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.books.Books; import com.google.appengine.api.appidentity.AppIdentityService; @@ -99,7 +99,7 @@ GoogleCredentials credentials = Books books = new Books.Builder( - GoogleNetHttpTransport.newTrustedTransport(), + new NetHttpTransport(), GsonFactory.getDefaultInstance(), new HttpCredentialsAdapter(credentials)) .setApplicationName("BooksExample/1.0") @@ -373,7 +373,7 @@ a private key downloaded from the [Google API Console][console]. For example, you can make a request in the following way: ```java -HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); +HttpTransport httpTransport = new NetHttpTransport(); JsonFactory jsonFactory = GsonFactory.getDefaultInstance(); //Build service account credential @@ -405,12 +405,12 @@ additionally call [`GoogleCredential.Builder.setServiceAccountUser(String)`][set This is the command-line authorization code flow described in [Using OAuth 2.0 for Installed Applications][oauth2-installed-app]. -Example snippet from [plus-cmdline-sample][plus-sample]: +Example usage: ```java public static void main(String[] args) { try { - httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + httpTransport = new NetHttpTransport(); dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR); // authorization Credential credential = authorize(); diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index c15c8a458..392cf4369 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -16,6 +16,8 @@ import com.google.api.client.util.SecurityUtils; import com.google.common.annotations.VisibleForTesting; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; @@ -69,21 +71,82 @@ public final class GoogleUtils { } /** Cached value for {@link #getCertificateTrustStore()}. */ + @VisibleForTesting static KeyStore certTrustStore; + /** Default JDK cacerts file path relative to java.home. */ + @VisibleForTesting + static String DEFAULT_CACERTS_PATH = "lib/security/cacerts"; + + /** Default password for JDK cacerts file. */ + static final String DEFAULT_CACERTS_PASSWORD = "changeit"; + + /** Java home system property key. */ + static final String JAVA_HOME_KEY = "java.home"; + + /** Name of bundled keystore. */ + static final String BUNDLED_KEYSTORE = "google.p12"; + + /** Bundled keystore password. */ + static final String BUNDLED_KEYSTORE_PASSWORD = "notasecret"; + + /** + * Loads the bundled google.p12 key store containing trusted root certificates. + * + * @return the loaded key store + */ + @VisibleForTesting + static KeyStore getBundledKeystore() throws IOException, GeneralSecurityException { + KeyStore ks = SecurityUtils.getPkcs12KeyStore(); + InputStream is = GoogleUtils.class.getResourceAsStream(BUNDLED_KEYSTORE); + SecurityUtils.loadKeyStore(ks, is, BUNDLED_KEYSTORE_PASSWORD); + return ks; + } + + /** + * Loads the default JDK key store (cacerts) containing trusted root certificates. + * Determines the path to the cacerts file based on the java.home system property. + * + * @return the loaded key store + */ + @VisibleForTesting + static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityException { + String javaHome = System.getProperty(JAVA_HOME_KEY); + File file = new File(javaHome, DEFAULT_CACERTS_PATH); + + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try (FileInputStream is = new FileInputStream(file)) { + trustStore.load(is, DEFAULT_CACERTS_PASSWORD.toCharArray()); + } + return trustStore; + } + /** - * Returns the key store for trusted root certificates to use for Google APIs. + * Returns a key store for trusted root certificates to use for Google APIs. * *

Value is cached, so subsequent access is fast. * + *

This method first attempts to load the JDK default keystore. If that fails or is not + * available, it falls back to loading the bundled Google certificate store. + * * @since 1.14 + * @deprecated This method is deprecated because it relies on a bundled certificate store that is not maintained. + * Please use {@link #getJdkDefaultKeyStore()} to load the JDK default keystore directly, or use your own certificate store as needed. */ + @Deprecated public static synchronized KeyStore getCertificateTrustStore() throws IOException, GeneralSecurityException { if (certTrustStore == null) { - certTrustStore = SecurityUtils.getPkcs12KeyStore(); - InputStream keyStoreStream = GoogleUtils.class.getResourceAsStream("google.p12"); - SecurityUtils.loadKeyStore(certTrustStore, keyStoreStream, "notasecret"); + // Try to load JDK default trust store first + try { + certTrustStore = getJdkDefaultKeyStore(); + } catch (Exception e) { + // If fails to load default, fall through to bundled certificates + } + + if (certTrustStore == null) { + certTrustStore = getBundledKeystore(); + } } return certTrustStore; } 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 66907fc72..a7aba8fb8 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 @@ -29,7 +29,10 @@ * * @since 1.14 * @author Yaniv Inbar + * @deprecated This is a legacy HttpTransport implementation is no longer being maintained. + * Please use {@link NetHttpTransport instead. */ +@Deprecated public class GoogleNetHttpTransport { /** diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java index ef8aa80b1..12eb1e155 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java @@ -19,6 +19,8 @@ import java.util.regex.Matcher; import junit.framework.TestCase; +import static org.junit.Assert.assertNotEquals; + /** * Tests {@link GoogleUtils}. * @@ -26,16 +28,33 @@ */ public class GoogleUtilsTest extends TestCase { - public void testGetCertificateTrustStore() throws Exception { + public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception { + GoogleUtils.certTrustStore = null; KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); - Enumeration aliases = trustStore.aliases(); - while (aliases.hasMoreElements()) { - String alias = aliases.nextElement(); - assertTrue(trustStore.isCertificateEntry(alias)); - } - // intentionally check the count of certificates, so it can help us detect if a new certificate - // has been added or removed - assertEquals(71, trustStore.size()); + + // Load bundled keystore to compare + KeyStore bundled = GoogleUtils.getBundledKeystore(); + + assertNotEquals("Certificate truststore should NOT contain the same amount of certificates as the bundled keystore", bundled.size(), trustStore.size()); + } + + public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultFails() throws Exception { + GoogleUtils.certTrustStore = null; + GoogleUtils.DEFAULT_CACERTS_PATH = "bad/path"; + + KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); + + // Load bundled keystore to compare + KeyStore bundled = GoogleUtils.getBundledKeystore(); + assertEquals("Certificate truststore should contain the same amount of certificates as the bundled keystore", trustStore.size(), bundled.size()); + } + + public void testGetCertificateTrustStore_IsCached() throws Exception { + KeyStore trustStore1 = GoogleUtils.getCertificateTrustStore(); + KeyStore trustStore2 = GoogleUtils.getCertificateTrustStore(); + + // Should return the exact same instance due to caching + assertSame("Trust store should be cached", trustStore1, trustStore2); } public void testVersionMatcher() { From 9f121ee17111f6265c305f5d0fd3dce78cf320f8 Mon Sep 17 00:00:00 2001 From: ldetmer Date: Mon, 9 Feb 2026 15:24:56 -0500 Subject: [PATCH 02/13] fixed formatting issues --- .../com/google/api/client/googleapis/GoogleUtils.java | 9 +++++---- .../google/api/client/googleapis/GoogleUtilsTest.java | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index 392cf4369..1402af7a7 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -76,7 +76,7 @@ public final class GoogleUtils { /** Default JDK cacerts file path relative to java.home. */ @VisibleForTesting - static String DEFAULT_CACERTS_PATH = "lib/security/cacerts"; + static String defaultCacertsPath = "lib/security/cacerts"; /** Default password for JDK cacerts file. */ static final String DEFAULT_CACERTS_PASSWORD = "changeit"; @@ -112,7 +112,7 @@ static KeyStore getBundledKeystore() throws IOException, GeneralSecurityExceptio @VisibleForTesting static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityException { String javaHome = System.getProperty(JAVA_HOME_KEY); - File file = new File(javaHome, DEFAULT_CACERTS_PATH); + File file = new File(javaHome, defaultCacertsPath); KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (FileInputStream is = new FileInputStream(file)) { @@ -130,8 +130,9 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep * available, it falls back to loading the bundled Google certificate store. * * @since 1.14 - * @deprecated This method is deprecated because it relies on a bundled certificate store that is not maintained. - * Please use {@link #getJdkDefaultKeyStore()} to load the JDK default keystore directly, or use your own certificate store as needed. + * @deprecated This method is deprecated because it relies on a bundled certificate store + * that is not maintained. Please use {@link #getJdkDefaultTrustStore()} to load the + * JDK default keystore directly, or use your own certificate store as needed. */ @Deprecated public static synchronized KeyStore getCertificateTrustStore() diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java index 12eb1e155..c0018235a 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java @@ -40,7 +40,7 @@ public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultFails() throws Exception { GoogleUtils.certTrustStore = null; - GoogleUtils.DEFAULT_CACERTS_PATH = "bad/path"; + GoogleUtils.defaultCacertsPath = "bad/path"; KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); From ba60520664e788bbb30f0fa03736c835176d538b Mon Sep 17 00:00:00 2001 From: ldetmer Date: Mon, 9 Feb 2026 15:35:25 -0500 Subject: [PATCH 03/13] fixed formatting issues --- .../api/client/googleapis/GoogleUtils.java | 33 +++++++++---------- .../javanet/GoogleNetHttpTransport.java | 4 +-- .../client/googleapis/GoogleUtilsTest.java | 22 ++++++++----- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index 1402af7a7..4ad9dbeab 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -71,12 +71,10 @@ public final class GoogleUtils { } /** Cached value for {@link #getCertificateTrustStore()}. */ - @VisibleForTesting - static KeyStore certTrustStore; + @VisibleForTesting static KeyStore certTrustStore; /** Default JDK cacerts file path relative to java.home. */ - @VisibleForTesting - static String defaultCacertsPath = "lib/security/cacerts"; + @VisibleForTesting static String defaultCacertsPath = "lib/security/cacerts"; /** Default password for JDK cacerts file. */ static final String DEFAULT_CACERTS_PASSWORD = "changeit"; @@ -91,9 +89,9 @@ public final class GoogleUtils { static final String BUNDLED_KEYSTORE_PASSWORD = "notasecret"; /** - * Loads the bundled google.p12 key store containing trusted root certificates. - * - * @return the loaded key store + * Loads the bundled google.p12 keystore containing trusted root certificates. + * + * @return the loaded keystore */ @VisibleForTesting static KeyStore getBundledKeystore() throws IOException, GeneralSecurityException { @@ -103,11 +101,11 @@ static KeyStore getBundledKeystore() throws IOException, GeneralSecurityExceptio return ks; } - /** - * Loads the default JDK key store (cacerts) containing trusted root certificates. - * Determines the path to the cacerts file based on the java.home system property. - * - * @return the loaded key store + /** + * Loads the default JDK keystore (cacerts) containing trusted root certificates. Determines the + * path to the cacerts file based on the java.home system property. + * + * @return the loaded keystore */ @VisibleForTesting static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityException { @@ -122,7 +120,7 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep } /** - * Returns a key store for trusted root certificates to use for Google APIs. + * Returns a keystore for trusted root certificates to use for Google APIs. * *

Value is cached, so subsequent access is fast. * @@ -130,19 +128,18 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep * available, it falls back to loading the bundled Google certificate store. * * @since 1.14 - * @deprecated This method is deprecated because it relies on a bundled certificate store - * that is not maintained. Please use {@link #getJdkDefaultTrustStore()} to load the - * JDK default keystore directly, or use your own certificate store as needed. + * @deprecated This method relies on a a bundled certificate store that is not maintained. Please + * use {@link #getJdkDefaultKeyStore()} to load the JDK default keystore directly, or use your + * own certificate store as needed. */ @Deprecated public static synchronized KeyStore getCertificateTrustStore() throws IOException, GeneralSecurityException { if (certTrustStore == null) { - // Try to load JDK default trust store first try { certTrustStore = getJdkDefaultKeyStore(); } catch (Exception e) { - // If fails to load default, fall through to bundled certificates + // If unable to retrieve default JDK keystore, fall through to bundled certificates } if (certTrustStore == 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 a7aba8fb8..eb4d00b46 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 @@ -29,8 +29,8 @@ * * @since 1.14 * @author Yaniv Inbar - * @deprecated This is a legacy HttpTransport implementation is no longer being maintained. - * Please use {@link NetHttpTransport instead. + * @deprecated This legacy HttpTransport implementation is no longer being maintained. + * Please use {@link NetHttpTransport instead. */ @Deprecated public class GoogleNetHttpTransport { diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java index c0018235a..d86abb304 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java @@ -14,13 +14,12 @@ package com.google.api.client.googleapis; +import static org.junit.Assert.assertNotEquals; + import java.security.KeyStore; -import java.util.Enumeration; import java.util.regex.Matcher; import junit.framework.TestCase; -import static org.junit.Assert.assertNotEquals; - /** * Tests {@link GoogleUtils}. * @@ -35,10 +34,14 @@ public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception // Load bundled keystore to compare KeyStore bundled = GoogleUtils.getBundledKeystore(); - assertNotEquals("Certificate truststore should NOT contain the same amount of certificates as the bundled keystore", bundled.size(), trustStore.size()); + assertNotEquals( + "Certificate truststore should NOT contain the same amount of certificates as the bundled keystore", + bundled.size(), + trustStore.size()); } - public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultFails() throws Exception { + public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFails() + throws Exception { GoogleUtils.certTrustStore = null; GoogleUtils.defaultCacertsPath = "bad/path"; @@ -46,13 +49,16 @@ public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultFails() // Load bundled keystore to compare KeyStore bundled = GoogleUtils.getBundledKeystore(); - assertEquals("Certificate truststore should contain the same amount of certificates as the bundled keystore", trustStore.size(), bundled.size()); - } + assertEquals( + "Certificate truststore should contain the same amount of certificates as the bundled keystore", + trustStore.size(), + bundled.size()); + } public void testGetCertificateTrustStore_IsCached() throws Exception { KeyStore trustStore1 = GoogleUtils.getCertificateTrustStore(); KeyStore trustStore2 = GoogleUtils.getCertificateTrustStore(); - + // Should return the exact same instance due to caching assertSame("Trust store should be cached", trustStore1, trustStore2); } From 07748090050a46fb6a0aa65820ee0c02e8ec9712 Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 09:08:28 -0500 Subject: [PATCH 04/13] validate if this passes CI checks --- .../api/client/googleapis/GoogleUtils.java | 59 +++++++++++++------ .../client/googleapis/GoogleUtilsTest.java | 4 +- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index 4ad9dbeab..09895ca36 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -73,15 +73,6 @@ public final class GoogleUtils { /** Cached value for {@link #getCertificateTrustStore()}. */ @VisibleForTesting static KeyStore certTrustStore; - /** Default JDK cacerts file path relative to java.home. */ - @VisibleForTesting static String defaultCacertsPath = "lib/security/cacerts"; - - /** Default password for JDK cacerts file. */ - static final String DEFAULT_CACERTS_PASSWORD = "changeit"; - - /** Java home system property key. */ - static final String JAVA_HOME_KEY = "java.home"; - /** Name of bundled keystore. */ static final String BUNDLED_KEYSTORE = "google.p12"; @@ -102,21 +93,53 @@ static KeyStore getBundledKeystore() throws IOException, GeneralSecurityExceptio } /** - * Loads the default JDK keystore (cacerts) containing trusted root certificates. Determines the - * path to the cacerts file based on the java.home system property. + * Loads the default JDK keystore (cacerts) containing trusted root certificates. + * Uses Java's system properties to locate the default trust store. * * @return the loaded keystore */ @VisibleForTesting static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityException { - String javaHome = System.getProperty(JAVA_HOME_KEY); - File file = new File(javaHome, defaultCacertsPath); - - KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (FileInputStream is = new FileInputStream(file)) { - trustStore.load(is, DEFAULT_CACERTS_PASSWORD.toCharArray()); + // Get trust store location and type from system properties, or use defaults + String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType()); + String trustStorePath = System.getProperty("javax.net.ssl.trustStore"); + String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword", "changeit"); + + KeyStore keyStore = KeyStore.getInstance(trustStoreType); + + if (trustStorePath != null && !trustStorePath.isEmpty()) { + // User specified a custom trust store via system property + try (FileInputStream fis = new FileInputStream(trustStorePath)) { + keyStore.load(fis, trustStorePassword.toCharArray()); + System.out.println("loaded keystore from truststore path"); + } + } else { + // Find the default JDK cacerts location + String javaHome = System.getProperty("java.home"); + String[] possiblePaths = { + "lib/security/cacerts", // Java 9+ + "jre/lib/security/cacerts" // Java 8 and earlier + }; + + File cacertsFile = null; + for (String path : possiblePaths) { + File candidate = new File(javaHome, path); + if (candidate.exists() && candidate.canRead()) { + cacertsFile = candidate; + break; + } + } + + if (cacertsFile == null) { + throw new IOException("Unable to find JDK cacerts file in java.home: " + javaHome); + } + + try (FileInputStream fis = new FileInputStream(cacertsFile)) { + keyStore.load(fis, trustStorePassword.toCharArray()); + } } - return trustStore; + + return keyStore; } /** diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java index d86abb304..e91379d99 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java @@ -40,7 +40,7 @@ public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception trustStore.size()); } - public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFails() + /* public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFails() throws Exception { GoogleUtils.certTrustStore = null; GoogleUtils.defaultCacertsPath = "bad/path"; @@ -53,7 +53,7 @@ public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFai "Certificate truststore should contain the same amount of certificates as the bundled keystore", trustStore.size(), bundled.size()); - } + }*/ public void testGetCertificateTrustStore_IsCached() throws Exception { KeyStore trustStore1 = GoogleUtils.getCertificateTrustStore(); From 3b3fef5b50f87a01c9c88f3275c9048b936dae0d Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 09:29:56 -0500 Subject: [PATCH 05/13] fix formatting --- .../java/com/google/api/client/googleapis/GoogleUtils.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index 09895ca36..cdbbadf69 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -101,9 +101,11 @@ static KeyStore getBundledKeystore() throws IOException, GeneralSecurityExceptio @VisibleForTesting static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityException { // Get trust store location and type from system properties, or use defaults - String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType()); + String trustStoreType = + System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType()); String trustStorePath = System.getProperty("javax.net.ssl.trustStore"); - String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword", "changeit"); + String trustStorePassword = + System.getProperty("javax.net.ssl.trustStorePassword", "changeit"); KeyStore keyStore = KeyStore.getInstance(trustStoreType); From 2bf7084482cc90181a1e153bec827de2f309952f Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 10:06:26 -0500 Subject: [PATCH 06/13] temporarily comment out test to validate everything else is working correctly in CI --- .../apache/v5/ITGoogleApache5HttpTransportTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java index f8e9cbed1..631d6c7a2 100644 --- a/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java +++ b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java @@ -29,11 +29,11 @@ public class ITGoogleApache5HttpTransportTest { - @Test + /*@Test public void testHttpRequestFailsWhenMakingRequestToSiteWithoutGoogleCerts() throws GeneralSecurityException, IOException { Apache5HttpTransport apache5HttpTransport = GoogleApache5HttpTransport.newTrustedTransport(); - HttpGet httpGet = new HttpGet("https://maven.com/"); + HttpGet httpGet = new HttpGet("https://shopify.com/"); Exception exception = null; try { apache5HttpTransport @@ -54,7 +54,7 @@ public Void handleResponse(ClassicHttpResponse response) { assertNotNull(exception); assertEquals(exception.getClass(), SSLHandshakeException.class); - } + }*/ @Test public void testHttpRequestPassesWhenMakingRequestToGoogleSite() throws Exception { From 4747d715a88e166f220a600a11e6751041392090 Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 10:19:09 -0500 Subject: [PATCH 07/13] clean up code --- .../api/client/googleapis/GoogleUtils.java | 56 ++++++------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index cdbbadf69..c2bee08ea 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -100,48 +100,26 @@ static KeyStore getBundledKeystore() throws IOException, GeneralSecurityExceptio */ @VisibleForTesting static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityException { - // Get trust store location and type from system properties, or use defaults - String trustStoreType = - System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType()); - String trustStorePath = System.getProperty("javax.net.ssl.trustStore"); - String trustStorePassword = - System.getProperty("javax.net.ssl.trustStorePassword", "changeit"); - - KeyStore keyStore = KeyStore.getInstance(trustStoreType); - - if (trustStorePath != null && !trustStorePath.isEmpty()) { - // User specified a custom trust store via system property - try (FileInputStream fis = new FileInputStream(trustStorePath)) { - keyStore.load(fis, trustStorePassword.toCharArray()); - System.out.println("loaded keystore from truststore path"); - } - } else { - // Find the default JDK cacerts location - String javaHome = System.getProperty("java.home"); - String[] possiblePaths = { - "lib/security/cacerts", // Java 9+ - "jre/lib/security/cacerts" // Java 8 and earlier - }; - - File cacertsFile = null; - for (String path : possiblePaths) { - File candidate = new File(javaHome, path); - if (candidate.exists() && candidate.canRead()) { - cacertsFile = candidate; - break; - } - } - - if (cacertsFile == null) { - throw new IOException("Unable to find JDK cacerts file in java.home: " + javaHome); - } - + KeyStore keyStore = SecurityUtils.getDefaultKeyStore(); + + // Find the default JDK cacerts location + String javaHome = System.getProperty("java.home"); + String[] possiblePaths = { + "lib/security/cacerts", // Java 9+ + "jre/lib/security/cacerts" // Java 8 and earlier + }; + + for (String path : possiblePaths) { + File cacertsFile = new File(javaHome, path); try (FileInputStream fis = new FileInputStream(cacertsFile)) { - keyStore.load(fis, trustStorePassword.toCharArray()); + keyStore.load(fis, "changeit".toCharArray()); + return keyStore; + } catch (IOException e) { + // File doesn't exist or can't be read, try next path } } - - return keyStore; + + throw new IOException("Unable to find JDK cacerts file in java.home: " + javaHome); } /** From 8f0076c8f7cde28589e9379f846e39661ca1121a Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 10:33:09 -0500 Subject: [PATCH 08/13] clean up code + add back failing tests --- .../v5/ITGoogleApache5HttpTransportTest.java | 11 +++++---- .../api/client/googleapis/GoogleUtils.java | 23 +++++++++++++------ .../client/googleapis/GoogleUtilsTest.java | 6 ++--- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java index 631d6c7a2..db03ab720 100644 --- a/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java +++ b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java @@ -29,11 +29,12 @@ public class ITGoogleApache5HttpTransportTest { - /*@Test - public void testHttpRequestFailsWhenMakingRequestToSiteWithoutGoogleCerts() + @Test + public void testHttpRequestFailsWhenMakingRequestToSiteWithoutDefaultJdkCerts() throws GeneralSecurityException, IOException { Apache5HttpTransport apache5HttpTransport = GoogleApache5HttpTransport.newTrustedTransport(); - HttpGet httpGet = new HttpGet("https://shopify.com/"); + // Use a self-signed certificate site that won't be trusted by default trust store + HttpGet httpGet = new HttpGet("https://self-signed.badssl.com/"); Exception exception = null; try { apache5HttpTransport @@ -43,7 +44,7 @@ public void testHttpRequestFailsWhenMakingRequestToSiteWithoutGoogleCerts() new HttpClientResponseHandler() { @Override public Void handleResponse(ClassicHttpResponse response) { - fail("Should not have been able to complete SSL request on non google site."); + fail("Should not have been able to complete SSL request with untrusted cert."); return null; } }); @@ -54,7 +55,7 @@ public Void handleResponse(ClassicHttpResponse response) { assertNotNull(exception); assertEquals(exception.getClass(), SSLHandshakeException.class); - }*/ + } @Test public void testHttpRequestPassesWhenMakingRequestToGoogleSite() throws Exception { diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index c2bee08ea..dbc181b12 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -79,6 +79,19 @@ public final class GoogleUtils { /** Bundled keystore password. */ static final String BUNDLED_KEYSTORE_PASSWORD = "notasecret"; + /** Default JDK cacerts file path relative to java.home. */ + @VisibleForTesting + static String[] possibleJdkPaths = { + "lib/security/cacerts", // Java 9+ + "jre/lib/security/cacerts" // Java 8 and earlier + }; + + /** Java home system property key. */ + static final String JAVA_HOME_KEY = "java.home"; + + /** Default password for JDK cacerts file. */ + static final String DEFAULT_CACERTS_PASSWORD = "changeit"; + /** * Loads the bundled google.p12 keystore containing trusted root certificates. * @@ -103,16 +116,12 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep KeyStore keyStore = SecurityUtils.getDefaultKeyStore(); // Find the default JDK cacerts location - String javaHome = System.getProperty("java.home"); - String[] possiblePaths = { - "lib/security/cacerts", // Java 9+ - "jre/lib/security/cacerts" // Java 8 and earlier - }; + String javaHome = System.getProperty(JAVA_HOME_KEY); - for (String path : possiblePaths) { + for (String path : possibleJdkPaths) { File cacertsFile = new File(javaHome, path); try (FileInputStream fis = new FileInputStream(cacertsFile)) { - keyStore.load(fis, "changeit".toCharArray()); + keyStore.load(fis, DEFAULT_CACERTS_PASSWORD.toCharArray()); return keyStore; } catch (IOException e) { // File doesn't exist or can't be read, try next path diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java index e91379d99..d81b86ed8 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java @@ -40,10 +40,10 @@ public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception trustStore.size()); } - /* public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFails() + public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFails() throws Exception { GoogleUtils.certTrustStore = null; - GoogleUtils.defaultCacertsPath = "bad/path"; + GoogleUtils.possibleJdkPaths = new String[0]; KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); @@ -53,7 +53,7 @@ public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception "Certificate truststore should contain the same amount of certificates as the bundled keystore", trustStore.size(), bundled.size()); - }*/ + } public void testGetCertificateTrustStore_IsCached() throws Exception { KeyStore trustStore1 = GoogleUtils.getCertificateTrustStore(); From 01df67a94f5af56c786631bbfdbab241fe03fe31 Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 10:37:02 -0500 Subject: [PATCH 09/13] need to reset static vars in tests --- .../java/com/google/api/client/googleapis/GoogleUtilsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java index d81b86ed8..9e531ab30 100644 --- a/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java +++ b/google-api-client/src/test/java/com/google/api/client/googleapis/GoogleUtilsTest.java @@ -43,6 +43,7 @@ public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFails() throws Exception { GoogleUtils.certTrustStore = null; + String[] originalPaths = GoogleUtils.possibleJdkPaths; GoogleUtils.possibleJdkPaths = new String[0]; KeyStore trustStore = GoogleUtils.getCertificateTrustStore(); @@ -53,6 +54,8 @@ public void testGetCertificateTrustStore_LoadsBundledKeystoreIfJdkDefaultLoadFai "Certificate truststore should contain the same amount of certificates as the bundled keystore", trustStore.size(), bundled.size()); + + GoogleUtils.possibleJdkPaths = originalPaths; } public void testGetCertificateTrustStore_IsCached() throws Exception { From b787b893d73b6f2cb6d51e7e8ec220e16910fa54 Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 12:43:42 -0500 Subject: [PATCH 10/13] cleaned up comments --- .../com/google/api/client/googleapis/GoogleUtils.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index dbc181b12..db2ac7f9e 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -107,7 +107,7 @@ static KeyStore getBundledKeystore() throws IOException, GeneralSecurityExceptio /** * Loads the default JDK keystore (cacerts) containing trusted root certificates. - * Uses Java's system properties to locate the default trust store. + * Uses Java's system properties + known cert locations to locate the default trust store. * * @return the loaded keystore */ @@ -128,7 +128,7 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep } } - throw new IOException("Unable to find JDK cacerts file in java.home: " + javaHome); + throw new IOException("Unable to load default JDK cacerts file"); } /** @@ -140,9 +140,8 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep * available, it falls back to loading the bundled Google certificate store. * * @since 1.14 - * @deprecated This method relies on a a bundled certificate store that is not maintained. Please - * use {@link #getJdkDefaultKeyStore()} to load the JDK default keystore directly, or use your - * own certificate store as needed. + * @deprecated Depending on your build environment this method potentially can contain outdated certs if loading jdk default certs fails. + * Instead of getting trusted certs directly use an HttpTransport wrapper such as {@link NetHttpTransport} which use java jdk private classes to load default jdk certs specifically for your build environment. If you need to access the keystore directly please create your own keystore file. */ @Deprecated public static synchronized KeyStore getCertificateTrustStore() From 897b1bacae4e209a8783bb63ff3b92595d4705dc Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 12:49:05 -0500 Subject: [PATCH 11/13] fix lint --- .../java/com/google/api/client/googleapis/GoogleUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index db2ac7f9e..b164f9234 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -140,8 +140,11 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep * available, it falls back to loading the bundled Google certificate store. * * @since 1.14 - * @deprecated Depending on your build environment this method potentially can contain outdated certs if loading jdk default certs fails. - * Instead of getting trusted certs directly use an HttpTransport wrapper such as {@link NetHttpTransport} which use java jdk private classes to load default jdk certs specifically for your build environment. If you need to access the keystore directly please create your own keystore file. + * @deprecated Depending on your build environment this method potentially can contain outdated + * certs if loading jdk default certs fails. + * Instead of getting trusted certs directly use an HttpTransport wrapper such as {@link NetHttpTransport} + * which uses java jdk internal classes to load default jdk certs specifically for your build environment. + * If you need to access the keystore directly please create your own keystore file. */ @Deprecated public static synchronized KeyStore getCertificateTrustStore() From ea6fdd080e8a37119602b6b82b63dbfdd886e0cc Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 13:16:33 -0500 Subject: [PATCH 12/13] fix formatting --- .../java/com/google/api/client/googleapis/GoogleUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index b164f9234..be7df22ca 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -143,8 +143,8 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep * @deprecated Depending on your build environment this method potentially can contain outdated * certs if loading jdk default certs fails. * Instead of getting trusted certs directly use an HttpTransport wrapper such as {@link NetHttpTransport} - * which uses java jdk internal classes to load default jdk certs specifically for your build environment. - * If you need to access the keystore directly please create your own keystore file. + * which uses java jdk internal classes to load default jdk certs specifically for your build + * environment. If you need to access the keystore directly please create your own keystore file. */ @Deprecated public static synchronized KeyStore getCertificateTrustStore() From e20a10708212cf79f093063ac63997e3f13dccdf Mon Sep 17 00:00:00 2001 From: ldetmer Date: Tue, 10 Feb 2026 13:28:24 -0500 Subject: [PATCH 13/13] fix formatting --- .../api/client/googleapis/GoogleUtils.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java index be7df22ca..e1cb2d9de 100644 --- a/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java +++ b/google-api-client/src/main/java/com/google/api/client/googleapis/GoogleUtils.java @@ -82,8 +82,8 @@ public final class GoogleUtils { /** Default JDK cacerts file path relative to java.home. */ @VisibleForTesting static String[] possibleJdkPaths = { - "lib/security/cacerts", // Java 9+ - "jre/lib/security/cacerts" // Java 8 and earlier + "lib/security/cacerts", // Java 9+ + "jre/lib/security/cacerts" // Java 8 and earlier }; /** Java home system property key. */ @@ -106,8 +106,8 @@ static KeyStore getBundledKeystore() throws IOException, GeneralSecurityExceptio } /** - * Loads the default JDK keystore (cacerts) containing trusted root certificates. - * Uses Java's system properties + known cert locations to locate the default trust store. + * Loads the default JDK keystore (cacerts) containing trusted root certificates. Uses Java's + * system properties + known cert locations to locate the default trust store. * * @return the loaded keystore */ @@ -141,10 +141,12 @@ static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityExcep * * @since 1.14 * @deprecated Depending on your build environment this method potentially can contain outdated - * certs if loading jdk default certs fails. - * Instead of getting trusted certs directly use an HttpTransport wrapper such as {@link NetHttpTransport} - * which uses java jdk internal classes to load default jdk certs specifically for your build - * environment. If you need to access the keystore directly please create your own keystore file. + * certs if loading jdk default certs fails. Instead of getting trusted certs directly use an + * HttpTransport wrapper such as {@link NetHttpTransport} + * which uses java jdk internal classes to load default jdk certs specifically for a build + * environment. If you need to access the keystore directly please create your own keystore + * file. */ @Deprecated public static synchronized KeyStore getCertificateTrustStore()