1

I use following file to encounter the issue

https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/appengine-java8/appidentity/src/main/java/com/example/appengine/appidentity/SignForAppServlet.java

Write method test to generate signed JWT using app engine default service account

private String test() throws CertificateException, UnsupportedEncodingException, 
    NoSuchAlgorithmException, InvalidKeyException, SignatureException {

           long now = System.currentTimeMillis() / 1000;

           JSONObject headerJson = new JSONObject();
           headerJson.put("typ", "JWT");
           headerJson.put("alg", "RS256");

           JSONObject payloadJson = new JSONObject();
           payloadJson.put("iat", now);
           payloadJson.put("exp", now + 3600);
           payloadJson.put("iss", "{test-project}@appspot.gserviceaccount.com");
           payloadJson.put("sub", "{test-project}@appspot.gserviceaccount.com");
           payloadJson.put("aud", "https://echo-api.endpoints.{test-project}.cloud.goog");

          String headerAndPayload = String.format("%s.%s", Base64.getUrlEncoder().encodeToString(headerJson.toString().getBytes()), Base64.getUrlEncoder().encodeToString(payloadJson.toString().getBytes()));

          AppIdentityService appIdentityService = AppIdentityServiceFactory.getAppIdentityService();
          AppIdentityService.SigningResult signingResult = appIdentityService.signForApp(headerAndPayload.getBytes());

          String signedJwt = String.format("%s.%s", headerAndPayload , new 
          String(Base64.getUrlEncoder().encode(signingResult.getSignature())));

          return signedJwt;
}

I need generate signed JWT to authenticate java backend running in app engine. API secured using open api cloud end points. Following is my openapi.yaml

  swagger: "2.0"
       info:
               description: "A simple Google Cloud Endpoints API example."
       title: "Endpoints Example"
       version: "1.0.0"
       host: "echo-api.endpoints.{test-project}.cloud.goog"

consumes:
            - "application/json"
produces:
            - "application/json"
schemes:
          - "https"

 paths:
         "/test/echo":
          post:
                description: "Echo back a given message."
                operationId: "echo"
          produces:
                  - "application/json"
          responses:
                  200:
          description: "Echo"
          schema:
                   $ref: "#/definitions/echoMessage"
          parameters:
           -
                   description: "Message to echo"
                   in: body
                   name: message
                   required: true
                   schema:
                           $ref: "#/definitions/echoMessage"
                   security:
                          - api_key: []
                            google_jwt: []

       definitions:
                  echoMessage:
                        type: "object"
                        properties:
                        message:
                        type: "string"

       securityDefinitions:
              google_jwt:
              authorizationUrl: ""
              flow: "implicit"
              type: "oauth2"
              x-google-issuer: "stl-cardio-dev@appspot.gserviceaccount.com"
              x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/jwk/{test-project}@appspot.gserviceaccount.com"

After creating JWT I tried to access /test/echo it given "JWT validation failed: BAD_SIGNATURE".

I tried to python client described in

https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/getting-started/clients/service_to_service_gae_default/main.py

I use following "header and payload" and "sign method" , I received following results.

(01). python header_and_payload + python app_identity.sign_blob method => Success

(02). python header_and_payload + java appIdentity.signForApp() => Error

(03). java headerAndPayload + python app_identity.sign_blob method => Success

(04). java headerAndPayload + java appIdentity.signForApp() => Error

I see problem in appIdentity.signForApp() result in my java implementation.

I cannot find complete example or documentation. How I generate correct signed JWT using java.

Thank you.

Nuwan Sameera
  • 739
  • 1
  • 8
  • 25
  • Have you tried calling getBytes with a charset of utf-8 instead, when Base64 encoding? – saiyr Jun 25 '18 at 17:52
  • I tried with it. String headerAndPayload = String.format("%s.%s", Base64.getUrlEncoder().encodeToString(headerJson.toString().getBytes(UTF-8)), Base64.getUrlEncoder().encodeToString(payloadJson.toString().getBytes(UTF-8))); But result is same. – Nuwan Sameera Jun 26 '18 at 03:49
  • Follow the [Troubleshooting JWT Validation](https://cloud.google.com/endpoints/docs/openapi/troubleshoot-jwt-openapi) steps to find out if there is an issue with signed JWT or with your `openapi.yaml`. Narrow down the issue and let me know so we can proceed with solving the issue. – komarkovich Jun 26 '18 at 13:08
  • I follow this and I can confirm problem in signed JWT. Also python agent perfectly working with same openapi.yaml. Documentation says "signingResult.getSignature()" of sign result is "SHA256withRSA" hashed. we need convert this hashed byte array to "Base64 url safe encode" to generate final JWT. I see problem in there. SHA256withRSA -> Base64 or SHA256withRSA -> String required. – Nuwan Sameera Jun 27 '18 at 01:44
  • Try using [this example](https://cloud.google.com/iot/docs/how-tos/credentials/jwts#iot-core-jwt-java) for creating signed JWTs – komarkovich Jun 27 '18 at 14:43
  • Thank you. It is working. But it use key file to sign JWT. Because my service running in google cloud app engine , default app engine account can sign JWT using app identity. This describe in https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/getting-started/clients/service_to_service_gae_default/main.py Only problem in sign part in java. – Nuwan Sameera Jun 28 '18 at 02:42
  • @NuwanSameera I am with Google Cloud support and I can see that you have an ongoing support case opened. I would just ask you to share the resolution (when you get one) here for the benefit of the community. – komarkovich Jul 05 '18 at 14:58

1 Answers1

0

If am not wrong you are looking for Java implementation of JWT.

To generate and Validate a JWT token min requirement is Algorithm and Secret key.

Am using this Java implementation of JWT

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

Class file

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;


...


    public String generateToken(String username) {
        String audience = generateAudience();
        return Jwts.builder()
                .setIssuer( APP_NAME )
                .setSubject(username)
                .setAudience(audience)
                .setIssuedAt(timeProvider.now())
                .setExpiration(generateExpirationDate())
                .signWith( SIGNATURE_ALGORITHM, SECRET )
                .compact();
    }



    private Claims getAllClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            LOGGER.error("Could not get all claims Token from passed token");
            claims = null;
        }
        return claims;
    }

Here generateToken will take all Claim details along with Secret key and Algorithm.

getAllClaimsFromToken is used to read details from token. It basically validates Token. If there is any error will throw exception accordingly

I wrote an answer for this. This is working code. You can refer TokenHelper.java to get more detail

MyTwoCents
  • 7,284
  • 3
  • 24
  • 52
  • Thank you for the reply. I used my own JWT implementation before this. It is perfectly working. Now I moved my back end to google app engine and I need to authenticate using app engine service account and google cloud end points. Also header and payload part is correct, problem in signature part. I need to do it using AppIdentityService. – Nuwan Sameera Jun 24 '18 at 12:59