Skip to content

Commit

Permalink
renaming to have less differences in library replacement
Browse files Browse the repository at this point in the history
  • Loading branch information
strehle committed Dec 1, 2023
1 parent 8685f62 commit ebd2433
Show file tree
Hide file tree
Showing 17 changed files with 120 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.cloudfoundry.identity.uaa.oauth.KeyInfo;
import org.cloudfoundry.identity.uaa.oauth.KeyInfoService;
import org.cloudfoundry.identity.uaa.oauth.jwt.ChainedSignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.CommonSignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.SignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants;
import org.cloudfoundry.identity.uaa.util.JwtTokenSignedByThisUAA;
import org.slf4j.Logger;
Expand Down Expand Up @@ -71,7 +71,7 @@ private Set<String> getClientWhitelist(HttpServletRequest request) {
if (idToken != null) {
try {
Map<String, KeyInfo> keys = keyInfoService.getKeys();
List<CommonSignatureVerifier> signatureVerifiers = keys.values().stream().map(i -> i.getVerifier()).collect(Collectors.toList());
List<SignatureVerifier> signatureVerifiers = keys.values().stream().map(i -> i.getVerifier()).collect(Collectors.toList());
JwtTokenSignedByThisUAA jwtToken =buildIdTokenValidator(idToken, new ChainedSignatureVerifier(signatureVerifiers), keyInfoService);
clientId = (String) jwtToken.getClaims().get(ClaimConstants.AZP);
} catch (InvalidTokenException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.X509CertUtils;
import org.cloudfoundry.identity.uaa.oauth.jwk.JsonWebKey;
import org.cloudfoundry.identity.uaa.oauth.jwt.CommonSignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.SignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper;
import org.cloudfoundry.identity.uaa.oauth.jwt.UaaMacSigner;
import org.cloudfoundry.identity.uaa.util.UaaUrlUtils;
Expand All @@ -39,7 +39,7 @@
public class KeyInfo {
private final boolean isAsymmetric;
private JWSSigner signer;
private CommonSignatureVerifier verifier;
private SignatureVerifier verifier;
private final String keyId;
private final String keyUrl;
private final String verifierKey;
Expand All @@ -65,13 +65,13 @@ public KeyInfo(String keyId, String signingKey, String keyUrl, String sigAlg, St
algorithm = Optional.ofNullable(sigAlg).orElse(JWSAlgorithm.RS256.getName());
keyPair = jwk.toRSAKey().toKeyPair();
this.signer = new RSASSASigner(keyPair.getPrivate(), true);
this.verifier = new CommonSignatureVerifier(keyId, algorithm, jwk);
this.verifier = new SignatureVerifier(keyId, algorithm, jwk);
this.type = RSA;
} else if (jwtAlg.startsWith("EC")) {
algorithm = Optional.ofNullable(sigAlg).orElse(JWSAlgorithm.ES256.getName());
keyPair = jwk.toECKey().toKeyPair();
this.signer = new ECDSASigner((ECPrivateKey) keyPair.getPrivate());
this.verifier = new CommonSignatureVerifier(keyId, algorithm, jwk);
this.verifier = new SignatureVerifier(keyId, algorithm, jwk);
this.type = EC;
} else {
throw new IllegalArgumentException("Invalid JWK");
Expand All @@ -86,14 +86,14 @@ public KeyInfo(String keyId, String signingKey, String keyUrl, String sigAlg, St
algorithm = Optional.ofNullable(sigAlg).orElse(JWSAlgorithm.HS256.getName());
SecretKey hmacKey = new SecretKeySpec(signingKey.getBytes(), algorithm);
this.signer = new UaaMacSigner(hmacKey);
this.verifier = new CommonSignatureVerifier(keyId, algorithm, jwk);
this.verifier = new SignatureVerifier(keyId, algorithm, jwk);
this.verifierKey = signingKey;
this.verifierCertificate = Optional.empty();
this.type = MAC;
}
}

public CommonSignatureVerifier getVerifier() {
public SignatureVerifier getVerifier() {
return this.verifier;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,28 @@
import java.util.List;

public class ChainedSignatureVerifier {
private final List<CommonSignatureVerifier> delegates;
private final List<SignatureVerifier> delegates;

public ChainedSignatureVerifier(JsonWebKeySet<? extends JsonWebKey> keys) {
if(keys == null || keys.getKeys() == null || keys.getKeys().isEmpty()) {
throw new IllegalArgumentException("keys cannot be null or empty");
}
List<CommonSignatureVerifier> ds = new ArrayList<>(keys.getKeys().size());
List<SignatureVerifier> ds = new ArrayList<>(keys.getKeys().size());
for (JsonWebKey key : keys.getKeys()) {
ds.add(new CommonSignatureVerifier(key));
ds.add(new SignatureVerifier(key));
}
delegates = Collections.unmodifiableList(ds);
}

public ChainedSignatureVerifier(List<CommonSignatureVerifier> delegates) {
public ChainedSignatureVerifier(List<SignatureVerifier> delegates) {
this.delegates = delegates;
}

public String algorithm() {
return null;
}

public List<CommonSignatureVerifier> getDelegates() {
public List<SignatureVerifier> getDelegates() {
return delegates;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import java.util.Set;
import java.util.UUID;

import static org.cloudfoundry.identity.uaa.util.UaaTokenUtils.getMapFromClaims;

public class JwtClientAuthentication {

public static final String GRANT_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
Expand Down Expand Up @@ -80,7 +82,7 @@ public String getClientAssertion(OIDCIdentityProviderDefinition config) {
claims.setExp(Instant.now().plusSeconds(300).getEpochSecond());
KeyInfo signingKeyInfo = Optional.ofNullable(keyInfoService.getKey(kid)).orElseThrow(() -> new BadCredentialsException("Missing requested signing key"));
return signingKeyInfo.verifierCertificate().isPresent() ?
JwtHelper.encodePlusX5t(JsonUtils.writeValueAsString(claims), signingKeyInfo, signingKeyInfo.verifierCertificate().orElseThrow()).getEncoded() :
JwtHelper.encodePlusX5t(getMapFromClaims(claims), signingKeyInfo, signingKeyInfo.verifierCertificate().orElseThrow()).getEncoded() :
JwtHelper.encode(JsonUtils.writeValueAsString(claims), signingKeyInfo).getEncoded();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;

/**
* @author Luke Taylor
Expand All @@ -57,12 +58,12 @@ public static Jwt decode(String token) {
return new JwtImpl(token);
}

public static Jwt encodePlusX5t(CharSequence content, KeyInfo keyInfo, X509Certificate x509Certificate) {
public static Jwt encodePlusX5t(Map<String, Object> payLoad, KeyInfo keyInfo, X509Certificate x509Certificate) {
JwtHeader header;
HeaderParameters headerParameters = new HeaderParameters(keyInfo.algorithm(), keyInfo.keyId(), null);
headerParameters.setX5t(getX509CertThumbprint(getX509CertEncoded(x509Certificate), "SHA-1"));
header = JwtHeaderHelper.create(headerParameters);
return createJwt(content, keyInfo, header);
return createJwt(header, payLoad, keyInfo);
}

public static Jwt encode(CharSequence content, KeyInfo keyInfo) {
Expand All @@ -75,6 +76,10 @@ private static JwtImpl createJwt(CharSequence content, KeyInfo keyInfo, JwtHeade
return new JwtImpl(header, content, keyInfo.getSigner());
}

private static JwtImpl createJwt(JwtHeader header, Map<String, Object> payLoad, KeyInfo keyInfo) {
return new JwtImpl(header, payLoad, keyInfo.getSigner());
}

public static byte[] getX509CertEncoded(X509Certificate x509Certificate) {
try {
return x509Certificate.getEncoded();
Expand Down Expand Up @@ -144,38 +149,54 @@ public String toString() {
}

class JwtImpl implements Jwt {
private final String orgJwt;
private final JWT interalJwt;
private final String parsedJwtObject;
private final JWT signedJwtObject;
private final JwtHeader header;

private final CharSequence content;

private final JWSSigner crypto;

private String claims;
private final JWSSigner signature;
private final JWTClaimsSet claimsSet;

/**
* @param header the header, containing the JWS/JWE algorithm information.
* @param content the base64-decoded "claims" segment (may be encrypted, depending on
* header information).
* @param crypto the base64-decoded "crypto" segment.
* @param signature the base64-decoded "signature" segment.
*/
JwtImpl(JwtHeader header, CharSequence content, JWSSigner crypto) {
JwtImpl(JwtHeader header, CharSequence content, JWSSigner signature) {
this.header = header;
this.content = content;
this.crypto = crypto;
this.signature = signature;
try {
this.claimsSet = JWTClaimsSet.parse(String.valueOf(content));
JWSHeader jwsHeader = JWSHeader.parse(JsonUtils.convertValue(header.parameters, HashMap.class));
if (signature != null) {
SignedJWT signedJWT = new SignedJWT(jwsHeader, claimsSet);
signedJWT.sign(signature);
signedJwtObject = signedJWT;
parsedJwtObject = null;
} else {
signedJwtObject = null;
parsedJwtObject = null;
}
} catch (ParseException | JOSEException e) {
throw new InvalidTokenException("Invalid token", e);
}
}

JwtImpl(JwtHeader header, Map<String, Object> payLoad, JWSSigner signature) {
this.header = header;
this.signature = signature;
this.parsedJwtObject = null;
this.content = null;
try {
this.claimsSet = JWTClaimsSet.parse(payLoad);
JWSHeader joseHeader = JWSHeader.parse(JsonUtils.convertValue(header.parameters, HashMap.class));
if (crypto != null) {
if (signature != null) {
SignedJWT signedJWT = new SignedJWT(joseHeader, claimsSet);
signedJWT.sign(crypto);
interalJwt = signedJWT;
orgJwt = null;
signedJWT.sign(signature);
signedJwtObject = signedJWT;
} else {
interalJwt = null;
orgJwt = null;
signedJwtObject = null;
}
} catch (ParseException | JOSEException e) {
throw new InvalidTokenException("Invalid token", e);
Expand All @@ -187,31 +208,31 @@ class JwtImpl implements Jwt {
throw new InsufficientAuthenticationException("Unable to decode expected id_token");
}
try {
this.interalJwt = JWTParser.parse(token);
this.claimsSet = interalJwt.getJWTClaimsSet();
this.header = new JwtHeader(JsonUtils.convertValue(interalJwt.getHeader().toJSONObject(), HeaderParameters.class));
this.orgJwt = token;
this.signedJwtObject = JWTParser.parse(token);
this.claimsSet = signedJwtObject.getJWTClaimsSet();
this.header = new JwtHeader(JsonUtils.convertValue(signedJwtObject.getHeader().toJSONObject(), HeaderParameters.class));
this.parsedJwtObject = token;
} catch (ParseException e) {
throw new InvalidTokenException("Invalid token", e);
}
this.content = null;
this.crypto = null;
this.signature = null;
}
/**
* Validates a signature contained in the 'crypto' segment.
* Validates a signature contained in the 'signature' segment.
*
* @param verifier the signature verifier
*/
@Override
public void verifySignature(Object verifier) {
if (interalJwt != null && verifier instanceof CommonSignatureVerifier commonSignatureVerifier) {
validateClientJWToken(interalJwt, commonSignatureVerifier.getJwkSet());
if (signedJwtObject != null && verifier instanceof SignatureVerifier signatureVerifier) {
validateClientJWToken(signedJwtObject, signatureVerifier.getJwkSet());
return;
} else if (interalJwt != null && verifier instanceof ChainedSignatureVerifier chainedSignatureVerifier) {
} else if (signedJwtObject != null && verifier instanceof ChainedSignatureVerifier chainedSignatureVerifier) {
Exception last = new InvalidSignatureException("No matching keys found.");
for (CommonSignatureVerifier delegate : chainedSignatureVerifier.getDelegates()) {
for (SignatureVerifier delegate : chainedSignatureVerifier.getDelegates()) {
try {
validateClientJWToken(interalJwt, delegate.getJwkSet(((JWSHeader) interalJwt.getHeader()).getKeyID()));
validateClientJWToken(signedJwtObject, delegate.getJwkSet(((JWSHeader) signedJwtObject.getHeader()).getKeyID()));
//success
return;
} catch (Exception e) {
Expand All @@ -234,12 +255,20 @@ public JWTClaimsSet getClaimSet() {

@Override
public String getEncoded() {
return orgJwt != null ? orgJwt : crypto != null && interalJwt != null ? interalJwt.serialize() : "";
return isJwtParsedOrCreated() ? parsedJwtObject : getEncodedSignedJwt();
}

private boolean isJwtParsedOrCreated() {
return parsedJwtObject != null;
}

private String getEncodedSignedJwt() {
return signature != null && signedJwtObject != null ? signedJwtObject.serialize() : "";
}

@Override
public String toString() {
return header + " " + claims;
return getClaims();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
import java.util.Map;
import java.util.Optional;

public class CommonSignatureVerifier {
public class SignatureVerifier {
private final JsonWebKey delegate;
private final String algorithm;
private JWKSet jwk;

public CommonSignatureVerifier(String keyId, String alg, JWK verificationKey) {
public SignatureVerifier(String keyId, String alg, JWK verificationKey) {
if (keyId == null || alg == null) {
this.jwk = new JWKSet(verificationKey);
this.delegate = JsonWebKeyHelper.parseConfiguration(verificationKey.toJSONString()).getKeys().get(0);
Expand All @@ -44,7 +44,7 @@ public CommonSignatureVerifier(String keyId, String alg, JWK verificationKey) {
}
}

public CommonSignatureVerifier(JsonWebKey verificationKey) {
public SignatureVerifier(JsonWebKey verificationKey) {
if(verificationKey == null) {
throw new IllegalArgumentException("verificationKey cannot be null");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
import java.util.LinkedHashSet;
import java.util.Set;

/**
* This class is a fork to nimbus-jose because
* UAA has legacy key and key sizes. Removing the support
* of this, would be a regression.
*/
public class UaaMacSigner implements JWSSigner {

public static final Set<JWSAlgorithm> SUPPORTED_ALGORITHMS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import org.cloudfoundry.identity.uaa.oauth.jwk.JsonWebKeyHelper;
import org.cloudfoundry.identity.uaa.oauth.jwk.JsonWebKeySet;
import org.cloudfoundry.identity.uaa.oauth.jwt.ChainedSignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.CommonSignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.SignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.Jwt;
import org.cloudfoundry.identity.uaa.oauth.jwt.JwtClientAuthentication;
import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper;
Expand Down Expand Up @@ -644,7 +644,7 @@ private JwtTokenSignedByThisUAA validateToken(String idToken, AbstractExternalOA
JwtTokenSignedByThisUAA jwtToken;

if (tokenEndpointBuilder.getTokenEndpoint(IdentityZoneHolder.get()).equals(config.getIssuer())) {
List<CommonSignatureVerifier> signatureVerifiers = getTokenKeyForUaaOrigin();
List<SignatureVerifier> signatureVerifiers = getTokenKeyForUaaOrigin();
jwtToken = buildIdTokenValidator(idToken, new ChainedSignatureVerifier(signatureVerifiers), keyInfoService);
} else {
JsonWebKeySet<JsonWebKey> tokenKeyFromOAuth = getTokenKeyFromOAuth(config);
Expand All @@ -655,7 +655,7 @@ private JwtTokenSignedByThisUAA validateToken(String idToken, AbstractExternalOA
return jwtToken.checkExpiry();
}

protected List<CommonSignatureVerifier> getTokenKeyForUaaOrigin() {
protected List<SignatureVerifier> getTokenKeyForUaaOrigin() {
Map<String, KeyInfo> keys = keyInfoService.getKeys();
return keys.values().stream()
.map(i -> i.getVerifier())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

import com.nimbusds.jwt.JWTClaimsSet;
import org.cloudfoundry.identity.uaa.oauth.jwt.ChainedSignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.CommonSignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.jwt.SignatureVerifier;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
Expand Down Expand Up @@ -108,7 +108,7 @@ private JwtTokenSignedByThisUAA(String token, KeyInfoService keyInfoService) {
this.keyInfoService = keyInfoService;
}

private CommonSignatureVerifier fetchSignatureVerifierFromToken(Jwt tokenJwt) {
private SignatureVerifier fetchSignatureVerifierFromToken(Jwt tokenJwt) {
String kid = tokenJwt.getHeader().getKid();
if (kid == null) {
throw new InvalidTokenException("kid claim not found in JWT token header");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,4 +271,8 @@ public static Map<String, Object> getClaims(String jwtToken) {
public static Claims getClaimsFromTokenString(String jwtToken) {
return getClaims(jwtToken, Claims.class);
}

public static Map<String, Object> getMapFromClaims(Claims claims) {
return JsonUtils.convertValue(claims, Map.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.cloudfoundry.identity.uaa.oauth.approval.InMemoryApprovalStore;
import org.cloudfoundry.identity.uaa.oauth.jwt.Jwt;
import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper;
import org.cloudfoundry.identity.uaa.oauth.jwt.SignatureVerifier;
import org.cloudfoundry.identity.uaa.oauth.openid.IdTokenCreator;
import org.cloudfoundry.identity.uaa.oauth.openid.IdTokenGranter;
import org.cloudfoundry.identity.uaa.oauth.refresh.RefreshTokenCreator;
Expand Down Expand Up @@ -339,7 +340,7 @@ public String getIdTokenAsString(List<String> scopes) {
public Jwt getIdToken(List<String> scopes) {
CompositeToken accessToken = getCompositeAccessToken(scopes);
Jwt tokenJwt = JwtHelper.decode(accessToken.getValue());
Object verifier = keyInfoService.getKey(tokenJwt.getHeader().getKid()).getVerifier();
SignatureVerifier verifier = keyInfoService.getKey(tokenJwt.getHeader().getKid()).getVerifier();
tokenJwt.verifySignature(verifier);
assertNotNull("Token must not be null", tokenJwt);

Expand Down
Loading

0 comments on commit ebd2433

Please sign in to comment.