• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.testing;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static java.nio.charset.StandardCharsets.UTF_8;
21 import static java.util.concurrent.TimeUnit.SECONDS;
22 import static org.junit.Assert.assertThrows;
23 
24 import com.google.crypto.tink.KeyTemplates;
25 import com.google.crypto.tink.internal.KeyTemplateProtoConverter;
26 import com.google.crypto.tink.jwt.JwtHmacKeyManager;
27 import com.google.crypto.tink.jwt.JwtMacConfig;
28 import com.google.crypto.tink.jwt.JwtSignatureConfig;
29 import com.google.crypto.tink.testing.proto.AnnotatedKeyset;
30 import com.google.crypto.tink.testing.proto.CreationRequest;
31 import com.google.crypto.tink.testing.proto.CreationResponse;
32 import com.google.crypto.tink.testing.proto.JwtClaimValue;
33 import com.google.crypto.tink.testing.proto.JwtFromJwkSetRequest;
34 import com.google.crypto.tink.testing.proto.JwtFromJwkSetResponse;
35 import com.google.crypto.tink.testing.proto.JwtGrpc;
36 import com.google.crypto.tink.testing.proto.JwtSignRequest;
37 import com.google.crypto.tink.testing.proto.JwtSignResponse;
38 import com.google.crypto.tink.testing.proto.JwtToJwkSetRequest;
39 import com.google.crypto.tink.testing.proto.JwtToJwkSetResponse;
40 import com.google.crypto.tink.testing.proto.JwtToken;
41 import com.google.crypto.tink.testing.proto.JwtValidator;
42 import com.google.crypto.tink.testing.proto.JwtVerifyRequest;
43 import com.google.crypto.tink.testing.proto.JwtVerifyResponse;
44 import com.google.crypto.tink.testing.proto.KeysetGenerateRequest;
45 import com.google.crypto.tink.testing.proto.KeysetGenerateResponse;
46 import com.google.crypto.tink.testing.proto.KeysetGrpc;
47 import com.google.crypto.tink.testing.proto.KeysetPublicRequest;
48 import com.google.crypto.tink.testing.proto.KeysetPublicResponse;
49 import com.google.crypto.tink.testing.proto.NullValue;
50 import com.google.protobuf.ByteString;
51 import com.google.protobuf.StringValue;
52 import com.google.protobuf.Timestamp;
53 import io.grpc.ManagedChannel;
54 import io.grpc.Server;
55 import io.grpc.inprocess.InProcessChannelBuilder;
56 import io.grpc.inprocess.InProcessServerBuilder;
57 import org.junit.After;
58 import org.junit.Before;
59 import org.junit.Test;
60 import org.junit.runner.RunWith;
61 import org.junit.runners.JUnit4;
62 
63 @RunWith(JUnit4.class)
64 public final class JwtServiceImplTest {
65   private Server server;
66   private ManagedChannel channel;
67   KeysetGrpc.KeysetBlockingStub keysetStub;
68   JwtGrpc.JwtBlockingStub jwtStub;
69 
70   @Before
setUp()71   public void setUp() throws Exception {
72     JwtMacConfig.register();
73     JwtSignatureConfig.register();
74 
75     String serverName = InProcessServerBuilder.generateName();
76     server =
77         InProcessServerBuilder.forName(serverName)
78             .directExecutor()
79             .addService(new KeysetServiceImpl())
80             .addService(new JwtServiceImpl())
81             .build()
82             .start();
83     channel = InProcessChannelBuilder.forName(serverName).directExecutor().build();
84     keysetStub = KeysetGrpc.newBlockingStub(channel);
85     jwtStub = JwtGrpc.newBlockingStub(channel);
86   }
87 
88   @After
tearDown()89   public void tearDown() throws Exception {
90     assertThat(channel.shutdown().awaitTermination(5, SECONDS)).isTrue();
91     assertThat(server.shutdown().awaitTermination(5, SECONDS)).isTrue();
92   }
93 
generateKeyset( KeysetGrpc.KeysetBlockingStub keysetStub, byte[] template)94   private static KeysetGenerateResponse generateKeyset(
95       KeysetGrpc.KeysetBlockingStub keysetStub, byte[] template) {
96     KeysetGenerateRequest genRequest =
97         KeysetGenerateRequest.newBuilder().setTemplate(ByteString.copyFrom(template)).build();
98     return keysetStub.generate(genRequest);
99   }
100 
publicKeyset( KeysetGrpc.KeysetBlockingStub keysetStub, byte[] privateKeyset)101   private static KeysetPublicResponse publicKeyset(
102       KeysetGrpc.KeysetBlockingStub keysetStub, byte[] privateKeyset) {
103     KeysetPublicRequest request =
104         KeysetPublicRequest.newBuilder()
105             .setPrivateKeyset(ByteString.copyFrom(privateKeyset))
106             .build();
107     return keysetStub.public_(request);
108   }
109 
generateToken(String audience, long expSeconds, int expNanos)110   private JwtToken generateToken(String audience, long expSeconds, int expNanos) {
111     return JwtToken.newBuilder()
112         .setTypeHeader(StringValue.newBuilder().setValue("typeHeader"))
113         .setIssuer(StringValue.newBuilder().setValue("issuer"))
114         .addAudiences(audience)
115         .addAudiences(audience + "2")
116         .setJwtId(StringValue.newBuilder().setValue("123abc"))
117         .putCustomClaims("boolean", JwtClaimValue.newBuilder().setBoolValue(true).build())
118         .putCustomClaims(
119             "null", JwtClaimValue.newBuilder().setNullValue(NullValue.NULL_VALUE).build())
120         .putCustomClaims("number", JwtClaimValue.newBuilder().setNumberValue(123.456).build())
121         .putCustomClaims("string", JwtClaimValue.newBuilder().setStringValue("foo").build())
122         .putCustomClaims(
123             "json_array",
124             JwtClaimValue.newBuilder()
125                 .setJsonArrayValue("[123,\"value\",null,[],{\"a\":42}]")
126                 .build())
127         .putCustomClaims(
128             "json_object",
129             JwtClaimValue.newBuilder().setJsonObjectValue("{\"a\":[null,{\"b\":42}]}").build())
130         .setExpiration(Timestamp.newBuilder().setSeconds(expSeconds).setNanos(expNanos))
131         .build();
132   }
133 
134   @Test
jwtMacCreateKeyset_success()135   public void jwtMacCreateKeyset_success() throws Exception {
136     byte[] template = KeyTemplateProtoConverter.toByteArray(KeyTemplates.get("JWT_HS256"));
137     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
138     assertThat(keysetResponse.getErr()).isEmpty();
139     CreationResponse response =
140         jwtStub.createJwtMac(
141             CreationRequest.newBuilder()
142                 .setAnnotatedKeyset(
143                     AnnotatedKeyset.newBuilder()
144                         .setSerializedKeyset(keysetResponse.getKeyset())
145                         .build())
146                 .build());
147     assertThat(response.getErr()).isEmpty();
148   }
149 
150   @Test
jwtMacCreateKeyset_fails()151   public void jwtMacCreateKeyset_fails() throws Exception {
152     CreationResponse response =
153         jwtStub.createJwtMac(
154             CreationRequest.newBuilder()
155                 .setAnnotatedKeyset(
156                     AnnotatedKeyset.newBuilder()
157                         .setSerializedKeyset(ByteString.copyFrom(new byte[] {(byte) 0x80}))
158                         .build())
159                 .build());
160     assertThat(response.getErr()).isNotEmpty();
161   }
162 
163   @Test
jwtComputeVerifyMac_success()164   public void jwtComputeVerifyMac_success() throws Exception {
165     byte[] template = KeyTemplateProtoConverter.toByteArray(KeyTemplates.get("JWT_HS256"));
166     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
167     assertThat(keysetResponse.getErr()).isEmpty();
168     byte[] keyset = keysetResponse.getKeyset().toByteArray();
169 
170     long expSecs = 1234 + 100;
171     int expNanos = 567000000;
172     JwtToken token = generateToken("audience", expSecs, expNanos);
173 
174     JwtSignRequest signRequest =
175         JwtSignRequest.newBuilder()
176             .setAnnotatedKeyset(
177                 AnnotatedKeyset.newBuilder()
178                     .setSerializedKeyset(ByteString.copyFrom(keyset))
179                     .build())
180             .setRawJwt(token)
181             .build();
182     JwtSignResponse signResponse = jwtStub.computeMacAndEncode(signRequest);
183     assertThat(signResponse.getErr()).isEmpty();
184 
185     JwtValidator validator =
186         JwtValidator.newBuilder()
187             .setExpectedTypeHeader(StringValue.newBuilder().setValue("typeHeader"))
188             .setExpectedIssuer(StringValue.newBuilder().setValue("issuer"))
189             .setExpectedAudience(StringValue.newBuilder().setValue("audience"))
190             .setNow(Timestamp.newBuilder().setSeconds(1234))
191             .build();
192     JwtVerifyRequest verifyRequest =
193         JwtVerifyRequest.newBuilder()
194             .setAnnotatedKeyset(
195                 AnnotatedKeyset.newBuilder()
196                     .setSerializedKeyset(ByteString.copyFrom(keyset))
197                     .build())
198             .setSignedCompactJwt(signResponse.getSignedCompactJwt())
199             .setValidator(validator)
200             .build();
201 
202     JwtToken expectedToken = generateToken("audience", expSecs, 0);
203     JwtVerifyResponse verifyResponse = jwtStub.verifyMacAndDecode(verifyRequest);
204     assertThat(verifyResponse.getErr()).isEmpty();
205     assertThat(verifyResponse.getVerifiedJwt()).isEqualTo(expectedToken);
206   }
207 
208   @Test
jwtEmptyTokenComputeVerifyMac_success()209   public void jwtEmptyTokenComputeVerifyMac_success() throws Exception {
210     byte[] template = KeyTemplateProtoConverter.toByteArray(JwtHmacKeyManager.hs256Template());
211     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
212     assertThat(keysetResponse.getErr()).isEmpty();
213     byte[] keyset = keysetResponse.getKeyset().toByteArray();
214 
215     JwtToken token = JwtToken.getDefaultInstance();
216 
217     JwtSignRequest signRequest =
218         JwtSignRequest.newBuilder()
219             .setAnnotatedKeyset(
220                 AnnotatedKeyset.newBuilder()
221                     .setSerializedKeyset(ByteString.copyFrom(keyset))
222                     .build())
223             .setRawJwt(token)
224             .build();
225     JwtSignResponse signResponse = jwtStub.computeMacAndEncode(signRequest);
226     assertThat(signResponse.getErr()).isEmpty();
227 
228     JwtValidator validator = JwtValidator.newBuilder().setAllowMissingExpiration(true).build();
229     JwtVerifyRequest verifyRequest =
230         JwtVerifyRequest.newBuilder()
231             .setAnnotatedKeyset(
232                 AnnotatedKeyset.newBuilder()
233                     .setSerializedKeyset(ByteString.copyFrom(keyset))
234                     .build())
235             .setSignedCompactJwt(signResponse.getSignedCompactJwt())
236             .setValidator(validator)
237             .build();
238 
239     JwtVerifyResponse verifyResponse = jwtStub.verifyMacAndDecode(verifyRequest);
240     assertThat(verifyResponse.getErr()).isEmpty();
241     assertThat(verifyResponse.getVerifiedJwt()).isEqualTo(token);
242   }
243 
244   @Test
jwtPublicKeySignCreateKeyset_success()245   public void jwtPublicKeySignCreateKeyset_success() throws Exception {
246     byte[] template = KeyTemplateProtoConverter.toByteArray(KeyTemplates.get("JWT_ES256"));
247     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
248     assertThat(keysetResponse.getErr()).isEmpty();
249     CreationResponse response =
250         jwtStub.createJwtPublicKeySign(
251             CreationRequest.newBuilder()
252                 .setAnnotatedKeyset(
253                     AnnotatedKeyset.newBuilder()
254                         .setSerializedKeyset(keysetResponse.getKeyset())
255                         .build())
256                 .build());
257     assertThat(response.getErr()).isEmpty();
258   }
259 
260   @Test
jwtPublicKeySignCreateKeyset_fails()261   public void jwtPublicKeySignCreateKeyset_fails() throws Exception {
262     CreationResponse response =
263         jwtStub.createJwtPublicKeySign(
264             CreationRequest.newBuilder()
265                 .setAnnotatedKeyset(
266                     AnnotatedKeyset.newBuilder()
267                         .setSerializedKeyset(ByteString.copyFrom(new byte[] {(byte) 0x80})))
268                 .build());
269     assertThat(response.getErr()).isNotEmpty();
270   }
271 
272   @Test
jwtPublicKeyVerifyCreateKeyset_success()273   public void jwtPublicKeyVerifyCreateKeyset_success() throws Exception {
274     byte[] template = KeyTemplateProtoConverter.toByteArray(KeyTemplates.get("JWT_ES256"));
275     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
276     assertThat(keysetResponse.getErr()).isEmpty();
277     byte[] privateKeyset = keysetResponse.getKeyset().toByteArray();
278 
279     KeysetPublicResponse pubResponse = publicKeyset(keysetStub, privateKeyset);
280     assertThat(pubResponse.getErr()).isEmpty();
281     CreationResponse response =
282         jwtStub.createJwtPublicKeyVerify(
283             CreationRequest.newBuilder()
284                 .setAnnotatedKeyset(
285                     AnnotatedKeyset.newBuilder()
286                         .setSerializedKeyset(pubResponse.getPublicKeyset())
287                         .build())
288                 .build());
289     assertThat(response.getErr()).isEmpty();
290   }
291 
292   @Test
jwtPublicKeyVerifyCreateKeyset_fails()293   public void jwtPublicKeyVerifyCreateKeyset_fails() throws Exception {
294     CreationResponse response =
295         jwtStub.createJwtPublicKeyVerify(
296             CreationRequest.newBuilder()
297                 .setAnnotatedKeyset(
298                     AnnotatedKeyset.newBuilder()
299                         .setSerializedKeyset(ByteString.copyFrom(new byte[] {(byte) 0x80}))
300                         .build())
301                 .build());
302     assertThat(response.getErr()).isNotEmpty();
303   }
304 
305   @Test
publicKeySignVerify_success()306   public void publicKeySignVerify_success() throws Exception {
307     byte[] template = KeyTemplateProtoConverter.toByteArray(KeyTemplates.get("JWT_ES256"));
308     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
309     assertThat(keysetResponse.getErr()).isEmpty();
310     byte[] privateKeyset = keysetResponse.getKeyset().toByteArray();
311 
312     KeysetPublicResponse pubResponse = publicKeyset(keysetStub, privateKeyset);
313     assertThat(pubResponse.getErr()).isEmpty();
314     byte[] publicKeyset = pubResponse.getPublicKeyset().toByteArray();
315 
316     long expSecs = 1234 + 100;
317     int expNanos = 567000000;
318     JwtToken token = generateToken("audience", expSecs, expNanos);
319 
320     JwtSignRequest signRequest =
321         JwtSignRequest.newBuilder()
322             .setAnnotatedKeyset(
323                 AnnotatedKeyset.newBuilder()
324                     .setSerializedKeyset(ByteString.copyFrom(privateKeyset))
325                     .build())
326             .setRawJwt(token)
327             .build();
328     JwtSignResponse signResponse = jwtStub.publicKeySignAndEncode(signRequest);
329     assertThat(signResponse.getErr()).isEmpty();
330 
331     JwtValidator validator =
332         JwtValidator.newBuilder()
333             .setExpectedTypeHeader(StringValue.newBuilder().setValue("typeHeader"))
334             .setExpectedIssuer(StringValue.newBuilder().setValue("issuer"))
335             .setExpectedAudience(StringValue.newBuilder().setValue("audience"))
336             .setNow(Timestamp.newBuilder().setSeconds(1234))
337             .build();
338     JwtVerifyRequest verifyRequest =
339         JwtVerifyRequest.newBuilder()
340             .setAnnotatedKeyset(
341                 AnnotatedKeyset.newBuilder()
342                     .setSerializedKeyset(ByteString.copyFrom(publicKeyset))
343                     .build())
344             .setSignedCompactJwt(signResponse.getSignedCompactJwt())
345             .setValidator(validator)
346             .build();
347 
348     JwtToken expectedToken = generateToken("audience", expSecs, 0);
349     JwtVerifyResponse verifyResponse = jwtStub.publicKeyVerifyAndDecode(verifyRequest);
350     assertThat(verifyResponse.getErr()).isEmpty();
351     assertThat(verifyResponse.getVerifiedJwt()).isEqualTo(expectedToken);
352   }
353 
354   @Test
signFailsOnBadKeyset()355   public void signFailsOnBadKeyset() throws Exception {
356     byte[] badKeyset = "bad keyset".getBytes(UTF_8);
357 
358     JwtToken token = generateToken("audience", 1234, 0);
359     JwtSignRequest signRequest =
360         JwtSignRequest.newBuilder()
361             .setAnnotatedKeyset(
362                 AnnotatedKeyset.newBuilder()
363                     .setSerializedKeyset(ByteString.copyFrom(badKeyset))
364                     .build())
365             .setRawJwt(token)
366             .build();
367     assertThrows(
368         io.grpc.StatusRuntimeException.class, () -> jwtStub.computeMacAndEncode(signRequest));
369   }
370 
371   @Test
verifyFailsWhenExpired()372   public void verifyFailsWhenExpired() throws Exception {
373     byte[] template = KeyTemplateProtoConverter.toByteArray(JwtHmacKeyManager.hs256Template());
374     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
375     assertThat(keysetResponse.getErr()).isEmpty();
376     byte[] keyset = keysetResponse.getKeyset().toByteArray();
377 
378     JwtToken token = generateToken("audience", 1234 - 10, 0);
379 
380     JwtSignRequest signRequest =
381         JwtSignRequest.newBuilder()
382             .setAnnotatedKeyset(
383                 AnnotatedKeyset.newBuilder()
384                     .setSerializedKeyset(ByteString.copyFrom(keyset))
385                     .build())
386             .setRawJwt(token)
387             .build();
388     JwtSignResponse signResponse = jwtStub.computeMacAndEncode(signRequest);
389     assertThat(signResponse.getErr()).isEmpty();
390 
391     JwtValidator validator =
392         JwtValidator.newBuilder()
393             .setExpectedTypeHeader(StringValue.newBuilder().setValue("typeHeader"))
394             .setExpectedIssuer(StringValue.newBuilder().setValue("issuer"))
395             .setExpectedAudience(StringValue.newBuilder().setValue("audience"))
396             .setNow(Timestamp.newBuilder().setSeconds(1234))
397             .build();
398     JwtVerifyRequest verifyRequest =
399         JwtVerifyRequest.newBuilder()
400             .setAnnotatedKeyset(
401                 AnnotatedKeyset.newBuilder()
402                     .setSerializedKeyset(ByteString.copyFrom(keyset))
403                     .build())
404             .setSignedCompactJwt(signResponse.getSignedCompactJwt())
405             .setValidator(validator)
406             .build();
407 
408     JwtVerifyResponse verifyResponse = jwtStub.verifyMacAndDecode(verifyRequest);
409     assertThat(verifyResponse.getErr()).isNotEmpty();
410   }
411 
412   @Test
verifyFailsWithWrongAudience()413   public void verifyFailsWithWrongAudience() throws Exception {
414     byte[] template = KeyTemplateProtoConverter.toByteArray(JwtHmacKeyManager.hs256Template());
415     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
416     assertThat(keysetResponse.getErr()).isEmpty();
417     byte[] keyset = keysetResponse.getKeyset().toByteArray();
418 
419     JwtToken token = generateToken("wrong_audience", 1234 + 100, 0);
420 
421     JwtSignRequest signRequest =
422         JwtSignRequest.newBuilder()
423             .setAnnotatedKeyset(
424                 AnnotatedKeyset.newBuilder()
425                     .setSerializedKeyset(ByteString.copyFrom(keyset))
426                     .build())
427             .setRawJwt(token)
428             .build();
429     JwtSignResponse signResponse = jwtStub.computeMacAndEncode(signRequest);
430     assertThat(signResponse.getErr()).isEmpty();
431 
432     JwtValidator validator =
433         JwtValidator.newBuilder()
434             .setExpectedTypeHeader(StringValue.newBuilder().setValue("typeHeader"))
435             .setExpectedIssuer(StringValue.newBuilder().setValue("issuer"))
436             .setExpectedAudience(StringValue.newBuilder().setValue("audience"))
437             .setNow(Timestamp.newBuilder().setSeconds(1234))
438             .build();
439     JwtVerifyRequest verifyRequest =
440         JwtVerifyRequest.newBuilder()
441             .setAnnotatedKeyset(
442                 AnnotatedKeyset.newBuilder()
443                     .setSerializedKeyset(ByteString.copyFrom(keyset))
444                     .build())
445             .setSignedCompactJwt(signResponse.getSignedCompactJwt())
446             .setValidator(validator)
447             .build();
448 
449     JwtVerifyResponse verifyResponse = jwtStub.verifyMacAndDecode(verifyRequest);
450     assertThat(verifyResponse.getErr()).isNotEmpty();
451   }
452 
453   @Test
verifyFailsWithWrongKey()454   public void verifyFailsWithWrongKey() throws Exception {
455     byte[] template = KeyTemplateProtoConverter.toByteArray(JwtHmacKeyManager.hs256Template());
456 
457     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
458     assertThat(keysetResponse.getErr()).isEmpty();
459     byte[] keyset = keysetResponse.getKeyset().toByteArray();
460 
461     JwtToken token = generateToken("audience", 1234 + 100, 0);
462 
463     JwtSignRequest signRequest =
464         JwtSignRequest.newBuilder()
465             .setAnnotatedKeyset(
466                 AnnotatedKeyset.newBuilder()
467                     .setSerializedKeyset(ByteString.copyFrom(keyset))
468                     .build())
469             .setRawJwt(token)
470             .build();
471     JwtSignResponse signResponse = jwtStub.computeMacAndEncode(signRequest);
472     assertThat(signResponse.getErr()).isEmpty();
473 
474     KeysetGenerateResponse wrongKeysetResponse = generateKeyset(keysetStub, template);
475     assertThat(wrongKeysetResponse.getErr()).isEmpty();
476     byte[] wrongKeyset = wrongKeysetResponse.getKeyset().toByteArray();
477 
478     JwtValidator validator =
479         JwtValidator.newBuilder()
480             .setExpectedTypeHeader(StringValue.newBuilder().setValue("typeHeader"))
481             .setExpectedIssuer(StringValue.newBuilder().setValue("issuer"))
482             .setExpectedAudience(StringValue.newBuilder().setValue("audience"))
483             .setNow(Timestamp.newBuilder().setSeconds(1234))
484             .build();
485     JwtVerifyRequest verifyRequest =
486         JwtVerifyRequest.newBuilder()
487             .setAnnotatedKeyset(
488                 AnnotatedKeyset.newBuilder()
489                     .setSerializedKeyset(ByteString.copyFrom(wrongKeyset))
490                     .build())
491             .setSignedCompactJwt(signResponse.getSignedCompactJwt())
492             .setValidator(validator)
493             .build();
494 
495     JwtVerifyResponse verifyResponse = jwtStub.verifyMacAndDecode(verifyRequest);
496     assertThat(verifyResponse.getErr()).isNotEmpty();
497   }
498 
499   @Test
jwtToFromJwt_success()500   public void jwtToFromJwt_success() throws Exception {
501     byte[] template = KeyTemplateProtoConverter.toByteArray(KeyTemplates.get("JWT_ES256"));
502     KeysetGenerateResponse keysetResponse = generateKeyset(keysetStub, template);
503     assertThat(keysetResponse.getErr()).isEmpty();
504     byte[] privateKeyset = keysetResponse.getKeyset().toByteArray();
505 
506     KeysetPublicResponse pubResponse = publicKeyset(keysetStub, privateKeyset);
507     assertThat(pubResponse.getErr()).isEmpty();
508     byte[] publicKeyset = pubResponse.getPublicKeyset().toByteArray();
509 
510     JwtToken token = generateToken("audience", 1245, 0);
511 
512     JwtSignRequest signRequest =
513         JwtSignRequest.newBuilder()
514             .setAnnotatedKeyset(
515                 AnnotatedKeyset.newBuilder()
516                     .setSerializedKeyset(ByteString.copyFrom(privateKeyset))
517                     .build())
518             .setRawJwt(token)
519             .build();
520     JwtSignResponse signResponse = jwtStub.publicKeySignAndEncode(signRequest);
521     assertThat(signResponse.getErr()).isEmpty();
522 
523     // Convert the public keyset to a JWK set
524     JwtToJwkSetRequest toRequest =
525         JwtToJwkSetRequest.newBuilder().setKeyset(ByteString.copyFrom(publicKeyset)).build();
526     JwtToJwkSetResponse toResponse = jwtStub.toJwkSet(toRequest);
527     assertThat(toResponse.getErr()).isEmpty();
528     assertThat(toResponse.getJwkSet()).contains("{\"keys\":[{\"kty\":\"EC\",\"crv\":\"P-256\",");
529     // Convert the public keyset to a JWK set
530     JwtFromJwkSetRequest fromRequest =
531         JwtFromJwkSetRequest.newBuilder().setJwkSet(toResponse.getJwkSet()).build();
532     JwtFromJwkSetResponse fromResponse = jwtStub.fromJwkSet(fromRequest);
533     assertThat(fromResponse.getErr()).isEmpty();
534 
535     // Use that output keyset to verify the token
536     JwtValidator validator =
537         JwtValidator.newBuilder()
538             .setExpectedTypeHeader(StringValue.newBuilder().setValue("typeHeader"))
539             .setExpectedIssuer(StringValue.newBuilder().setValue("issuer"))
540             .setExpectedAudience(StringValue.newBuilder().setValue("audience"))
541             .setNow(Timestamp.newBuilder().setSeconds(1234))
542             .build();
543     JwtVerifyRequest verifyRequest =
544         JwtVerifyRequest.newBuilder()
545             .setAnnotatedKeyset(
546                 AnnotatedKeyset.newBuilder().setSerializedKeyset(fromResponse.getKeyset()).build())
547             .setSignedCompactJwt(signResponse.getSignedCompactJwt())
548             .setValidator(validator)
549             .build();
550     JwtVerifyResponse verifyResponse = jwtStub.publicKeyVerifyAndDecode(verifyRequest);
551     assertThat(verifyResponse.getErr()).isEmpty();
552   }
553 }
554