aboutsummaryrefslogtreecommitdiff
path: root/java_src
diff options
context:
space:
mode:
authorioannanedelcu <ioannanedelcu@google.com>2023-07-25 10:12:09 -0700
committerCopybara-Service <copybara-worker@google.com>2023-07-25 10:13:35 -0700
commit4216d80439ad3a176be17eab7ad490470f43a8d8 (patch)
tree82aa2a1dfa714f64c67147cbd012a3bf555b24e6 /java_src
parented72a9b175ace4a4bc3bed7dc1d71087bb948493 (diff)
downloadtink-4216d80439ad3a176be17eab7ad490470f43a8d8.tar.gz
Add parsers and serializers for JwtRsaSsaPkcs1 parameters, public key and private key.
PiperOrigin-RevId: 550921147
Diffstat (limited to 'java_src')
-rw-r--r--java_src/BUILD.bazel2
-rw-r--r--java_src/src/main/java/com/google/crypto/tink/internal/testing/BUILD.bazel10
-rw-r--r--java_src/src/main/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtil.java39
-rw-r--r--java_src/src/main/java/com/google/crypto/tink/jwt/BUILD.bazel56
-rw-r--r--java_src/src/main/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerialization.java372
-rw-r--r--java_src/src/test/java/com/google/crypto/tink/internal/testing/BUILD.bazel11
-rw-r--r--java_src/src/test/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtilTest.java46
-rw-r--r--java_src/src/test/java/com/google/crypto/tink/jwt/BUILD.bazel28
-rw-r--r--java_src/src/test/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerializationTest.java588
9 files changed, 1152 insertions, 0 deletions
diff --git a/java_src/BUILD.bazel b/java_src/BUILD.bazel
index 1900a28ae..d7bba6372 100644
--- a/java_src/BUILD.bazel
+++ b/java_src/BUILD.bazel
@@ -241,6 +241,7 @@ gen_maven_jar_rules(
"//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_private_key",
+ "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_proto_serialization",
"//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",
@@ -673,6 +674,7 @@ gen_maven_jar_rules(
"//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_private_key-android",
+ "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_proto_serialization-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",
diff --git a/java_src/src/main/java/com/google/crypto/tink/internal/testing/BUILD.bazel b/java_src/src/main/java/com/google/crypto/tink/internal/testing/BUILD.bazel
index 4a2b5ccc8..d0c534909 100644
--- a/java_src/src/main/java/com/google/crypto/tink/internal/testing/BUILD.bazel
+++ b/java_src/src/main/java/com/google/crypto/tink/internal/testing/BUILD.bazel
@@ -82,3 +82,13 @@ android_library(
"@maven//:com_google_truth_truth",
],
)
+
+android_library(
+ name = "big_integer_test_util-android",
+ srcs = ["BigIntegerTestUtil.java"],
+)
+
+java_library(
+ name = "big_integer_test_util",
+ srcs = ["BigIntegerTestUtil.java"],
+)
diff --git a/java_src/src/main/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtil.java b/java_src/src/main/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtil.java
new file mode 100644
index 000000000..5ce9928bb
--- /dev/null
+++ b/java_src/src/main/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtil.java
@@ -0,0 +1,39 @@
+// Copyright 2023 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
+//
+// 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 specified language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package com.google.crypto.tink.internal.testing;
+
+/** Test helpers for BigInteger usage. */
+public final class BigIntegerTestUtil {
+ /**
+ * Adds a leading zero to the big-endian encoding, if necessary.
+ *
+ * <p>When encoding a BigInteger using `toBigEndianBytes()`, the minimal big-endian encoding uses
+ * the two's complement representation. This means that the encoding may have a leading zero.
+ */
+ public static byte[] ensureLeadingZeroBit(byte[] minimalEncodedBigInteger) {
+ if (minimalEncodedBigInteger[0] < 0) {
+ // Add a leading zero to the encoding.
+ byte[] twosComplementEncoded = new byte[minimalEncodedBigInteger.length + 1];
+ System.arraycopy(
+ minimalEncodedBigInteger, 0, twosComplementEncoded, 1, minimalEncodedBigInteger.length);
+ return twosComplementEncoded;
+ }
+ return minimalEncodedBigInteger;
+ }
+
+ private BigIntegerTestUtil() {}
+}
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 b8ecbbd92..c8b3ee51d 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
@@ -1201,3 +1201,59 @@ java_library(
"@maven//:com_google_errorprone_error_prone_annotations",
],
)
+
+android_library(
+ name = "jwt_rsa_ssa_pkcs1_proto_serialization-android",
+ srcs = ["JwtRsaSsaPkcs1ProtoSerialization.java"],
+ deps = [
+ ":jwt_rsa_ssa_pkcs1_parameters-android",
+ ":jwt_rsa_ssa_pkcs1_private_key-android",
+ ":jwt_rsa_ssa_pkcs1_public_key-android",
+ "//proto:jwt_rsa_ssa_pkcs1_java_proto_lite",
+ "//proto:tink_java_proto_lite",
+ "//src/main/java/com/google/crypto/tink:accesses_partial_key-android",
+ "//src/main/java/com/google/crypto/tink:secret_key_access-android",
+ "//src/main/java/com/google/crypto/tink/internal:big_integer_encoding-android",
+ "//src/main/java/com/google/crypto/tink/internal:enum_type_proto_converter-android",
+ "//src/main/java/com/google/crypto/tink/internal:key_parser-android",
+ "//src/main/java/com/google/crypto/tink/internal:key_serializer-android",
+ "//src/main/java/com/google/crypto/tink/internal:mutable_serialization_registry-android",
+ "//src/main/java/com/google/crypto/tink/internal:parameters_parser-android",
+ "//src/main/java/com/google/crypto/tink/internal:parameters_serializer-android",
+ "//src/main/java/com/google/crypto/tink/internal:proto_key_serialization-android",
+ "//src/main/java/com/google/crypto/tink/internal:proto_parameters_serialization-android",
+ "//src/main/java/com/google/crypto/tink/internal:util-android",
+ "//src/main/java/com/google/crypto/tink/util:bytes-android",
+ "//src/main/java/com/google/crypto/tink/util:secret_big_integer-android",
+ "@maven//:com_google_code_findbugs_jsr305",
+ "@maven//:com_google_protobuf_protobuf_javalite",
+ ],
+)
+
+java_library(
+ name = "jwt_rsa_ssa_pkcs1_proto_serialization",
+ srcs = ["JwtRsaSsaPkcs1ProtoSerialization.java"],
+ deps = [
+ ":jwt_rsa_ssa_pkcs1_parameters",
+ ":jwt_rsa_ssa_pkcs1_private_key",
+ ":jwt_rsa_ssa_pkcs1_public_key",
+ "//proto:jwt_rsa_ssa_pkcs1_java_proto",
+ "//proto:tink_java_proto",
+ "//src/main/java/com/google/crypto/tink:accesses_partial_key",
+ "//src/main/java/com/google/crypto/tink:secret_key_access",
+ "//src/main/java/com/google/crypto/tink/internal:big_integer_encoding",
+ "//src/main/java/com/google/crypto/tink/internal:enum_type_proto_converter",
+ "//src/main/java/com/google/crypto/tink/internal:key_parser",
+ "//src/main/java/com/google/crypto/tink/internal:key_serializer",
+ "//src/main/java/com/google/crypto/tink/internal:mutable_serialization_registry",
+ "//src/main/java/com/google/crypto/tink/internal:parameters_parser",
+ "//src/main/java/com/google/crypto/tink/internal:parameters_serializer",
+ "//src/main/java/com/google/crypto/tink/internal:proto_key_serialization",
+ "//src/main/java/com/google/crypto/tink/internal:proto_parameters_serialization",
+ "//src/main/java/com/google/crypto/tink/internal:util",
+ "//src/main/java/com/google/crypto/tink/util:bytes",
+ "//src/main/java/com/google/crypto/tink/util:secret_big_integer",
+ "@maven//:com_google_code_findbugs_jsr305",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
diff --git a/java_src/src/main/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerialization.java b/java_src/src/main/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerialization.java
new file mode 100644
index 000000000..9c54b9f94
--- /dev/null
+++ b/java_src/src/main/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerialization.java
@@ -0,0 +1,372 @@
+// 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.crypto.tink.internal.Util.toBytesFromPrintableAscii;
+
+import com.google.crypto.tink.AccessesPartialKey;
+import com.google.crypto.tink.SecretKeyAccess;
+import com.google.crypto.tink.internal.BigIntegerEncoding;
+import com.google.crypto.tink.internal.EnumTypeProtoConverter;
+import com.google.crypto.tink.internal.KeyParser;
+import com.google.crypto.tink.internal.KeySerializer;
+import com.google.crypto.tink.internal.MutableSerializationRegistry;
+import com.google.crypto.tink.internal.ParametersParser;
+import com.google.crypto.tink.internal.ParametersSerializer;
+import com.google.crypto.tink.internal.ProtoKeySerialization;
+import com.google.crypto.tink.internal.ProtoParametersSerialization;
+import com.google.crypto.tink.proto.JwtRsaSsaPkcs1Algorithm;
+import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
+import com.google.crypto.tink.proto.KeyTemplate;
+import com.google.crypto.tink.proto.OutputPrefixType;
+import com.google.crypto.tink.util.Bytes;
+import com.google.crypto.tink.util.SecretBigInteger;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.ExtensionRegistryLite;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import javax.annotation.Nullable;
+
+/**
+ * Methods to serialize and parse {@link JwtRsaSsaPkcs1PrivateKey} and {@link
+ * JwtRsaSsaPkcs1PublicKey} objects and {@link JwtRsaSsaPkcs1Parameters} objects.
+ */
+@AccessesPartialKey
+@SuppressWarnings("UnnecessarilyFullyQualified") // Fully specifying proto types is more readable
+final class JwtRsaSsaPkcs1ProtoSerialization {
+ private static final String PRIVATE_TYPE_URL =
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PrivateKey";
+ private static final Bytes PRIVATE_TYPE_URL_BYTES = toBytesFromPrintableAscii(PRIVATE_TYPE_URL);
+ private static final String PUBLIC_TYPE_URL =
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey";
+ private static final Bytes PUBLIC_TYPE_URL_BYTES = toBytesFromPrintableAscii(PUBLIC_TYPE_URL);
+
+ private static final ParametersSerializer<JwtRsaSsaPkcs1Parameters, ProtoParametersSerialization>
+ PARAMETERS_SERIALIZER =
+ ParametersSerializer.create(
+ JwtRsaSsaPkcs1ProtoSerialization::serializeParameters,
+ JwtRsaSsaPkcs1Parameters.class,
+ ProtoParametersSerialization.class);
+
+ private static final ParametersParser<ProtoParametersSerialization> PARAMETERS_PARSER =
+ ParametersParser.create(
+ JwtRsaSsaPkcs1ProtoSerialization::parseParameters,
+ PRIVATE_TYPE_URL_BYTES,
+ ProtoParametersSerialization.class);
+
+ private static final KeySerializer<JwtRsaSsaPkcs1PublicKey, ProtoKeySerialization>
+ PUBLIC_KEY_SERIALIZER =
+ KeySerializer.create(
+ JwtRsaSsaPkcs1ProtoSerialization::serializePublicKey,
+ JwtRsaSsaPkcs1PublicKey.class,
+ ProtoKeySerialization.class);
+
+ private static final KeyParser<ProtoKeySerialization> PUBLIC_KEY_PARSER =
+ KeyParser.create(
+ JwtRsaSsaPkcs1ProtoSerialization::parsePublicKey,
+ PUBLIC_TYPE_URL_BYTES,
+ ProtoKeySerialization.class);
+
+ private static final KeySerializer<JwtRsaSsaPkcs1PrivateKey, ProtoKeySerialization>
+ PRIVATE_KEY_SERIALIZER =
+ KeySerializer.create(
+ JwtRsaSsaPkcs1ProtoSerialization::serializePrivateKey,
+ JwtRsaSsaPkcs1PrivateKey.class,
+ ProtoKeySerialization.class);
+
+ private static final KeyParser<ProtoKeySerialization> PRIVATE_KEY_PARSER =
+ KeyParser.create(
+ JwtRsaSsaPkcs1ProtoSerialization::parsePrivateKey,
+ PRIVATE_TYPE_URL_BYTES,
+ ProtoKeySerialization.class);
+
+ private static final EnumTypeProtoConverter<
+ JwtRsaSsaPkcs1Algorithm, JwtRsaSsaPkcs1Parameters.Algorithm>
+ ALGORITHM_CONVERTER =
+ EnumTypeProtoConverter
+ .<JwtRsaSsaPkcs1Algorithm, JwtRsaSsaPkcs1Parameters.Algorithm>builder()
+ .add(JwtRsaSsaPkcs1Algorithm.RS256, JwtRsaSsaPkcs1Parameters.Algorithm.RS256)
+ .add(JwtRsaSsaPkcs1Algorithm.RS384, JwtRsaSsaPkcs1Parameters.Algorithm.RS384)
+ .add(JwtRsaSsaPkcs1Algorithm.RS512, JwtRsaSsaPkcs1Parameters.Algorithm.RS512)
+ .build();
+
+ private static OutputPrefixType toProtoOutputPrefixType(JwtRsaSsaPkcs1Parameters parameters) {
+ if (parameters
+ .getKidStrategy()
+ .equals(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID)) {
+ return OutputPrefixType.TINK;
+ }
+ return OutputPrefixType.RAW;
+ }
+
+ /** Encodes a BigInteger using a big-endian encoding. */
+ private static ByteString encodeBigInteger(BigInteger i) {
+ // Note that toBigEndianBytes() returns the minimal big-endian encoding using the two's
+ // complement representation. This means that the encoding may have a leading zero.
+ byte[] encoded = BigIntegerEncoding.toBigEndianBytes(i);
+ return ByteString.copyFrom(encoded);
+ }
+
+ private static com.google.crypto.tink.proto.JwtRsaSsaPkcs1KeyFormat getProtoKeyFormat(
+ JwtRsaSsaPkcs1Parameters parameters) throws GeneralSecurityException {
+ if (!parameters.getKidStrategy().equals(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED)
+ && !parameters
+ .getKidStrategy()
+ .equals(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID)) {
+ throw new GeneralSecurityException(
+ "Unable to serialize Parameters object with KidStrategy " + parameters.getKidStrategy());
+ }
+ return com.google.crypto.tink.proto.JwtRsaSsaPkcs1KeyFormat.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(ALGORITHM_CONVERTER.toProtoEnum(parameters.getAlgorithm()))
+ .setModulusSizeInBits(parameters.getModulusSizeBits())
+ .setPublicExponent(encodeBigInteger(parameters.getPublicExponent()))
+ .build();
+ }
+
+ private static ProtoParametersSerialization serializeParameters(
+ JwtRsaSsaPkcs1Parameters parameters) throws GeneralSecurityException {
+ OutputPrefixType outputPrefixType = toProtoOutputPrefixType(parameters);
+ return ProtoParametersSerialization.create(
+ KeyTemplate.newBuilder()
+ .setTypeUrl(PRIVATE_TYPE_URL)
+ .setValue(getProtoKeyFormat(parameters).toByteString())
+ .setOutputPrefixType(outputPrefixType)
+ .build());
+ }
+
+ private static com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey getProtoPublicKey(
+ JwtRsaSsaPkcs1PublicKey key) throws GeneralSecurityException {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.Builder builder =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(ALGORITHM_CONVERTER.toProtoEnum(key.getParameters().getAlgorithm()))
+ .setN(encodeBigInteger(key.getModulus()))
+ .setE(encodeBigInteger(key.getParameters().getPublicExponent()));
+ if (key.getParameters().getKidStrategy().equals(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM)) {
+ builder.setCustomKid(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.CustomKid.newBuilder()
+ .setValue(key.getKid().get())
+ .build());
+ }
+ return builder.build();
+ }
+
+ private static ProtoKeySerialization serializePublicKey(
+ JwtRsaSsaPkcs1PublicKey key, @Nullable SecretKeyAccess access)
+ throws GeneralSecurityException {
+ return ProtoKeySerialization.create(
+ PUBLIC_TYPE_URL,
+ getProtoPublicKey(key).toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ toProtoOutputPrefixType(key.getParameters()),
+ key.getIdRequirementOrNull());
+ }
+
+ private static ByteString encodeSecretBigInteger(SecretBigInteger i, SecretKeyAccess access) {
+ return encodeBigInteger(i.getBigInteger(access));
+ }
+
+ private static ProtoKeySerialization serializePrivateKey(
+ JwtRsaSsaPkcs1PrivateKey key, @Nullable SecretKeyAccess access)
+ throws GeneralSecurityException {
+ SecretKeyAccess a = SecretKeyAccess.requireAccess(access);
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey protoPrivateKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey.newBuilder()
+ .setVersion(0)
+ .setPublicKey(getProtoPublicKey(key.getPublicKey()))
+ .setD(encodeSecretBigInteger(key.getPrivateExponent(), a))
+ .setP(encodeSecretBigInteger(key.getPrimeP(), a))
+ .setQ(encodeSecretBigInteger(key.getPrimeQ(), a))
+ .setDp(encodeSecretBigInteger(key.getPrimeExponentP(), a))
+ .setDq(encodeSecretBigInteger(key.getPrimeExponentQ(), a))
+ .setCrt(encodeSecretBigInteger(key.getCrtCoefficient(), a))
+ .build();
+ return ProtoKeySerialization.create(
+ PRIVATE_TYPE_URL,
+ protoPrivateKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PRIVATE,
+ toProtoOutputPrefixType(key.getParameters()),
+ key.getIdRequirementOrNull());
+ }
+
+ private static BigInteger decodeBigInteger(ByteString data) {
+ return BigIntegerEncoding.fromUnsignedBigEndianBytes(data.toByteArray());
+ }
+
+ private static void validateVersion(int version) throws GeneralSecurityException {
+ if (version != 0) {
+ throw new GeneralSecurityException("Parsing failed: unknown version " + version);
+ }
+ }
+
+ private static JwtRsaSsaPkcs1Parameters parseParameters(
+ ProtoParametersSerialization serialization) throws GeneralSecurityException {
+ if (!serialization.getKeyTemplate().getTypeUrl().equals(PRIVATE_TYPE_URL)) {
+ throw new IllegalArgumentException(
+ "Wrong type URL in call to JwtRsaSsaPkcs1ProtoSerialization.parseParameters: "
+ + serialization.getKeyTemplate().getTypeUrl());
+ }
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1KeyFormat format;
+ try {
+ format =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1KeyFormat.parseFrom(
+ serialization.getKeyTemplate().getValue(), ExtensionRegistryLite.getEmptyRegistry());
+ } catch (InvalidProtocolBufferException e) {
+ throw new GeneralSecurityException("Parsing JwtRsaSsaPkcs1Parameters failed: ", e);
+ }
+ validateVersion(format.getVersion());
+ JwtRsaSsaPkcs1Parameters.KidStrategy kidStrategy = null;
+ if (serialization.getKeyTemplate().getOutputPrefixType().equals(OutputPrefixType.TINK)) {
+ kidStrategy = JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID;
+ }
+ if (serialization.getKeyTemplate().getOutputPrefixType().equals(OutputPrefixType.RAW)) {
+ kidStrategy = JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED;
+ }
+ if (kidStrategy == null) {
+ throw new GeneralSecurityException("Invalid OutputPrefixType for JwtHmacKeyFormat");
+ }
+ return JwtRsaSsaPkcs1Parameters.builder()
+ .setKidStrategy(kidStrategy)
+ .setAlgorithm(ALGORITHM_CONVERTER.fromProtoEnum(format.getAlgorithm()))
+ .setPublicExponent(decodeBigInteger(format.getPublicExponent()))
+ .setModulusSizeBits(format.getModulusSizeInBits())
+ .build();
+ }
+
+ private static JwtRsaSsaPkcs1PublicKey getPublicKeyFromProto(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoKey,
+ OutputPrefixType outputPrefixType,
+ @Nullable Integer idRequirement)
+ throws GeneralSecurityException {
+ validateVersion(protoKey.getVersion());
+
+ JwtRsaSsaPkcs1Parameters.Builder parametersBuilder = JwtRsaSsaPkcs1Parameters.builder();
+ JwtRsaSsaPkcs1PublicKey.Builder keyBuilder = JwtRsaSsaPkcs1PublicKey.builder();
+
+ if (outputPrefixType.equals(OutputPrefixType.TINK)) {
+ if (protoKey.hasCustomKid()) {
+ throw new GeneralSecurityException(
+ "Keys serialized with OutputPrefixType TINK should not have a custom kid");
+ }
+ if (idRequirement == null) {
+ throw new GeneralSecurityException(
+ "Keys serialized with OutputPrefixType TINK need an ID Requirement");
+ }
+ parametersBuilder.setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID);
+ keyBuilder.setIdRequirement(idRequirement);
+ } else if (outputPrefixType.equals(OutputPrefixType.RAW)) {
+ if (protoKey.hasCustomKid()) {
+ parametersBuilder.setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM);
+ keyBuilder.setCustomKid(protoKey.getCustomKid().getValue());
+ } else {
+ parametersBuilder.setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED);
+ }
+ }
+
+ BigInteger modulus = decodeBigInteger(protoKey.getN());
+ int modulusSizeInBits = modulus.bitLength();
+ parametersBuilder
+ .setAlgorithm(ALGORITHM_CONVERTER.fromProtoEnum(protoKey.getAlgorithm()))
+ .setPublicExponent(decodeBigInteger(protoKey.getE()))
+ .setModulusSizeBits(modulusSizeInBits);
+
+ keyBuilder.setModulus(modulus).setParameters(parametersBuilder.build());
+
+ return keyBuilder.build();
+ }
+
+ @SuppressWarnings("UnusedException")
+ private static JwtRsaSsaPkcs1PublicKey parsePublicKey(
+ ProtoKeySerialization serialization, @Nullable SecretKeyAccess access)
+ throws GeneralSecurityException {
+ if (!serialization.getTypeUrl().equals(PUBLIC_TYPE_URL)) {
+ throw new IllegalArgumentException(
+ "Wrong type URL in call to JwtRsaSsaPkcs1ProtoSerialization.parsePublicKey: "
+ + serialization.getTypeUrl());
+ }
+ try {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.parseFrom(
+ serialization.getValue(), ExtensionRegistryLite.getEmptyRegistry());
+ return getPublicKeyFromProto(
+ protoKey, serialization.getOutputPrefixType(), serialization.getIdRequirementOrNull());
+ } catch (InvalidProtocolBufferException e) {
+ throw new GeneralSecurityException("Parsing JwtRsaSsaPkcs1PublicKey failed");
+ }
+ }
+
+ private static SecretBigInteger decodeSecretBigInteger(ByteString data, SecretKeyAccess access) {
+ return SecretBigInteger.fromBigInteger(
+ BigIntegerEncoding.fromUnsignedBigEndianBytes(data.toByteArray()), access);
+ }
+
+ @SuppressWarnings("UnusedException")
+ private static JwtRsaSsaPkcs1PrivateKey parsePrivateKey(
+ ProtoKeySerialization serialization, @Nullable SecretKeyAccess access)
+ throws GeneralSecurityException {
+ if (!serialization.getTypeUrl().equals(PRIVATE_TYPE_URL)) {
+ throw new IllegalArgumentException(
+ "Wrong type URL in call to JwtRsaSsaPkcs1ProtoSerialization.parsePrivateKey: "
+ + serialization.getTypeUrl());
+ }
+ try {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey protoKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey.parseFrom(
+ serialization.getValue(), ExtensionRegistryLite.getEmptyRegistry());
+ validateVersion(protoKey.getVersion());
+
+ JwtRsaSsaPkcs1PublicKey publicKey =
+ getPublicKeyFromProto(
+ protoKey.getPublicKey(),
+ serialization.getOutputPrefixType(),
+ serialization.getIdRequirementOrNull());
+
+ SecretKeyAccess a = SecretKeyAccess.requireAccess(access);
+ return JwtRsaSsaPkcs1PrivateKey.builder()
+ .setPublicKey(publicKey)
+ .setPrimes(
+ decodeSecretBigInteger(protoKey.getP(), a),
+ decodeSecretBigInteger(protoKey.getQ(), a))
+ .setPrivateExponent(decodeSecretBigInteger(protoKey.getD(), a))
+ .setPrimeExponents(
+ decodeSecretBigInteger(protoKey.getDp(), a),
+ decodeSecretBigInteger(protoKey.getDq(), a))
+ .setCrtCoefficient(decodeSecretBigInteger(protoKey.getCrt(), a))
+ .build();
+ } catch (InvalidProtocolBufferException e) {
+ throw new GeneralSecurityException("Parsing JwtRsaSsaPkcs1PrivateKey failed");
+ }
+ }
+
+ public static void register() throws GeneralSecurityException {
+ register(MutableSerializationRegistry.globalInstance());
+ }
+
+ public static void register(MutableSerializationRegistry registry)
+ throws GeneralSecurityException {
+ registry.registerParametersSerializer(PARAMETERS_SERIALIZER);
+ registry.registerParametersParser(PARAMETERS_PARSER);
+ registry.registerKeySerializer(PUBLIC_KEY_SERIALIZER);
+ registry.registerKeyParser(PUBLIC_KEY_PARSER);
+ registry.registerKeySerializer(PRIVATE_KEY_SERIALIZER);
+ registry.registerKeyParser(PRIVATE_KEY_PARSER);
+ }
+
+ private JwtRsaSsaPkcs1ProtoSerialization() {}
+}
diff --git a/java_src/src/test/java/com/google/crypto/tink/internal/testing/BUILD.bazel b/java_src/src/test/java/com/google/crypto/tink/internal/testing/BUILD.bazel
index 012df0e0e..6e62c513a 100644
--- a/java_src/src/test/java/com/google/crypto/tink/internal/testing/BUILD.bazel
+++ b/java_src/src/test/java/com/google/crypto/tink/internal/testing/BUILD.bazel
@@ -68,3 +68,14 @@ java_test(
"@maven//:junit_junit",
],
)
+
+java_test(
+ name = "BigIntegerTestUtilTest",
+ size = "small",
+ srcs = ["BigIntegerTestUtilTest.java"],
+ deps = [
+ "//src/main/java/com/google/crypto/tink/internal/testing:big_integer_test_util",
+ "@maven//:com_google_truth_truth",
+ "@maven//:junit_junit",
+ ],
+)
diff --git a/java_src/src/test/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtilTest.java b/java_src/src/test/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtilTest.java
new file mode 100644
index 000000000..726d77e87
--- /dev/null
+++ b/java_src/src/test/java/com/google/crypto/tink/internal/testing/BigIntegerTestUtilTest.java
@@ -0,0 +1,46 @@
+// 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.internal.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class BigIntegerTestUtilTest {
+
+ @Test
+ public void ensureLeadingZeroBit_bitNotSet_works() throws Exception {
+ // 258 = 1 * 256 + 2.
+ // If the most significant bit is not set, there is no leading zero.
+ byte[] encodingOf258 = new byte[] {(byte) 1, (byte) 2};
+
+ assertThat(BigIntegerTestUtil.ensureLeadingZeroBit(encodingOf258)).isEqualTo(encodingOf258);
+ }
+
+ @Test
+ public void ensureLeadingZeroBit_bitSet_works() throws Exception {
+ // If the most significant bit is set, then a leading zero is added.
+ byte[] encodingOf255 = new byte[] {(byte) 0xff};
+ byte[] twoComplementEncodingOf255 = new byte[] {(byte) 0, (byte) 0xff};
+
+ assertThat(BigIntegerTestUtil.ensureLeadingZeroBit(encodingOf255))
+ .isEqualTo(twoComplementEncodingOf255);
+ }
+}
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 79e027604..58d80f6a0 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
@@ -543,3 +543,31 @@ java_test(
"@maven//:junit_junit",
],
)
+
+java_test(
+ name = "JwtRsaSsaPkcs1ProtoSerializationTest",
+ size = "small",
+ srcs = ["JwtRsaSsaPkcs1ProtoSerializationTest.java"],
+ deps = [
+ "//proto:jwt_rsa_ssa_pkcs1_java_proto",
+ "//proto:tink_java_proto",
+ "//src/main/java/com/google/crypto/tink:insecure_secret_key_access",
+ "//src/main/java/com/google/crypto/tink:key",
+ "//src/main/java/com/google/crypto/tink:parameters",
+ "//src/main/java/com/google/crypto/tink/internal:big_integer_encoding",
+ "//src/main/java/com/google/crypto/tink/internal:mutable_serialization_registry",
+ "//src/main/java/com/google/crypto/tink/internal:proto_key_serialization",
+ "//src/main/java/com/google/crypto/tink/internal:proto_parameters_serialization",
+ "//src/main/java/com/google/crypto/tink/internal/testing:asserts",
+ "//src/main/java/com/google/crypto/tink/internal/testing:big_integer_test_util",
+ "//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_private_key",
+ "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_proto_serialization",
+ "//src/main/java/com/google/crypto/tink/jwt:jwt_rsa_ssa_pkcs1_public_key",
+ "//src/main/java/com/google/crypto/tink/subtle:base64",
+ "//src/main/java/com/google/crypto/tink/util:secret_big_integer",
+ "@maven//:com_google_protobuf_protobuf_java",
+ "@maven//:com_google_truth_truth",
+ "@maven//:junit_junit",
+ ],
+)
diff --git a/java_src/src/test/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerializationTest.java b/java_src/src/test/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerializationTest.java
new file mode 100644
index 000000000..6d654d613
--- /dev/null
+++ b/java_src/src/test/java/com/google/crypto/tink/jwt/JwtRsaSsaPkcs1ProtoSerializationTest.java
@@ -0,0 +1,588 @@
+// 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 com.google.crypto.tink.internal.testing.Asserts.assertEqualWhenValueParsed;
+import static org.junit.Assert.assertThrows;
+
+import com.google.crypto.tink.InsecureSecretKeyAccess;
+import com.google.crypto.tink.Key;
+import com.google.crypto.tink.Parameters;
+import com.google.crypto.tink.internal.BigIntegerEncoding;
+import com.google.crypto.tink.internal.MutableSerializationRegistry;
+import com.google.crypto.tink.internal.ProtoKeySerialization;
+import com.google.crypto.tink.internal.ProtoParametersSerialization;
+import com.google.crypto.tink.internal.testing.BigIntegerTestUtil;
+import com.google.crypto.tink.proto.JwtRsaSsaPkcs1Algorithm;
+import com.google.crypto.tink.proto.JwtRsaSsaPkcs1KeyFormat;
+import com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.CustomKid;
+import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
+import com.google.crypto.tink.proto.OutputPrefixType;
+import com.google.crypto.tink.subtle.Base64;
+import com.google.crypto.tink.util.SecretBigInteger;
+import com.google.protobuf.ByteString;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.FromDataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public final class JwtRsaSsaPkcs1ProtoSerializationTest {
+ private static final String TYPE_URL =
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PrivateKey";
+
+ // Test vector from https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2
+ static final byte[] EXPONENT_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(Base64.urlSafeDecode("AQAB"));
+ static final BigInteger EXPONENT = new BigInteger(1, EXPONENT_BYTES);
+ static final byte[] MODULUS_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(
+ Base64.urlSafeDecode(
+ "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
+ + "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
+ + "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
+ + "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
+ + "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
+ + "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ"));
+ static final BigInteger MODULUS = new BigInteger(1, MODULUS_BYTES);
+ static final byte[] P_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(
+ Base64.urlSafeDecode(
+ "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
+ + "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
+ + "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc"));
+ static final BigInteger P = new BigInteger(1, P_BYTES);
+ static final byte[] Q_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(
+ Base64.urlSafeDecode(
+ "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
+ + "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
+ + "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc"));
+ static final BigInteger Q = new BigInteger(1, Q_BYTES);
+ static final byte[] D_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(
+ Base64.urlSafeDecode(
+ "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
+ + "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
+ + "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
+ + "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
+ + "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
+ + "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ"));
+ static final BigInteger D = new BigInteger(1, D_BYTES);
+ static final byte[] DP_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(
+ Base64.urlSafeDecode(
+ "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
+ + "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
+ + "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0"));
+ static final BigInteger DP = new BigInteger(1, DP_BYTES);
+ static final byte[] DQ_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(
+ Base64.urlSafeDecode(
+ "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
+ + "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
+ + "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU"));
+ static final BigInteger DQ = new BigInteger(1, DQ_BYTES);
+ static final byte[] Q_INV_BYTES =
+ BigIntegerTestUtil.ensureLeadingZeroBit(
+ Base64.urlSafeDecode(
+ "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
+ + "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
+ + "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"));
+ static final BigInteger Q_INV = new BigInteger(1, Q_INV_BYTES);
+
+ private static final MutableSerializationRegistry registry = new MutableSerializationRegistry();
+
+ private static final class AlgorithmTuple {
+ final JwtRsaSsaPkcs1Parameters.Algorithm algorithm;
+ final JwtRsaSsaPkcs1Algorithm protoAlgorithm;
+
+ AlgorithmTuple(
+ JwtRsaSsaPkcs1Parameters.Algorithm algorithm, JwtRsaSsaPkcs1Algorithm protoAlgorithm) {
+ this.algorithm = algorithm;
+ this.protoAlgorithm = protoAlgorithm;
+ }
+ }
+
+ @DataPoints("algorithms")
+ public static final AlgorithmTuple[] ALGORITHMS =
+ new AlgorithmTuple[] {
+ new AlgorithmTuple(JwtRsaSsaPkcs1Parameters.Algorithm.RS256, JwtRsaSsaPkcs1Algorithm.RS256),
+ new AlgorithmTuple(JwtRsaSsaPkcs1Parameters.Algorithm.RS384, JwtRsaSsaPkcs1Algorithm.RS384),
+ new AlgorithmTuple(JwtRsaSsaPkcs1Parameters.Algorithm.RS512, JwtRsaSsaPkcs1Algorithm.RS512),
+ };
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ JwtRsaSsaPkcs1ProtoSerialization.register(registry);
+ }
+
+ @Theory
+ public void serializeParseParameters_kidStrategyIgnored_works(
+ @FromDataPoints("algorithms") AlgorithmTuple algorithmTuple) throws Exception {
+ JwtRsaSsaPkcs1Parameters parameters =
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED)
+ .setAlgorithm(algorithmTuple.algorithm)
+ .build();
+ ProtoParametersSerialization serialization =
+ ProtoParametersSerialization.create(
+ TYPE_URL,
+ OutputPrefixType.RAW,
+ JwtRsaSsaPkcs1KeyFormat.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(algorithmTuple.protoAlgorithm)
+ .setModulusSizeInBits(2048)
+ .setPublicExponent(
+ ByteString.copyFrom(
+ BigIntegerEncoding.toBigEndianBytes(BigInteger.valueOf(65537))))
+ .build());
+
+ ProtoParametersSerialization serialized =
+ registry.serializeParameters(parameters, ProtoParametersSerialization.class);
+ assertEqualWhenValueParsed(JwtRsaSsaPkcs1KeyFormat.parser(), serialized, serialization);
+
+ Parameters parsed = registry.parseParameters(serialization);
+ assertThat(parsed).isEqualTo(parameters);
+ }
+
+ @Test
+ public void serializeParseParameters_kidStrategyBase64_works() throws Exception {
+ JwtRsaSsaPkcs1Parameters parameters =
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(EXPONENT)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID)
+ .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256)
+ .build();
+ ProtoParametersSerialization serialization =
+ ProtoParametersSerialization.create(
+ TYPE_URL,
+ OutputPrefixType.TINK,
+ JwtRsaSsaPkcs1KeyFormat.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setModulusSizeInBits(2048)
+ .setPublicExponent(ByteString.copyFrom(EXPONENT_BYTES))
+ .build());
+
+ ProtoParametersSerialization serialized =
+ registry.serializeParameters(parameters, ProtoParametersSerialization.class);
+ assertEqualWhenValueParsed(JwtRsaSsaPkcs1KeyFormat.parser(), serialized, serialization);
+
+ Parameters parsed = registry.parseParameters(serialization);
+ assertThat(parsed).isEqualTo(parameters);
+ }
+
+ @Test
+ public void serializeParameters_kidStrategyCustom_cannotBeSerialized_throws() throws Exception {
+ JwtRsaSsaPkcs1Parameters parameters =
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(EXPONENT)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM)
+ .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS512)
+ .build();
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.serializeParameters(parameters, ProtoParametersSerialization.class));
+ }
+
+ @Test
+ public void parseParameters_crunchy_cannotBeParsed_throws() throws Exception {
+ ProtoParametersSerialization serialization =
+ ProtoParametersSerialization.create(
+ TYPE_URL,
+ OutputPrefixType.CRUNCHY,
+ JwtRsaSsaPkcs1KeyFormat.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS512)
+ .setModulusSizeInBits(2048)
+ .setPublicExponent(ByteString.copyFrom(EXPONENT_BYTES))
+ .build());
+ assertThrows(GeneralSecurityException.class, () -> registry.parseParameters(serialization));
+ }
+
+ @Theory
+ public void serializeParsePublicKey_kidIgnored_equal(
+ @FromDataPoints("algorithms") AlgorithmTuple algorithmTuple) throws Exception {
+ JwtRsaSsaPkcs1PublicKey key =
+ JwtRsaSsaPkcs1PublicKey.builder()
+ .setParameters(
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED)
+ .setAlgorithm(algorithmTuple.algorithm)
+ .build())
+ .setModulus(MODULUS)
+ .build();
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(algorithmTuple.protoAlgorithm)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_kidCustom_equal() throws Exception {
+ JwtRsaSsaPkcs1PublicKey key =
+ JwtRsaSsaPkcs1PublicKey.builder()
+ .setParameters(
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM)
+ .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256)
+ .build())
+ .setModulus(MODULUS)
+ .setCustomKid("myCustomKid")
+ .build();
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .setCustomKid(CustomKid.newBuilder().setValue("myCustomKid").build())
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void serializeParsePublicKey_base64Kid_equal() throws Exception {
+ JwtRsaSsaPkcs1PublicKey key =
+ JwtRsaSsaPkcs1PublicKey.builder()
+ .setParameters(
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID)
+ .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256)
+ .build())
+ .setModulus(MODULUS)
+ .setIdRequirement(12345)
+ .build();
+
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ Key parsed = registry.parseKey(serialization, /* access= */ null);
+ assertThat(parsed.equalsKey(key)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(key, ProtoKeySerialization.class, /* access= */ null);
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void parsePublicKey_crunchy_cannotBeParsed_throws() throws Exception {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.CRUNCHY,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class, () -> registry.parseKey(serialization, /* access= */ null));
+ }
+
+ @Test
+ public void parsePublicKey_tinkAndCustomKeyId_throws() throws Exception {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .setCustomKid(CustomKid.newBuilder().setValue("WillNotParseWithTINK").build())
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class, () -> registry.parseKey(serialization, /* access= */ null));
+ }
+
+ @Test
+ public void parsePublicKey_wrongVersion_throws() throws Exception {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(1)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class, () -> registry.parseKey(serialization, /* access= */ null));
+ }
+
+ @Test
+ public void parsePublicKey_unknownAlgorithm_throws() throws Exception {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey protoPublicKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(1)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS_UNKNOWN)
+ .build();
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey",
+ protoPublicKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PUBLIC,
+ OutputPrefixType.TINK,
+ /* idRequirement= */ 12345);
+
+ assertThrows(
+ GeneralSecurityException.class, () -> registry.parseKey(serialization, /* access= */ null));
+ }
+
+ @Test
+ public void serializeParsePrivateKey_kidIgnored_equal() throws Exception {
+ JwtRsaSsaPkcs1PublicKey publicKey =
+ JwtRsaSsaPkcs1PublicKey.builder()
+ .setParameters(
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED)
+ .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256)
+ .build())
+ .setModulus(MODULUS)
+ .build();
+ JwtRsaSsaPkcs1PrivateKey privateKey =
+ JwtRsaSsaPkcs1PrivateKey.builder()
+ .setPublicKey(publicKey)
+ .setPrimes(
+ SecretBigInteger.fromBigInteger(P, InsecureSecretKeyAccess.get()),
+ SecretBigInteger.fromBigInteger(Q, InsecureSecretKeyAccess.get()))
+ .setPrivateExponent(SecretBigInteger.fromBigInteger(D, InsecureSecretKeyAccess.get()))
+ .setPrimeExponents(
+ SecretBigInteger.fromBigInteger(DP, InsecureSecretKeyAccess.get()),
+ SecretBigInteger.fromBigInteger(DQ, InsecureSecretKeyAccess.get()))
+ .setCrtCoefficient(
+ SecretBigInteger.fromBigInteger(Q_INV, InsecureSecretKeyAccess.get()))
+ .build();
+
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey protoPrivateKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey.newBuilder()
+ .setVersion(0)
+ .setPublicKey(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .build())
+ .setD(ByteString.copyFrom(D_BYTES))
+ .setP(ByteString.copyFrom(P_BYTES))
+ .setQ(ByteString.copyFrom(Q_BYTES))
+ .setDp(ByteString.copyFrom(DP_BYTES))
+ .setDq(ByteString.copyFrom(DQ_BYTES))
+ .setCrt(ByteString.copyFrom(Q_INV_BYTES))
+ .build();
+
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PrivateKey",
+ protoPrivateKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PRIVATE,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ Key parsed = registry.parseKey(serialization, InsecureSecretKeyAccess.get());
+ assertThat(parsed.equalsKey(privateKey)).isTrue();
+
+ ProtoKeySerialization serialized =
+ registry.serializeKey(
+ privateKey, ProtoKeySerialization.class, InsecureSecretKeyAccess.get());
+ assertEqualWhenValueParsed(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey.parser(), serialized, serialization);
+ }
+
+ @Test
+ public void parsePrivateKey_invalidVersion_throws() throws Exception {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey protoPrivateKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey.newBuilder()
+ .setVersion(1)
+ .setPublicKey(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .build())
+ .setD(ByteString.copyFrom(D_BYTES))
+ .setP(ByteString.copyFrom(P_BYTES))
+ .setQ(ByteString.copyFrom(Q_BYTES))
+ .setDp(ByteString.copyFrom(DP_BYTES))
+ .setDq(ByteString.copyFrom(DQ_BYTES))
+ .setCrt(ByteString.copyFrom(Q_INV_BYTES))
+ .build();
+
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PrivateKey",
+ protoPrivateKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PRIVATE,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.parseKey(serialization, InsecureSecretKeyAccess.get()));
+ }
+
+ @Test
+ public void serializePrivateKey_noSecretKeyAccess_throws() throws Exception {
+ JwtRsaSsaPkcs1PublicKey publicKey =
+ JwtRsaSsaPkcs1PublicKey.builder()
+ .setParameters(
+ JwtRsaSsaPkcs1Parameters.builder()
+ .setModulusSizeBits(2048)
+ .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4)
+ .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED)
+ .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256)
+ .build())
+ .setModulus(MODULUS)
+ .build();
+ JwtRsaSsaPkcs1PrivateKey privateKey =
+ JwtRsaSsaPkcs1PrivateKey.builder()
+ .setPublicKey(publicKey)
+ .setPrimes(
+ SecretBigInteger.fromBigInteger(P, InsecureSecretKeyAccess.get()),
+ SecretBigInteger.fromBigInteger(Q, InsecureSecretKeyAccess.get()))
+ .setPrivateExponent(SecretBigInteger.fromBigInteger(D, InsecureSecretKeyAccess.get()))
+ .setPrimeExponents(
+ SecretBigInteger.fromBigInteger(DP, InsecureSecretKeyAccess.get()),
+ SecretBigInteger.fromBigInteger(DQ, InsecureSecretKeyAccess.get()))
+ .setCrtCoefficient(
+ SecretBigInteger.fromBigInteger(Q_INV, InsecureSecretKeyAccess.get()))
+ .build();
+
+ assertThrows(
+ GeneralSecurityException.class,
+ () -> registry.serializeKey(privateKey, ProtoKeySerialization.class, /* access= */ null));
+ }
+
+ @Test
+ public void parsePrivateKey_noSecretKeyAccess_throws() throws Exception {
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey protoPrivateKey =
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PrivateKey.newBuilder()
+ .setVersion(1)
+ .setPublicKey(
+ com.google.crypto.tink.proto.JwtRsaSsaPkcs1PublicKey.newBuilder()
+ .setVersion(0)
+ .setAlgorithm(JwtRsaSsaPkcs1Algorithm.RS256)
+ .setN(ByteString.copyFrom(MODULUS_BYTES))
+ .setE(ByteString.copyFrom(EXPONENT_BYTES))
+ .build())
+ .setD(ByteString.copyFrom(D_BYTES))
+ .setP(ByteString.copyFrom(P_BYTES))
+ .setQ(ByteString.copyFrom(Q_BYTES))
+ .setDp(ByteString.copyFrom(DP_BYTES))
+ .setDq(ByteString.copyFrom(DQ_BYTES))
+ .setCrt(ByteString.copyFrom(Q_INV_BYTES))
+ .build();
+
+ ProtoKeySerialization serialization =
+ ProtoKeySerialization.create(
+ "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PrivateKey",
+ protoPrivateKey.toByteString(),
+ KeyMaterialType.ASYMMETRIC_PRIVATE,
+ OutputPrefixType.RAW,
+ /* idRequirement= */ null);
+
+ assertThrows(
+ GeneralSecurityException.class, () -> registry.parseKey(serialization, /* access= */ null));
+ }
+}