diff --git a/implementation/common/pom.xml b/implementation/common/pom.xml
index e08df08c..c5f7dee0 100644
--- a/implementation/common/pom.xml
+++ b/implementation/common/pom.xml
@@ -51,6 +51,11 @@
org.jboss.logging
jboss-logging-processor
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
diff --git a/implementation/common/src/main/java/io/smallrye/jwt/algorithm/KeyEncryptionAlgorithm.java b/implementation/common/src/main/java/io/smallrye/jwt/algorithm/KeyEncryptionAlgorithm.java
index 0462e00c..2a54544d 100644
--- a/implementation/common/src/main/java/io/smallrye/jwt/algorithm/KeyEncryptionAlgorithm.java
+++ b/implementation/common/src/main/java/io/smallrye/jwt/algorithm/KeyEncryptionAlgorithm.java
@@ -6,30 +6,36 @@
* @see https://tools.ietf.org/html/rfc7518#section-4
*/
public enum KeyEncryptionAlgorithm {
- RSA_OAEP("RSA-OAEP"),
- RSA_OAEP_256("RSA-OAEP-256"),
- ECDH_ES("ECDH-ES"),
- ECDH_ES_A28KW("ECDH-ES+128KW"),
- ECDH_ES_A192KW("ECDH-ES+192KW"),
- ECDH_ES_A256KW("ECDH-ES+A256KW"),
- A128KW("A128KW"),
- A192KW("A192KW"),
- A256KW("A256KW"),
- PBES2_HS256_A128KW("PBES2-HS256+A128KW"),
- PBES2_HS384_A192KW("PBES2-HS384+A192KW"),
- PBES2_HS512_A256KW("PBES2-HS512+A256KW");
+ RSA_OAEP("RSA-OAEP", 2048),
+ RSA_OAEP_256("RSA-OAEP-256", 2048),
+ ECDH_ES("ECDH-ES", 256),
+ ECDH_ES_A128KW("ECDH-ES+128KW", 128),
+ ECDH_ES_A192KW("ECDH-ES+192KW", 192),
+ ECDH_ES_A256KW("ECDH-ES+A256KW", 256),
+ A128KW("A128KW", 128),
+ A192KW("A192KW", 192),
+ A256KW("A256KW", 256),
+ PBES2_HS256_A128KW("PBES2-HS256+A128KW", 256),
+ PBES2_HS384_A192KW("PBES2-HS384+A192KW", 384),
+ PBES2_HS512_A256KW("PBES2-HS512+A256KW", 512);
private String algorithmName;
+ private int keySizeBits;
- private KeyEncryptionAlgorithm(String algorithmName) {
+ private KeyEncryptionAlgorithm(String algorithmName, int keySizeBits) {
this.algorithmName = algorithmName;
- }
-
- public String getAlgorithm() {
- return algorithmName;
+ this.keySizeBits = keySizeBits;
}
public static KeyEncryptionAlgorithm fromAlgorithm(String algorithmName) {
return KeyEncryptionAlgorithm.valueOf(algorithmName.replaceAll("-", "_").replaceAll("\\+", "_"));
}
+
+ public int getKeySizeBits() {
+ return keySizeBits;
+ }
+
+ public String getAlgorithm() {
+ return algorithmName;
+ }
}
diff --git a/implementation/common/src/main/java/io/smallrye/jwt/algorithm/SignatureAlgorithm.java b/implementation/common/src/main/java/io/smallrye/jwt/algorithm/SignatureAlgorithm.java
index f727d4de..4602ce4a 100644
--- a/implementation/common/src/main/java/io/smallrye/jwt/algorithm/SignatureAlgorithm.java
+++ b/implementation/common/src/main/java/io/smallrye/jwt/algorithm/SignatureAlgorithm.java
@@ -6,18 +6,28 @@
* @see https://tools.ietf.org/html/rfc7518#section-3
*/
public enum SignatureAlgorithm {
- RS256,
- RS384,
- RS512,
- ES256,
- ES384,
- ES512,
- HS256,
- HS384,
- HS512,
- PS256,
- PS384,
- PS512;
+ RS256(256),
+ RS384(384),
+ RS512(512),
+ ES256(256),
+ ES384(384),
+ ES512(512),
+ HS256(256),
+ HS384(384),
+ HS512(512),
+ PS256(256),
+ PS384(384),
+ PS512(512);
+
+ private int keySize;
+
+ private SignatureAlgorithm(int keySize) {
+ this.keySize = keySize;
+ }
+
+ public int getKeySizeBits() {
+ return this.keySize;
+ }
public String getAlgorithm() {
return this.name();
diff --git a/implementation/common/src/main/java/io/smallrye/jwt/algorithm/SymmetricKeyAlgorithm.java b/implementation/common/src/main/java/io/smallrye/jwt/algorithm/SymmetricKeyAlgorithm.java
new file mode 100644
index 00000000..38e9bb5f
--- /dev/null
+++ b/implementation/common/src/main/java/io/smallrye/jwt/algorithm/SymmetricKeyAlgorithm.java
@@ -0,0 +1,27 @@
+package io.smallrye.jwt.algorithm;
+
+public enum SymmetricKeyAlgorithm {
+ HS256("HS256", 256),
+ HS384("HS384", 384),
+ HS512("HS512", 512);
+
+ private String algorithmName;
+ private int keySize;
+
+ private SymmetricKeyAlgorithm(String algorithmName, int keySize) {
+ this.algorithmName = algorithmName;
+ this.keySize = keySize;
+ }
+
+ public String getAlgorithm() {
+ return algorithmName;
+ }
+
+ public int getKeySize() {
+ return keySize;
+ }
+
+ public static SymmetricKeyAlgorithm fromAlgorithm(String algorithmName) {
+ return SymmetricKeyAlgorithm.valueOf(algorithmName.replaceAll("-", "_").replaceAll("\\+", "_"));
+ }
+}
\ No newline at end of file
diff --git a/implementation/common/src/main/java/io/smallrye/jwt/util/KeyUtils.java b/implementation/common/src/main/java/io/smallrye/jwt/util/KeyUtils.java
index 292464ca..207d77cc 100644
--- a/implementation/common/src/main/java/io/smallrye/jwt/util/KeyUtils.java
+++ b/implementation/common/src/main/java/io/smallrye/jwt/util/KeyUtils.java
@@ -31,6 +31,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
@@ -64,6 +65,7 @@ public final class KeyUtils {
private static final String RSA = "RSA";
private static final String EC = "EC";
+ private static final String HMAC = "HMAC";
private KeyUtils() {
}
@@ -243,7 +245,47 @@ public static PublicKey decodePublicKey(String pemEncoded) throws GeneralSecurit
public static SecretKey createSecretKeyFromSecret(String secret) {
byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
- return new SecretKeySpec(secretBytes, "AES");
+ return createSecretKeyFromSecret(secretBytes, "AES");
+ }
+
+ public static SecretKey createSecretKeyFromSecret(byte[] secretBytes, String algo) {
+ return new SecretKeySpec(secretBytes, algo);
+ }
+
+ /**
+ * Generates a SecretKey.
+ *
+ * @param algo key encryption algorithm.
+ * @return SecretKey.
+ * @throws NoSuchAlgorithmException algorithm not found.
+ */
+ public static SecretKey generateSecretKey(KeyEncryptionAlgorithm algo) throws NoSuchAlgorithmException {
+ final String algoName = getSymmetricAlgoName(algo);
+
+ int keySizeBits = algo.getKeySizeBits() / 8;
+ byte[] bytes = new byte[keySizeBits];
+ SecureRandom secureRandom = new SecureRandom();
+ secureRandom.nextBytes(bytes);
+
+ return createSecretKeyFromSecret(bytes, algoName);
+ }
+
+ /**
+ * Generates a SecretKey.
+ *
+ * @param algo signature algorithm.
+ * @return SecretKey.
+ * @throws NoSuchAlgorithmException algorithm not found.
+ */
+ public static SecretKey generateSecretKey(SignatureAlgorithm algo) throws NoSuchAlgorithmException {
+ final String algoName = getSymmetricAlgoName(algo);
+
+ int keySizeBits = algo.getKeySizeBits() / 8;
+ byte[] bytes = new byte[keySizeBits];
+ SecureRandom secureRandom = new SecureRandom();
+ secureRandom.nextBytes(bytes);
+
+ return createSecretKeyFromSecret(bytes, algoName);
}
/**
@@ -273,6 +315,20 @@ public static PublicKey decodeEncryptionPublicKey(String pemEncoded, KeyEncrypti
return kf.generatePublic(spec);
}
+ static String getSymmetricAlgoName(KeyEncryptionAlgorithm algo) throws NoSuchAlgorithmException {
+ if (algo.name().startsWith("PBES2")) {
+ return HMAC;
+ }
+ throw JWTUtilMessages.msg.unsupportedAlgorithm(algo.name());
+ }
+
+ static String getSymmetricAlgoName(SignatureAlgorithm algo) throws NoSuchAlgorithmException {
+ if (algo.name().startsWith("HS")) {
+ return HMAC;
+ }
+ throw JWTUtilMessages.msg.unsupportedAlgorithm(algo.name());
+ }
+
static String keyFactoryAlgorithm(SignatureAlgorithm algo) throws NoSuchAlgorithmException {
if (algo.name().startsWith("RS") || algo.name().startsWith("PS")) {
return RSA;
diff --git a/implementation/common/src/test/java/io/smallrye/jwt/util/KeyUtilsTest.java b/implementation/common/src/test/java/io/smallrye/jwt/util/KeyUtilsTest.java
new file mode 100644
index 00000000..e0e001a8
--- /dev/null
+++ b/implementation/common/src/test/java/io/smallrye/jwt/util/KeyUtilsTest.java
@@ -0,0 +1,43 @@
+package io.smallrye.jwt.util;
+
+import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm;
+import io.smallrye.jwt.algorithm.SignatureAlgorithm;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import javax.crypto.SecretKey;
+import java.security.NoSuchAlgorithmException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class KeyUtilsTest {
+
+ @ParameterizedTest
+ @EnumSource(mode = EnumSource.Mode.INCLUDE, names = { "PBES2_HS256_A128KW", "PBES2_HS384_A192KW", "PBES2_HS512_A256KW" })
+ void givenSymmetricKey_thenReturnSecretKey(KeyEncryptionAlgorithm algo) throws NoSuchAlgorithmException {
+ SecretKey keySpec = KeyUtils.generateSecretKey(algo);
+ assertEquals(algo.getKeySizeBits() / 8, keySpec.getEncoded().length);
+ assertEquals("HMAC", keySpec.getAlgorithm());
+ }
+
+ @ParameterizedTest
+ @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = { "PBES2_HS256_A128KW", "PBES2_HS384_A192KW", "PBES2_HS512_A256KW" })
+ void givenAsSymmetricKey_thenThrowNoSuchAlgorithmException(KeyEncryptionAlgorithm algo) {
+ assertThrows(NoSuchAlgorithmException.class, () -> KeyUtils.generateSecretKey(algo));
+ }
+
+ @ParameterizedTest
+ @EnumSource(mode = EnumSource.Mode.INCLUDE, names = { "HS256", "HS384", "HS512" })
+ void givenSymmetricKey_thenReturnSecretKey(SignatureAlgorithm algo) throws NoSuchAlgorithmException {
+ SecretKey keySpec = KeyUtils.generateSecretKey(algo);
+ assertEquals(algo.getKeySizeBits() / 8, keySpec.getEncoded().length);
+ assertEquals("HMAC", keySpec.getAlgorithm());
+ }
+
+ @ParameterizedTest
+ @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = { "HS256", "HS384", "HS512" })
+ void givenAsSymmetricKey_thenThrowNoSuchAlgorithmException(SignatureAlgorithm algo) {
+ assertThrows(NoSuchAlgorithmException.class, () -> KeyUtils.generateSecretKey(algo));
+ }
+}
\ No newline at end of file