/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.rs.security.oauth2.utils.crypto;
import java.security.Key;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.crypto.SecretKey;
import org.apache.cxf.rs.security.oauth2.common.Client;
import org.apache.cxf.rs.security.oauth2.common.OAuthPermission;
import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken;
import org.apache.cxf.rs.security.oauth2.common.UserSubject;
import org.apache.cxf.rs.security.oauth2.grants.code.ServerAuthorizationCodeGrant;
import org.apache.cxf.rs.security.oauth2.provider.OAuthDataProvider;
import org.apache.cxf.rs.security.oauth2.tokens.refresh.RefreshToken;
import org.apache.cxf.rt.security.crypto.CryptoUtils;
import org.apache.cxf.rt.security.crypto.KeyProperties;
/**
* Default Model Encryption helpers
*/
public final class ModelEncryptionSupport {
public static final String SEP = "|";
private ModelEncryptionSupport() {
}
public static String encryptClient(Client client, Key secretKey) throws SecurityException {
return encryptClient(client, secretKey, null);
}
public static String encryptClient(Client client, Key secretKey,
KeyProperties props) throws SecurityException {
String tokenSequence = tokenizeClient(client);
return CryptoUtils.encryptSequence(tokenSequence, secretKey, props);
}
public static String encryptAccessToken(ServerAccessToken token, Key secretKey) throws SecurityException {
return encryptAccessToken(token, secretKey, null);
}
public static String encryptAccessToken(ServerAccessToken token, Key secretKey,
KeyProperties props) throws SecurityException {
String tokenSequence = tokenizeServerToken(token);
return CryptoUtils.encryptSequence(tokenSequence, secretKey, props);
}
public static String encryptRefreshToken(RefreshToken token, Key secretKey) throws SecurityException {
return encryptRefreshToken(token, secretKey, null);
}
public static String encryptRefreshToken(RefreshToken token, Key secretKey,
KeyProperties props) throws SecurityException {
String tokenSequence = tokenizeRefreshToken(token);
return CryptoUtils.encryptSequence(tokenSequence, secretKey, props);
}
public static String encryptCodeGrant(ServerAuthorizationCodeGrant grant, Key secretKey)
throws SecurityException {
return encryptCodeGrant(grant, secretKey, null);
}
public static String encryptCodeGrant(ServerAuthorizationCodeGrant grant, Key secretKey,
KeyProperties props) throws SecurityException {
String tokenSequence = tokenizeCodeGrant(grant);
return CryptoUtils.encryptSequence(tokenSequence, secretKey, props);
}
public static Client decryptClient(String encodedSequence, String encodedSecretKey)
throws SecurityException {
return decryptClient(encodedSequence, encodedSecretKey, new KeyProperties("AES"));
}
public static Client decryptClient(String encodedSequence, String encodedSecretKey,
KeyProperties props) throws SecurityException {
SecretKey key = CryptoUtils.decodeSecretKey(encodedSecretKey, props.getKeyAlgo());
return decryptClient(encodedSequence, key, props);
}
public static Client decryptClient(String encodedSequence, Key secretKey) throws SecurityException {
return decryptClient(encodedSequence, secretKey, null);
}
public static Client decryptClient(String encodedData, Key secretKey,
KeyProperties props) throws SecurityException {
String decryptedSequence = CryptoUtils.decryptSequence(encodedData, secretKey, props);
return recreateClient(decryptedSequence);
}
public static ServerAccessToken decryptAccessToken(OAuthDataProvider provider,
String encodedToken,
String encodedSecretKey) throws SecurityException {
return decryptAccessToken(provider, encodedToken, encodedSecretKey, new KeyProperties("AES"));
}
public static ServerAccessToken decryptAccessToken(OAuthDataProvider provider,
String encodedToken,
String encodedSecretKey,
KeyProperties props) throws SecurityException {
SecretKey key = CryptoUtils.decodeSecretKey(encodedSecretKey, props.getKeyAlgo());
return decryptAccessToken(provider, encodedToken, key, props);
}
public static ServerAccessToken decryptAccessToken(OAuthDataProvider provider,
String encodedToken,
Key secretKey) throws SecurityException {
return decryptAccessToken(provider, encodedToken, secretKey, null);
}
public static ServerAccessToken decryptAccessToken(OAuthDataProvider provider,
String encodedData,
Key secretKey,
KeyProperties props) throws SecurityException {
String decryptedSequence = CryptoUtils.decryptSequence(encodedData, secretKey, props);
return recreateAccessToken(provider, encodedData, decryptedSequence);
}
public static RefreshToken decryptRefreshToken(OAuthDataProvider provider,
String encodedToken,
String encodedSecretKey) throws SecurityException {
return decryptRefreshToken(provider, encodedToken, encodedSecretKey, new KeyProperties("AES"));
}
public static RefreshToken decryptRefreshToken(OAuthDataProvider provider,
String encodedToken,
String encodedSecretKey,
KeyProperties props) throws SecurityException {
SecretKey key = CryptoUtils.decodeSecretKey(encodedSecretKey, props.getKeyAlgo());
return decryptRefreshToken(provider, encodedToken, key, props);
}
public static RefreshToken decryptRefreshToken(OAuthDataProvider provider,
String encodedToken,
Key key) throws SecurityException {
return decryptRefreshToken(provider, encodedToken, key, null);
}
public static RefreshToken decryptRefreshToken(OAuthDataProvider provider,
String encodedData,
Key key,
KeyProperties props) throws SecurityException {
String decryptedSequence = CryptoUtils.decryptSequence(encodedData, key, props);
return recreateRefreshToken(provider, encodedData, decryptedSequence);
}
public static ServerAuthorizationCodeGrant decryptCodeGrant(OAuthDataProvider provider,
String encodedToken,
String encodedSecretKey) throws SecurityException {
return decryptCodeGrant(provider, encodedToken, encodedSecretKey, new KeyProperties("AES"));
}
public static ServerAuthorizationCodeGrant decryptCodeGrant(OAuthDataProvider provider,
String encodedToken,
String encodedSecretKey,
KeyProperties props) throws SecurityException {
SecretKey key = CryptoUtils.decodeSecretKey(encodedSecretKey, props.getKeyAlgo());
return decryptCodeGrant(provider, encodedToken, key, props);
}
public static ServerAuthorizationCodeGrant decryptCodeGrant(OAuthDataProvider provider,
String encodedToken,
Key key) throws SecurityException {
return decryptCodeGrant(provider, encodedToken, key, null);
}
public static ServerAuthorizationCodeGrant decryptCodeGrant(OAuthDataProvider provider,
String encodedData,
Key key,
KeyProperties props) throws SecurityException {
String decryptedSequence = CryptoUtils.decryptSequence(encodedData, key, props);
return recreateCodeGrant(provider, decryptedSequence);
}
public static ServerAccessToken recreateAccessToken(OAuthDataProvider provider,
String newTokenKey,
String decryptedSequence) throws SecurityException {
return recreateAccessToken(provider, newTokenKey, getParts(decryptedSequence));
}
public static RefreshToken recreateRefreshToken(OAuthDataProvider provider,
String newTokenKey,
String decryptedSequence) throws SecurityException {
String[] parts = getParts(decryptedSequence);
ServerAccessToken token = recreateAccessToken(provider, newTokenKey, parts);
return new RefreshToken(token,
newTokenKey,
parseSimpleList(parts[parts.length - 1]));
}
public static ServerAuthorizationCodeGrant recreateCodeGrant(OAuthDataProvider provider,
String decryptedSequence) throws SecurityException {
return recreateCodeGrantInternal(provider, decryptedSequence);
}
public static Client recreateClient(String sequence) throws SecurityException {
return recreateClientInternal(sequence);
}
private static ServerAccessToken recreateAccessToken(OAuthDataProvider provider,
String newTokenKey,
String[] parts) {
@SuppressWarnings("serial")
final ServerAccessToken newToken = new ServerAccessToken(provider.getClient(parts[4]),
parts[1],
newTokenKey == null ? parts[0] : newTokenKey,
Long.valueOf(parts[2]),
Long.valueOf(parts[3])) {
};
newToken.setRefreshToken(getStringPart(parts[5]));
newToken.setGrantType(getStringPart(parts[6]));
newToken.setAudiences(parseSimpleList(parts[7]));
newToken.setParameters(parseSimpleMap(parts[8]));
// Permissions
if (!parts[9].trim().isEmpty()) {
List<OAuthPermission> perms = new LinkedList<OAuthPermission>();
String[] allPermParts = parts[9].split("\\.");
for (int i = 0; i + 4 < allPermParts.length; i = i + 5) {
OAuthPermission perm = new OAuthPermission(allPermParts[i], allPermParts[i + 1]);
perm.setDefaultPermission(Boolean.valueOf(allPermParts[i + 2]));
perm.setHttpVerbs(parseSimpleList(allPermParts[i + 3]));
perm.setUris(parseSimpleList(allPermParts[i + 4]));
perms.add(perm);
}
newToken.setScopes(perms);
}
//Client verifier:
newToken.setClientCodeVerifier(parts[10]);
//UserSubject:
newToken.setSubject(recreateUserSubject(parts[11]));
newToken.setExtraProperties(parseSimpleMap(parts[12]));
return newToken;
}
private static String tokenizeRefreshToken(RefreshToken token) {
String seq = tokenizeServerToken(token);
return seq + SEP + token.getAccessTokens().toString();
}
private static String tokenizeServerToken(ServerAccessToken token) {
StringBuilder state = new StringBuilder();
// 0: key
state.append(tokenizeString(token.getTokenKey()));
// 1: type
state.append(SEP);
state.append(tokenizeString(token.getTokenType()));
// 2: expiresIn
state.append(SEP);
state.append(token.getExpiresIn());
// 3: issuedAt
state.append(SEP);
state.append(token.getIssuedAt());
// 4: client id
state.append(SEP);
state.append(tokenizeString(token.getClient().getClientId()));
// 5: refresh token
state.append(SEP);
state.append(tokenizeString(token.getRefreshToken()));
// 6: grant type
state.append(SEP);
state.append(tokenizeString(token.getGrantType()));
// 7: audience
state.append(SEP);
state.append(token.getAudiences().toString());
// 8: other parameters
state.append(SEP);
// {key=value, key=value}
state.append(token.getParameters().toString());
// 9: permissions
state.append(SEP);
if (token.getScopes().isEmpty()) {
state.append(" ");
} else {
for (OAuthPermission p : token.getScopes()) {
// 9.1
state.append(tokenizeString(p.getPermission()));
state.append(".");
// 9.2
state.append(tokenizeString(p.getDescription()));
state.append(".");
// 9.3
state.append(p.isDefaultPermission());
state.append(".");
// 9.4
state.append(p.getHttpVerbs().toString());
state.append(".");
// 9.5
state.append(p.getUris().toString());
}
}
state.append(SEP);
// 10: code verifier
state.append(tokenizeString(token.getClientCodeVerifier()));
state.append(SEP);
// 11: user subject
tokenizeUserSubject(state, token.getSubject());
// 13: extra properties
state.append(SEP);
// {key=value, key=value}
state.append(token.getExtraProperties().toString());
return state.toString();
}
private static Client recreateClientInternal(String sequence) {
String[] parts = getParts(sequence);
Client c = new Client(parts[0],
parts[1],
Boolean.valueOf(parts[2]),
getStringPart(parts[3]), getStringPart(parts[4]));
c.setApplicationDescription(getStringPart(parts[5]));
c.setApplicationLogoUri(getStringPart(parts[6]));
c.setApplicationCertificates(parseSimpleList(parts[7]));
c.setAllowedGrantTypes(parseSimpleList(parts[8]));
c.setRedirectUris(parseSimpleList(parts[9]));
c.setRegisteredScopes(parseSimpleList(parts[10]));
c.setRegisteredAudiences(parseSimpleList(parts[11]));
c.setProperties(parseSimpleMap(parts[12]));
c.setSubject(recreateUserSubject(parts[13]));
return c;
}
private static String tokenizeClient(Client client) {
StringBuilder state = new StringBuilder();
// 0: id
state.append(tokenizeString(client.getClientId()));
state.append(SEP);
// 1: secret
state.append(tokenizeString(client.getClientSecret()));
state.append(SEP);
// 2: confidentiality
state.append(client.isConfidential());
state.append(SEP);
// 3: app name
state.append(tokenizeString(client.getApplicationName()));
state.append(SEP);
// 4: app web URI
state.append(tokenizeString(client.getApplicationWebUri()));
state.append(SEP);
// 5: app description
state.append(tokenizeString(client.getApplicationDescription()));
state.append(SEP);
// 6: app logo URI
state.append(tokenizeString(client.getApplicationLogoUri()));
state.append(SEP);
// 7: app certificates
state.append(client.getApplicationCertificates());
state.append(SEP);
// 8: grants
state.append(client.getAllowedGrantTypes().toString());
state.append(SEP);
// 9: redirect URIs
state.append(client.getRedirectUris().toString());
state.append(SEP);
// 10: registered scopes
state.append(client.getRegisteredScopes().toString());
state.append(SEP);
// 11: registered audiences
state.append(client.getRegisteredAudiences().toString());
state.append(SEP);
// 12: properties
state.append(client.getProperties().toString());
state.append(SEP);
// 13: subject
tokenizeUserSubject(state, client.getSubject());
return state.toString();
}
private static ServerAuthorizationCodeGrant recreateCodeGrantInternal(OAuthDataProvider provider,
String sequence) {
String[] parts = getParts(sequence);
ServerAuthorizationCodeGrant grant = new ServerAuthorizationCodeGrant(provider.getClient(parts[0]),
parts[1],
Long.valueOf(parts[2]),
Long.valueOf(parts[3]));
grant.setRedirectUri(getStringPart(parts[4]));
grant.setAudience(getStringPart(parts[5]));
grant.setClientCodeChallenge(getStringPart(parts[6]));
grant.setApprovedScopes(parseSimpleList(parts[7]));
grant.setSubject(recreateUserSubject(parts[8]));
grant.setExtraProperties(parseSimpleMap(parts[9]));
return grant;
}
private static String tokenizeCodeGrant(ServerAuthorizationCodeGrant grant) {
StringBuilder state = new StringBuilder();
// 0: client id
state.append(grant.getClient().getClientId());
state.append(SEP);
// 1: code
state.append(tokenizeString(grant.getCode()));
state.append(SEP);
// 2: expiresIn
state.append(grant.getExpiresIn());
state.append(SEP);
// 3: issuedAt
state.append(grant.getIssuedAt());
state.append(SEP);
// 4: redirect URI
state.append(tokenizeString(grant.getRedirectUri()));
state.append(SEP);
// 5: audience
state.append(tokenizeString(grant.getAudience()));
state.append(SEP);
// 6: code challenge
state.append(tokenizeString(grant.getClientCodeChallenge()));
state.append(SEP);
// 7: approved scopes
state.append(grant.getApprovedScopes().toString());
state.append(SEP);
// 8: subject
tokenizeUserSubject(state, grant.getSubject());
// 9: extra properties
state.append(SEP);
// {key=value, key=value}
state.append(grant.getExtraProperties().toString());
return state.toString();
}
public static String getStringPart(String str) {
return " ".equals(str) ? null : str;
}
private static String prepareSimpleString(String str) {
return str.trim().isEmpty() ? "" : str.substring(1, str.length() - 1);
}
private static List<String> parseSimpleList(String listStr) {
String pureStringList = prepareSimpleString(listStr);
if (pureStringList.isEmpty()) {
return Collections.emptyList();
} else {
return Arrays.asList(pureStringList.split(","));
}
}
public static Map<String, String> parseSimpleMap(String mapStr) {
Map<String, String> props = new HashMap<>();
List<String> entries = parseSimpleList(mapStr);
for (String entry : entries) {
String[] pair = entry.split("=");
props.put(pair[0], pair[1]);
}
return props;
}
public static String[] getParts(String sequence) {
return sequence.split("\\" + SEP);
}
private static UserSubject recreateUserSubject(String sequence) {
UserSubject subject = null;
if (!sequence.trim().isEmpty()) {
String[] subjectParts = sequence.split("\\.");
subject = new UserSubject(getStringPart(subjectParts[0]), getStringPart(subjectParts[1]));
subject.setRoles(parseSimpleList(subjectParts[2]));
subject.setProperties(parseSimpleMap(subjectParts[3]));
}
return subject;
}
private static void tokenizeUserSubject(StringBuilder state, UserSubject subject) {
if (subject != null) {
// 1
state.append(tokenizeString(subject.getLogin()));
state.append(".");
// 2
state.append(tokenizeString(subject.getId()));
state.append(".");
// 3
state.append(subject.getRoles().toString());
state.append(".");
// 4
state.append(subject.getProperties().toString());
} else {
state.append(" ");
}
}
public static String tokenizeString(String str) {
return str != null ? str : " ";
}
}