| // Copyright 2020 Google LLC |
| // |
| // 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 |
| // |
| // http://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.google.crypto.tink.jwt; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| import static org.junit.Assert.assertThrows; |
| |
| import com.google.crypto.tink.proto.OutputPrefixType; |
| import com.google.crypto.tink.subtle.Base64; |
| import com.google.gson.JsonObject; |
| import com.google.gson.JsonParser; |
| import java.security.InvalidAlgorithmParameterException; |
| import java.util.Optional; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Unit tests for JwtFormat */ |
| @RunWith(JUnit4.class) |
| public final class JwtFormatTest { |
| |
| @Test |
| public void createDecodeHeader_success() throws Exception { |
| String header = |
| JwtFormat.decodeHeader(JwtFormat.createHeader("RS256", Optional.empty(), Optional.empty())); |
| assertThat(header).isEqualTo("{\"alg\":\"RS256\"}"); |
| } |
| |
| @Test |
| public void createDecodeHeaderWithTyp_success() throws Exception { |
| String header = |
| JwtFormat.decodeHeader( |
| JwtFormat.createHeader("RS256", Optional.of("JWT"), Optional.empty())); |
| assertThat(header).isEqualTo("{\"alg\":\"RS256\",\"typ\":\"JWT\"}"); |
| } |
| |
| @Test |
| public void createDecodeHeaderWithKidAndTyp_success() throws Exception { |
| String header = |
| JwtFormat.decodeHeader( |
| JwtFormat.createHeader("RS256", Optional.of("JWT"), Optional.of("GsapRA"))); |
| assertThat(header).isEqualTo("{\"kid\":\"GsapRA\",\"alg\":\"RS256\",\"typ\":\"JWT\"}"); |
| } |
| |
| @Test |
| public void createDecodeHeaderWithInvalidUtf8_fails() throws Exception { |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.decodeHeader("eyJhbGciOiJIUzI1NiIsICJhIjoiwiJ9")); |
| } |
| |
| @Test |
| public void getKidFromTinkOutputPrefixType_success() throws Exception { |
| int keyId = 0x1ac6a944; |
| Optional<String> kid = JwtFormat.getKid(keyId, OutputPrefixType.TINK); |
| // ID Requirement(Hex): 1 a c 6 a 9 4 4 |
| // ID Requirement(Binary): 0001 1010 1100 0110 1010 1001 0100 0100 |
| // Regroup for base64: 000110 101100 011010 101001 010001 00 (+0000) |
| // Base64 encode: G s a p R A |
| assertThat(kid.get()).isEqualTo("GsapRA"); |
| assertThat(JwtFormat.getKeyId(kid.get()).get()).isEqualTo(0x1ac6a944); |
| } |
| |
| @Test |
| public void getKidFromRawOutputPrefixType_success() throws Exception { |
| int keyId = 0x1ac6a944; |
| Optional<String> kid = JwtFormat.getKid(keyId, OutputPrefixType.RAW); |
| assertThat(kid.isPresent()).isFalse(); |
| } |
| |
| @Test |
| public void keyIdKidConversion_outputIsEqual() throws Exception { |
| assertThat(JwtFormat.getKeyId(JwtFormat.getKid(0x12345678, OutputPrefixType.TINK).get()).get()) |
| .isEqualTo(0x12345678); |
| assertThat(JwtFormat.getKeyId(JwtFormat.getKid(-2147483648, OutputPrefixType.TINK).get()).get()) |
| .isEqualTo(-2147483648); |
| assertThat(JwtFormat.getKeyId(JwtFormat.getKid(-100, OutputPrefixType.TINK).get()).get()) |
| .isEqualTo(-100); |
| assertThat(JwtFormat.getKeyId(JwtFormat.getKid(0, OutputPrefixType.TINK).get()).get()) |
| .isEqualTo(0); |
| assertThat(JwtFormat.getKeyId(JwtFormat.getKid(100, OutputPrefixType.TINK).get()).get()) |
| .isEqualTo(100); |
| assertThat(JwtFormat.getKeyId(JwtFormat.getKid(2147483647, OutputPrefixType.TINK).get()).get()) |
| .isEqualTo(2147483647); |
| } |
| |
| @Test |
| public void getKeyId_wrongFormat_isNotPresent() throws Exception { |
| assertThat(JwtFormat.getKeyId("GsapRAA").isPresent()).isFalse(); |
| assertThat(JwtFormat.getKeyId("Gsap").isPresent()).isFalse(); |
| assertThat(JwtFormat.getKeyId("").isPresent()).isFalse(); |
| assertThat(JwtFormat.getKeyId("dBjftJeZ4CVP-mB92K27uhbUJU1p1r").isPresent()).isFalse(); |
| } |
| |
| @Test |
| public void getKidFromUnsupportedOutputPrefixType_fails() throws Exception { |
| int keyId = 0x1ac6a944; |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.getKid(keyId, OutputPrefixType.LEGACY)); |
| } |
| |
| @Test |
| public void createHeaderWithUnknownAlgorithm_fails() throws Exception { |
| assertThrows( |
| InvalidAlgorithmParameterException.class, |
| () -> JwtFormat.createHeader("UnknownAlgorithm", Optional.empty(), Optional.empty())); |
| } |
| |
| @Test |
| public void decodeHeaderA1_success() throws Exception { |
| // Example from https://tools.ietf.org/html/rfc7515#appendix-A.1 |
| String header = JwtFormat.decodeHeader("eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"); |
| assertThat(header).isEqualTo("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}"); |
| } |
| |
| @Test |
| public void decodeHeaderA2_success() throws Exception { |
| // Example from https://tools.ietf.org/html/rfc7515#appendix-A.2 |
| String header = JwtFormat.decodeHeader("eyJhbGciOiJSUzI1NiJ9"); |
| assertThat(header).isEqualTo("{\"alg\":\"RS256\"}"); |
| } |
| |
| @Test |
| public void decodeModifiedHeader_success() throws Exception { |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.decodeHeader("eyJhbGciOiJSUzI1NiJ9?")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.decodeHeader("eyJhbGciOiJ SUzI1NiJ9")); |
| assertThrows( |
| JwtInvalidException.class, () -> JwtFormat.decodeHeader("eyJhbGci\r\nOiJSUzI1NiJ9")); |
| } |
| |
| @Test |
| public void decodeHeader_success() throws Exception { |
| String headerStr = Base64.urlSafeEncode("{\"alg\":\"RS256\"}".getBytes(UTF_8)); |
| String header = JwtFormat.decodeHeader(headerStr); |
| assertThat(header).isEqualTo("{\"alg\":\"RS256\"}"); |
| } |
| |
| @Test |
| public void decodeInvalidHeader_fails() throws Exception { |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.decodeHeader("?=")); |
| } |
| |
| @Test |
| public void createDecodeValidateHeader_success() throws Exception { |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson( |
| JwtFormat.decodeHeader( |
| JwtFormat.createHeader("HS256", Optional.empty(), Optional.empty())))); |
| JwtFormat.validateHeader( |
| "HS384", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson( |
| JwtFormat.decodeHeader( |
| JwtFormat.createHeader("HS384", Optional.empty(), Optional.empty())))); |
| JwtFormat.validateHeader( |
| "HS512", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson( |
| JwtFormat.decodeHeader( |
| JwtFormat.createHeader("HS512", Optional.empty(), Optional.empty())))); |
| JwtFormat.validateHeader( |
| "ES256", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson( |
| JwtFormat.decodeHeader( |
| JwtFormat.createHeader("ES256", Optional.empty(), Optional.empty())))); |
| JwtFormat.validateHeader( |
| "RS256", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson( |
| JwtFormat.decodeHeader( |
| JwtFormat.createHeader("RS256", Optional.empty(), Optional.empty())))); |
| } |
| |
| @Test |
| public void validateHeaderWithWrongAlgorithm_fails() throws Exception { |
| String header = |
| JwtFormat.decodeHeader(JwtFormat.createHeader("HS256", Optional.empty(), Optional.empty())); |
| assertThrows( |
| InvalidAlgorithmParameterException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "HS384", Optional.empty(), Optional.empty(), JsonUtil.parseJson(header))); |
| } |
| |
| @Test |
| public void validateHeaderWithUnknownAlgorithm_fails() throws Exception { |
| assertThrows( |
| InvalidAlgorithmParameterException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "UnknownAlgorithm", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson("{\"alg\": \"UnknownAlgorithm\"}"))); |
| } |
| |
| @Test |
| public void validateHeaderIgnoresTyp() throws Exception { |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson("{\"alg\": \"HS256\", \"typ\": \"unknown\"}")); |
| } |
| |
| @Test |
| public void validateHeaderRejectsCrit() throws Exception { |
| assertThrows( |
| JwtInvalidException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson( |
| "{\"alg\": \"HS256\", \"crit\":[\"http://example.invalid/UNDEFINED\"], " |
| + "\"http://example.invalid/UNDEFINED\":true}"))); |
| } |
| |
| @Test |
| public void validateHeaderWithUnknownEntry_success() throws Exception { |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.empty(), Optional.empty(), |
| JsonUtil.parseJson("{\"alg\": \"HS256\", \"unknown\": \"header\"}")); |
| } |
| |
| @Test |
| public void validateEmptyHeader_fails() throws Exception { |
| assertThrows( |
| JwtInvalidException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "HS256", Optional.empty(), Optional.empty(), JsonUtil.parseJson("{}"))); |
| } |
| |
| @Test |
| public void validateHeaderWithTinkKid() throws Exception { |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.of("kid123"), Optional.empty(), |
| JsonUtil.parseJson("{\"alg\": \"HS256\", \"kid\": \"kid123\"}")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.of("kid123"), |
| Optional.empty(), |
| JsonUtil.parseJson("{\"alg\": \"HS256\", \"kid\": \"wrongKid\"}"))); |
| // If tinkKid is set, then the kid is required in the header. |
| assertThrows( |
| JwtInvalidException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.of("kid123"), |
| Optional.empty(), |
| JsonUtil.parseJson("{\"alg\": \"HS256\"}"))); |
| } |
| |
| @Test |
| public void validateHeaderWithCustomKid() throws Exception { |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.empty(), Optional.of("kid123"), |
| JsonUtil.parseJson("{\"alg\": \"HS256\", \"kid\": \"kid123\"}")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.empty(), |
| Optional.of("kid123"), |
| JsonUtil.parseJson("{\"alg\": \"HS256\", \"kid\": \"wrongKid\"}"))); |
| // If customKid is set, then the kid is not required in the header. |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.empty(), Optional.of("kid123"), |
| JsonUtil.parseJson("{\"alg\": \"HS256\"}")); |
| } |
| |
| @Test |
| public void validateHeaderWithBothTinkAndCustomKid_fails() throws Exception { |
| assertThrows( |
| JwtInvalidException.class, |
| () -> |
| JwtFormat.validateHeader( |
| "HS256", |
| Optional.of("kid123"), |
| Optional.of("kid123"), |
| JsonUtil.parseJson("{\"alg\": \"HS256\", \"kid\": \"kid123\"}"))); |
| } |
| |
| @Test |
| public void encodeDecodePayload_equal() throws Exception { |
| JsonObject payload = new JsonObject(); |
| payload.addProperty("iss", "joe"); |
| payload.addProperty("exp", 1300819380); |
| payload.addProperty("http://example.com/is_root", true); |
| String jsonPayload = payload.toString(); |
| String encodedPayload = JwtFormat.encodePayload(jsonPayload); |
| String decodedPayload = JwtFormat.decodePayload(encodedPayload); |
| assertThat(decodedPayload).isEqualTo(jsonPayload); |
| } |
| |
| @Test |
| public void decodePayload_success() throws Exception { |
| // Example from https://tools.ietf.org/html/rfc7515#appendix-A.1 |
| JsonObject payload = |
| JsonParser.parseString( |
| JwtFormat.decodePayload( |
| "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" |
| + "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ")) |
| .getAsJsonObject(); |
| assertThat(payload.get("iss").getAsString()).isEqualTo("joe"); |
| assertThat(payload.get("exp").getAsInt()).isEqualTo(1300819380); |
| assertThat(payload.get("http://example.com/is_root").getAsBoolean()).isTrue(); |
| } |
| |
| @Test |
| public void decodeInvalidPayload_fails() throws Exception { |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.decodePayload("?=")); |
| } |
| |
| @Test |
| public void createDecodePayloadWithInvalidUtf8_fails() throws Exception { |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.decodePayload("eyJpc3MiOiJqb2XCIn0")); |
| } |
| |
| @Test |
| public void signedCompactCreateSplit_success() throws Exception { |
| RawJwt rawJwt = RawJwt.newBuilder().setIssuer("joe").withoutExpiration().build(); |
| String encodedSignature = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; |
| byte[] signature = JwtFormat.decodeSignature(encodedSignature); |
| String unsignedCompact = JwtFormat.createUnsignedCompact("RS256", Optional.empty(), rawJwt); |
| String signedCompact = JwtFormat.createSignedCompact(unsignedCompact, signature); |
| JwtFormat.Parts parts = JwtFormat.splitSignedCompact(signedCompact); |
| |
| JwtFormat.validateHeader( |
| "RS256", Optional.empty(), Optional.empty(), JsonUtil.parseJson(parts.header)); |
| |
| assertThat(unsignedCompact).isEqualTo("eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UifQ"); |
| assertThat(signedCompact).isEqualTo( |
| "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"); |
| assertThat(parts.unsignedCompact).isEqualTo(unsignedCompact); |
| assertThat(parts.signatureOrMac).isEqualTo(signature); |
| assertThat(parts.header).isEqualTo("{\"alg\":\"RS256\"}"); |
| assertThat(parts.payload).isEqualTo("{\"iss\":\"joe\"}"); |
| } |
| |
| |
| @Test |
| public void signedCompactCreateSplitWithTypeHeader_success() throws Exception { |
| RawJwt rawJwt = |
| RawJwt.newBuilder().setTypeHeader("JWT").setIssuer("joe").withoutExpiration().build(); |
| String encodedSignature = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; |
| byte[] signature = JwtFormat.decodeSignature(encodedSignature); |
| String unsignedCompact = JwtFormat.createUnsignedCompact("RS256", Optional.empty(), rawJwt); |
| String signedCompact = JwtFormat.createSignedCompact(unsignedCompact, signature); |
| JwtFormat.Parts parts = JwtFormat.splitSignedCompact(signedCompact); |
| JsonObject parsedHeader = JsonUtil.parseJson(parts.header); |
| JwtFormat.validateHeader("RS256", Optional.empty(), Optional.empty(), parsedHeader); |
| assertThat(parsedHeader.getAsJsonPrimitive("typ").getAsString()).isEqualTo("JWT"); |
| |
| assertThat(unsignedCompact) |
| .isEqualTo("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJqb2UifQ"); |
| assertThat(parts.unsignedCompact).isEqualTo(unsignedCompact); |
| assertThat(parts.signatureOrMac).isEqualTo(signature); |
| assertThat(parts.header).isEqualTo("{\"alg\":\"RS256\",\"typ\":\"JWT\"}"); |
| assertThat(parts.payload).isEqualTo("{\"iss\":\"joe\"}"); |
| } |
| |
| @Test |
| public void signedCompactCreateSplitWithKeyIdentifier_success() throws Exception { |
| String kid = "AZxkm2U"; |
| RawJwt rawJwt = RawJwt.newBuilder().setIssuer("joe").withoutExpiration().build(); |
| String encodedSignature = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; |
| byte[] signature = JwtFormat.decodeSignature(encodedSignature); |
| String unsignedCompact = JwtFormat.createUnsignedCompact("RS256", Optional.of(kid), rawJwt); |
| String signedCompact = JwtFormat.createSignedCompact(unsignedCompact, signature); |
| JwtFormat.Parts parts = JwtFormat.splitSignedCompact(signedCompact); |
| JsonObject parsedHeader = JsonUtil.parseJson(parts.header); |
| JwtFormat.validateHeader("RS256", Optional.empty(), Optional.empty(), parsedHeader); |
| |
| assertThat(unsignedCompact) |
| .isEqualTo("eyJraWQiOiJBWnhrbTJVIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJqb2UifQ"); |
| assertThat(signedCompact) |
| .isEqualTo( |
| "eyJraWQiOiJBWnhrbTJVIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJqb2UifQ" |
| + ".dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"); |
| assertThat(parts.unsignedCompact).isEqualTo(unsignedCompact); |
| assertThat(parts.signatureOrMac).isEqualTo(signature); |
| assertThat(parts.header).isEqualTo("{\"kid\":\"AZxkm2U\",\"alg\":\"RS256\"}"); |
| assertThat(parts.payload).isEqualTo("{\"iss\":\"joe\"}"); |
| } |
| |
| @Test |
| public void splitEmptySignedCompact_success() throws Exception { |
| JwtFormat.Parts parts = JwtFormat.splitSignedCompact(".."); |
| assertThat(parts.unsignedCompact).isEqualTo("."); |
| assertThat(parts.signatureOrMac).isEmpty(); |
| assertThat(parts.header).isEmpty(); |
| assertThat(parts.payload).isEmpty(); |
| } |
| |
| @Test |
| public void splitSignedCompactWithBadFormat_fails() throws Exception { |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.splitSignedCompact("e30.e30.YWJj.abc")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.splitSignedCompact("e30.e30.YWJj.")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.splitSignedCompact(".e30.e30.YWJj")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.splitSignedCompact(".e30.e30.")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.splitSignedCompact("e30.e30")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.splitSignedCompact("e30")); |
| assertThrows( |
| JwtInvalidException.class, |
| () -> JwtFormat.splitSignedCompact("")); |
| } |
| |
| @Test |
| public void splitSignedCompactWithBadCharacters_fails() throws Exception { |
| // check that unmodified token works |
| JwtFormat.Parts parts = JwtFormat.splitSignedCompact("e30.e30.YWJj"); |
| assertThat(parts.unsignedCompact).isEqualTo("e30.e30"); |
| |
| // add bad characters |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("{e30.e30.YWJj")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact(" e30.e30.YWJj")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30. e30.YWJj")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30.e30.YWJj ")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30.e30.\nYWJj")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30.\re30.YWJj")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30$.e30.YWJj")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30.$e30.YWJj")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30.e30.YWJj$")); |
| assertThrows(JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30.e30.YWJj$")); |
| assertThrows( |
| JwtInvalidException.class, () -> JwtFormat.splitSignedCompact("e30.e30.YWJj\ud83c")); |
| } |
| |
| @Test |
| public void encodeDecodeSignature_success() throws Exception { |
| // Example from https://tools.ietf.org/html/rfc7515#appendix-A.1 |
| byte[] signatureBytes = |
| new byte[] { |
| (byte) 116, |
| (byte) 24, |
| (byte) 223, |
| (byte) 180, |
| (byte) 151, |
| (byte) 153, |
| (byte) 224, |
| (byte) 37, |
| (byte) 79, |
| (byte) 250, |
| (byte) 96, |
| (byte) 125, |
| (byte) 216, |
| (byte) 173, |
| (byte) 187, |
| (byte) 186, |
| (byte) 22, |
| (byte) 212, |
| (byte) 37, |
| (byte) 77, |
| (byte) 105, |
| (byte) 214, |
| (byte) 191, |
| (byte) 240, |
| (byte) 91, |
| (byte) 88, |
| (byte) 5, |
| (byte) 88, |
| (byte) 83, |
| (byte) 132, |
| (byte) 141, |
| (byte) 121 |
| }; |
| String encodeSignature = JwtFormat.encodeSignature(signatureBytes); |
| assertThat(encodeSignature) |
| .isEqualTo("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"); |
| assertThat(JwtFormat.decodeSignature(encodeSignature)) |
| .isEqualTo(signatureBytes); |
| } |
| } |