File CVE-2021-22573.patch of Package google-oauth-java-client.32892

Index: google-oauth-java-client-1.22.0/google-oauth-client/src/main/java/com/google/api/client/auth/openidconnect/Environment.java
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/main/java/com/google/api/client/auth/openidconnect/Environment.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.google.api.client.auth.openidconnect;
+
+class Environment {
+  public String getVariable(String name) {
+    return System.getenv(name);
+  }
+}
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/main/java/com/google/api/client/auth/openidconnect/HttpTransportFactory.java
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/main/java/com/google/api/client/auth/openidconnect/HttpTransportFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ *    * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.google.api.client.auth.openidconnect;
+
+import com.google.api.client.http.HttpTransport;
+
+/**
+ * A base interface for all {@link HttpTransport} factories.
+ *
+ * <p>Implementation must provide a public no-arg constructor. Loading of a factory implementation
+ * is done via {@link java.util.ServiceLoader}.
+ */
+public interface HttpTransportFactory {
+
+  /**
+   * Creates a {@code HttpTransport} instance.
+   *
+   * @return The HttpTransport instance.
+   */
+  HttpTransport create();
+}
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/main/java/com/google/api/client/auth/openidconnect/IdTokenVerifier.java
===================================================================
--- google-oauth-java-client-1.22.0.orig/google-oauth-client/src/main/java/com/google/api/client/auth/openidconnect/IdTokenVerifier.java
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/main/java/com/google/api/client/auth/openidconnect/IdTokenVerifier.java
@@ -14,12 +14,53 @@
 
 package com.google.api.client.auth.openidconnect;
 
+import com.google.api.client.http.GenericUrl;
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpResponse;
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.client.json.GenericJson;
+import com.google.api.client.json.gson.GsonFactory;
+import com.google.api.client.json.webtoken.JsonWebSignature.Header;
+import com.google.api.client.util.Base64;
 import com.google.api.client.util.Beta;
 import com.google.api.client.util.Clock;
+import com.google.api.client.util.Key;
 import com.google.api.client.util.Preconditions;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.RSAPublicKeySpec;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * {@link Beta} <br/>
