diff options
author | ioannanedelcu <ioannanedelcu@google.com> | 2023-07-20 05:59:06 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-07-20 05:59:56 -0700 |
commit | 5649ede627ddc4067dc8502b7aca61e41cd0481a (patch) | |
tree | 1e6ca3eae609e5b91713a2f29ce1349b9ae64b6f | |
parent | c5d022a07694180813f50171e0f5a1ce8aa50120 (diff) | |
download | tink-5649ede627ddc4067dc8502b7aca61e41cd0481a.tar.gz |
Add JwtRsaSsaPkcs1PublicKey class in Java.
PiperOrigin-RevId: 549607483
5 files changed, 591 insertions, 0 deletions
diff --git a/java_src/BUILD.bazel b/java_src/BUILD.bazel index e8e3432ce..6bf76aa8e 100644 --- a/java_src/BUILD.bazel +++ b/java_src/BUILD.bazel @@ -240,6 +240,7 @@ gen_maven_jar_rules( "//src/main/java/com/google/crypto/tink/jwt:jwt_public_key_verify_internal", "//src/main/java/com/google/crypto/tink/jwt:jwt_public_key_verify_wrapper", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_parameters", + "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_public_key", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_sign_key_manager", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_verify_key_manager", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pss_sign_key_manager", @@ -670,6 +671,7 @@ gen_maven_jar_rules( "//src/main/java/com/google/crypto/tink/jwt:jwt_public_key_verify_internal-android", "//src/main/java/com/google/crypto/tink/jwt:jwt_public_key_verify_wrapper-android", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_parameters-android", + "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_public_key-android", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_sign_key_manager-android", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_verify_key_manager-android", "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pss_sign_key_manager-android", diff --git a/java_src/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel b/java_src/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel index 5f205a5ee..1f11a87b2 100644 --- a/java_src/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel +++ b/java_src/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel @@ -1143,3 +1143,31 @@ java_library( "@maven//:com_google_errorprone_error_prone_annotations", ], ) + +java_library( + name = "jwt_rsa_ssa_pkcs1_public_key", + srcs = ["JwtRsaSsaPkcs1PublicKey.java"], + deps = [ + ":jwt_rsa_ssa_pkcs1_parameters", + ":jwt_signature_public_key", + "//src/main/java/com/google/crypto/tink:accesses_partial_key", + "//src/main/java/com/google/crypto/tink:key", + "//src/main/java/com/google/crypto/tink/subtle:base64", + "@maven//:com_google_code_findbugs_jsr305", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) + +android_library( + name = "jwt_rsa_ssa_pkcs1_public_key-android", + srcs = ["JwtRsaSsaPkcs1PublicKey.java"], + deps = [ + ":jwt_rsa_ssa_pkcs1_parameters-android", + ":jwt_signature_public_key-android", + "//src/main/java/com/google/crypto/tink:accesses_partial_key-android", + "//src/main/java/com/google/crypto/tink:key-android", + "//src/main/java/com/google/crypto/tink/subtle:base64-android", + "@maven//:com_google_code_findbugs_jsr305", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) diff --git a/java_src/src/main/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1PublicKey.java b/java_src/src/main/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1PublicKey.java new file mode 100644 index 000000000..e0d7d6459 --- /dev/null +++ b/java_src/src/main/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1PublicKey.java @@ -0,0 +1,202 @@ +// Copyright 2023 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 com.google.crypto.tink.AccessesPartialKey; +import com.google.crypto.tink.Key; +import com.google.crypto.tink.subtle.Base64; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.RestrictedApi; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.Optional; +import javax.annotation.Nullable; + +/** + * Represents a public key for the JWT RSA SSA PKCS1 signature primitive. + * + * <p>Standard: https://datatracker.ietf.org/doc/html/rfc7518 + */ +public final class JwtRsaSsaPkcs1PublicKey extends JwtSignaturePublicKey { + private final JwtRsaSsaPkcs1Parameters parameters; + private final BigInteger modulus; + private final Optional<Integer> idRequirement; + private final Optional<String> kid; + + /** Builder for JwtRsaSsaPkcs1PublicKey. */ + public static class Builder { + private Optional<JwtRsaSsaPkcs1Parameters> parameters = Optional.empty(); + private Optional<BigInteger> modulus = Optional.empty(); + private Optional<Integer> idRequirement = Optional.empty(); + private Optional<String> customKid = Optional.empty(); + + private Builder() {} + + @CanIgnoreReturnValue + public Builder setParameters(JwtRsaSsaPkcs1Parameters parameters) { + this.parameters = Optional.of(parameters); + return this; + } + + @CanIgnoreReturnValue + public Builder setModulus(BigInteger modulus) { + this.modulus = Optional.of(modulus); + return this; + } + + @CanIgnoreReturnValue + public Builder setIdRequirement(Integer idRequirement) { + this.idRequirement = Optional.of(idRequirement); + return this; + } + + @CanIgnoreReturnValue + public Builder setCustomKid(String customKid) { + this.customKid = Optional.of(customKid); + return this; + } + + private Optional<String> computeKid() throws GeneralSecurityException { + if (parameters + .get() + .getKidStrategy() + .equals(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID)) { + if (customKid.isPresent()) { + throw new GeneralSecurityException( + "customKid must not be set for KidStrategy BASE64_ENCODED_KEY_ID"); + } + byte[] bigEndianKeyId = ByteBuffer.allocate(4).putInt(idRequirement.get()).array(); + return Optional.of(Base64.urlSafeEncode(bigEndianKeyId)); + } + if (parameters.get().getKidStrategy().equals(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM)) { + if (!customKid.isPresent()) { + throw new GeneralSecurityException("customKid needs to be set for KidStrategy CUSTOM"); + } + return customKid; + } + if (parameters.get().getKidStrategy().equals(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED)) { + if (customKid.isPresent()) { + throw new GeneralSecurityException("customKid must not be set for KidStrategy IGNORED"); + } + return Optional.empty(); + } + throw new IllegalStateException("Unknown kid strategy"); + } + + public JwtRsaSsaPkcs1PublicKey build() throws GeneralSecurityException { + if (!parameters.isPresent()) { + throw new GeneralSecurityException("Cannot build without parameters"); + } + + if (!modulus.isPresent()) { + throw new GeneralSecurityException("Cannot build without modulus"); + } + int modulusSize = modulus.get().bitLength(); + int paramModulusSize = parameters.get().getModulusSizeBits(); + // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf, B.3 requires p and q + // to be chosen such that 2^(paramModulusSize-1) < modulus < 2^paramModulusSize. + if (modulusSize != paramModulusSize) { + throw new GeneralSecurityException( + "Got modulus size " + + modulusSize + + ", but parameters requires modulus size " + + paramModulusSize); + } + + if (parameters.get().hasIdRequirement() && !idRequirement.isPresent()) { + throw new GeneralSecurityException( + "Cannot create key without ID requirement with parameters with ID requirement"); + } + + if (!parameters.get().hasIdRequirement() && idRequirement.isPresent()) { + throw new GeneralSecurityException( + "Cannot create key with ID requirement with parameters without ID requirement"); + } + + return new JwtRsaSsaPkcs1PublicKey( + parameters.get(), modulus.get(), idRequirement, computeKid()); + } + } + + private JwtRsaSsaPkcs1PublicKey( + JwtRsaSsaPkcs1Parameters parameters, + BigInteger modulus, + Optional<Integer> idRequirement, + Optional<String> kid) { + this.parameters = parameters; + this.modulus = modulus; + this.idRequirement = idRequirement; + this.kid = kid; + } + + @RestrictedApi( + explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", + link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", + allowedOnPath = ".*Test\\.java", + allowlistAnnotations = {AccessesPartialKey.class}) + public static Builder builder() { + return new Builder(); + } + + /** Returns the underlying key bytes. */ + @RestrictedApi( + explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", + link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", + allowedOnPath = ".*Test\\.java", + allowlistAnnotations = {AccessesPartialKey.class}) + public BigInteger getModulus() { + return modulus; + } + + /** + * Returns the "kid" to be used for this key. + * + * <p>If present, this kid will be written into the {@code kid} header during {{@code + * PublicKeySign#signAndEncode}. If absent, no kid will be written. + * + * <p>If present, and the {@code kid} header is present, the contents of the {@code kid} header + * needs to match the return value of this function. + */ + @Override + public Optional<String> getKid() { + return kid; + } + + @Override + public JwtRsaSsaPkcs1Parameters getParameters() { + return parameters; + } + + @Override + @Nullable + public Integer getIdRequirementOrNull() { + return idRequirement.orElse(null); + } + + @Override + public boolean equalsKey(Key o) { + if (!(o instanceof JwtRsaSsaPkcs1PublicKey)) { + return false; + } + JwtRsaSsaPkcs1PublicKey that = (JwtRsaSsaPkcs1PublicKey) o; + return that.parameters.equals(parameters) + && that.modulus.equals(modulus) + && that.kid.equals(kid) + && that.idRequirement.equals(idRequirement); + } +} diff --git a/java_src/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel b/java_src/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel index 6371ee03d..337614535 100644 --- a/java_src/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel +++ b/java_src/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel @@ -510,3 +510,17 @@ java_test( "@maven//:junit_junit", ], ) + +java_test( + name = "JwtRsaSsaPkcs1PublicKeyTest", + size = "small", + srcs = ["JwtRsaSsaPkcs1PublicKeyTest.java"], + deps = [ + "//src/main/java/com/google/crypto/tink/internal:key_tester", + "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_parameters", + "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_public_key", + "//src/main/java/com/google/crypto/tink/subtle:base64", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + ], +) diff --git a/java_src/src/test/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1PublicKeyTest.java b/java_src/src/test/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1PublicKeyTest.java new file mode 100644 index 000000000..4239f47ef --- /dev/null +++ b/java_src/src/test/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1PublicKeyTest.java @@ -0,0 +1,345 @@ +// Copyright 2023 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 org.junit.Assert.assertThrows; + +import com.google.crypto.tink.internal.KeyTester; +import com.google.crypto.tink.subtle.Base64; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.util.Optional; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class JwtRsaSsaPkcs1PublicKeyTest { + + // Test vector from https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2 + static final BigInteger MODULUS = + new BigInteger( + 1, + Base64.urlSafeDecode( + "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx" + + "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs" + + "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH" + + "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV" + + "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8" + + "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ")); + + @Test + public void build_kidStrategyIgnored_hasExpectedValues() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey key = + JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(MODULUS).build(); + assertThat(key.getParameters()).isEqualTo(parameters); + assertThat(key.getModulus()).isEqualTo(MODULUS); + assertThat(key.getKid()).isEqualTo(Optional.empty()); + assertThat(key.getIdRequirementOrNull()).isNull(); + } + + @Test + public void build_kidStrategyIgnored_setCustomKid_fails() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey.Builder builder = + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(parameters) + .setModulus(MODULUS) + .setCustomKid("customKid23"); + assertThrows(GeneralSecurityException.class, builder::build); + } + + @Test + public void build_kidStrategyIgnored_setIdRequirement_fails() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey.Builder builder = + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(parameters) + .setIdRequirement(123) + .setModulus(MODULUS); + assertThrows(GeneralSecurityException.class, builder::build); + } + + @Test + public void build_kidStrategyCustom_hasExpectedValues() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey key = + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(parameters) + .setModulus(MODULUS) + .setCustomKid("customKid777") + .build(); + assertThat(key.getParameters()).isEqualTo(parameters); + assertThat(key.getModulus()).isEqualTo(MODULUS); + assertThat(key.getKid().get()).isEqualTo("customKid777"); + assertThat(key.getIdRequirementOrNull()).isNull(); + } + + @Test + public void build_kidStrategyCustom_setIdRequirement_fails() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey.Builder builder = + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(parameters) + .setIdRequirement(123) + .setCustomKid("customKid777") + .setModulus(MODULUS); + assertThrows(GeneralSecurityException.class, builder::build); + } + + @Test + public void buildKidStrategyCustom_missingCustomKid_fails() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey.Builder builder = + JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(MODULUS); + assertThrows(GeneralSecurityException.class, builder::build); + } + + @Test + public void build_kidStrategyBase64_getProperties_succeeds() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey key = + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(parameters) + .setModulus(MODULUS) + .setIdRequirement(0x1ac6a944) + .build(); + assertThat(key.getParameters()).isEqualTo(parameters); + assertThat(key.getIdRequirementOrNull()).isEqualTo(0x1ac6a944); + // See JwtFormatTest.getKidFromTinkOutputPrefixType_success + assertThat(key.getKid()).isEqualTo(Optional.of("GsapRA")); + } + + @Test + public void build_kidStrategyBase64_noIdRequirement_throws() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey.Builder builder = + JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(MODULUS); + assertThrows(GeneralSecurityException.class, builder::build); + } + + @Test + public void build_kidStrategyBase64_setCustomKid_throws() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1PublicKey.Builder builder = + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(parameters) + .setIdRequirement(0x89abcdef) + .setCustomKid("customKid") + .setModulus(MODULUS); + assertThrows(GeneralSecurityException.class, builder::build); + } + + @Test + public void emptyBuild_fails() throws Exception { + assertThrows(GeneralSecurityException.class, () -> JwtRsaSsaPkcs1PublicKey.builder().build()); + } + + @Test + public void buildWithoutParameters_fails() throws Exception { + assertThrows( + GeneralSecurityException.class, + () -> JwtRsaSsaPkcs1PublicKey.builder().setModulus(MODULUS).build()); + } + + @Test + public void build_withoutModulus_fails() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + assertThrows( + GeneralSecurityException.class, + () -> JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).build()); + } + + @Test + public void build_invalidModulusSize_fails() throws Exception { + JwtRsaSsaPkcs1Parameters parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(3456) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + + // Modulus between 2^3455 and 2^3456 are valid. + BigInteger tooSmall = BigInteger.valueOf(2).pow(3455).subtract(BigInteger.ONE); + BigInteger tooBig = BigInteger.valueOf(2).pow(3456).add(BigInteger.ONE); + + assertThrows( + GeneralSecurityException.class, + () -> + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(parameters) + .setModulus(tooSmall) + .build()); + assertThrows( + GeneralSecurityException.class, + () -> + JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(tooBig).build()); + } + + @Test + public void testEqualities() throws Exception { + JwtRsaSsaPkcs1Parameters kidStrategyIgnoredParameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + JwtRsaSsaPkcs1Parameters kidStrategyIgnoredParametersCopy = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + + JwtRsaSsaPkcs1Parameters kidStrategyCustomParameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + + JwtRsaSsaPkcs1Parameters kidStrategyBase64Parameters = + JwtRsaSsaPkcs1Parameters.builder() + .setModulusSizeBits(2048) + .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) + .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) + .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) + .build(); + + new KeyTester() + .addEqualityGroup( + "KID Ignored, R256", + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyIgnoredParameters) + .setModulus(MODULUS) + .build(), + // the same key built twice must be equal + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyIgnoredParameters) + .setModulus(MODULUS) + .build(), + // the same key built with a copy of parameters must be equal + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyIgnoredParametersCopy) + .setModulus(MODULUS) + .build()) + // This group checks that keys with different key bytes are not equal + .addEqualityGroup( + "KID Ignored, different modulus", + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyIgnoredParameters) + .setModulus(MODULUS.add(BigInteger.ONE)) + .build()) + // These groups checks that keys with different customKid are not equal + .addEqualityGroup( + "KID Custom, customKid1", + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyCustomParameters) + .setModulus(MODULUS) + .setCustomKid("customKid1") + .build()) + .addEqualityGroup( + "KID Custom, customKid2", + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyCustomParameters) + .setModulus(MODULUS) + .setCustomKid("customKid2") + .build()) + // These groups checks that keys with different ID Requirements are not equal + .addEqualityGroup( + "Tink with key id 1907", + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyBase64Parameters) + .setModulus(MODULUS) + .setIdRequirement(1907) + .build()) + .addEqualityGroup( + "Tink with key id 1908", + JwtRsaSsaPkcs1PublicKey.builder() + .setParameters(kidStrategyBase64Parameters) + .setModulus(MODULUS) + .setIdRequirement(1908) + .build()) + .doTests(); + } +} |