/*
* oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
*
* Copyright (c) 2014, Gluu
*/
package org.xdi.oxauth.model.token;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONObject;
import org.xdi.oxauth.model.common.AuthenticationMethod;
import org.xdi.oxauth.model.configuration.AppConfiguration;
import org.xdi.oxauth.model.crypto.AbstractCryptoProvider;
import org.xdi.oxauth.model.crypto.CryptoProviderFactory;
import org.xdi.oxauth.model.crypto.signature.SignatureAlgorithm;
import org.xdi.oxauth.model.exception.InvalidJwtException;
import org.xdi.oxauth.model.jwt.Jwt;
import org.xdi.oxauth.model.jwt.JwtClaimName;
import org.xdi.oxauth.model.jwt.JwtHeaderName;
import org.xdi.oxauth.model.jwt.JwtType;
import org.xdi.oxauth.model.registration.Client;
import org.xdi.oxauth.model.util.JwtUtil;
import org.xdi.oxauth.service.ClientService;
import org.xdi.oxauth.util.ServerUtil;
import org.xdi.service.cdi.util.CdiUtil;
import org.xdi.util.security.StringEncrypter;
import com.google.common.base.Strings;
/**
* @author Javier Rojas Blum
* @version June 15, 2016
*/
public class ClientAssertion {
private Jwt jwt;
private String clientSecret;
public ClientAssertion(AppConfiguration appConfiguration, String clientId, ClientAssertionType clientAssertionType, String encodedAssertion)
throws InvalidJwtException {
try {
if (!load(appConfiguration, clientId, clientAssertionType, encodedAssertion)) {
throw new InvalidJwtException("Cannot load the JWT");
}
} catch (StringEncrypter.EncryptionException e) {
throw new InvalidJwtException(e.getMessage(), e);
} catch (Exception e) {
throw new InvalidJwtException("Cannot verify the JWT", e);
}
}
public String getSubjectIdentifier() {
return jwt.getClaims().getClaimAsString(JwtClaimName.SUBJECT_IDENTIFIER);
}
public String getClientSecret() {
return clientSecret;
}
private boolean load(AppConfiguration appConfiguration, String clientId, ClientAssertionType clientAssertionType, String encodedAssertion)
throws Exception {
boolean result;
if (clientAssertionType == ClientAssertionType.JWT_BEARER) {
if (StringUtils.isNotBlank(encodedAssertion)) {
jwt = Jwt.parse(encodedAssertion);
// TODO: Store jti this value to check for duplicates
// Validate clientId
String issuer = jwt.getClaims().getClaimAsString(JwtClaimName.ISSUER);
String subject = jwt.getClaims().getClaimAsString(JwtClaimName.SUBJECT_IDENTIFIER);
List<String> audience = jwt.getClaims().getClaimAsStringList(JwtClaimName.AUDIENCE);
Date expirationTime = jwt.getClaims().getClaimAsDate(JwtClaimName.EXPIRATION_TIME);
//SignatureAlgorithm algorithm = SignatureAlgorithm.fromName(jwt.getHeader().getClaimAsString(JwtHeaderName.ALGORITHM));
if ((clientId == null && StringUtils.isNotBlank(issuer) && StringUtils.isNotBlank(subject)
&& issuer.equals(subject)) || (StringUtils.isNotBlank(clientId)
&& StringUtils.isNotBlank(issuer)
&& StringUtils.isNotBlank(subject)
&& clientId.equals(issuer) && issuer.equals(subject))) {
// Validate audience
String tokenUrl = appConfiguration.getTokenEndpoint();
if (audience != null && audience.contains(tokenUrl)) {
// Validate expiration
if (expirationTime.after(new Date())) {
ClientService clientService = CdiUtil.bean(ClientService.class);
Client client = clientService.getClient(subject);
// Validate client
if (client != null) {
JwtType jwtType = JwtType.fromString(jwt.getHeader().getClaimAsString(JwtHeaderName.TYPE));
AuthenticationMethod authenticationMethod = client.getAuthenticationMethod();
SignatureAlgorithm signatureAlgorithm = jwt.getHeader().getAlgorithm();
if (jwtType == null && signatureAlgorithm != null) {
jwtType = signatureAlgorithm.getJwtType();
}
if (jwtType != null && signatureAlgorithm != null && signatureAlgorithm.getFamily() != null &&
((authenticationMethod == AuthenticationMethod.CLIENT_SECRET_JWT && signatureAlgorithm.getFamily().equals("HMAC"))
|| (authenticationMethod == AuthenticationMethod.PRIVATE_KEY_JWT && (signatureAlgorithm.getFamily().equals("RSA") || signatureAlgorithm.getFamily().equals("EC"))))) {
clientSecret = clientService.decryptSecret(client.getClientSecret());
// Validate the crypto segment
String keyId = jwt.getHeader().getKeyId();
JSONObject jwks = Strings.isNullOrEmpty(client.getJwks()) ?
JwtUtil.getJSONWebKeys(client.getJwksUri()) :
new JSONObject(client.getJwks());
String sharedSecret = clientService.decryptSecret(client.getClientSecret());
AbstractCryptoProvider cryptoProvider = CryptoProviderFactory.getCryptoProvider(
appConfiguration);
boolean validSignature = cryptoProvider.verifySignature(jwt.getSigningInput(), jwt.getEncodedSignature(),
keyId, jwks, sharedSecret, signatureAlgorithm);
if (validSignature) {
result = true;
} else {
throw new InvalidJwtException("Invalid cryptographic segment");
}
} else {
throw new InvalidJwtException("Invalid authentication method");
}
} else {
throw new InvalidJwtException("Invalid client");
}
} else {
throw new InvalidJwtException("JWT has expired");
}
} else {
throw new InvalidJwtException("Invalid audience: " + audience + ", tokenUrl: " + tokenUrl);
}
} else {
throw new InvalidJwtException("Invalid clientId");
}
} else {
throw new InvalidJwtException("The Client Assertion is null or empty");
}
} else {
throw new InvalidJwtException("Invalid Client Assertion Type");
}
return result;
}
}