@@ -44,7 +85,7 @@ import java.util.Collections;
  * <p>
  * Note that {@link #verify(IdToken)} only implements a subset of the verification steps, mostly
  * just the MUST steps. Please read <a
- * href="http://openid.net/specs/openid-connect-basic-1_0-27.html#id.token.validation>ID Token
+ * href="http://openid.net/specs/openid-connect-basic-1_0-27.html#id.token.validation">ID Token
  * Validation</a> for the full list of verification steps.
  * </p>
  *
@@ -52,13 +93,26 @@ import java.util.Collections;
  */
 @Beta
 public class IdTokenVerifier {
+  private static final Logger LOGGER = Logger.getLogger(IdTokenVerifier.class.getName());
+  private static final String IAP_CERT_URL = "https://www.gstatic.com/iap/verify/public_key-jwk";
+  private static final String FEDERATED_SIGNON_CERT_URL =
+      "https://www.googleapis.com/oauth2/v3/certs";
+  private static final Set<String> SUPPORTED_ALGORITHMS = ImmutableSet.of("RS256", "ES256");
+  private static final String NOT_SUPPORTED_ALGORITHM =
+      "Unexpected signing algorithm %s: expected either RS256 or ES256";
 
+  static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
+  static final String SKIP_SIGNATURE_ENV_VAR = "OAUTH_CLIENT_SKIP_SIGNATURE";
   /** Default value for seconds of time skew to accept when verifying time (5 minutes). */
   public static final long DEFAULT_TIME_SKEW_SECONDS = 300;
 
   /** Clock to use for expiration checks. */
   private final Clock clock;
 
+  private final String certificatesLocation;
+  private final Environment environment;
+  private final LoadingCache<String, Map<String, PublicKey>> publicKeyCache;
+
   /** Seconds of time skew to accept when verifying time. */
   private final long acceptableTimeSkewSeconds;
 
@@ -82,11 +136,21 @@ public class IdTokenVerifier {
    * @param builder builder
    */
   protected IdTokenVerifier(Builder builder) {
+    this.certificatesLocation = builder.certificatesLocation;
     clock = builder.clock;
     acceptableTimeSkewSeconds = builder.acceptableTimeSkewSeconds;
     issuers = builder.issuers == null ? null : Collections.unmodifiableCollection(builder.issuers);
     audience =
         builder.audience == null ? null : Collections.unmodifiableCollection(builder.audience);
+    HttpTransportFactory transport =
+        builder.httpTransportFactory == null
+            ? new DefaultHttpTransportFactory()
+            : builder.httpTransportFactory;
+    this.publicKeyCache =
+        CacheBuilder.newBuilder()
+            .expireAfterWrite(1, TimeUnit.HOURS)
+            .build(new PublicKeyLoader(transport));
+    this.environment = builder.environment == null ? new Environment() : builder.environment;
   }
 
   /** Returns the clock. */
@@ -133,13 +197,20 @@ public class IdTokenVerifier {
    * It verifies:
    *
    * <ul>
-   * <li>The issuer is one of {@link #getIssuers()} by calling {@link
-   * IdToken#verifyIssuer(String)}.</li>
-   * <li>The audience is one of {@link #getAudience()} by calling
-   * {@link IdToken#verifyAudience(Collection)}.</li>
-   * <li>The current time against the issued at and expiration time, using the {@link #getClock()}
-   * and allowing for a time skew specified in {#link {@link #getAcceptableTimeSkewSeconds()} , by
-   * calling {@link IdToken#verifyTime(long, long)}.</li>
+   *   <li>The issuer is one of {@link #getIssuers()} by calling {@link
+   *       IdToken#verifyIssuer(String)}.</li>
+   *   <li>The audience is one of {@link #getAudience()} by calling
+   *       {@link IdToken#verifyAudience(Collection)}.</li>
+   *   <li>The current time against the issued at and expiration time, using the {@link #getClock()}
+   *       and allowing for a time skew specified in {#link {@link #getAcceptableTimeSkewSeconds()} , by
+   *       calling {@link IdToken#verifyTime(long, long)}.</li>
+   *   <li>This method verifies token signature per current OpenID Connect Spec:
+   *       https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation. By default,
+   *       method gets a certificate from well-known location. A request to certificate location is
+   *       performed using {@link com.google.api.client.http.javanet.NetHttpTransport} Both
+   *       certificate location and transport implementation can be overridden via {@link Builder}
+   *       not recommended: this check can be disabled with OAUTH_CLIENT_SKIP_SIGNATURE environment
+   *       variable set to true.</li>
    * </ul>
    *
    * <p>
@@ -150,9 +221,74 @@ public class IdTokenVerifier {
    * @return {@code true} if verified successfully or {@code false} if failed
    */
   public boolean verify(IdToken idToken) {
-    return (issuers == null || idToken.verifyIssuer(issuers))
-        && (audience == null || idToken.verifyAudience(audience))
-        && idToken.verifyTime(clock.currentTimeMillis(), acceptableTimeSkewSeconds);
+    boolean tokenFieldsValid =
+        (issuers == null || idToken.verifyIssuer(issuers))
+            && (audience == null || idToken.verifyAudience(audience))
+            && idToken.verifyTime(clock.currentTimeMillis(), acceptableTimeSkewSeconds);
+
+    if (!tokenFieldsValid) {
+      return false;
+    }
+
+    try {
+      return verifySignature(idToken);
+    } catch (VerificationException ex) {
+      LOGGER.log(
+          Level.SEVERE,
+          "id token signature verification failed. "
+              + "Please see docs for IdTokenVerifier for default settings and configuration options",
+          ex);
+      return false;
+    }
+  }
+
+  @VisibleForTesting
+  boolean verifySignature(IdToken idToken) throws VerificationException {
+    if (Boolean.parseBoolean(environment.getVariable(SKIP_SIGNATURE_ENV_VAR))) {
+      return true;
+    }
+
+    // Short-circuit signature types
+    if (!SUPPORTED_ALGORITHMS.contains(idToken.getHeader().getAlgorithm())) {
+      throw new VerificationException(
+          String.format(NOT_SUPPORTED_ALGORITHM, idToken.getHeader().getAlgorithm()));
+    }
+
+    PublicKey publicKeyToUse = null;
+    try {
+      String certificateLocation = getCertificateLocation(idToken.getHeader());
+      publicKeyToUse = publicKeyCache.get(certificateLocation).get(idToken.getHeader().getKeyId());
+    } catch (ExecutionException | UncheckedExecutionException e) {
+      throw new VerificationException(
+          "Error fetching PublicKey from certificate location " + certificatesLocation, e);
+    }
+
+    if (publicKeyToUse == null) {
+      throw new VerificationException(
+          "Could not find PublicKey for provided keyId: " + idToken.getHeader().getKeyId());
+    }
+
+    try {
+      if (idToken.verifySignature(publicKeyToUse)) {
+        return true;
+      }
+      throw new VerificationException("Invalid signature");
+    } catch (GeneralSecurityException e) {
+      throw new VerificationException("Error validating token", e);
+    }
+  }
+
+  private String getCertificateLocation(Header header) throws VerificationException {
+    if (certificatesLocation != null) return certificatesLocation;
+
+    switch (header.getAlgorithm()) {
+      case "RS256":
+        return FEDERATED_SIGNON_CERT_URL;
+      case "ES256":
+        return IAP_CERT_URL;
+    }
+
+    throw new VerificationException(String.format(NOT_SUPPORTED_ALGORITHM, header.getAlgorithm()));
   }
 
   /**
@@ -171,6 +307,11 @@ public class IdTokenVerifier {
     /** Clock. */
     Clock clock = Clock.SYSTEM;
 
+    String certificatesLocation;
+
+    /** wrapper for environment variables */
+    Environment environment;
+
     /** Seconds of time skew to accept when verifying time. */
     long acceptableTimeSkewSeconds = DEFAULT_TIME_SKEW_SECONDS;
 
@@ -180,6 +321,8 @@ public class IdTokenVerifier {
     /** List of trusted audience client IDs or {@code null} to suppress the audience check. */
     Collection<String> audience;
 
+    HttpTransportFactory httpTransportFactory;
+
     /** Builds a new instance of {@link IdTokenVerifier}. */
     public IdTokenVerifier build() {
       return new IdTokenVerifier(this);
@@ -231,6 +374,18 @@ public class IdTokenVerifier {
     }
 
     /**
+     * Override the location URL that contains published public keys. Defaults to well-known Google
+     * locations.
+     *
+     * @param certificatesLocation URL to published public keys
+     * @return the builder
+     */
+    public Builder setCertificatesLocation(String certificatesLocation) {
+      this.certificatesLocation = certificatesLocation;
+      return this;
+    }
+
+    /**
      * Returns the equivalent expected issuers or {@code null} if issuer check suppressed.
      *
      * @since 1.21.0
@@ -303,5 +458,172 @@ public class IdTokenVerifier {
       return this;
     }
 
+    /** Returns an instance of the {@link Environment} */
+    final Environment getEnvironment() {
+      return environment;
+    }
+
+    /** Sets the environment. Used mostly for testing */
+    Builder setEnvironment(Environment environment) {
+      this.environment = environment;
+      return this;
+    }
+
+    /**
+     * Sets the HttpTransportFactory used for requesting public keys from the certificate URL. Used
+     * mostly for testing.
+     *
+     * @param httpTransportFactory the HttpTransportFactory used to build certificate URL requests
+     * @return the builder
+     */
+    public Builder setHttpTransportFactory(HttpTransportFactory httpTransportFactory) {
+      this.httpTransportFactory = httpTransportFactory;
+      return this;
+    }
+  }
+
+  /** Custom CacheLoader for mapping certificate urls to the contained public keys. */
+  static class PublicKeyLoader extends CacheLoader<String, Map<String, PublicKey>> {
+    private final HttpTransportFactory httpTransportFactory;
+
+    /**
+     * Data class used for deserializing a JSON Web Key Set (JWKS) from an external HTTP request.
+     */
+    public static class JsonWebKeySet extends GenericJson {
+      @Key public List<JsonWebKey> keys;
+    }
+
+    /** Data class used for deserializing a single JSON Web Key. */
+    public static class JsonWebKey {
+      @Key public String alg;
+
+      @Key public String crv;
+
+      @Key public String kid;
+
+      @Key public String kty;
+
+      @Key public String use;
+
+      @Key public String x;
+
+      @Key public String y;
+
+      @Key public String e;
+
+      @Key public String n;
+    }
+
+    PublicKeyLoader(HttpTransportFactory httpTransportFactory) {
+      super();
+      this.httpTransportFactory = httpTransportFactory;
+    }
+
+    @Override
+    public Map<String, PublicKey> load(String certificateUrl) throws Exception {
+      HttpTransport httpTransport = httpTransportFactory.create();
+      JsonWebKeySet jwks;
+      try {
+        HttpRequest request =
+            httpTransport
+                .createRequestFactory()
+                .buildGetRequest(new GenericUrl(certificateUrl))
+                .setParser(GsonFactory.getDefaultInstance().createJsonObjectParser());
+        HttpResponse response = request.execute();
+        jwks = response.parseAs(JsonWebKeySet.class);
+      } catch (IOException io) {
+        LOGGER.log(
+            Level.WARNING,
+            "Failed to get a certificate from certificate location " + certificateUrl,
+            io);
+        return ImmutableMap.of();
+      }
+
+      ImmutableMap.Builder<String, PublicKey> keyCacheBuilder = new ImmutableMap.Builder<>();
+      if (jwks.keys == null) {
+        // Fall back to x509 formatted specification
+        for (String keyId : jwks.keySet()) {
+          String publicKeyPem = (String) jwks.get(keyId);
+          keyCacheBuilder.put(keyId, buildPublicKey(publicKeyPem));
+        }
+      } else {
+        for (JsonWebKey key : jwks.keys) {
+          try {
+            keyCacheBuilder.put(key.kid, buildPublicKey(key));
+          } catch (NoSuchAlgorithmException
+              | InvalidKeySpecException
+              | InvalidParameterSpecException ignored) {
+            LOGGER.log(Level.WARNING, "Failed to put a key into the cache", ignored);
+          }
+        }
+      }
+
+      return keyCacheBuilder.build();
+    }
+
+    private PublicKey buildPublicKey(JsonWebKey key)
+        throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
+      if ("ES256".equals(key.alg)) {
+        return buildEs256PublicKey(key);
+      } else if ("RS256".equals((key.alg))) {
+        return buildRs256PublicKey(key);
+      } else {
+        return null;
+      }
+    }
+
+    private PublicKey buildPublicKey(String publicPem)
+        throws CertificateException, UnsupportedEncodingException {
+      return CertificateFactory.getInstance("X.509")
+          .generateCertificate(new ByteArrayInputStream(publicPem.getBytes("UTF-8")))
+          .getPublicKey();
+    }
+
+    private PublicKey buildRs256PublicKey(JsonWebKey key)
+        throws NoSuchAlgorithmException, InvalidKeySpecException {
+      com.google.common.base.Preconditions.checkArgument("RSA".equals(key.kty));
+      com.google.common.base.Preconditions.checkNotNull(key.e);
+      com.google.common.base.Preconditions.checkNotNull(key.n);
+
+      BigInteger modulus = new BigInteger(1, Base64.decodeBase64(key.n));
+      BigInteger exponent = new BigInteger(1, Base64.decodeBase64(key.e));
+
+      RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
+      KeyFactory factory = KeyFactory.getInstance("RSA");
+      return factory.generatePublic(spec);
+    }
+
+    private PublicKey buildEs256PublicKey(JsonWebKey key)
+        throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
+      com.google.common.base.Preconditions.checkArgument("EC".equals(key.kty));
+      com.google.common.base.Preconditions.checkArgument("P-256".equals(key.crv));
+
+      BigInteger x = new BigInteger(1, Base64.decodeBase64(key.x));
+      BigInteger y = new BigInteger(1, Base64.decodeBase64(key.y));
+      ECPoint pubPoint = new ECPoint(x, y);
+      AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
+      parameters.init(new ECGenParameterSpec("secp256r1"));
+      ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
+      ECPublicKeySpec pubSpec = new ECPublicKeySpec(pubPoint, ecParameters);
+      KeyFactory kf = KeyFactory.getInstance("EC");
+      return kf.generatePublic(pubSpec);
+    }
+  }
+
+  /** Custom exception for wrapping all verification errors. */
+  static class VerificationException extends Exception {
+    public VerificationException(String message) {
+      super(message);
+    }
+
+    public VerificationException(String message, Throwable cause) {
+      super(message, cause);
+    }
+  }
+
+  static class DefaultHttpTransportFactory implements HttpTransportFactory {
+    public HttpTransport create() {
+      return HTTP_TRANSPORT;
+    }
   }
 }
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/test/java/com/google/api/client/auth/openidconnect/IdTokenVerifierTest.java
===================================================================
--- google-oauth-java-client-1.22.0.orig/google-oauth-client/src/test/java/com/google/api/client/auth/openidconnect/IdTokenVerifierTest.java
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/test/java/com/google/api/client/auth/openidconnect/IdTokenVerifierTest.java
@@ -15,15 +15,30 @@
 package com.google.api.client.auth.openidconnect;
 
 import com.google.api.client.auth.openidconnect.IdToken.Payload;
+import com.google.api.client.auth.openidconnect.IdTokenVerifier.VerificationException;
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.http.LowLevelHttpRequest;
+import com.google.api.client.http.LowLevelHttpResponse;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.client.json.JsonFactory;
+import com.google.api.client.json.gson.GsonFactory;
 import com.google.api.client.json.webtoken.JsonWebSignature.Header;
+import com.google.api.client.testing.http.MockHttpTransport;
+import com.google.api.client.testing.http.MockLowLevelHttpRequest;
+import com.google.api.client.testing.http.MockLowLevelHttpResponse;
 import com.google.api.client.util.Clock;
 import com.google.api.client.util.Lists;
-
-import junit.framework.TestCase;
-
+import com.google.common.io.CharStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import junit.framework.TestCase;
 
 /**
  * Tests {@link IdTokenVerifier}.
@@ -41,6 +56,25 @@ public class IdTokenVerifierTest extends
   private static final String ISSUER2 = ISSUER + "2";
   private static final String ISSUER3 = ISSUER + "3";
 
+  private static final String ES256_TOKEN =
+      "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im1wZjBEQSJ9.eyJhdWQiOiIvcHJvamVjdHMvNjUyNTYyNzc2Nzk4L2FwcHMvY2xvdWQtc2FtcGxlcy10ZXN0cy1waHAtaWFwIiwiZW1haWwiOiJjaGluZ29yQGdvb2dsZS5jb20iLCJleHAiOjE1ODQwNDc2MTcsImdvb2dsZSI6eyJhY2Nlc3NfbGV2ZWxzIjpbImFjY2Vzc1BvbGljaWVzLzUxODU1MTI4MDkyNC9hY2Nlc3NMZXZlbHMvcmVjZW50U2VjdXJlQ29ubmVjdERhdGEiLCJhY2Nlc3NQb2xpY2llcy81MTg1NTEyODA5MjQvYWNjZXNzTGV2ZWxzL3Rlc3ROb09wIiwiYWNjZXNzUG9saWNpZXMvNTE4NTUxMjgwOTI0L2FjY2Vzc0xldmVscy9ldmFwb3JhdGlvblFhRGF0YUZ1bGx5VHJ1c3RlZCJdfSwiaGQiOiJnb29nbGUuY29tIiwiaWF0IjoxNTg0MDQ3MDE3LCJpc3MiOiJodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vaWFwIiwic3ViIjoiYWNjb3VudHMuZ29vZ2xlLmNvbToxMTIxODE3MTI3NzEyMDE5NzI4OTEifQ.yKNtdFY5EKkRboYNexBdfugzLhC3VuGyFcuFYA8kgpxMqfyxa41zkML68hYKrWu2kOBTUW95UnbGpsIi_u1fiA";
+
+  private static final String FEDERATED_SIGNON_RS256_TOKEN =
+      "eyJhbGciOiJSUzI1NiIsImtpZCI6ImY5ZDk3YjRjYWU5MGJjZDc2YWViMjAwMjZmNmI3NzBjYWMyMjE3ODMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2V4YW1wbGUuY29tL3BhdGgiLCJhenAiOiJpbnRlZ3JhdGlvbi10ZXN0c0BjaGluZ29yLXRlc3QuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJlbWFpbCI6ImludGVncmF0aW9uLXRlc3RzQGNoaW5nb3ItdGVzdC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1ODc2Mjk4ODgsImlhdCI6MTU4NzYyNjI4OCwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTA0MDI5MjkyODUzMDk5OTc4MjkzIn0.Pj4KsJh7riU7ZIbPMcHcHWhasWEcbVjGP4yx_5E0iOpeDalTdri97E-o0dSSkuVX2FeBIgGUg_TNNgJ3YY97T737jT5DUYwdv6M51dDlLmmNqlu_P6toGCSRC8-Beu5gGmqS2Y82TmpHH9Vhoh5PsK7_rVHk8U6VrrVVKKTWm_IzTFhqX1oYKPdvfyaNLsXPbCt_NFE0C3DNmFkgVhRJu7LtzQQN-ghaqd3Ga3i6KH222OEI_PU4BUTvEiNOqRGoMlT_YOsyFN3XwqQ6jQGWhhkArL1z3CG2BVQjHTKpgVsRyy_H6WTZiju2Q-XWobgH-UPSZbyymV8-cFT9XKEtZQ";
+  private static final String LEGACY_FEDERATED_SIGNON_CERT_URL =
+      "https://www.googleapis.com/oauth2/v1/certs";
+
+  private static final String SERVICE_ACCOUNT_RS256_TOKEN =
+      "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJlZjc3YjM4YTFiMDM3MDQ4NzA0MzkxNmFjYmYyN2Q3NGVkZDA4YjEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2V4YW1wbGUuY29tL2F1ZGllbmNlIiwiZXhwIjoxNTg3NjMwNTQzLCJpYXQiOjE1ODc2MjY5NDMsImlzcyI6InNvbWUgaXNzdWVyIiwic3ViIjoic29tZSBzdWJqZWN0In0.gGOQW0qQgs4jGUmCsgRV83RqsJLaEy89-ZOG6p1u0Y26FyY06b6Odgd7xXLsSTiiSnch62dl0Lfi9D0x2ByxvsGOCbovmBl2ZZ0zHr1wpc4N0XS9lMUq5RJQbonDibxXG4nC2zroDfvD0h7i-L8KMXeJb9pYwW7LkmrM_YwYfJnWnZ4bpcsDjojmPeUBlACg7tjjOgBFbyQZvUtaERJwSRlaWibvNjof7eCVfZChE0PwBpZc_cGqSqKXv544L4ttqdCnmONjqrTATXwC4gYxruevkjHfYI5ojcQmXoWDJJ0-_jzfyPE4MFFdCFgzLgnfIOwe5ve0MtquKuv2O0pgvg";
+  private static final String SERVICE_ACCOUNT_CERT_URL =
+      "https://www.googleapis.com/robot/v1/metadata/x509/integration-tests%40chingor-test.iam.gserviceaccount.com";
+
+  private static final List<String> ALL_TOKENS =
+      Arrays.asList(ES256_TOKEN, FEDERATED_SIGNON_RS256_TOKEN, SERVICE_ACCOUNT_RS256_TOKEN);
+
+  static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
+  static final MockClock FIXED_CLOCK = new MockClock(1584047020000L);
+
   private static IdToken newIdToken(String issuer, String audience) {
     Payload payload = new Payload();
     payload.setIssuer(issuer);
@@ -56,8 +90,8 @@ public class IdTokenVerifierTest extends
     assertEquals(Clock.SYSTEM, builder.getClock());
     assertEquals(ISSUER, builder.getIssuer());
     assertEquals(Collections.singleton(ISSUER), builder.getIssuers());
-    assertTrue(TRUSTED_CLIENT_IDS.equals(builder.getAudience()));
-    Clock clock = new MyClock();
+    assertEquals(TRUSTED_CLIENT_IDS, builder.getAudience());
+    Clock clock = new MockClock();
     builder.setClock(clock);
     assertEquals(clock, builder.getClock());
     IdTokenVerifier verifier = builder.build();
@@ -67,22 +101,22 @@ public class IdTokenVerifierTest extends
     assertEquals(TRUSTED_CLIENT_IDS, Lists.newArrayList(verifier.getAudience()));
   }
 
-  static class MyClock implements Clock {
-
-    long timeMillis;
-
-    public long currentTimeMillis() {
-      return timeMillis;
-    }
-  }
-
   public void testVerify() throws Exception {
-    MyClock clock = new MyClock();
-    IdTokenVerifier verifier = new IdTokenVerifier.Builder()
-        .setIssuers(Arrays.asList(ISSUER, ISSUER3))
-        .setAudience(Arrays.asList(CLIENT_ID)).setClock(clock).build();
+    MockClock clock = new MockClock();
+    MockEnvironment testEnvironment = new MockEnvironment();
+    testEnvironment.setVariable(IdTokenVerifier.SKIP_SIGNATURE_ENV_VAR, "true");
+    IdTokenVerifier verifier =
+        new IdTokenVerifier.Builder()
+            .setIssuers(Arrays.asList(ISSUER, ISSUER3))
+            .setAudience(Arrays.asList(CLIENT_ID))
+            .setClock(clock)
+            .setEnvironment(testEnvironment)
+            .build();
+
     // verifier flexible doesn't check issuer and audience
-    IdTokenVerifier verifierFlexible = new IdTokenVerifier.Builder().setClock(clock).build();
+    IdTokenVerifier verifierFlexible =
+        new IdTokenVerifier.Builder().setClock(clock).setEnvironment(testEnvironment).build();
+
     // issuer
     clock.timeMillis = 1500000L;
     IdToken idToken = newIdToken(ISSUER, CLIENT_ID);
@@ -136,4 +170,165 @@ public class IdTokenVerifierTest extends
     assertNull(verifier.getIssuers());
     assertNull(verifier.getIssuer());
   }
+
+  public void testVerifyEs256TokenPublicKeyMismatch() throws Exception {
+    // Mock HTTP requests
+    HttpTransportFactory httpTransportFactory =
+        new HttpTransportFactory() {
+          @Override
+          public HttpTransport create() {
+            return new MockHttpTransport() {
+              @Override
+              public LowLevelHttpRequest buildRequest(String method, String url)
+                  throws IOException {
+                return new MockLowLevelHttpRequest() {
+                  @Override
+                  public LowLevelHttpResponse execute() throws IOException {
+                    MockLowLevelHttpResponse response = new MockLowLevelHttpResponse();
+                    response.setStatusCode(200);
+                    response.setContentType("application/json");
+                    response.setContent("");
+                    return response;
+                  }
+                };
+              }
+            };
+          }
+        };
+    IdTokenVerifier tokenVerifier =
+        new IdTokenVerifier.Builder()
+            .setClock(FIXED_CLOCK)
+            .setHttpTransportFactory(httpTransportFactory)
+            .build();
+
+    try {
+      tokenVerifier.verifySignature(IdToken.parse(JSON_FACTORY, ES256_TOKEN));
+      fail("Should have failed verification");
+    } catch (VerificationException ex) {
+      assertTrue(ex.getMessage().contains("Error fetching PublicKey"));
+    }
+  }
+
+  public void testVerifyEs256Token() throws VerificationException, IOException {
+    HttpTransportFactory httpTransportFactory =
+        mockTransport(
+            "https://www.gstatic.com/iap/verify/public_key-jwk",
+            readResourceAsString("iap_keys.json"));
+    IdTokenVerifier tokenVerifier =
+        new IdTokenVerifier.Builder()
+            .setClock(FIXED_CLOCK)
+            .setHttpTransportFactory(httpTransportFactory)
+            .build();
+    assertTrue(tokenVerifier.verify(IdToken.parse(JSON_FACTORY, ES256_TOKEN)));
+  }
+
+  public void testVerifyRs256Token() throws VerificationException, IOException {
+    HttpTransportFactory httpTransportFactory =
+        mockTransport(
+            "https://www.googleapis.com/oauth2/v3/certs",
+            readResourceAsString("federated_keys.json"));
+    MockClock clock = new MockClock(1587625988000L);
+    IdTokenVerifier tokenVerifier =
+        new IdTokenVerifier.Builder()
+            .setClock(clock)
+            .setHttpTransportFactory(httpTransportFactory)
+            .build();
+    assertTrue(tokenVerifier.verify(IdToken.parse(JSON_FACTORY, FEDERATED_SIGNON_RS256_TOKEN)));
+  }
+
+  public void testVerifyRs256TokenWithLegacyCertificateUrlFormat()
+      throws VerificationException, IOException {
+    HttpTransportFactory httpTransportFactory =
+        mockTransport(
+            LEGACY_FEDERATED_SIGNON_CERT_URL, readResourceAsString("legacy_federated_keys.json"));
+    MockClock clock = new MockClock(1587626288000L);
+    IdTokenVerifier tokenVerifier =
+        new IdTokenVerifier.Builder()
+            .setCertificatesLocation(LEGACY_FEDERATED_SIGNON_CERT_URL)
+            .setClock(clock)
+            .setHttpTransportFactory(httpTransportFactory)
+            .build();
+    assertTrue(tokenVerifier.verify(IdToken.parse(JSON_FACTORY, FEDERATED_SIGNON_RS256_TOKEN)));
+  }
+
+  public void testVerifyServiceAccountRs256Token() throws VerificationException, IOException {
+    MockClock clock = new MockClock(1587626643000L);
+    IdTokenVerifier tokenVerifier =
+        new IdTokenVerifier.Builder()
+            .setClock(clock)
+            .setCertificatesLocation(SERVICE_ACCOUNT_CERT_URL)
+            .setHttpTransportFactory(new DefaultHttpTransportFactory())
+            .build();
+    assertTrue(tokenVerifier.verify(IdToken.parse(JSON_FACTORY, SERVICE_ACCOUNT_RS256_TOKEN)));
+  }
+
+  static String readResourceAsString(String resourceName) throws IOException {
+    InputStream inputStream =
+        IdTokenVerifierTest.class.getClassLoader().getResourceAsStream(resourceName);
+    try (final Reader reader = new InputStreamReader(inputStream)) {
+      return CharStreams.toString(reader);
+    }
+  }
+
+  static HttpTransportFactory mockTransport(String url, String certificates) {
+    final String certificatesContent = certificates;
+    final String certificatesUrl = url;
+    return new HttpTransportFactory() {
+      @Override
+      public HttpTransport create() {
+        return new MockHttpTransport() {
+          @Override
+          public LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
+            assertEquals(certificatesUrl, url);
+            return new MockLowLevelHttpRequest() {
+              @Override
+              public LowLevelHttpResponse execute() throws IOException {
+                MockLowLevelHttpResponse response = new MockLowLevelHttpResponse();
+                response.setStatusCode(200);
+                response.setContentType("application/json");
+                response.setContent(certificatesContent);
+                return response;
+              }
+            };
+          }
+        };
+      }
+    };
+  }
+
+  /** A mock implementation of {@link Clock} to set clock for testing */
+  static class MockClock implements Clock {
+    public MockClock() {}
+
+    public MockClock(long timeMillis) {
+      this.timeMillis = timeMillis;
+    }
+
+    long timeMillis;
+
+    public long currentTimeMillis() {
+      return timeMillis;
+    }
+  }
+
+  /** A default http transport factory for testing */
+  static class DefaultHttpTransportFactory implements HttpTransportFactory {
+    public HttpTransport create() {
+      return new NetHttpTransport();
+    }
+  }
+
+  /** A mock implementation of {@link Environment} to set environment variables for testing */
+  class MockEnvironment extends Environment {
+    private final Map<String, String> variables = new HashMap<>();
+
+    @Override
+    public String getVariable(String name) {
+      return variables.get(name);
+    }
+
+    public void setVariable(String name, String value) {
+      variables.put(name, value);
+    }
+  }
 }
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/aws_security_credentials.json
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/aws_security_credentials.json
@@ -0,0 +1,9 @@
+{
+  "Code" : "Success",
+  "LastUpdated" : "2020-08-11T19:33:07Z",
+  "Type" : "AWS-HMAC",
+  "AccessKeyId" : "ASIARD4OQDT6A77FR3CL",
+  "SecretAccessKey" : "Y8AfSaucF37G4PpvfguKZ3/l7Id4uocLXxX0+VTx",
+  "Token" : "IQoJb3JpZ2luX2VjEIz//////////wEaCXVzLWVhc3QtMiJGMEQCIH7MHX/Oy/OB8OlLQa9GrqU1B914+iMikqWQW7vPCKlgAiA/Lsv8Jcafn14owfxXn95FURZNKaaphj0ykpmS+Ki+CSq0AwhlEAAaDDA3NzA3MTM5MTk5NiIMx9sAeP1ovlMTMKLjKpEDwuJQg41/QUKx0laTZYjPlQvjwSqS3OB9P1KAXPWSLkliVMMqaHqelvMF/WO/glv3KwuTfQsavRNs3v5pcSEm4SPO3l7mCs7KrQUHwGP0neZhIKxEXy+Ls//1C/Bqt53NL+LSbaGv6RPHaX82laz2qElphg95aVLdYgIFY6JWV5fzyjgnhz0DQmy62/Vi8pNcM2/VnxeCQ8CC8dRDSt52ry2v+nc77vstuI9xV5k8mPtnaPoJDRANh0bjwY5Sdwkbp+mGRUJBAQRlNgHUJusefXQgVKBCiyJY4w3Csd8Bgj9IyDV+Azuy1jQqfFZWgP68LSz5bURyIjlWDQunO82stZ0BgplKKAa/KJHBPCp8Qi6i99uy7qh76FQAqgVTsnDuU6fGpHDcsDSGoCls2HgZjZFPeOj8mmRhFk1Xqvkbjuz8V1cJk54d3gIJvQt8gD2D6yJQZecnuGWd5K2e2HohvCc8Fc9kBl1300nUJPV+k4tr/A5R/0QfEKOZL1/k5lf1g9CREnrM8LVkGxCgdYMxLQow1uTL+QU67AHRRSp5PhhGX4Rek+01vdYSnJCMaPhSEgcLqDlQkhk6MPsyT91QMXcWmyO+cAZwUPwnRamFepuP4K8k2KVXs/LIJHLELwAZ0ekyaS7CptgOqS7uaSTFG3U+vzFZLEnGvWQ7y9IPNQZ+Dffgh4p3vF4J68y9049sI6Sr5d5wbKkcbm8hdCDHZcv4lnqohquPirLiFQ3q7B17V9krMPu3mz1cg4Ekgcrn/E09NTsxAqD8NcZ7C7ECom9r+X3zkDOxaajW6hu3Az8hGlyylDaMiFfRbBJpTIlxp7jfa7CxikNgNtEKLH9iCzvuSg2vhA==",
+  "Expiration" : "2020-08-11T07:35:49Z"
+}
\ No newline at end of file
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/client_secret.json
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/client_secret.json
@@ -0,0 +1,16 @@
+{
+  "web": {
+    "client_id":"ya29.1.AADtN_UtlxN3PuGAxrN2XQnZTVRvDyVWnYq4I6dws",
+    "auth_uri":"https://accounts.google.com/o/oauth2/auth",
+    "token_uri":"https://accounts.google.com/o/oauth2/token",
+    "auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
+    "client_secret":"jakuaL9YyieakhECKL2SwZcu",
+    "redirect_uris":[
+      "http://example.appspot.com/oauth2callback",
+      "http://localhost:8080/oauth2callback"
+    ],
+    "javascript_origins":[
+      "https://www.example.com"
+    ]
+  }
+}
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/federated_keys.json
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/federated_keys.json
@@ -0,0 +1,20 @@
+{
+  "keys": [
+    {
+      "kid": "f9d97b4cae90bcd76aeb20026f6b770cac221783",
+      "e": "AQAB",
+      "kty": "RSA",
+      "alg": "RS256",
+      "n": "ya_7gVJrvqFp5xfYPOco8gBLY38kQDlTlT6ueHtUtbTkRVE1X5tFmPqChnX7wWd2fK7MS4-nclYaGLL7IvJtN9tjrD0h_3_HvnrRZTaVyS-yfWqCQDRq_0VW1LBEygwYRqbO2T0lOocTY-5qUosDvJfe-o-lQYMH7qtDAyiq9XprVzKYTfS545BTECXi0he9ikJl5Q_RAP1BZoaip8F0xX5Y_60G90VyXFWuy16nm5ASW8fwqzdn1lL_ogiO1LirgBFFEXz_t4PwmjWzfQwkoKv4Ab_l9u2FdAoKtFH2CwKaGB8hatIK3bOAJJgRebeU3w6Ah3gxRfi8HWPHbAGjtw",
+      "use": "sig"
+    },
+    {
+      "kid": "28b741e8de984a47159f19e6d7783e9d4fa810db",
+      "e": "AQAB",
+      "kty": "RSA",
+      "alg": "RS256",
+      "n": "zc4ELn-9nLzCZb4PdXGVhtUtzwmQI8HZH8tOIEg9omx6CW-PZ5xtVQ5O5EBG2AA5_K-aOWvVEWyfeHe8WwZltM1cXu6QNdXbpVVYeZ0th9hm7ZflNz7h1PMM9lNXLJjokax5gxGskc8CsjhkwurEot1TD2zbGIQsOYoebQTvJ2AYxIjk77BU20nLplurge8jrK-V1G3zJlp0xIKqxjsfIFYm1Mp-HQhJzdMbjNEScs0dDT4rPxdA-wOVGix0wrPdIE1gM4GxZ7AlSZ7IcjuYMZIe6d6oAeKG0FG0avbtipAQglxTHM3UOge6PmThr_mmiI82oLqGutul-XYgy1S2NQ",
+      "use": "sig"
+    }
+  ]
+}
\ No newline at end of file
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/iap_keys.json
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/iap_keys.json
@@ -0,0 +1,49 @@
+{
+  "keys" : [
+    {
+      "alg" : "ES256",
+      "crv" : "P-256",
+      "kid" : "2nMJtw",
+      "kty" : "EC",
+      "use" : "sig",
+      "x" : "9e1x7YRZg53A5zIJ0p2ZQ9yTrgPLGIf4ntOk-4O2R28",
+      "y" : "q8iDm7nsnpz1xPdrWBtTZSowzciS3O7bMYtFFJ8saYo"
+    },
+    {
+      "alg" : "ES256",
+      "crv" : "P-256",
+      "kid" : "LYyP2g",
+      "kty" : "EC",
+      "use" : "sig",
+      "x" : "SlXFFkJ3JxMsXyXNrqzE3ozl_0913PmNbccLLWfeQFU",
+      "y" : "GLSahrZfBErmMUcHP0MGaeVnJdBwquhrhQ8eP05NfCI"
+    },
+    {
+      "alg" : "ES256",
+      "crv" : "P-256",
+      "kid" : "mpf0DA",
+      "kty" : "EC",
+      "use" : "sig",
+      "x" : "fHEdeT3a6KaC1kbwov73ZwB_SiUHEyKQwUUtMCEn0aI",
+      "y" : "QWOjwPhInNuPlqjxLQyhveXpWqOFcQPhZ3t-koMNbZI"
+    },
+    {
+      "alg" : "ES256",
+      "crv" : "P-256",
+      "kid" : "b9vTLA",
+      "kty" : "EC",
+      "use" : "sig",
+      "x" : "qCByTAvci-jRAD7uQSEhTdOs8iA714IbcY2L--YzynI",
+      "y" : "WQY0uCoQyPSozWKGQ0anmFeOH5JNXiZa9i6SNqOcm7w"
+    },
+    {
+      "alg" : "ES256",
+      "crv" : "P-256",
+      "kid" : "0oeLcQ",
+      "kty" : "EC",
+      "use" : "sig",
+      "x" : "MdhRXGEoGJLtBjQEIjnYLPkeci9rXnca2TffkI0Kac0",
+      "y" : "9BoREHfX7g5OK8ELpA_4RcOnFCGSjfR4SGZpBo7juEY"
+    }
+  ]
+}
\ No newline at end of file
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/legacy_federated_keys.json
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/legacy_federated_keys.json
@@ -0,0 +1,4 @@
+{
+  "f9d97b4cae90bcd76aeb20026f6b770cac221783": "-----BEGIN CERTIFICATE-----\nMIIDJjCCAg6gAwIBAgIILRTfnfU3e2gwDQYJKoZIhvcNAQEFBQAwNjE0MDIGA1UE\nAxMrZmVkZXJhdGVkLXNpZ25vbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTAe\nFw0yMDA0MTQwNDI5MzBaFw0yMDA0MzAxNjQ0MzBaMDYxNDAyBgNVBAMTK2ZlZGVy\nYXRlZC1zaWdub24uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wggEiMA0GCSqG\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJr/uBUmu+oWnnF9g85yjyAEtjfyRAOVOV\nPq54e1S1tORFUTVfm0WY+oKGdfvBZ3Z8rsxLj6dyVhoYsvsi8m0322OsPSH/f8e+\netFlNpXJL7J9aoJANGr/RVbUsETKDBhGps7ZPSU6hxNj7mpSiwO8l976j6VBgwfu\nq0MDKKr1emtXMphN9LnjkFMQJeLSF72KQmXlD9EA/UFmhqKnwXTFflj/rQb3RXJc\nVa7LXqebkBJbx/CrN2fWUv+iCI7UuKuAEUURfP+3g/CaNbN9DCSgq/gBv+X27YV0\nCgq0UfYLApoYHyFq0grds4AkmBF5t5TfDoCHeDFF+LwdY8dsAaO3AgMBAAGjODA2\nMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsG\nAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4IBAQA1Wrx3XsIAAYOaycAkV2mZW1j+Vqxx\nSAeUyuhLoaJ7jntd7LqTuTr+qRnR/fH/CjTbPzngvCyVE6hjClh159YRpf4TJ4aL\nMJ97qDxc/f/pM/7yklIaHHOwqYU10plIyw+m0dnQutPqy1o/aDUytDznNmM6L3v+\ncot2bxyd2PtjGfa1hPNNnEnrZfS2Gc0qqR64RUWbsdLVVQB8MKcaNUqjk9o/1O4p\nNNk2D2VcofdaLPpwSmtzV8wEd4vfzI17qFSPi6gbTfydvxkejk0kdSyWUPw+1YC4\nv2o2rzwXub9hcP2zXyZvTGKPMAkZ8VKuzWuvfuSsTtgcPJ20GpIkin/j\n-----END CERTIFICATE-----\n",
+  "28b741e8de984a47159f19e6d7783e9d4fa810db": "-----BEGIN CERTIFICATE-----\nMIIDJjCCAg6gAwIBAgIIcog+uwMaMb8wDQYJKoZIhvcNAQEFBQAwNjE0MDIGA1UE\nAxMrZmVkZXJhdGVkLXNpZ25vbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTAe\nFw0yMDA0MjIwNDI5MzBaFw0yMDA1MDgxNjQ0MzBaMDYxNDAyBgNVBAMTK2ZlZGVy\nYXRlZC1zaWdub24uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wggEiMA0GCSqG\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNzgQuf72cvMJlvg91cZWG1S3PCZAjwdkf\ny04gSD2ibHoJb49nnG1VDk7kQEbYADn8r5o5a9URbJ94d7xbBmW0zVxe7pA11dul\nVVh5nS2H2Gbtl+U3PuHU8wz2U1csmOiRrHmDEayRzwKyOGTC6sSi3VMPbNsYhCw5\nih5tBO8nYBjEiOTvsFTbScumW6uB7yOsr5XUbfMmWnTEgqrGOx8gVibUyn4dCEnN\n0xuM0RJyzR0NPis/F0D7A5UaLHTCs90gTWAzgbFnsCVJnshyO5gxkh7p3qgB4obQ\nUbRq9u2KkBCCXFMczdQ6B7o+ZOGv+aaIjzaguoa626X5diDLVLY1AgMBAAGjODA2\nMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsG\nAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4IBAQBEfCN7qgI2GJJAC99PDbafqC1EMBlv\nBT/7UiQdTuDV04+cQH9IpzROW7IZc/ILcqpF6KXUmj6j0sWO+hxKFY66TJKPcypK\n/ZMI58epwwVgGZyYU0BbAIZ9uvOgDfuveMildlMDMg1cJNp7WjBrEJ2DcGfC56wJ\nuKvqB1upxnfh+Ceg3ApU50k6Ld6+dbDDR0Vzt/wGZlZZ5Uj6AwDFe+5p9zEpWg61\nHeny/tSBfgZ19vP2h3ye9ZTK1OFRMNufj8iSzmlkbSqWuy82XVSBRKy5QslqXsYe\nU3gM3EVvXHA/Of3sROFpvznCXNr+Kn03wTv0ny6rnSgHQUzj7p9fydXY\n-----END CERTIFICATE-----\n"
+}
\ No newline at end of file
Index: google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/service_account_keys.json
===================================================================
--- /dev/null
+++ google-oauth-java-client-1.22.0/google-oauth-client/src/test/resources/service_account_keys.json
@@ -0,0 +1,4 @@
+{
+  "a8611b6a9c0a0a8b940d0f915c326fd1605c8ac6": "-----BEGIN CERTIFICATE-----\nMIIDPDCCAiSgAwIBAgIIFJsPvyc/ZSUwDQYJKoZIhvcNAQEFBQAwQTE/MD0GA1UE\nAxM2aW50ZWdyYXRpb24tdGVzdHMuY2hpbmdvci10ZXN0LmlhbS5nc2VydmljZWFj\nY291bnQuY29tMB4XDTIwMDQwMjIyMjIxN1oXDTIyMDUwMTEzNTYxNVowQTE/MD0G\nA1UEAxM2aW50ZWdyYXRpb24tdGVzdHMuY2hpbmdvci10ZXN0LmlhbS5nc2Vydmlj\nZWFjY291bnQuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Yys\nP5LIa1rRxQY93FXIJDzq6Tai4VuetffJbltRtYbdwC5Vyl99O2zoVdRlg+iYXK5B\nb6kidjmWOf0kNimQ5FwYvu+xsm6w8vjL/XShkHEKiURszyCua8wvLeGVCiGBg/XU\nDOgYMjzRIH5fTuj3PTZk4sMj02ZCpCQEMQ6ogpLXjaLp3ZXtFhkuHyCxVYbTRr+k\nGU86JAg4XwD6AdC349v+8FEQD7YtJezUAAKEgXh9e5UeL5CpOo3Vsdv/yEVo00jh\nYuWzLM6Oxt55WAhiD29vKrm7VQPSr1XwwqpdyFL2BlmqyTlb3amwvc9qv2kojGvM\nSUqgS83dc0jFqtMvEQIDAQABozgwNjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQE\nAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEA\nm3XUMKOtXdpw0oRjykLHdYzTHHFjQTMmhWD172rsxSzwpkFoErAC7bnmEvpcRU7D\nr4M+pE5VuDJ64J3lEpAh7W0zMXezPtGyWr39hVxL3vp3nh4CbCzzkUCfFvBOFFhm\nOI9qnjtMtaozoGi5zLs5jEaFmgR3wfij9KQjNGZJxAg0ZkwcSNb76qOCG1/vG5au\n4UuoIaq8WqSxMqBF/g+NrAE2PZhjNGnUwFPTre3SyR0otYDzJfmpL/tp5VDie8hM\nL5UZU/CmZk46+T9VbvnZ5mkPAjGiPumiptO5iliBOHPtPdn8VrP+aSQM1btHA094\n1HwfbFp7pZHBUn9COAP/1Q==\n-----END CERTIFICATE-----\n",
+  "2ef77b38a1b0370487043916acbf27d74edd08b1": "-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIIIwRR4+AftjswDQYJKoZIhvcNAQEFBQAwIDEeMBwGA1UE\nAxMVMTA0MDI5MjkyODUzMDk5OTc4MjkzMB4XDTIwMDMwNDA1NTIyMloXDTMwMDMw\nMjA1NTIyMlowIDEeMBwGA1UEAxMVMTA0MDI5MjkyODUzMDk5OTc4MjkzMIIBIjAN\nBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm4jAbNdDEDkG/36wP07lFKMTAnWy\nhtV/Vp4QFSIE456moU/HEmBwJX2wocPgvoxPat7FxUv7mwdgHq7+sczis4DrDIIY\n8XfZ+D98+X+rOfkS1WLXpO76REZE4JCUfkB3NKVMP0kfoCFPf2pafz1NJRrZczUw\nbSi/q1+KYHmbk8YS+Q7Iq7gW9dvQtWrsRH8dQIrToJfGH+rbSQyKUFN7skFOflw4\n/OSuT0wvD6z57JcRFtAD3zgeUuCPNRIbkPQC3vCLwWGLKSYWLJ3eM9PPW9bk+czf\nSxJOie7zRMToh4BchLO6ZQgshoEaBHbwdOTu8455skqlRJMU9SKwA6eqVQIDAQAB\nozgwNjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAK\nBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEAXvt8M2GFK+5UHG0GclIqse8j\n+EgqXvDTkbeTxFP+onbA/RwKM+fzdYpDwrH1dQ6jJervmceewUTTMegfFzhF33GC\nxvjQfhs+yVOXQiBHosd93CgR19dMUYs/r1wuUpwqBGdW2S81ns3yreY72BHrikrl\nHNLD3aSJ6hq5CZ01EFpjTW10ndBdPhJRSWD2g8VI1lpd716HEmrXfPHX73KVkk5/\nWfvrMA1UK/Ag+TWQerKG3iQFUAPIUiyepdaG4uFWTBY9nzLPiC1cx3bVPVZ+5yul\nJN15hmAMd3qPgSbbeQ6JC72zXCfW3buBE2n9cGtRbZF1URJZ3NbvwRS5BD425g==\n-----END CERTIFICATE-----\n"
+}
\ No newline at end of file
openSUSE Build Service is sponsored by