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..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 @@ -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,96 @@ public final class GoogleUtils { } /** Cached value for {@link #getCertificateTrustStore()}. */ - static KeyStore certTrustStore; + @VisibleForTesting static KeyStore certTrustStore; + + /** Name of bundled keystore. */ + static final String BUNDLED_KEYSTORE = "google.p12"; + + /** 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. + * + * @return the loaded keystore + */ + @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; + } /** - * Returns the key store for trusted root certificates to use for Google APIs. + * 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 + */ + @VisibleForTesting + static KeyStore getJdkDefaultKeyStore() throws IOException, GeneralSecurityException { + KeyStore keyStore = SecurityUtils.getDefaultKeyStore(); + + // Find the default JDK cacerts location + String javaHome = System.getProperty(JAVA_HOME_KEY); + + for (String path : possibleJdkPaths) { + File cacertsFile = new File(javaHome, path); + try (FileInputStream fis = new FileInputStream(cacertsFile)) { + keyStore.load(fis, DEFAULT_CACERTS_PASSWORD.toCharArray()); + return keyStore; + } catch (IOException e) { + // File doesn't exist or can't be read, try next path + } + } + + throw new IOException("Unable to load default JDK cacerts file"); + } + + /** + * Returns a keystore 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 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 a build
+ * environment. If you need to access the keystore directly please create your own keystore
+ * file.
*/
+ @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 {
+ certTrustStore = getJdkDefaultKeyStore();
+ } catch (Exception e) {
+ // If unable to retrieve default JDK keystore, 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..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,7 +29,10 @@
*
* @since 1.14
* @author Yaniv Inbar
+ * @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 ef8aa80b1..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
@@ -14,8 +14,9 @@
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;
@@ -26,16 +27,43 @@
*/
public class GoogleUtilsTest extends TestCase {
- public void testGetCertificateTrustStore() throws Exception {
+ public void testGetCertificateTrustStore_LoadsJdkDefaultFirst() throws Exception {
+ GoogleUtils.certTrustStore = null;
+ KeyStore trustStore = GoogleUtils.getCertificateTrustStore();
+
+ // 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_LoadsBundledKeystoreIfJdkDefaultLoadFails()
+ throws Exception {
+ GoogleUtils.certTrustStore = null;
+ String[] originalPaths = GoogleUtils.possibleJdkPaths;
+ GoogleUtils.possibleJdkPaths = new String[0];
+
KeyStore trustStore = GoogleUtils.getCertificateTrustStore();
- Enumeration