diff --git a/compute/pom.xml b/compute/pom.xml
index ce278be55f4..becce02d9c5 100644
--- a/compute/pom.xml
+++ b/compute/pom.xml
@@ -34,6 +34,7 @@
error-reportingmailjetsendgrid
+ signed-metadata
diff --git a/compute/signed-metadata/README.md b/compute/signed-metadata/README.md
new file mode 100644
index 00000000000..de4f375efdd
--- /dev/null
+++ b/compute/signed-metadata/README.md
@@ -0,0 +1,39 @@
+# Verify instance identity Java sample for Google Compute Engine
+
+This repository contains example code in Java that downloads and verifies JWT
+provided by metadata endpoint, which is available on Compute Engine instances in
+GCP.
+
+More about this verification can be read on official Google Cloud documentation
+[Veryfying the Identity of
+Instances](https://cloud.google.com/compute/docs/instances/verifying-instance-identity).
+
+## Running on Compute Engine
+
+To run the sample, you will need to do the following:
+
+1. Create a compute instance on the Google Cloud Platform Developer's Console
+1. SSH into the instance you created
+1. Update packages and install required packages
+
+ `sudo apt-get update && sudo apt-get install git-core openjdk-8-jdk maven`
+
+1. Clone the repo
+
+ `git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git`
+
+1. Navigate to `java-docs-samples/compute/signed-metadata/`
+
+ `cd java-docs-samples/compute/signed-metadata/`
+
+1. Use maven to package the class as a jar
+
+ `mvn clean package`
+
+1. Make sure that openjdk 8 is the selected java version
+
+ `sudo update-alternatives --config java`
+
+1. Execute the jar file
+
+ `java -jar target/compute-signed-metadata-1.0-SNAPSHOT-jar-with-dependencies.jar`
diff --git a/compute/signed-metadata/pom.xml b/compute/signed-metadata/pom.xml
new file mode 100644
index 00000000000..26c4b3f5182
--- /dev/null
+++ b/compute/signed-metadata/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+ com.google.cloud.example.jwt
+ compute-signed-metadata
+ 1.0-SNAPSHOT
+ compute-signed-metadata
+
+ com.google.cloud
+ compute
+ 1.0.0
+ ..
+
+
+
+ com.auth0
+ java-jwt
+ 3.2.0
+
+
+ com.google.code.gson
+ gson
+ 2.8.1
+
+
+ com.google.guava
+ guava
+ 23.0
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.2
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.0.0
+
+
+ package
+
+ single
+
+
+
+
+
+
+ com.example.compute.signedmetadata.App
+
+
+
+ jar-with-dependencies
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.6.2
+
+
+ 1.8
+
+
+
+
+
diff --git a/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/App.java b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/App.java
new file mode 100644
index 00000000000..63c42ed1f51
--- /dev/null
+++ b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/App.java
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.example.compute.signedmetadata;
+
+public final class App {
+
+ // AUDIENCE should be set according to your domain and needs.
+ // Please check https://cloud.google.com/compute/docs/instances/verifying-instance-identity
+ // for details.
+ private static final String AUDIENCE = "http://example.com";
+
+ public static void main(String[] args) {
+ // We get token from one instance and verify it trusted machine.
+ String token = new GCPInstance(AUDIENCE).provideToken();
+ new VerifyingInstance(AUDIENCE).verifyToken(token);
+ }
+}
diff --git a/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/GCPInstance.java b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/GCPInstance.java
new file mode 100644
index 00000000000..712c3554cc9
--- /dev/null
+++ b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/GCPInstance.java
@@ -0,0 +1,36 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.example.compute.signedmetadata;
+
+import com.example.compute.signedmetadata.token.TokenDownloader;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+class GCPInstance {
+
+ private String audience;
+
+ GCPInstance(String audience) {
+ this.audience = audience;
+ }
+
+ String provideToken() {
+ try {
+ return new TokenDownloader().getTokenWithAudience(audience);
+ } catch (URISyntaxException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/VerifyingInstance.java b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/VerifyingInstance.java
new file mode 100644
index 00000000000..d03a21cc990
--- /dev/null
+++ b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/VerifyingInstance.java
@@ -0,0 +1,65 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.example.compute.signedmetadata;
+
+import com.auth0.jwt.exceptions.AlgorithmMismatchException;
+import com.auth0.jwt.exceptions.InvalidClaimException;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.exceptions.SignatureVerificationException;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.example.compute.signedmetadata.token.DecodedGoogleJWTWrapper;
+import com.example.compute.signedmetadata.token.TokenVerifier;
+
+class VerifyingInstance {
+
+ private String audience;
+
+ VerifyingInstance(String audience) {
+ this.audience = audience;
+ }
+
+ void verifyToken(String token) {
+ TokenVerifier gtv = new TokenVerifier();
+ // JWTVerificationException is runtime exception, we don't need to catch it if we want to exit
+ // process in case of verification problem. However, to handle verification problems
+ // programmatically we can can JWTVerificationException or specific subclass.
+ // Following are examples how to handle verification failure.
+ try {
+ DecodedGoogleJWTWrapper decodedJWT = gtv.verifyWithAudience(audience, token);
+ System.out.println("Project id : " + decodedJWT.getProjectId());
+ System.out.println("Project number : " + decodedJWT.getProjectNumber());
+ // This are examples how to handle exceptions, which indicate verification failure.
+ } catch (AlgorithmMismatchException e) {
+ // We assume that downloaded certs are RSA256, this exception will happen if this changes.
+ throw e;
+ } catch (SignatureVerificationException e) {
+ // Could not verify signature of a token, possibly someone provided forged token.
+ throw e;
+ } catch (TokenExpiredException e) {
+ // We encountered old token, possibly replay attack.
+ throw e;
+ } catch (InvalidClaimException e) {
+ // Different Audience for token and for verification, possibly token for other verifier.
+ throw e;
+ } catch (JWTVerificationException e) {
+ // Some other problem during verification
+ // JWTVerificationException is super-class to:
+ // - SignatureVerificationException
+ // - TokenExpiredException
+ // - InvalidClaimException
+ throw e;
+ }
+
+ }
+}
diff --git a/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/token/DecodedGoogleJWTWrapper.java b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/token/DecodedGoogleJWTWrapper.java
new file mode 100644
index 00000000000..b19d46461b5
--- /dev/null
+++ b/compute/signed-metadata/src/main/java/com/example/compute/signedmetadata/token/DecodedGoogleJWTWrapper.java
@@ -0,0 +1,73 @@
+// Copyright 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.example.compute.signedmetadata.token;
+
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.google.common.base.Suppliers;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+public class DecodedGoogleJWTWrapper {
+
+ private static final String KEY_PROJECT_ID = "project_id";
+ private static final String KEY_PROJECT_NUMBER = "project_number";
+ private static final String GOOGLE_METADATA_SPACE = "google";
+ private static final String COMPUTE_ENGINE_METADATA_SUBSPACE = "compute_engine";
+
+ private DecodedJWT jwt;
+ private Supplier