Skip to content

Commit

Permalink
fix(jans-auth-server): fixed key rotation and added test to key gener…
Browse files Browse the repository at this point in the history
…ator context #3415
  • Loading branch information
yuriyz committed Jan 27, 2023
1 parent 0fd696e commit f3fb562
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.apache.log4j.Logger;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.status.StatusLogger;
import org.jetbrains.annotations.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;

Expand Down Expand Up @@ -56,6 +57,7 @@ public class KeyGenerator {
private static final String KEY_LENGTH = "key_length";
private static final String HELP = "h";
private static final String TEST_PROP_FILE = "test_prop_file";
private static final String KEY_OPS = "key_ops";

private static final String KEY_NAME_SUFFIX = "_keyId";

Expand Down Expand Up @@ -95,6 +97,7 @@ public Cli(String[] args) {
options.addOption(EXPIRATION, true, "Expiration in days.");
options.addOption(EXPIRATION_HOURS, true, "Expiration in hours.");
options.addOption(KEY_LENGTH, true, "Key length");
options.addOption(KEY_OPS, true, "Key Operations");
options.addOption(TEST_PROP_FILE, true, "Tests property file.");
options.addOption(HELP, false, "Show help.");
}
Expand All @@ -116,6 +119,8 @@ public void parse() {
help();
}

final KeyOps keyOps = parseKeyOps(cmd);

String[] sigAlgorithms = cmd.getOptionValues(SIGNING_KEYS);
String[] encAlgorithms = cmd.getOptionValues(ENCRYPTION_KEYS);
List<Algorithm> signatureAlgorithms = cmd.hasOption(SIGNING_KEYS) ? Algorithm.fromString(sigAlgorithms, Use.SIGNATURE) : new ArrayList<>();
Expand All @@ -130,6 +135,7 @@ public void parse() {
context.setExpirationHours(StringHelper.toInt(cmd.getOptionValue(EXPIRATION_HOURS), 0));
context.calculateExpiration();
context.setTestPropFile(TestPropFile.create(cmd));
context.setKeyOps(keyOps);

if (cmd.hasOption(OXELEVEN_ACCESS_TOKEN) && cmd.hasOption(OXELEVEN_GENERATE_KEY_ENDPOINT)) {
generateKeysWithEleven(cmd, signatureAlgorithms, encryptionAlgorithms, context);
Expand All @@ -146,6 +152,19 @@ public void parse() {
}
}

@Nullable
private KeyOps parseKeyOps(CommandLine cmd) {
if (!cmd.hasOption(KEY_OPS)) {
help();
}

final KeyOps keyOps = KeyOps.fromString(cmd.getOptionValue(KEY_OPS));
if (keyOps == null) {
help();
}
return keyOps;
}

private void generateKeysWithJansAuth(CommandLine cmd, List<Algorithm> signatureAlgorithms, List<Algorithm> encryptionAlgorithms, KeyGeneratorContext context) {
String keystore = cmd.getOptionValue(KEY_STORE_FILE);
String keypasswd = cmd.getOptionValue(KEY_STORE_PASSWORD);
Expand Down Expand Up @@ -183,10 +202,16 @@ private void generateKeys(KeyGeneratorContext context, List<Algorithm> signature
List<Algorithm> encryptionAlgorithms) throws CryptoProviderException, IOException {
JSONWebKeySet jwks = new JSONWebKeySet();

generateSignatureKeys(context, signatureAlgorithms, jwks, KeyOps.CONNECT);
generateSignatureKeys(context, signatureAlgorithms, jwks, KeyOps.SSA);
generateEncryptionKeys(context, encryptionAlgorithms, jwks, KeyOps.CONNECT);
generateEncryptionKeys(context, encryptionAlgorithms, jwks, KeyOps.SSA);
final KeyOps keyOps = context.getKeyOps();
if (keyOps == KeyOps.ALL) {
generateSignatureKeys(context, signatureAlgorithms, jwks, KeyOps.CONNECT);
generateSignatureKeys(context, signatureAlgorithms, jwks, KeyOps.SSA);
generateEncryptionKeys(context, encryptionAlgorithms, jwks, KeyOps.CONNECT);
generateEncryptionKeys(context, encryptionAlgorithms, jwks, KeyOps.SSA);
} else {
generateSignatureKeys(context, signatureAlgorithms, jwks, keyOps);
generateEncryptionKeys(context, encryptionAlgorithms, jwks, keyOps);
}

context.getTestPropFile().generate();
System.out.println(jwks);
Expand All @@ -195,7 +220,7 @@ private void generateKeys(KeyGeneratorContext context, List<Algorithm> signature
private void generateEncryptionKeys(KeyGeneratorContext context, List<Algorithm> encryptionAlgorithms, JSONWebKeySet jwks, KeyOps keyOps) throws CryptoProviderException {
for (Algorithm algorithm : encryptionAlgorithms) {
KeyEncryptionAlgorithm encryptionAlgorithm = KeyEncryptionAlgorithm.fromName(algorithm.getParamName());
JSONObject result = context.getCryptoProvider().generateKey(algorithm, context.calculateExpiration().getTimeInMillis(), context.getKeyLength(), keyOps);
JSONObject result = context.getCryptoProvider().generateKey(algorithm, context.getExpirationForKeyOps(keyOps), context.getKeyLength(), keyOps);

JSONWebKey key = new JSONWebKey();

Expand Down Expand Up @@ -225,7 +250,7 @@ private void generateEncryptionKeys(KeyGeneratorContext context, List<Algorithm>
private void generateSignatureKeys(KeyGeneratorContext context, List<Algorithm> signatureAlgorithms, JSONWebKeySet jwks, KeyOps keyOps) throws CryptoProviderException {
for (Algorithm algorithm : signatureAlgorithms) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.fromString(algorithm.getParamName());
JSONObject result = context.getCryptoProvider().generateKey(algorithm, context.calculateExpiration().getTimeInMillis(), context.getKeyLength(), keyOps);
JSONObject result = context.getCryptoProvider().generateKey(algorithm, context.getExpirationForKeyOps(keyOps), context.getKeyLength(), keyOps);

JSONWebKey key = new JSONWebKey();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.jans.as.client.util;

import io.jans.as.model.crypto.AbstractCryptoProvider;
import io.jans.as.model.jwk.KeyOps;

import java.util.Calendar;
import java.util.GregorianCalendar;
Expand All @@ -17,13 +18,33 @@ public class KeyGeneratorContext {
private int expirationDays;
private int expirationHours;
private Calendar expiration;
private KeyOps keyOps;

public Calendar calculateExpiration() {
public void calculateExpiration() {
Calendar calendar = new GregorianCalendar();
calendar.add(Calendar.DATE, getExpirationDays());
calendar.add(Calendar.HOUR, getExpirationHours());
this.expiration = calendar;
return calendar;
}

public long getExpirationForKeyOps(KeyOps keyOps) {
if (expiration == null) {
calculateExpiration();
}
if (keyOps == KeyOps.SSA) {
Calendar calendar = new GregorianCalendar();
calendar.add(Calendar.YEAR, 50);
return calendar.getTimeInMillis();
}
return expiration.getTimeInMillis();
}

public KeyOps getKeyOps() {
return keyOps;
}

public void setKeyOps(KeyOps keyOps) {
this.keyOps = keyOps;
}

public Calendar getExpiration() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.jans.as.client.util;

import io.jans.as.model.jwk.KeyOps;
import org.testng.annotations.Test;

import java.util.Calendar;

import static org.testng.Assert.assertTrue;

/**
* @author Yuriy Z
*/
public class KeyGeneratorContextTest {

@Test
public void getExpirationForKeyOps_forConnectKeyOps_shouldReturnPassedExpiration() {
KeyGeneratorContext context = new KeyGeneratorContext();
context.setExpirationHours(1);

final long expirationForKeyOps = context.getExpirationForKeyOps(KeyOps.CONNECT);

assertTrue(expirationForKeyOps < futureIn2Hours());
}

@Test
public void getExpirationForKeyOps_forSSAKeyOps_shouldReturnExpirationFarInFuture() {
KeyGeneratorContext context = new KeyGeneratorContext();
context.setExpirationHours(1);

final long expirationForKeyOps = context.getExpirationForKeyOps(KeyOps.SSA);

assertTrue(expirationForKeyOps > futureIn2Hours());
}

private long futureIn2Hours() {
Calendar future2hours = Calendar.getInstance();
future2hours.add(2, Calendar.HOUR_OF_DAY);
return future2hours.getTimeInMillis();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,14 @@ private static String getKidSuffix(Algorithm algorithm) {
return "_" + algorithm.getUse().getParamName().toLowerCase() + "_" + algorithm.getParamName().toLowerCase();
}

public String getAliasByAlgorithmForDeletion(Algorithm algorithm, String newAlias) throws KeyStoreException {
public String getAliasByAlgorithmForDeletion(Algorithm algorithm, String newAlias, KeyOps keyOps) throws KeyStoreException {
for (String alias : Collections.list(keyStore.aliases())) {

if (newAlias.equals(alias) || alias.startsWith(KeyOps.SSA.getValue())) { // skip newly created alias or ssa keys
if (newAlias.equals(alias)) { // skip newly created alias or ssa keys
continue;
}

if (alias.endsWith(getKidSuffix(algorithm))) {
if (alias.startsWith(keyOps.getValue()) && alias.endsWith(getKidSuffix(algorithm))) {
return alias;
}
}
Expand Down Expand Up @@ -441,26 +441,23 @@ private JSONObject generateKeySignature(Algorithm algorithm, Long expirationTime
KeyPairGenerator keyGen = null;
final AlgorithmFamily algorithmFamily = algorithm.getFamily();
switch (algorithmFamily) {
case RSA: {
case RSA:
keyGen = KeyPairGenerator.getInstance(algorithmFamily.toString(), "BC");
keyGen.initialize(keyLength, new SecureRandom());
break;
}
case EC: {
case EC:
ECGenParameterSpec eccgen = new ECGenParameterSpec(signatureAlgorithm.getCurve().getAlias());
keyGen = KeyPairGenerator.getInstance(algorithmFamily.toString(), "BC");
keyGen.initialize(eccgen, new SecureRandom());
break;
}
case ED: {
case ED:
EdDSAParameterSpec edSpec = new EdDSAParameterSpec(signatureAlgorithm.getCurve().getAlias());
keyGen = KeyPairGenerator.getInstance(signatureAlgorithm.getName(), "BC");
keyGen.initialize(edSpec, new SecureRandom());
break;
}
default: {
default:
throw new IllegalStateException("The provided signature algorithm parameter is not supported: algorithmFamily = " + algorithmFamily);
}

}
return getJson(algorithm, keyGen, signatureAlgorithm.getAlgorithm(), expirationTime, keyOps);
}
Expand All @@ -477,23 +474,21 @@ private JSONObject generateKeyEncryption(Algorithm algorithm, Long expirationTim
String signatureAlgorithm = null;
final AlgorithmFamily algorithmFamily = algorithm.getFamily();
switch (algorithmFamily) {
case RSA: {
case RSA:
keyGen = KeyPairGenerator.getInstance(algorithmFamily.toString(), "BC");
keyGen.initialize(keyLength, new SecureRandom());
signatureAlgorithm = "SHA256WITHRSA";
break;
}
case EC: {
case EC:
ECGenParameterSpec eccgen = new ECGenParameterSpec(keyEncryptionAlgorithm.getCurve().getAlias());
keyGen = KeyPairGenerator.getInstance(algorithmFamily.toString(), "BC");
keyGen.initialize(eccgen, new SecureRandom());
signatureAlgorithm = "SHA256WITHECDSA";
break;
}
default: {
default:
throw new IllegalStateException(
"The provided key encryption algorithm parameter is not supported: algorithmFamily = " + algorithmFamily);
}

}
return getJson(algorithm, keyGen, signatureAlgorithm, expirationTime, keyOps);
}
Expand All @@ -520,7 +515,7 @@ private JSONObject getJson(final Algorithm algorithm, final KeyPairGenerator key
String alias = getKid(algorithm, keyOps);
keyStore.setKeyEntry(alias, pk, keyStoreSecret.toCharArray(), chain);

final String oldAliasByAlgorithm = getAliasByAlgorithmForDeletion(algorithm, alias);
final String oldAliasByAlgorithm = getAliasByAlgorithmForDeletion(algorithm, alias, keyOps);
if (StringUtils.isNotBlank(oldAliasByAlgorithm)) {
keyStore.deleteEntry(oldAliasByAlgorithm);
LOG.trace("New key: " + alias + ", deleted key: " + oldAliasByAlgorithm);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
*/
public enum KeyOps {
CONNECT("connect"),
SSA("ssa");
SSA("ssa"),

ALL("all");

private final String value;

Expand Down

0 comments on commit f3fb562

Please sign in to comment.