Merge pull request #137 from patriot1burke/master

factor out jwt/jose/jws
This commit is contained in:
Bill Burke 2013-12-16 08:21:16 -08:00
commit 6fe9a03997
70 changed files with 3243 additions and 378 deletions

71
core-jaxrs/pom.xml Executable file
View file

@ -0,0 +1,71 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-core-jaxrs</artifactId>
<name>Keycloak Core JAX-RS</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -66,7 +66,7 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
try {
SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
SkeletonKeySession skSession = new SkeletonKeySession(tokenString, token, resourceMetadata);
ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;

View file

@ -14,23 +14,18 @@
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -1,6 +1,7 @@
package org.keycloak;
import org.jboss.resteasy.util.Base64;
import org.keycloak.util.Base64;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;

View file

@ -1,8 +1,7 @@
package org.keycloak;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.SkeletonKeyToken;
import java.io.IOException;
@ -13,11 +12,6 @@ import java.security.PublicKey;
* @version $Revision: 1 $
*/
public class RSATokenVerifier {
public static SkeletonKeyToken verifyToken(String tokenString, ResourceMetadata metadata) throws VerificationException {
PublicKey realmKey = metadata.getRealmKey();
String realm = metadata.getRealm();
return verifyToken(tokenString, realmKey, realm);
}
public static SkeletonKeyToken verifyToken(String tokenString, PublicKey realmKey, String realm) throws VerificationException {
JWSInput input = new JWSInput(tokenString);
@ -31,7 +25,7 @@ public class RSATokenVerifier {
SkeletonKeyToken token = null;
try {
token = JsonSerialization.fromBytes(SkeletonKeyToken.class, input.getContent());
token = input.readJsonContent(SkeletonKeyToken.class);
} catch (IOException e) {
throw new VerificationException(e);
}

View file

@ -9,6 +9,7 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public class TokenIdGenerator {
private static final AtomicLong counter = new AtomicLong();
public static String generateId() {
return UUID.randomUUID().toString() + "-" + System.currentTimeMillis();
}

View file

@ -0,0 +1,18 @@
package org.keycloak.jose.jws;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public enum Algorithm {
none,
HS256,
HS384,
HS512,
RS256,
RS384,
RS512,
ES256,
ES384,
ES512
}

View file

@ -0,0 +1,138 @@
package org.keycloak.jose.jws;
import org.keycloak.jose.jws.crypto.HMACProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.util.Base64Url;
import org.keycloak.util.JsonSerialization;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JWSBuilder {
String type;
String contentType;
byte[] contentBytes;
public JWSBuilder type(String type) {
this.type = type;
return this;
}
public JWSBuilder contentType(String type) {
this.contentType = type;
return this;
}
public EncodingBuilder content(byte[] bytes) {
this.contentBytes = bytes;
return new EncodingBuilder();
}
public EncodingBuilder jsonContent(Object object) {
try {
this.contentBytes = JsonSerialization.writeValueAsBytes(object);
} catch (IOException e) {
throw new RuntimeException(e);
}
return new EncodingBuilder();
}
protected String encodeHeader(Algorithm alg) {
StringBuilder builder = new StringBuilder("{");
builder.append("\"alg\":\"").append(alg.toString()).append("\"");
if (type != null) builder.append(",\"typ\" : \"").append(type).append("\"");
if (contentType != null) builder.append(",\"cty\":\"").append(contentType).append("\"");
builder.append("}");
try {
return Base64Url.encode(builder.toString().getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
protected String encode(Algorithm alg, byte[] data, byte[] signature) {
StringBuffer encoding = new StringBuffer();
encoding.append(encodeHeader(alg));
encoding.append('.');
encoding.append(Base64Url.encode(data));
encoding.append('.');
if (alg != Algorithm.none) {
encoding.append(Base64Url.encode(signature));
}
return encoding.toString();
}
protected byte[] marshalContent() {
return contentBytes;
}
public class EncodingBuilder {
public String none() {
byte[] data = marshalContent();
return encode(Algorithm.none, data, null);
}
public String rsa256(PrivateKey privateKey) {
byte[] data = marshalContent();
byte[] signature = RSAProvider.sign(data, Algorithm.RS256, privateKey);
return encode(Algorithm.RS256, data, signature);
}
public String rsa384(PrivateKey privateKey) {
byte[] data = marshalContent();
byte[] signature = RSAProvider.sign(data, Algorithm.RS384, privateKey);
return encode(Algorithm.RS384, data, signature);
}
public String rsa512(PrivateKey privateKey) {
byte[] data = marshalContent();
byte[] signature = RSAProvider.sign(data, Algorithm.RS512, privateKey);
return encode(Algorithm.RS512, data, signature);
}
public String hmac256(byte[] sharedSecret) {
byte[] data = marshalContent();
byte[] signature = HMACProvider.sign(data, Algorithm.HS256, sharedSecret);
return encode(Algorithm.HS256, data, signature);
}
public String hmac384(byte[] sharedSecret) {
byte[] data = marshalContent();
byte[] signature = HMACProvider.sign(data, Algorithm.HS384, sharedSecret);
return encode(Algorithm.HS384, data, signature);
}
public String hmac512(byte[] sharedSecret) {
byte[] data = marshalContent();
byte[] signature = HMACProvider.sign(data, Algorithm.HS512, sharedSecret);
return encode(Algorithm.HS512, data, signature);
}
public String hmac256(SecretKey sharedSecret) {
byte[] data = marshalContent();
byte[] signature = HMACProvider.sign(data, Algorithm.HS256, sharedSecret);
return encode(Algorithm.HS256, data, signature);
}
public String hmac384(SecretKey sharedSecret) {
byte[] data = marshalContent();
byte[] signature = HMACProvider.sign(data, Algorithm.HS384, sharedSecret);
return encode(Algorithm.HS384, data, signature);
}
public String hmac512(SecretKey sharedSecret) {
byte[] data = marshalContent();
byte[] signature = HMACProvider.sign(data, Algorithm.HS512, sharedSecret);
return encode(Algorithm.HS512, data, signature);
}
}
}

View file

@ -0,0 +1,64 @@
package org.keycloak.jose.jws;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.io.IOException;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JWSHeader implements Serializable {
@JsonProperty("alg")
private Algorithm algorithm;
@JsonProperty("typ")
private String type;
@JsonProperty("cty")
private String contentType;
public JWSHeader() {
}
public JWSHeader(Algorithm algorithm, String type, String contentType) {
this.algorithm = algorithm;
this.type = type;
this.contentType = contentType;
}
public Algorithm getAlgorithm() {
return algorithm;
}
public String getType() {
return type;
}
public String getContentType() {
return contentType;
}
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public String toString() {
try {
return mapper.writeValueAsString(this);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,82 @@
package org.keycloak.jose.jws;
import org.keycloak.util.Base64Url;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JWSInput {
String wireString;
String encodedHeader;
String encodedContent;
String encodedSignature;
JWSHeader header;
byte[] content;
byte[] signature;
public JWSInput(String wire) {
this.wireString = wire;
String[] parts = wire.split("\\.");
if (parts.length < 2 || parts.length > 3) throw new IllegalArgumentException("Parsing error");
encodedHeader = parts[0];
encodedContent = parts[1];
try {
content = Base64Url.decode(encodedContent);
if (parts.length > 2) {
encodedSignature = parts[2];
signature = Base64Url.decode(encodedSignature);
}
byte[] headerBytes = Base64Url.decode(encodedHeader);
header = JsonSerialization.readValue(headerBytes, JWSHeader.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String getWireString() {
return wireString;
}
public String getEncodedHeader() {
return encodedHeader;
}
public String getEncodedContent() {
return encodedContent;
}
public String getEncodedSignature() {
return encodedSignature;
}
public JWSHeader getHeader() {
return header;
}
public byte[] getContent() {
return content;
}
public byte[] getSignature() {
return signature;
}
public <T> T readJsonContent(Class<T> type) throws IOException {
return JsonSerialization.readValue(content, type);
}
public String readContentAsString() {
try {
return new String(content, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,86 @@
package org.keycloak.jose.jws.crypto;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.util.Base64Url;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HMACProvider {
public static String getJavaAlgorithm(Algorithm alg) {
switch (alg) {
case HS256:
return "HMACSHA256";
case HS384:
return "HMACSHA384";
case HS512:
return "HMACSHA512";
default:
throw new IllegalArgumentException("Not a MAC Algorithm");
}
}
public static Mac getMAC(final Algorithm alg) {
try {
return Mac.getInstance(getJavaAlgorithm(alg));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unsupported HMAC algorithm: " + e.getMessage(), e);
}
}
public static byte[] sign(byte[] data, Algorithm algorithm, byte[] sharedSecret) {
try {
Mac mac = getMAC(algorithm);
mac.init(new SecretKeySpec(sharedSecret, mac.getAlgorithm()));
mac.update(data);
return mac.doFinal();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static byte[] sign(byte[] data, Algorithm algorithm, SecretKey key) {
try {
Mac mac = getMAC(algorithm);
mac.init(key);
mac.update(data);
return mac.doFinal();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(JWSInput input, SecretKey key) {
try {
byte[] signature = sign(input.getContent(), input.getHeader().getAlgorithm(), key);
String x = Base64Url.encode(signature);
return x.equals(input.getEncodedSignature());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(JWSInput input, byte[] sharedSecret) {
try {
byte[] signature = sign(input.getContent(), input.getHeader().getAlgorithm(), sharedSecret);
String x = Base64Url.encode(signature);
return x.equals(input.getEncodedSignature());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,61 @@
package org.keycloak.jose.jws.crypto;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSInput;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RSAProvider {
public static String getJavaAlgorithm(Algorithm alg) {
switch (alg) {
case RS256:
return "SHA256withRSA";
case RS384:
return "SHA384withRSA";
case RS512:
return "SHA512withRSA";
default:
throw new IllegalArgumentException("Not an RSA Algorithm");
}
}
public static Signature getSignature(Algorithm alg) {
try {
return Signature.getInstance(getJavaAlgorithm(alg));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static byte[] sign(byte[] data, Algorithm algorithm, PrivateKey privateKey) {
try {
Signature signature = getSignature(algorithm);
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean verify(JWSInput input, PublicKey publicKey) {
try {
Signature verifier = getSignature(input.getHeader().getAlgorithm());
verifier.initVerify(publicKey);
verifier.update(input.getContent());
return verifier.verify(input.getSignature());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,136 @@
package org.keycloak.jwt;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JsonWebToken implements Serializable {
@JsonProperty("jti")
protected String id;
@JsonProperty("exp")
protected long expiration;
@JsonProperty("nbf")
protected long notBefore;
@JsonProperty("iat")
protected long issuedAt;
@JsonProperty("iss")
protected String issuer;
@JsonProperty("aud")
protected String audience;
@JsonProperty("prn")
protected String principal;
@JsonProperty("typ")
protected String type;
public String getId() {
return id;
}
public JsonWebToken id(String id) {
this.id = id;
return this;
}
public long getExpiration() {
return expiration;
}
public JsonWebToken expiration(long expiration) {
this.expiration = expiration;
return this;
}
@JsonIgnore
public boolean isExpired() {
long time = System.currentTimeMillis() / 1000;
return time > expiration;
}
public long getNotBefore() {
return notBefore;
}
public JsonWebToken notBefore(long notBefore) {
this.notBefore = notBefore;
return this;
}
@JsonIgnore
public boolean isNotBefore() {
return (System.currentTimeMillis() / 1000) >= notBefore;
}
/**
* Tests that the token is not expired and is not-before.
*
* @return
*/
@JsonIgnore
public boolean isActive() {
return (!isExpired() || expiration == 0) && (isNotBefore() || notBefore == 0);
}
public long getIssuedAt() {
return issuedAt;
}
/**
* Set issuedAt to the current time
*/
@JsonIgnore
public JsonWebToken issuedNow() {
issuedAt = System.currentTimeMillis() / 1000;
return this;
}
public JsonWebToken issuedAt(long issuedAt) {
this.issuedAt = issuedAt;
return this;
}
public String getIssuer() {
return issuer;
}
public JsonWebToken issuer(String issuer) {
this.issuer = issuer;
return this;
}
public String getAudience() {
return audience;
}
public JsonWebToken audience(String audience) {
this.audience = audience;
return this;
}
public String getPrincipal() {
return principal;
}
public JsonWebToken principal(String principal) {
this.principal = principal;
return this;
}
public String getType() {
return type;
}
public JsonWebToken type(String type) {
this.type = type;
return this;
}
}

View file

@ -1,6 +1,7 @@
package org.keycloak.representations;
import javax.ws.rs.core.MultivaluedHashMap;
import org.keycloak.util.MultivaluedHashMap;
/**
* Key is resource desired. Values are roles desired for that resource

View file

@ -2,7 +2,7 @@ package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.jboss.resteasy.jwt.JsonWebToken;
import org.keycloak.jwt.JsonWebToken;
import java.util.HashMap;
import java.util.HashSet;

View file

@ -2,8 +2,6 @@ package org.keycloak.representations.idm;
import org.codehaus.jackson.annotate.JsonProperty;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**

View file

@ -1,9 +1,9 @@
package org.keycloak.representations.idm;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CredentialRepresentation {
public static final String PASSWORD = "password";
public static final String TOTP = "totp";

View file

@ -19,7 +19,7 @@ public class MappingsRepresentation {
this.realmMappings = realmMappings;
}
public Map<String,ApplicationMappingsRepresentation> getApplicationMappings() {
public Map<String, ApplicationMappingsRepresentation> getApplicationMappings() {
return applicationMappings;
}

View file

@ -1,6 +1,5 @@
package org.keycloak.representations.idm;
import java.util.ArrayList;
import java.util.List;
/**

View file

@ -41,8 +41,7 @@ public class AdminAction {
}
@JsonIgnore
public boolean isExpired()
{
public boolean isExpired() {
long time = System.currentTimeMillis() / 1000;
return time > expiration;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
package org.keycloak.util;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Base64Url {
public static String encode(byte[] bytes) {
String s = Base64.encodeBytes(bytes);
s = s.split("=")[0]; // Remove any trailing '='s
s = s.replace('+', '-'); // 62nd char of encoding
s = s.replace('/', '_'); // 63rd char of encoding
return s;
}
public static byte[] decode(String s) {
s = s.replace('-', '+'); // 62nd char of encoding
s = s.replace('_', '/'); // 63rd char of encoding
switch (s.length() % 4) // Pad with trailing '='s
{
case 0:
break; // No pad chars in this case
case 2:
s += "==";
break; // Two pad chars
case 3:
s += "=";
break; // One pad char
default:
throw new RuntimeException(
"Illegal base64url string!");
}
try {
return Base64.decode(s);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,45 @@
package org.keycloak.util;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.io.IOException;
import java.io.InputStream;
/**
* Any class that extends JsonWebToken will use NON_DEFAULT inclusion
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JsonSerialization {
public static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public static String writeValueAsString(Object obj) throws IOException {
return mapper.writeValueAsString(obj);
}
public static byte[] writeValueAsBytes(Object obj) throws IOException {
return mapper.writeValueAsBytes(obj);
}
public static <T> T readValue(byte[] bytes, Class<T> type) throws IOException {
return mapper.readValue(bytes, type);
}
public static <T> T readValue(String bytes, Class<T> type) throws IOException {
return mapper.readValue(bytes, type);
}
public static <T> T readValue(InputStream bytes, Class<T> type) throws IOException {
return mapper.readValue(bytes, type);
}
}

View file

@ -0,0 +1,105 @@
package org.keycloak.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@SuppressWarnings("serial")
public class MultivaluedHashMap<K, V> extends HashMap<K, List<V>>
{
public void putSingle(K key, V value)
{
List<V> list = new ArrayList<V>();
list.add(value);
put(key, list);
}
public void addAll(K key, V... newValues)
{
for (V value : newValues)
{
add(key, value);
}
}
public void addAll(K key, List<V> valueList)
{
for (V value : valueList)
{
add(key, value);
}
}
public void addFirst(K key, V value)
{
List<V> list = get(key);
if (list == null)
{
add(key, value);
return;
}
else
{
list.add(0, value);
}
}
public final void add(K key, V value)
{
getList(key).add(value);
}
public final void addMultiple(K key, Collection<V> values)
{
getList(key).addAll(values);
}
public V getFirst(K key)
{
List<V> list = get(key);
return list == null ? null : list.get(0);
}
public final List<V> getList(K key)
{
List<V> list = get(key);
if (list == null)
put(key, list = new ArrayList<V>());
return list;
}
public void addAll(MultivaluedHashMap<K, V> other)
{
for (Map.Entry<K, List<V>> entry : other.entrySet())
{
getList(entry.getKey()).addAll(entry.getValue());
}
}
public boolean equalsIgnoreValueOrder(MultivaluedHashMap<K, V> omap) {
if (this == omap) {
return true;
}
if (!keySet().equals(omap.keySet())) {
return false;
}
for (Map.Entry<K, List<V>> e : entrySet()) {
List<V> olist = omap.get(e.getKey());
if (e.getValue().size() != olist.size()) {
return false;
}
for (V v : e.getValue()) {
if (!olist.contains(v)) {
return false;
}
}
}
return true;
}
}

View file

@ -4,11 +4,10 @@ import junit.framework.Assert;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.jboss.resteasy.jose.jws.JWSBuilder;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.representations.SkeletonKeyToken;
import javax.security.auth.x500.X500Principal;
@ -30,94 +29,80 @@ import java.util.Date;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RSAVerifierTest
{
private static X509Certificate[] idpCertificates;
private static KeyPair idpPair;
private static KeyPair badPair;
private static KeyPair clientPair;
private static X509Certificate[] clientCertificateChain;
private ResourceMetadata metadata;
private SkeletonKeyToken token;
public class RSAVerifierTest {
private static X509Certificate[] idpCertificates;
private static KeyPair idpPair;
private static KeyPair badPair;
private static KeyPair clientPair;
private static X509Certificate[] clientCertificateChain;
private SkeletonKeyToken token;
static
{
if (Security.getProvider("BC") == null) Security.addProvider(new BouncyCastleProvider());
}
static {
if (Security.getProvider("BC") == null) Security.addProvider(new BouncyCastleProvider());
}
public static X509Certificate generateTestCertificate(String subject, String issuer, KeyPair pair) throws InvalidKeyException,
NoSuchProviderException, SignatureException
{
public static X509Certificate generateTestCertificate(String subject, String issuer, KeyPair pair) throws InvalidKeyException,
NoSuchProviderException, SignatureException {
X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setIssuerDN(new X500Principal(issuer));
certGen.setNotBefore(new Date(System.currentTimeMillis() - 10000));
certGen.setNotAfter(new Date(System.currentTimeMillis() + 10000));
certGen.setSubjectDN(new X500Principal(subject));
certGen.setPublicKey(pair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setIssuerDN(new X500Principal(issuer));
certGen.setNotBefore(new Date(System.currentTimeMillis() - 10000));
certGen.setNotAfter(new Date(System.currentTimeMillis() + 10000));
certGen.setSubjectDN(new X500Principal(subject));
certGen.setPublicKey(pair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
return certGen.generateX509Certificate(pair.getPrivate(), "BC");
}
return certGen.generateX509Certificate(pair.getPrivate(), "BC");
}
@BeforeClass
public static void setupCerts() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException
{
badPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
idpPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
idpCertificates = new X509Certificate[]{generateTestCertificate("CN=IDP", "CN=IDP", idpPair)};
clientPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
clientCertificateChain = new X509Certificate[]{generateTestCertificate("CN=Client", "CN=IDP", idpPair)};
}
@BeforeClass
public static void setupCerts() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {
badPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
idpPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
idpCertificates = new X509Certificate[]{generateTestCertificate("CN=IDP", "CN=IDP", idpPair)};
clientPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
clientCertificateChain = new X509Certificate[]{generateTestCertificate("CN=Client", "CN=IDP", idpPair)};
}
@Before
public void initTest()
{
metadata = new ResourceMetadata();
metadata.setResourceName("service");
metadata.setRealm("domain");
metadata.setRealmKey(idpPair.getPublic());
@Before
public void initTest() {
token = new SkeletonKeyToken();
token.principal("CN=Client")
.audience("domain")
.addAccess("service").addRole("admin");
}
token = new SkeletonKeyToken();
token.principal("CN=Client")
.audience("domain")
.addAccess("service").addRole("admin");
}
@Test
public void testPemWriter() throws Exception
{
PublicKey realmPublicKey = idpPair.getPublic();
StringWriter sw = new StringWriter();
PEMWriter writer = new PEMWriter(sw);
try
{
writer.writeObject(realmPublicKey);
writer.flush();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
System.out.println(sw.toString());
}
@Test
public void testPemWriter() throws Exception {
PublicKey realmPublicKey = idpPair.getPublic();
StringWriter sw = new StringWriter();
PEMWriter writer = new PEMWriter(sw);
try {
writer.writeObject(realmPublicKey);
writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println(sw.toString());
}
@Test
public void testSimpleVerification() throws Exception
{
@Test
public void testSimpleVerification() throws Exception {
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken token = verifySkeletonKeyToken(encoded);
Assert.assertTrue(token.getResourceAccess("service").getRoles().contains("admin"));
Assert.assertEquals("CN=Client", token.getPrincipal());
}
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
String encoded = new JWSBuilder()
.content(tokenBytes)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken token = RSATokenVerifier.verifyToken(encoded, metadata);
Assert.assertTrue(token.getResourceAccess("service").getRoles().contains("admin"));
Assert.assertEquals("CN=Client", token.getPrincipal());
}
private SkeletonKeyToken verifySkeletonKeyToken(String encoded) throws VerificationException {
return RSATokenVerifier.verifyToken(encoded, idpPair.getPublic(), "domain");
}
/*
@Test
@ -143,137 +128,105 @@ public class RSAVerifierTest
*/
@Test
public void testBadSignature() throws Exception
{
@Test
public void testBadSignature() throws Exception {
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(badPair.getPrivate());
String encoded = new JWSBuilder()
.content(tokenBytes)
.rsa256(badPair.getPrivate());
SkeletonKeyToken v = null;
try {
v = verifySkeletonKeyToken(encoded);
Assert.fail();
} catch (VerificationException ignored) {
}
}
SkeletonKeyToken v = null;
try
{
v = RSATokenVerifier.verifyToken(encoded, metadata);
Assert.fail();
}
catch (VerificationException ignored)
{
}
}
@Test
public void testNotBeforeGood() throws Exception {
token.notBefore((System.currentTimeMillis() / 1000) - 100);
@Test
public void testNotBeforeGood() throws Exception
{
token.notBefore((System.currentTimeMillis()/1000) - 100);
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(idpPair.getPrivate());
String encoded = new JWSBuilder()
.content(tokenBytes)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken v = null;
try {
v = verifySkeletonKeyToken(encoded);
} catch (VerificationException ignored) {
throw ignored;
}
}
SkeletonKeyToken v = null;
try
{
v = RSATokenVerifier.verifyToken(encoded, metadata);
}
catch (VerificationException ignored)
{
throw ignored;
}
}
@Test
public void testNotBeforeBad() throws Exception {
token.notBefore((System.currentTimeMillis() / 1000) + 100);
@Test
public void testNotBeforeBad() throws Exception
{
token.notBefore((System.currentTimeMillis()/1000) + 100);
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(idpPair.getPrivate());
String encoded = new JWSBuilder()
.content(tokenBytes)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken v = null;
try {
v = verifySkeletonKeyToken(encoded);
Assert.fail();
} catch (VerificationException ignored) {
System.out.println(ignored.getMessage());
}
}
SkeletonKeyToken v = null;
try
{
v = RSATokenVerifier.verifyToken(encoded, metadata);
Assert.fail();
}
catch (VerificationException ignored)
{
System.out.println(ignored.getMessage());
}
}
@Test
public void testExpirationGood() throws Exception {
token.expiration((System.currentTimeMillis() / 1000) + 100);
@Test
public void testExpirationGood() throws Exception
{
token.expiration((System.currentTimeMillis()/1000) + 100);
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(idpPair.getPrivate());
String encoded = new JWSBuilder()
.content(tokenBytes)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken v = null;
try {
v = verifySkeletonKeyToken(encoded);
} catch (VerificationException ignored) {
throw ignored;
}
}
SkeletonKeyToken v = null;
try
{
v = RSATokenVerifier.verifyToken(encoded, metadata);
}
catch (VerificationException ignored)
{
throw ignored;
}
}
@Test
public void testExpirationBad() throws Exception {
token.expiration((System.currentTimeMillis() / 1000) - 100);
@Test
public void testExpirationBad() throws Exception
{
token.expiration((System.currentTimeMillis()/1000) - 100);
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(idpPair.getPrivate());
String encoded = new JWSBuilder()
.content(tokenBytes)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken v = null;
try {
v = verifySkeletonKeyToken(encoded);
Assert.fail();
} catch (VerificationException ignored) {
System.out.println(ignored.getMessage());
}
}
SkeletonKeyToken v = null;
try
{
v = RSATokenVerifier.verifyToken(encoded, metadata);
Assert.fail();
}
catch (VerificationException ignored)
{
System.out.println(ignored.getMessage());
}
}
@Test
public void testTokenAuth() throws Exception {
token = new SkeletonKeyToken();
token.principal("CN=Client")
.audience("domain")
.addAccess("service").addRole("admin").verifyCaller(true);
@Test
public void testTokenAuth() throws Exception
{
token = new SkeletonKeyToken();
token.principal("CN=Client")
.audience("domain")
.addAccess("service").addRole("admin").verifyCaller(true);
byte[] tokenBytes = JsonSerialization.toByteArray(token, false);
String encoded = new JWSBuilder()
.content(tokenBytes)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken v = null;
try
{
v = RSATokenVerifier.verifyToken(encoded, metadata);
}
catch (VerificationException ignored)
{
System.out.println(ignored.getMessage());
}
}
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(idpPair.getPrivate());
SkeletonKeyToken v = null;
try {
v = verifySkeletonKeyToken(encoded);
} catch (VerificationException ignored) {
System.out.println(ignored.getMessage());
}
}
}

View file

@ -1,11 +1,11 @@
package org.keycloak;
import junit.framework.Assert;
import org.jboss.resteasy.jose.jws.JWSBuilder;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.junit.Test;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.util.JsonSerialization;
import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken;
@ -26,7 +26,7 @@ public class SkeletonKeyTokenTest
scope2.add("one", "admin");
scope2.add("one", "buyer");
scope2.add("two", "seller");
String json = JsonSerialization.toString(scope2, true);
String json = JsonSerialization.writeValueAsString(scope2);
System.out.println(json);
@ -40,10 +40,10 @@ public class SkeletonKeyTokenTest
token.addAccess("foo").addRole("admin");
token.addAccess("bar").addRole("user");
String json = JsonSerialization.toString(token, true);
String json = JsonSerialization.writeValueAsString(token);
System.out.println(json);
token = JsonSerialization.fromString(SkeletonKeyToken.class, json);
token = JsonSerialization.readValue(json, SkeletonKeyToken.class);
Assert.assertEquals("111", token.getId());
SkeletonKeyToken.Access foo = token.getResourceAccess("foo");
Assert.assertNotNull(foo);
@ -60,18 +60,16 @@ public class SkeletonKeyTokenTest
token.addAccess("bar").addRole("user");
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
byte[] tokenBytes = JsonSerialization.toByteArray(token, true);
String encoded = new JWSBuilder()
.content(tokenBytes)
.jsonContent(token)
.rsa256(keyPair.getPrivate());
System.out.println(encoded);
JWSInput input = new JWSInput(encoded);
byte[] content = input.getContent();
token = JsonSerialization.fromBytes(SkeletonKeyToken.class, content);
token = input.readJsonContent(SkeletonKeyToken.class);
Assert.assertEquals("111", token.getId());
Assert.assertTrue(RSAProvider.verify(input, keyPair.getPublic()));
}

View file

@ -38,6 +38,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -5,7 +5,6 @@
<module name="org.bouncycastle"/>
<module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
<module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
<module name="org.jboss.resteasy.jose-jwt" />
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -34,6 +34,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -3,7 +3,6 @@
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<dependencies>
<module name="org.bouncycastle"/>
<module name="org.jboss.resteasy.jose-jwt" />
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -38,6 +38,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -5,7 +5,6 @@
<module name="org.bouncycastle"/>
<module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
<module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
<module name="org.jboss.resteasy.jose-jwt" />
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -15,11 +15,20 @@
<description/>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
@ -120,11 +129,6 @@
<scope>provided</scope>
</dependency>
<!-- resteasy -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>

View file

@ -1,6 +1,6 @@
package org.keycloak.example.demo;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.keycloak.util.JsonSerialization;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.RealmRepresentation;
@ -10,7 +10,6 @@ import org.keycloak.services.resources.KeycloakApplication;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
@ -39,17 +38,8 @@ public class DemoApplication extends KeycloakApplication {
public static RealmRepresentation loadJson(String path)
{
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
try {
while ( (c = is.read()) != -1)
{
os.write(c);
}
byte[] bytes = os.toByteArray();
//System.out.println(new String(bytes));
return JsonSerialization.fromBytes(RealmRepresentation.class, bytes);
return JsonSerialization.readValue(is, RealmRepresentation.class);
} catch (IOException e) {
throw new RuntimeException(e);
}

View file

@ -31,6 +31,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>

View file

@ -15,11 +15,6 @@
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>

View file

@ -36,6 +36,16 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>

View file

@ -1,7 +1,8 @@
package org.keycloak;
package org.keycloak.adapters;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.keycloak.ResourceMetadata;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.UriBuilder;

View file

@ -31,11 +31,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>

View file

@ -72,7 +72,7 @@ public class CatalinaBearerTokenAuthenticator {
tokenString = split[1];
try {
token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
} catch (VerificationException e) {
log.error("Failed to verify token", e);
challengeResponse(response, "invalid_token", e.getMessage());

View file

@ -13,19 +13,19 @@ import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.realm.GenericPrincipal;
import org.jboss.logging.Logger;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.as7.config.CatalinaAdapterConfigLoader;
import org.keycloak.adapters.as7.config.RealmConfigurationLoader;
import org.keycloak.adapters.config.AdapterConfig;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.representations.idm.admin.LogoutAction;
import org.keycloak.util.JsonSerialization;
import javax.security.auth.login.LoginException;
import javax.servlet.ServletException;
@ -148,7 +148,7 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
protected void remoteLogout(JWSInput token, HttpServletResponse response) throws IOException {
try {
log.debug("->> remoteLogout: ");
LogoutAction action = JsonSerialization.fromBytes(LogoutAction.class, token.getContent());
LogoutAction action = JsonSerialization.readValue(token.getContent(), LogoutAction.class);
if (action.isExpired()) {
log.warn("admin request failed, expired token");
response.sendError(400, "Expired token");

View file

@ -2,7 +2,7 @@ package org.keycloak.adapters.as7;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.VerificationException;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.SkeletonKeyToken;
@ -256,7 +256,7 @@ public class ServletOAuthLogin {
tokenString = tokenResponse.getToken();
try {
token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata());
token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata().getRealmKey(), realmInfo.getMetadata().getRealm());
log.debug("Token Verification succeeded!");
} catch (VerificationException e) {
log.error("failed verification of token");

View file

@ -4,7 +4,7 @@ import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.adapters.config.AdapterConfigLoader;
import javax.ws.rs.core.UriBuilder;

View file

@ -31,11 +31,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>

View file

@ -78,7 +78,7 @@ public class BearerTokenAuthenticator {
}
try {
token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
} catch (VerificationException e) {
log.error("Failed to verify token", e);
challenge = challengeResponse(exchange, "invalid_token", e.getMessage());

View file

@ -6,7 +6,7 @@ import io.undertow.security.idm.Account;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.AttachmentKey;
import org.jboss.logging.Logger;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
import org.keycloak.SkeletonKeySession;

View file

@ -8,7 +8,7 @@ import io.undertow.server.handlers.CookieImpl;
import io.undertow.util.Headers;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.VerificationException;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.SkeletonKeyToken;
@ -269,7 +269,7 @@ public class OAuthAuthenticator {
tokenString = tokenResponse.getToken();
try {
token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata());
token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata().getRealmKey(), realmInfo.getMetadata().getRealm());
log.debug("Token Verification succeeded!");
} catch (VerificationException e) {
log.error("failed verification of token");

View file

@ -4,7 +4,7 @@ import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.adapters.config.AdapterConfigLoader;
import javax.ws.rs.core.UriBuilder;

View file

@ -3,7 +3,7 @@ package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.ConfidentialPortManager;
import io.undertow.servlet.handlers.ServletRequestContext;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.config.AdapterConfig;

View file

@ -2,7 +2,7 @@ package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.ConfidentialPortManager;
import org.keycloak.RealmConfiguration;
import org.keycloak.adapters.RealmConfiguration;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>

View file

@ -74,6 +74,7 @@
<modules>
<module>core</module>
<module>core-jaxrs</module>
<module>model</module>
<module>services</module>
<module>integration</module>
@ -117,12 +118,12 @@
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<artifactId>resteasy-client</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>

View file

@ -15,11 +15,6 @@
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-ui</artifactId>
@ -103,6 +98,16 @@
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>

View file

@ -1,7 +1,7 @@
package org.keycloak.server;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.util.JsonSerialization;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.RealmManager;
@ -11,7 +11,6 @@ import org.keycloak.services.resources.KeycloakApplication;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -63,13 +62,7 @@ public class KeycloakServerApplication extends KeycloakApplication {
private static <T> T loadJson(InputStream is, Class<T> type) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
return JsonSerialization.fromBytes(type, bytes);
return JsonSerialization.readValue(is, type);
} catch (IOException e) {
throw new RuntimeException("Failed to parse json", e);
}

View file

@ -24,6 +24,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
@ -92,11 +98,6 @@
<artifactId>resteasy-crypto</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>

View file

@ -1,12 +1,11 @@
package org.keycloak.services.managers;
import org.jboss.resteasy.jose.jws.JWSBuilder;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
@ -85,14 +84,8 @@ public class AuthenticationManager {
}
protected String encodeToken(RealmModel realm, Object token) {
byte[] tokenBytes = null;
try {
tokenBytes = JsonSerialization.toByteArray(token, false);
} catch (Exception e) {
throw new RuntimeException(e);
}
String encodedToken = new JWSBuilder()
.content(tokenBytes)
.jsonContent(token)
.rsa256(realm.getPrivateKey());
return encodedToken;
}

View file

@ -1,9 +1,8 @@
package org.keycloak.services.managers;
import org.jboss.resteasy.jose.Base64Url;
import org.jboss.resteasy.jose.jws.JWSBuilder;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.util.JsonSerialization;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
@ -11,6 +10,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.util.Base64Url;
import javax.ws.rs.core.MultivaluedMap;
import java.io.IOException;
@ -149,7 +149,7 @@ public class TokenManager {
public String encodeScope(SkeletonKeyScope scope) {
String token = null;
try {
token = JsonSerialization.toString(scope, false);
token = JsonSerialization.writeValueAsString(scope);
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -160,7 +160,7 @@ public class TokenManager {
SkeletonKeyScope scope = null;
byte[] bytes = Base64Url.decode(scopeParam);
try {
scope = JsonSerialization.fromBytes(SkeletonKeyScope.class, bytes);
scope = JsonSerialization.readValue(bytes, SkeletonKeyScope.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -204,14 +204,8 @@ public class TokenManager {
public String encodeToken(RealmModel realm, Object token) {
byte[] tokenBytes = null;
try {
tokenBytes = JsonSerialization.toByteArray(token, false);
} catch (Exception e) {
throw new RuntimeException(e);
}
String encodedToken = new JWSBuilder()
.content(tokenBytes)
.jsonContent(token)
.rsa256(realm.getPrivateKey());
return encodedToken;
}

View file

@ -21,12 +21,12 @@
*/
package org.keycloak.services.resources;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.jaxrs.JaxrsOAuthClient;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.*;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.SkeletonKeyToken;
@ -295,7 +295,7 @@ public class AccountService {
}
String path = new JaxrsOAuthClient().checkStateCookie(uriInfo, headers);
JWSInput input = new JWSInput(code, providers);
JWSInput input = new JWSInput(code);
boolean verifiedCode = false;
try {
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
@ -306,7 +306,7 @@ public class AccountService {
logger.debug("unverified access code");
throw new BadRequestException();
}
String key = input.readContent(String.class);
String key = input.readContentAsString();
AccessCodeEntry accessCode = tokenManager.pullAccessCode(key);
if (accessCode == null) {
logger.debug("bad access code");

View file

@ -21,10 +21,10 @@
*/
package org.keycloak.services.resources;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
@ -321,7 +321,7 @@ public class RequiredActionsService {
return null;
}
JWSInput input = new JWSInput(code, providers);
JWSInput input = new JWSInput(code);
boolean verifiedCode = false;
try {
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
@ -335,7 +335,7 @@ public class RequiredActionsService {
return null;
}
String key = input.readContent(String.class);
String key = input.readContentAsString();
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
if (accessCodeEntry == null) {
logger.debug("getAccessCodeEntry access code entry null");

View file

@ -1,14 +1,14 @@
package org.keycloak.services.resources;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotImplementedYetException;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.jaxrs.JaxrsOAuthClient;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
@ -257,7 +257,7 @@ public class SaasService {
}
String path = new JaxrsOAuthClient().checkStateCookie(uriInfo, headers);
JWSInput input = new JWSInput(code, providers);
JWSInput input = new JWSInput(code);
boolean verifiedCode = false;
try {
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
@ -268,7 +268,7 @@ public class SaasService {
logger.debug("unverified access code");
throw new BadRequestException();
}
String key = input.readContent(String.class);
String key = input.readContentAsString();
AccessCodeEntry accessCode = tokenManager.pullAccessCode(key);
if (accessCode == null) {
logger.debug("bad access code");

View file

@ -1,13 +1,12 @@
package org.keycloak.services.resources;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.jose.jws.JWSBuilder;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
@ -406,7 +405,7 @@ public class TokenService {
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
}
JWSInput input = new JWSInput(code, providers);
JWSInput input = new JWSInput(code);
boolean verifiedCode = false;
try {
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
@ -420,7 +419,7 @@ public class TokenService {
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
.build();
}
String key = input.readContent(String.class);
String key = input.readContentAsString();
AccessCodeEntry accessCode = tokenManager.pullAccessCode(key);
if (accessCode == null) {
Map<String, String> res = new HashMap<String, String>();
@ -457,13 +456,7 @@ public class TokenService {
}
protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) {
byte[] tokenBytes = null;
try {
tokenBytes = JsonSerialization.toByteArray(token, false);
} catch (Exception e) {
throw new RuntimeException(e);
}
String encodedToken = new JWSBuilder().content(tokenBytes).rsa256(privateKey);
String encodedToken = new JWSBuilder().jsonContent(token).rsa256(privateKey);
return accessTokenResponse(token, encodedToken);
}
@ -589,7 +582,7 @@ public class TokenService {
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
String code = formData.getFirst("code");
JWSInput input = new JWSInput(code, providers);
JWSInput input = new JWSInput(code);
boolean verifiedCode = false;
try {
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
@ -599,7 +592,7 @@ public class TokenService {
if (!verifiedCode) {
return oauth.forwardToSecurityFailure("Illegal access code.");
}
String key = input.readContent(String.class);
String key = input.readContentAsString();
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
if (accessCodeEntry == null) {
return oauth.forwardToSecurityFailure("Unknown access code.");

View file

@ -4,12 +4,12 @@ import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.FilterInfo;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.util.JsonSerialization;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.filters.KeycloakSessionServletFilter;
import org.keycloak.services.resources.KeycloakApplication;
@ -65,6 +65,6 @@ public class AbstractKeycloakServerTest {
byte[] bytes = os.toByteArray();
System.out.println(new String(bytes));
return JsonSerialization.fromBytes(RealmRepresentation.class, bytes);
return JsonSerialization.readValue(bytes, RealmRepresentation.class);
}
}

View file

@ -46,6 +46,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
@ -149,11 +154,11 @@
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<artifactId>resteasy-multipart-provider</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<artifactId>resteasy-jackson-provider</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>

View file

@ -33,10 +33,10 @@ import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DefaultServletConfig;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.FilterInfo;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.keycloak.util.JsonSerialization;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@ -49,15 +49,11 @@ import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import javax.servlet.DispatcherType;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -100,13 +96,7 @@ public class KeycloakServer {
private static <T> T loadJson(InputStream is, Class<T> type) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
return JsonSerialization.fromBytes(type, bytes);
return JsonSerialization.readValue(is, type);
} catch (IOException e) {
throw new RuntimeException("Failed to parse json", e);
}

View file

@ -30,15 +30,15 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.jboss.resteasy.jose.Base64Url;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.security.PemUtils;
import org.json.JSONObject;
import org.junit.Assert;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
import org.keycloak.util.JsonSerialization;
import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.util.Base64Url;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
@ -200,7 +200,7 @@ public class OAuthClient {
if (scope != null) {
try {
b.queryParam("scope", Base64Url.encode(JsonSerialization.toByteArray(scope, false)));
b.queryParam("scope", Base64Url.encode(JsonSerialization.writeValueAsBytes(scope)));
} catch (Exception e) {
throw new RuntimeException("Failed to serialize scope", e);
}

View file

@ -23,8 +23,8 @@ package org.keycloak.testsuite.rule;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.ServletInfo;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.junit.rules.ExternalResource;
import org.keycloak.util.JsonSerialization;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -93,7 +93,7 @@ public class KeycloakRule extends ExternalResource {
os.write(c);
}
byte[] bytes = os.toByteArray();
return JsonSerialization.fromBytes(RealmRepresentation.class, bytes);
return JsonSerialization.readValue(bytes, RealmRepresentation.class);
}
public void configure(KeycloakSetup configurer) {

View file

@ -20,6 +20,11 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>