Skip to content

Commit

Permalink
Merge pull request #315 from JanssenProject/yuriy_build
Browse files Browse the repository at this point in the history
Revert "Revert "Extending crypto algorithms, Sub PR 2""
  • Loading branch information
yuriyz committed Nov 8, 2021
2 parents 5e1a0e5 + 856df9f commit 250f1d3
Show file tree
Hide file tree
Showing 16 changed files with 1,230 additions and 411 deletions.
9 changes: 5 additions & 4 deletions client/src/main/java/io/jans/as/client/util/KeyGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.jans.as.model.crypto.ElevenCryptoProvider;
import io.jans.as.model.crypto.encryption.KeyEncryptionAlgorithm;
import io.jans.as.model.crypto.signature.SignatureAlgorithm;
import io.jans.as.model.exception.CryptoProviderException;
import io.jans.as.model.jwk.Algorithm;
import io.jans.as.model.jwk.JSONWebKey;
import io.jans.as.model.jwk.JSONWebKeySet;
Expand All @@ -32,6 +33,7 @@
import org.json.JSONObject;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
Expand Down Expand Up @@ -185,7 +187,7 @@ public void parse() {
}

private void generateKeys(AbstractCryptoProvider cryptoProvider, List<Algorithm> signatureAlgorithms,
List<Algorithm> encryptionAlgorithms, int expiration, int expirationHours, String testPropFile) throws Exception {
List<Algorithm> encryptionAlgorithms, int expiration, int expirationHours, String testPropFile) throws CryptoProviderException, IOException {
JSONWebKeySet jwks = new JSONWebKeySet();

Calendar calendar = new GregorianCalendar();
Expand All @@ -198,7 +200,7 @@ private void generateKeys(AbstractCryptoProvider cryptoProvider, List<Algorithm>

for (Algorithm algorithm : signatureAlgorithms) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.fromString(algorithm.getParamName());
JSONObject result = cryptoProvider.generateKey(algorithm, calendar.getTimeInMillis(), Use.SIGNATURE);
JSONObject result = cryptoProvider.generateKey(algorithm, calendar.getTimeInMillis());

JSONWebKey key = new JSONWebKey();

Expand Down Expand Up @@ -226,8 +228,7 @@ private void generateKeys(AbstractCryptoProvider cryptoProvider, List<Algorithm>
}
for (Algorithm algorithm : encryptionAlgorithms) {
KeyEncryptionAlgorithm encryptionAlgorithm = KeyEncryptionAlgorithm.fromName(algorithm.getParamName());
JSONObject result = cryptoProvider.generateKey(algorithm,
calendar.getTimeInMillis(), Use.ENCRYPTION);
JSONObject result = cryptoProvider.generateKey(algorithm, calendar.getTimeInMillis());

JSONWebKey key = new JSONWebKey();

Expand Down
158 changes: 102 additions & 56 deletions model/src/main/java/io/jans/as/model/crypto/AbstractCryptoProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,77 @@
import io.jans.as.model.crypto.signature.AlgorithmFamily;
import io.jans.as.model.crypto.signature.EllipticEdvardsCurve;
import io.jans.as.model.crypto.signature.SignatureAlgorithm;
import io.jans.as.model.jwk.*;
import io.jans.as.model.exception.CryptoProviderException;
import io.jans.as.model.exception.InvalidParameterException;
import io.jans.as.model.jwk.Algorithm;
import io.jans.as.model.jwk.JSONWebKey;
import io.jans.as.model.jwk.JSONWebKeySet;
import io.jans.as.model.jwk.JWKParameter;
import io.jans.as.model.jwk.Use;
import io.jans.as.model.util.Base64Util;
import io.jans.eleven.model.JwksRequestParam;
import io.jans.eleven.model.KeyRequestParam;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.*;
import java.security.spec.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;

/**
* @author Javier Rojas Blum
* @version September 30, 2021
* @author Sergey Manoylo
* @version October 26, 2021
*/
public abstract class AbstractCryptoProvider {

protected static final Logger LOG = Logger.getLogger(AbstractCryptoProvider.class);

private int keyRegenerationIntervalInDays = -1;
private static final String DEF_EXPIRESON = "\n\tExpires On: ";
private static final String DEF_TODAYSDATE = "\n\tToday's Date: ";
private static final String DEF_DAYS = " days";

public JSONObject generateKey(Algorithm algorithm, Long expirationTime) throws Exception {
return generateKey(algorithm, expirationTime, Use.SIGNATURE);
}
private int keyRegenerationIntervalInDays = -1;

public abstract JSONObject generateKey(Algorithm algorithm, Long expirationTime, Use use) throws Exception;
public abstract JSONObject generateKey(Algorithm algorithm, Long expirationTime) throws CryptoProviderException;

public abstract String sign(String signingInput, String keyId, String sharedSecret, SignatureAlgorithm signatureAlgorithm) throws Exception;
public abstract String sign(String signingInput, String keyId, String sharedSecret, SignatureAlgorithm signatureAlgorithm) throws CryptoProviderException;

public abstract boolean verifySignature(String signingInput, String encodedSignature, String keyId, JSONObject jwks, String sharedSecret, SignatureAlgorithm signatureAlgorithm) throws Exception;
public abstract boolean verifySignature(String signingInput, String encodedSignature, String keyId, JSONObject jwks, String sharedSecret, SignatureAlgorithm signatureAlgorithm) throws CryptoProviderException;

public abstract boolean deleteKey(String keyId) throws Exception;
public abstract boolean deleteKey(String keyId) throws CryptoProviderException;

public abstract boolean containsKey(String keyId);

public List<String> getKeys() {
return Lists.newArrayList();
}

public abstract PrivateKey getPrivateKey(String keyId) throws Exception;
public abstract PrivateKey getPrivateKey(String keyId) throws CryptoProviderException;

public abstract PublicKey getPublicKey(String alias) throws CryptoProviderException;

public String getKeyId(JSONWebKeySet jsonWebKeySet, Algorithm algorithm, Use use) throws Exception {
@SuppressWarnings("java:S1130")
public String getKeyId(JSONWebKeySet jsonWebKeySet, Algorithm algorithm, Use use) throws CryptoProviderException {
if (algorithm == null || AlgorithmFamily.HMAC.equals(algorithm.getFamily())) {
return null;
}
Expand All @@ -72,7 +94,7 @@ public String getKeyId(JSONWebKeySet jsonWebKeySet, Algorithm algorithm, Use use
return null;
}

public JwksRequestParam getJwksRequestParam(JSONObject jwkJsonObject) throws JSONException {
public JwksRequestParam getJwksRequestParam(JSONObject jwkJsonObject) {
JwksRequestParam jwks = new JwksRequestParam();
jwks.setKeyRequestParams(new ArrayList<>());

Expand All @@ -95,7 +117,7 @@ public JwksRequestParam getJwksRequestParam(JSONObject jwkJsonObject) throws JSO
}

public static JSONObject generateJwks(AbstractCryptoProvider cryptoProvider, AppConfiguration configuration) {
Calendar expirationTime = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
GregorianCalendar expirationTime = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
expirationTime.add(Calendar.HOUR, configuration.getKeyRegenerationInterval());
expirationTime.add(Calendar.SECOND, configuration.getIdTokenLifetime());

Expand All @@ -107,12 +129,12 @@ public static JSONObject generateJwks(AbstractCryptoProvider cryptoProvider, App
for (Algorithm alg : Algorithm.values()) {
try {
if (!allowedAlgs.isEmpty() && !allowedAlgs.contains(alg.getParamName())) {
LOG.debug("Key generation for " + alg + " is skipped because it's not allowed by keyAlgsAllowedForGeneration configuration property.");
LOG.debug(String.format("Key generation for %s is skipped because it's not allowed by keyAlgsAllowedForGeneration configuration property.", alg.toString()));
continue;
}
keys.put(cryptoProvider.generateKey(alg, expiration, alg.getUse()));
keys.put(cryptoProvider.generateKey(alg, expiration));
} catch (Exception ex) {
LOG.error("Algorithm: " + alg + ex.getMessage(), ex);
LOG.error(String.format("Algorithm: %s", alg), ex);
}
}

Expand All @@ -122,61 +144,84 @@ public static JSONObject generateJwks(AbstractCryptoProvider cryptoProvider, App
return jsonObject;
}

public PublicKey getPublicKey(String alias, JSONObject jwks, Algorithm requestedAlgorithm) throws Exception {
public PublicKey getPublicKey(String alias, JSONObject jwks, Algorithm requestedAlgorithm) throws CryptoProviderException {
JSONArray webKeys = jwks.getJSONArray(JWKParameter.JSON_WEB_KEY_SET);
if (alias == null) {
if (webKeys.length() == 1) {
JSONObject key = webKeys.getJSONObject(0);
return processKey(requestedAlgorithm, alias, key);
} else {
return null;

try {
if (alias == null) {
if (webKeys.length() == 1) {
JSONObject key = webKeys.getJSONObject(0);
return processKey(requestedAlgorithm, alias, key);
} else {
return null;
}
}
}
for (int i = 0; i < webKeys.length(); i++) {
JSONObject key = webKeys.getJSONObject(i);
if (alias.equals(key.getString(JWKParameter.KEY_ID))) {
PublicKey publicKey = processKey(requestedAlgorithm, alias, key);
if (publicKey != null) {
return publicKey;
for (int i = 0; i < webKeys.length(); i++) {
JSONObject key = webKeys.getJSONObject(i);
if (alias.equals(key.getString(JWKParameter.KEY_ID))) {
PublicKey publicKey = processKey(requestedAlgorithm, alias, key);
if (publicKey != null) {
return publicKey;
}
}
}
} catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidParameterSpecException | InvalidParameterException e) {
throw new CryptoProviderException(e);
}

return null;
}

private PublicKey processKey(Algorithm requestedAlgorithm, String alias, JSONObject key) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidParameterSpecException {
private PublicKey processKey(Algorithm requestedAlgorithm, String alias, JSONObject key) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException, InvalidParameterException {
PublicKey publicKey = null;
AlgorithmFamily family = null;
AlgorithmFamily algorithmFamily = null;

if (key.has(JWKParameter.ALGORITHM)) {
Algorithm algorithm = Algorithm.fromString(key.optString(JWKParameter.ALGORITHM));

if (requestedAlgorithm != null && !requestedAlgorithm.equals(algorithm)) {
LOG.trace("kid matched but algorithm does not match. kid algorithm:" + algorithm + ", requestedAlgorithm:" + requestedAlgorithm + ", kid:" + alias);
LOG.trace("kid matched but algorithm does not match. kid algorithm:" + algorithm
+ ", requestedAlgorithm:" + requestedAlgorithm + ", kid:" + alias);
return null;
}
family = algorithm.getFamily();
algorithmFamily = algorithm.getFamily();
} else if (key.has(JWKParameter.KEY_TYPE)) {
family = AlgorithmFamily.fromString(key.get(JWKParameter.KEY_TYPE).toString());
algorithmFamily = AlgorithmFamily.fromString(key.getString(JWKParameter.KEY_TYPE));
} else {
throw new InvalidParameterException("Wrong key (JSONObject): doesn't contain 'alg' and 'kty' properties");
}

if (AlgorithmFamily.RSA.equals(family)) {
switch (algorithmFamily) {
case RSA: {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
new BigInteger(1, Base64Util.base64urldecode(key.getString(JWKParameter.MODULUS))),
new BigInteger(1, Base64Util.base64urldecode(key.getString(JWKParameter.EXPONENT))));
publicKey = keyFactory.generatePublic(pubKeySpec);
} else if (AlgorithmFamily.EC.equals(family)) {
break;
}
case EC: {
EllipticEdvardsCurve curve = EllipticEdvardsCurve.fromString(key.optString(JWKParameter.CURVE));
AlgorithmParameters parameters = AlgorithmParameters.getInstance(AlgorithmFamily.EC.toString());
parameters.init(new ECGenParameterSpec(curve.getAlias()));
ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);

publicKey = KeyFactory.getInstance(AlgorithmFamily.EC.toString()).generatePublic(new ECPublicKeySpec(
new ECPoint(
new BigInteger(1, Base64Util.base64urldecode(key.getString(JWKParameter.X))),
new BigInteger(1, Base64Util.base64urldecode(key.getString(JWKParameter.Y)))
), ecParameters));
publicKey = KeyFactory.getInstance(AlgorithmFamily.EC.toString())
.generatePublic(new ECPublicKeySpec(
new ECPoint(
new BigInteger(1, Base64Util.base64urldecode(key.getString(JWKParameter.X))),
new BigInteger(1, Base64Util.base64urldecode(key.getString(JWKParameter.Y)))),
ecParameters));
break;
}
case ED: {
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
Base64Util.base64urldecode(key.getString(JWKParameter.X)));
publicKey = KeyFactory.getInstance(key.optString(JWKParameter.ALGORITHM)).generatePublic(publicKeySpec);
break;
}
default: {
throw new InvalidParameterException(String.format("Wrong AlgorithmFamily value: %s", algorithmFamily));
}
}

if (key.has(JWKParameter.EXPIRATION_TIME)) {
Expand All @@ -193,26 +238,27 @@ protected void checkKeyExpiration(String alias, Long expirationTime) {
Date today = new Date();
long expiresInDays = (expirationTime - today.getTime()) / (24 * 60 * 60 * 1000);
if (expiresInDays == 0) {
LOG.warn(String.format("%nWARNING! Key will expire soon, alias: %s%nExpires On: %s%nToday's Date: %s",
alias, ft.format(expirationDate), ft.format(today)));
LOG.warn("\nWARNING! Key will expire soon, alias: " + alias + DEF_EXPIRESON
+ ft.format(expirationDate) + DEF_TODAYSDATE + ft.format(today));
return;
}
if (expiresInDays < 0) {
LOG.warn(String.format("%nWARNING! Expired Key is used, alias: %s%nExpires On: %s%nToday's Date: %s",
alias, ft.format(expirationDate), ft.format(today)));
LOG.warn("\nWARNING! Expired Key is used, alias: " + alias + DEF_EXPIRESON
+ ft.format(expirationDate) + DEF_TODAYSDATE + ft.format(today));
return;
}

// re-generation interval is unknown, therefore we default to 30 days period warning
if (keyRegenerationIntervalInDays <= 0 && expiresInDays < 30) {
LOG.warn(String.format("%nWARNING! Key with alias: %s%nExpires In: %s days%nExpires On: %s%nToday's Date: %s",
alias, expiresInDays, ft.format(expirationDate), ft.format(today)));
LOG.warn("\nWARNING! Key with alias: " + alias + "\n\tExpires In: " + expiresInDays + DEF_DAYS
+ DEF_EXPIRESON + ft.format(expirationDate) + DEF_TODAYSDATE + ft.format(today));
return;
}

if (expiresInDays < keyRegenerationIntervalInDays) {
LOG.warn(String.format("%nWARNING! Key with alias: %s%nExpires In: %s days%nExpires On: %s%nKey Regeneration In: %s days%nToday's Date: %s",
alias, expiresInDays, ft.format(expirationDate), keyRegenerationIntervalInDays, ft.format(today)));
LOG.warn("\nWARNING! Key with alias: " + alias + "\n\tExpires In: " + expiresInDays + DEF_DAYS
+ DEF_EXPIRESON + ft.format(expirationDate) + "\n\tKey Regeneration In: "
+ keyRegenerationIntervalInDays + DEF_DAYS + DEF_TODAYSDATE + ft.format(today));
}
} catch (Exception e) {
LOG.error("Failed to check key expiration.", e);
Expand Down
Loading

0 comments on commit 250f1d3

Please sign in to comment.