Skip to content

Commit

Permalink
Add methods for secret key generation (smallrye#342)
Browse files Browse the repository at this point in the history
  • Loading branch information
ejba committed Feb 21, 2021
1 parent 8a24d88 commit 2838ce0
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 1 deletion.
5 changes: 5 additions & 0 deletions implementation/common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ 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_A128KW("ECDH-ES+128KW"),
ECDH_ES_A192KW("ECDH-ES+192KW"),
ECDH_ES_A256KW("ECDH-ES+A256KW"),
A128KW("A128KW"),
Expand All @@ -32,4 +32,5 @@ public String getAlgorithm() {
public static KeyEncryptionAlgorithm fromAlgorithm(String algorithmName) {
return KeyEncryptionAlgorithm.valueOf(algorithmName.replaceAll("-", "_").replaceAll("\\+", "_"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;

import org.jboss.logging.Messages;
Expand All @@ -21,4 +22,7 @@ interface JWTUtilMessages {

@Message(id = 2, value = "No resource with the named %s location exists")
IOException keyNotFound(String keyLocation);

@Message(id = 3, value = "Algorithm %s is not a symmetric-key algorithm")
InvalidAlgorithmParameterException requiresSymmetricAlgo(String algorithmName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
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;
Expand All @@ -39,6 +41,7 @@
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;

import javax.crypto.SecretKey;
Expand Down Expand Up @@ -68,6 +71,21 @@ public final class KeyUtils {
private KeyUtils() {
}

public static final EnumMap<KeyEncryptionAlgorithm, Integer> KEY_ENCRYPTION_BITS = new EnumMap(
KeyEncryptionAlgorithm.class);
static {
KEY_ENCRYPTION_BITS.put(KeyEncryptionAlgorithm.A128KW, 128);
KEY_ENCRYPTION_BITS.put(KeyEncryptionAlgorithm.A192KW, 192);
KEY_ENCRYPTION_BITS.put(KeyEncryptionAlgorithm.A256KW, 256);
}

public static final EnumMap<SignatureAlgorithm, Integer> SIGNATURE_ALGORITHM_BITS = new EnumMap(SignatureAlgorithm.class);
static {
SIGNATURE_ALGORITHM_BITS.put(SignatureAlgorithm.HS256, 256);
SIGNATURE_ALGORITHM_BITS.put(SignatureAlgorithm.HS384, 384);
SIGNATURE_ALGORITHM_BITS.put(SignatureAlgorithm.HS512, 512);
}

public static PrivateKey readPrivateKey(String pemResName) throws IOException, GeneralSecurityException {
return readPrivateKey(pemResName, SignatureAlgorithm.RS256);
}
Expand Down Expand Up @@ -246,6 +264,44 @@ public static SecretKey createSecretKeyFromSecret(String secret) {
return new SecretKeySpec(secretBytes, "AES");
}

/**
* Generates a SecretKey.
*
* @param algo key encryption algorithm.
* @return SecretKey.
* @throws NoSuchAlgorithmException algorithm not found.
*/
public static SecretKey generateSecretKey(KeyEncryptionAlgorithm algo) throws InvalidAlgorithmParameterException {
if (!KEY_ENCRYPTION_BITS.containsKey(algo)) {
throw JWTUtilMessages.msg.requiresSymmetricAlgo(algo.name());
}

byte[] secretBytes = new byte[KEY_ENCRYPTION_BITS.get(algo) / 8];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(secretBytes);

return new SecretKeySpec(secretBytes, "AES");
}

/**
* Generates a SecretKey.
*
* @param algo signature algorithm.
* @return SecretKey.
* @throws NoSuchAlgorithmException algorithm not found.
*/
public static SecretKey generateSecretKey(SignatureAlgorithm algo) throws InvalidAlgorithmParameterException {
if (!SIGNATURE_ALGORITHM_BITS.containsKey(algo)) {
throw JWTUtilMessages.msg.requiresSymmetricAlgo(algo.name());
}

byte[] secretBytes = new byte[SIGNATURE_ALGORITHM_BITS.get(algo) / 8];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(secretBytes);

return new SecretKeySpec(secretBytes, "HMAC");
}

/**
* Decode a PEM encoded public key string to an RSA or EllipticCurve PublicKey
*
Expand Down Expand Up @@ -273,6 +329,24 @@ public static PublicKey decodeEncryptionPublicKey(String pemEncoded, KeyEncrypti
return kf.generatePublic(spec);
}

static int getKeySizeFromAlgo(SignatureAlgorithm algo) throws InvalidAlgorithmParameterException {
String algoName = algo.name();
if (algoName.startsWith("HS")) {
String extractedSize = algoName.substring(2);
return Integer.parseInt(extractedSize) / 8;
}
throw JWTUtilMessages.msg.requiresSymmetricAlgo(algo.name());
}

static int getKeySizeFromAlgo(KeyEncryptionAlgorithm algo) throws InvalidAlgorithmParameterException {
String algoName = algo.name();
if (algoName.startsWith("PBES2_HS")) {
String extractedSize = algoName.substring(13, 16);
return Integer.parseInt(extractedSize) / 8;
}
throw JWTUtilMessages.msg.requiresSymmetricAlgo(algo.name());
}

static String keyFactoryAlgorithm(SignatureAlgorithm algo) throws NoSuchAlgorithmException {
if (algo.name().startsWith("RS") || algo.name().startsWith("PS")) {
return RSA;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.smallrye.jwt.util;

import static org.junit.Assert.assertEquals;

import java.security.InvalidAlgorithmParameterException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import javax.crypto.SecretKey;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm;

@RunWith(Parameterized.class)
public class GenerateSecretFromKeyEncryptionAlgoTest {

@Parameterized.Parameters
public static Collection<Object[]> data() {
Collection<Object[]> args = new ArrayList<>();
for (Map.Entry<KeyEncryptionAlgorithm, Integer> e : KeyUtils.KEY_ENCRYPTION_BITS.entrySet()) {
args.add(new Object[] { e.getKey().getAlgorithm(), e.getValue() });
}
return args;
}

@Parameterized.Parameter
public String algoName;

@Parameterized.Parameter(1)
public Integer keySizeInBits;

@Test
public void givenSymmetricAlgo_thenReturnSecretKey() throws InvalidAlgorithmParameterException {
KeyEncryptionAlgorithm algo = KeyEncryptionAlgorithm.fromAlgorithm(algoName);
SecretKey keySpec = KeyUtils.generateSecretKey(algo);
assertEquals("AES", keySpec.getAlgorithm());
assertEquals(keySizeInBits / 8, keySpec.getEncoded().length);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.smallrye.jwt.util;

import static org.junit.Assert.assertEquals;

import java.security.InvalidAlgorithmParameterException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import javax.crypto.SecretKey;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import io.smallrye.jwt.algorithm.SignatureAlgorithm;

@RunWith(Parameterized.class)
public class GenerateSecretFromSignatureAlgoTest {

@Parameterized.Parameters
public static Collection<Object[]> data() {
Collection<Object[]> args = new ArrayList<>();
for (Map.Entry<SignatureAlgorithm, Integer> e : KeyUtils.SIGNATURE_ALGORITHM_BITS.entrySet()) {
args.add(new Object[] { e.getKey().getAlgorithm(), e.getValue() });
}
return args;
}

@Parameterized.Parameter
public String algoName;

@Parameterized.Parameter(1)
public Integer keySizeInBits;

@Test
public void givenSymmetricAlgo_thenReturnSecretKey() throws InvalidAlgorithmParameterException {
SignatureAlgorithm algo = SignatureAlgorithm.fromAlgorithm(algoName);
SecretKey keySpec = KeyUtils.generateSecretKey(algo);
assertEquals("HMAC", keySpec.getAlgorithm());
assertEquals(keySizeInBits / 8, keySpec.getEncoded().length);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.smallrye.jwt.util;

import java.security.InvalidAlgorithmParameterException;

import javax.crypto.SecretKey;

import org.junit.Test;

import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm;
import io.smallrye.jwt.algorithm.SignatureAlgorithm;

public class KeyUtilsTest {

@Test(expected = InvalidAlgorithmParameterException.class)
public void givenAsymmetricKeyEncryptionAlgo_thenThrowsInvalidAlgorithmException()
throws InvalidAlgorithmParameterException {
SecretKey keySpec = KeyUtils.generateSecretKey(KeyEncryptionAlgorithm.RSA_OAEP);
}

@Test(expected = InvalidAlgorithmParameterException.class)
public void givenAsymmetricSignatureAlgo_thenThrowsInvalidAlgorithmException() throws InvalidAlgorithmParameterException {
SecretKey keySpec = KeyUtils.generateSecretKey(SignatureAlgorithm.RS256);
}
}

0 comments on commit 2838ce0

Please sign in to comment.