Skip to content
14 changes: 7 additions & 7 deletions docs/oauth-2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -59,7 +59,7 @@ GoogleCredentials credentials =

Books books =
new Books.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
Gnew NetHttpTransport(),
GsonFactory.getDefaultInstance(),
new HttpCredentialsAdapter(credentials))
.setApplicationName("BooksExample/1.0")
Expand All @@ -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;
Expand All @@ -99,7 +99,7 @@ GoogleCredentials credentials =

Books books =
new Books.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
new NetHttpTransport(),
GsonFactory.getDefaultInstance(),
new HttpCredentialsAdapter(credentials))
.setApplicationName("BooksExample/1.0")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
public class ITGoogleApache5HttpTransportTest {

@Test
public void testHttpRequestFailsWhenMakingRequestToSiteWithoutGoogleCerts()
public void testHttpRequestFailsWhenMakingRequestToSiteWithoutDefaultJdkCerts()
throws GeneralSecurityException, IOException {
Apache5HttpTransport apache5HttpTransport = GoogleApache5HttpTransport.newTrustedTransport();
HttpGet httpGet = new HttpGet("https://maven.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
Expand All @@ -43,7 +44,7 @@ public void testHttpRequestFailsWhenMakingRequestToSiteWithoutGoogleCerts()
new HttpClientResponseHandler<Void>() {
@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;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*
* <p>Value is cached, so subsequent access is fast.
*
* <p>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 <a
* href="https://docs.cloud.google.com/java/docs/reference/google-http-client/latest/com.google.api.client.http.javanet.NetHttpTransport">NetHttpTransport</a>}
* 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
*
* @since 1.14
* @author Yaniv Inbar
* @deprecated This legacy HttpTransport implementation is no longer being maintained.
* Please use {@link <a href="https://docs.cloud.google.com/java/docs/reference/google-http-client/latest/com.google.api.client.http.javanet.NetHttpTransport">NetHttpTransport</a> instead.
*/
@Deprecated
public class GoogleNetHttpTransport {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<String> 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();
assertEquals(
"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 {
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() {
Expand Down
Loading