cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From serg...@apache.org
Subject [1/2] [CXF-5311] Initial JWT code, more to follow
Date Tue, 20 May 2014 11:42:55 GMT
Repository: cxf
Updated Branches:
  refs/heads/master 5bd695d0d -> fd0528c0f


http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenReaderWriter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenReaderWriter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenReaderWriter.java
new file mode 100644
index 0000000..9d95771
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenReaderWriter.java
@@ -0,0 +1,254 @@
+/**
+ * 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.jwt;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+
+public class JwtTokenReaderWriter implements JwtTokenReader, JwtTokenWriter {
+    private static final Set<String> DATE_PROPERTIES = 
+        new HashSet<String>(Arrays.asList(JwtConstants.CLAIM_EXPIRY, 
+                                          JwtConstants.CLAIM_ISSUED_AT, 
+                                          JwtConstants.CLAIM_NOT_BEFORE));
+    private boolean format;
+    
+    @Override
+    public String headersToJson(JwtHeaders headers) {
+        return toJson(headers);
+    }
+
+    @Override
+    public String claimsToJson(JwtClaims claims) {
+        return toJson(claims);
+    }
+
+    @Override
+    public JwtTokenJson tokenToJson(JwtToken token) {
+        return new JwtTokenJson(toJson(token.getHeaders()),
+                                    toJson(token.getClaims()));
+    }
+    
+    @Override
+    public JwtHeaders fromJsonHeaders(String headersJson) {
+        JwtHeaders headers = new JwtHeaders();
+        fromJsonInternal(headers, headersJson);
+        return headers;
+    }
+    
+    @Override
+    public JwtClaims fromJsonClaims(String claimsJson) {
+        JwtClaims claims = new JwtClaims();
+        fromJsonInternal(claims, claimsJson);
+        return claims;
+        
+    }
+    
+    @Override
+    public JwtToken fromJson(String headersJson, String claimsJson) {
+        JwtHeaders headers = fromJsonHeaders(headersJson);
+        JwtClaims claims = fromJsonClaims(claimsJson);
+        return new JwtToken(headers, claims);
+    }
+    
+    @Override
+    public JwtToken fromJson(JwtTokenJson pair) {
+        return fromJson(pair.getHeadersJson(), pair.getClaimsJson());
+    }
+    
+    private String toJson(AbstractJwtObject jwt) {
+        StringBuilder sb = new StringBuilder();
+        toJsonInternal(sb, jwt.asMap());
+        return sb.toString();
+    }
+
+    private void toJsonInternal(StringBuilder sb, Map<String, Object> map) {
+        sb.append("{");
+        for (Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext();) {
+            Map.Entry<String, Object> entry = it.next();
+            sb.append("\"").append(entry.getKey()).append("\"");
+            sb.append(":");
+            toJsonInternal(sb, entry.getValue(), it.hasNext());
+        }
+        sb.append("}");
+    }
+    
+    private void toJsonInternal(StringBuilder sb, Object[] array) {
+        toJsonInternal(sb, Arrays.asList(array));
+    }
+    
+    private void toJsonInternal(StringBuilder sb, Collection<?> coll) {
+        sb.append("[");
+        formatIfNeeded(sb);
+        for (Iterator<?> iter = coll.iterator(); iter.hasNext();) {
+            toJsonInternal(sb, iter.next(), iter.hasNext());
+        }
+        formatIfNeeded(sb);
+        sb.append("]");
+    }
+    
+    @SuppressWarnings("unchecked")
+    private void toJsonInternal(StringBuilder sb, Object value, boolean hasNext) {
+        if (AbstractJwtObject.class.isAssignableFrom(value.getClass())) {
+            sb.append(toJson((AbstractJwtObject)value));
+        } else if (value.getClass().isArray()) {
+            toJsonInternal(sb, (Object[])value);
+        } else if (Collection.class.isAssignableFrom(value.getClass())) {
+            toJsonInternal(sb, (Collection<?>)value);
+        } else if (Map.class.isAssignableFrom(value.getClass())) {
+            toJsonInternal(sb, (Map<String, Object>)value);
+        } else {
+            if (value.getClass() == String.class) {
+                sb.append("\"");
+            }
+            sb.append(value);
+            if (value.getClass() == String.class) {
+                sb.append("\"");
+            }
+        }
+        if (hasNext) {
+            sb.append(",");
+            formatIfNeeded(sb);
+        }
+        
+    }
+    
+    private void formatIfNeeded(StringBuilder sb) {
+        if (format) {
+            sb.append("\r\n ");
+        }
+    }
+        
+    private void fromJsonInternal(AbstractJwtObject jwt, String json) {
+        Map<String, Object> values = readJwtObjectAsMap(json.substring(1, json.length() - 1));
+        fromJsonInternal(jwt, values);
+    }
+    
+    private void fromJsonInternal(AbstractJwtObject jwt, Map<String, Object> values) {
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            jwt.setValue(entry.getKey(), entry.getValue());
+        }
+    }
+    
+    private Map<String, Object> readJwtObjectAsMap(String json) {
+        Map<String, Object> values = new LinkedHashMap<String, Object>();
+        for (int i = 0; i < json.length(); i++) {
+            if (isWhiteSpace(json.charAt(i))) {
+                continue;
+            }
+            
+            int closingQuote = json.indexOf('"', i + 1);
+            int from = json.charAt(i) == '"' ? i + 1 : i;
+            String name = json.substring(from, closingQuote);
+            int sepIndex = json.indexOf(':', closingQuote + 1);
+            
+            int j = 1;
+            while (isWhiteSpace(json.charAt(sepIndex + j))) {
+                j++;
+            }
+            if (json.charAt(sepIndex + j) == '{') {
+                int closingIndex = getClosingIndex(json, '{', '}', sepIndex + j);
+                String newJson = json.substring(sepIndex + j + 1, closingIndex);
+                values.put(name, readJwtObjectAsMap(newJson));
+                i = closingIndex + 1;
+            } else if (json.charAt(sepIndex + j) == '[') {
+                int closingIndex = getClosingIndex(json, '[', ']', sepIndex + j);
+                String newJson = json.substring(sepIndex + j + 1, closingIndex);
+                values.put(name, readJwtObjectAsList(newJson));
+                i = closingIndex + 1;
+            } else {
+                int commaIndex = getCommaIndex(json, sepIndex + j);
+                Object value = readPrimitiveValue(json, sepIndex + j, commaIndex);
+                if (DATE_PROPERTIES.contains(name)) {
+                    value = Integer.valueOf(value.toString());
+                }
+                values.put(name, value);
+                i = commaIndex + 1;
+            }
+            
+        }
+        return values;
+    }
+    private List<Object> readJwtObjectAsList(String json) {
+        List<Object> values = new LinkedList<Object>();
+        for (int i = 0; i < json.length(); i++) {
+            if (isWhiteSpace(json.charAt(i))) {
+                continue;
+            }
+            if (json.charAt(i) == '{') {
+                int closingIndex = getClosingIndex(json, '{', '}', i);
+                values.add(readJwtObjectAsMap(json.substring(i + 1, closingIndex - 1)));
+                i = closingIndex + 1;
+            } else {
+                int commaIndex = getCommaIndex(json, i);
+                Object value = readPrimitiveValue(json, i, commaIndex);
+                values.add(value);
+                i = commaIndex + 1;
+            }
+        }
+        
+        return values;
+    }
+    private Object readPrimitiveValue(String json, int from, int to) {
+        Object value = json.substring(from, to);
+        String valueStr = value.toString().trim(); 
+        if (valueStr.startsWith("\"")) {
+            value = valueStr.substring(1, valueStr.length() - 1);
+        } else if ("true".equals(value) || "false".equals(value)) {
+            value = Boolean.valueOf(valueStr);
+        }
+        return value;
+    }
+    
+    private static int getCommaIndex(String json, int from) {
+        int commaIndex = json.indexOf(",", from);
+        if (commaIndex == -1) {
+            commaIndex = json.length();
+        }
+        return commaIndex;
+    }
+    private int getClosingIndex(String json, char openChar, char closeChar, int from) {
+        int nextOpenIndex = json.indexOf(openChar, from + 1);
+        int closingIndex = json.indexOf(closeChar, from + 1);
+        while (nextOpenIndex != -1 && nextOpenIndex < closingIndex) {
+            nextOpenIndex = json.indexOf(openChar, closingIndex + 1);
+            closingIndex = json.indexOf(closeChar, closingIndex + 1);
+        }
+        return closingIndex;
+    }
+    private boolean isWhiteSpace(char jsonChar) {
+        return jsonChar == ' ' || jsonChar == '\r' || jsonChar == '\n' || jsonChar == '\t';
+    }
+
+    public void setFormat(boolean format) {
+        this.format = format;
+    }
+
+    
+
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenWriter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenWriter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenWriter.java
new file mode 100644
index 0000000..f235c9b
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/JwtTokenWriter.java
@@ -0,0 +1,28 @@
+/**
+ * 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.jwt;
+
+
+
+public interface JwtTokenWriter extends JwtHeadersWriter {
+    
+    String claimsToJson(JwtClaims claims);
+    JwtTokenJson tokenToJson(JwtToken token);
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/AbstractJwtHandler.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/AbstractJwtHandler.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/AbstractJwtHandler.java
new file mode 100644
index 0000000..ff1bc48
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/AbstractJwtHandler.java
@@ -0,0 +1,92 @@
+/**
+ * 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.jwt.grant;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.grants.AbstractGrantHandler;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtClaims;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtHeaders;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+
+
+/**
+ * The "JWT Bearer" grant handler
+ */
+public abstract class AbstractJwtHandler extends AbstractGrantHandler {
+    private Set<String> supportedIssuers; 
+        
+    protected AbstractJwtHandler(List<String> grants) {
+        super(grants);
+    }
+    
+    protected void validateSignature(JwtHeaders headers, String plainSequence, byte[] signature) {
+        
+    }
+    
+    protected void validateClaims(Client client, JwtClaims claims) {
+        validateIssuer(claims.getIssuer());
+        validateSubject(client, claims.getSubject());
+        validateAudience(client, claims.getAudience());
+        validateExpiryTime(claims.getExpiryTime());
+        validateNotBeforeTime(claims.getNotBefore());
+        validateIssuedAtTime(claims.getIssuedAt());
+        validateTokenId(claims.getTokenId());
+    }
+
+    protected void validateIssuer(String issuer) {
+        if (issuer == null || !supportedIssuers.contains(issuer)) {
+            throw new OAuthServiceException(OAuthConstants.INVALID_GRANT);
+        }
+    }
+    
+    protected void validateSubject(Client client, String subject) {
+        //TODO
+    }
+    protected void validateAudience(Client client, String audience) {
+        //TODO
+    }
+    protected void validateExpiryTime(Integer timestamp) {
+        if (timestamp != null) {
+            //TODO
+        }
+    }
+    protected void validateNotBeforeTime(Integer timestamp) {
+        if (timestamp != null) {
+            //TODO    
+        }
+    }
+    protected void validateIssuedAtTime(Integer timestamp) {
+        if (timestamp != null) {
+            //TODO
+        }
+    }
+    protected void validateTokenId(String tokenId) {
+        if (tokenId != null) {
+            //TODO
+        }
+    }
+    public void setSupportedIssuers(Set<String> supportedIssuers) {
+        this.supportedIssuers = supportedIssuers;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/Constants.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/Constants.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/Constants.java
new file mode 100644
index 0000000..c7630b2
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/Constants.java
@@ -0,0 +1,33 @@
+/**
+ * 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.jwt.grant;
+
+public final class Constants {
+    public static final String JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
+    public static final String CLIENT_GRANT_ASSERTION_PARAM = "assertion";
+    
+    public static final String CLIENT_AUTH_ASSERTION_PARAM = "client_assertion";
+    public static final String CLIENT_AUTH_ASSERTION_TYPE = "client_assertion_type";
+    public static final String CLIENT_AUTH_JWT_BEARER = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
+   
+    
+    private Constants() {
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/JwtBearerGrantHandler.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/JwtBearerGrantHandler.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/JwtBearerGrantHandler.java
new file mode 100644
index 0000000..1d4f9f5
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/grant/JwtBearerGrantHandler.java
@@ -0,0 +1,91 @@
+/**
+ * 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.jwt.grant;
+
+import java.util.Arrays;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.rs.security.oauth2.common.Client;
+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.jws.JwsCompactConsumer;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtToken;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtTokenReader;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils;
+
+/**
+ * The "JWT Bearer" grant handler
+ */
+public class JwtBearerGrantHandler extends AbstractJwtHandler {
+    private static final String ENCODED_JWT_BEARER_GRANT;
+    static {
+        //  AccessTokenService may be configured with the form provider
+        // which will not decode by default - so listing both the actual 
+        // and encoded grant type value will help
+        ENCODED_JWT_BEARER_GRANT = HttpUtils.urlEncode(Constants.JWT_BEARER_GRANT, "UTF-8");
+    }
+    private JwtTokenReader jwtReader;
+    public JwtBearerGrantHandler() {
+        super(Arrays.asList(Constants.JWT_BEARER_GRANT, ENCODED_JWT_BEARER_GRANT));
+    }
+
+    @Override
+    public ServerAccessToken createAccessToken(Client client, MultivaluedMap<String, String> params)
+        throws OAuthServiceException {
+        String assertion = params.getFirst(Constants.CLIENT_GRANT_ASSERTION_PARAM);
+        if (assertion == null) {
+            throw new OAuthServiceException(OAuthConstants.INVALID_GRANT);
+        }
+        try {
+            JwsCompactConsumer jwsReader = getJwsReader(assertion);
+            JwtToken jwtToken = jwsReader.getJwtToken();
+            super.validateSignature(jwtToken.getHeaders(),
+                                    jwsReader.getUnsignedEncodedToken(), 
+                                    jwsReader.getDecodedSignature());
+            
+                   
+            super.validateClaims(client, jwtToken.getClaims());
+            UserSubject grantSubject = new UserSubject(jwtToken.getClaims().getSubject());
+            
+            return doCreateAccessToken(client, 
+                                       grantSubject,
+                                       Constants.JWT_BEARER_GRANT,
+                                       OAuthUtils.parseScope(params.getFirst(OAuthConstants.SCOPE)));
+        } catch (OAuthServiceException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new OAuthServiceException(OAuthConstants.INVALID_GRANT, ex);
+        }    
+        
+    }
+
+    protected JwsCompactConsumer getJwsReader(String assertion) {
+        return new JwsCompactConsumer(assertion, jwtReader);
+    }
+    
+    public void setJwtReader(JwtTokenReader tokenReader) {
+        this.jwtReader = tokenReader;
+    }
+
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweContainerRequestFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweContainerRequestFilter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweContainerRequestFilter.java
new file mode 100644
index 0000000..89c47c6
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweContainerRequestFilter.java
@@ -0,0 +1,36 @@
+/**
+ * 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.jwt.jaxrs;
+
+import java.io.IOException;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+
+@PreMatching
+public class JweContainerRequestFilter implements ContainerRequestFilter {
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        // TODO Auto-generated method stub
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsContainerRequestFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsContainerRequestFilter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsContainerRequestFilter.java
new file mode 100644
index 0000000..8c5c43a
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsContainerRequestFilter.java
@@ -0,0 +1,36 @@
+/**
+ * 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.jwt.jaxrs;
+
+import java.io.IOException;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+
+@PreMatching
+public class JwsContainerRequestFilter implements ContainerRequestFilter {
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        // TODO Auto-generated method stub
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jwk/JsonWebKey.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jwk/JsonWebKey.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jwk/JsonWebKey.java
new file mode 100644
index 0000000..e26ee0c
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jwk/JsonWebKey.java
@@ -0,0 +1,132 @@
+/**
+ * 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.jwt.jwk;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.rs.security.oauth2.jwt.AbstractJwtObject;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtConstants;
+
+
+public class JsonWebKey extends AbstractJwtObject {
+    
+    public static final String KEY_TYPE = "kty";
+    public static final String PUBLIC_KEY_USE = "use";
+    public static final String KEY_OPERATIONS = "key_ops";
+    public static final String KEY_ALGO = JwtConstants.HEADER_ALGORITHM;
+    public static final String KEY_ID = JwtConstants.HEADER_KEY_ID;
+    public static final String X509_URL = JwtConstants.HEADER_X509_URL;
+    public static final String X509_CHAIN = JwtConstants.HEADER_X509_CHAIN;
+    public static final String X509_THUMBPRINT = JwtConstants.HEADER_X509_THUMBPRINT;
+    
+    public static final String KEY_TYPE_OCTET = "oct";
+    public static final String KEY_TYPE_RSA = "RSA";
+    public static final String KEY_TYPE_ECURVE = "EC";
+    
+    public static final String PUBLIC_KEY_USE_SIGN = "sig";
+    public static final String PUBLIC_KEY_USE_ENCRYPT = "enc";
+    
+    public static final String KEY_OPER_SIGN = "sign";
+    public static final String KEY_OPER_VERIFY = "verify";
+    public static final String KEY_OPER_ENCRYPT = "encrypt";
+    public static final String KEY_OPER_DECRYPT = "decrypt";
+    
+    public JsonWebKey() {
+        
+    }
+    
+    public JsonWebKey(Map<String, Object> values) {
+        super(values);
+    }
+    
+    public void setKeyType(String keyType) {
+        super.setValue(KEY_TYPE, keyType);
+    }
+
+    public String getKeyType() {
+        return (String)super.getValue(KEY_TYPE);
+    }
+
+    public void setPublicKeyUse(String use) {
+        super.setValue(PUBLIC_KEY_USE, use);
+    }
+    
+    public String getPublicKeyUse() {
+        return (String)super.getValue(PUBLIC_KEY_USE);
+    }
+
+    public void setKeyOperation(List<String> keyOperation) {
+        super.setValue(KEY_OPERATIONS, keyOperation);
+    }
+
+    public List<String> getKeyOperation() {
+        return CastUtils.cast((List<?>)super.getValue(KEY_OPERATIONS));
+    }
+    
+    public void setAlgorithm(String algorithm) {
+        super.setValue(KEY_ALGO, algorithm);
+    }
+
+    public String getAlgorithm() {
+        return (String)super.getValue(KEY_ALGO);
+    }
+    
+    public void setKid(String kid) {
+        super.setValue(KEY_ID, kid);
+    }
+
+    public String getKid() {
+        return (String)super.getValue(KEY_ID);
+    }
+    
+    public void setX509Url(String x509Url) {
+        super.setValue(X509_URL, x509Url);
+    }
+    
+    public String getX509Url() {
+        return (String)super.getValue(X509_URL);
+    }
+
+    public void setX509Chain(String x509Chain) {
+        super.setValue(X509_CHAIN, x509Chain);
+    }
+
+    public String getX509Chain() {
+        return (String)super.getValue(X509_CHAIN);
+    }
+    
+    public void setX509Thumbprint(String x509Thumbprint) {
+        super.setValue(X509_THUMBPRINT, x509Thumbprint);
+    }
+    
+    public String getX509Thumbprint() {
+        return (String)super.getValue(X509_THUMBPRINT);
+    }
+    
+    public JsonWebKey setProperty(String name, Object value) {
+        super.setValue(name, value);
+        return this;
+    }
+    
+    public Object getProperty(String name) {
+        return super.getValue(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
new file mode 100644
index 0000000..e3f8d9b
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
@@ -0,0 +1,121 @@
+/**
+ * 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.jwe;
+
+import java.security.Security;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+
+import org.apache.cxf.rs.security.oauth2.jws.JwsCompactReaderWriterTest;
+import org.apache.cxf.rs.security.oauth2.jwt.Algorithms;
+import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class JweCompactReaderWriterTest extends Assert {
+    private static final boolean IGNORE_TESTS;
+    static {
+        IGNORE_TESTS = System.getProperty("java.version").contains("1.6");
+    }
+    private static final byte[] CONTENT_ENCRYPTION_KEY = {
+        (byte)177, (byte)161, (byte)244, (byte)128, 84, (byte)143, (byte)225,
+        115, 63, (byte)180, 3, (byte)255, 107, (byte)154, (byte)212, (byte)246,
+        (byte)138, 7, 110, 91, 112, 46, 34, 105, 47, 
+        (byte)130, (byte)203, 46, 122, (byte)234, 64, (byte)252};
+    private static final String RSA_MODULUS_ENCODED = "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW"
+           + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S"
+           + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a"
+           + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS"
+           + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj"
+           + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw";
+    private static final String RSA_PUBLIC_EXPONENT_ENCODED = "AQAB";
+    private static final String RSA_PRIVATE_EXPONENT_ENCODED = 
+        "kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N"
+        + "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9"
+        + "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk"
+        + "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl"
+        + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd"
+        + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ";
+    
+    private static final byte[] INIT_VECTOR = {(byte)227, (byte)197, 117, (byte)252, 2, (byte)219, 
+        (byte)233, 68, (byte)180, (byte)225, 77, (byte)219};
+     
+    @BeforeClass
+    public static void registerBouncyCastleIfNeeded() throws Exception {
+        if (!IGNORE_TESTS) {    
+            try {
+                // Java 8 apparently has it
+                Cipher.getInstance(Algorithms.A256GCM_ALGO_JAVA);
+            } catch (Throwable t) {
+                // Oracle Java 7
+                Security.addProvider(new BouncyCastleProvider());    
+            }
+        }
+    }
+    @AfterClass
+    public static void unregisterBouncyCastleIfNeeded() throws Exception {
+        if (!IGNORE_TESTS) {    
+            Security.removeProvider(BouncyCastleProvider.class.getName());    
+        }
+    }
+    
+    @Test
+    public void testEncryptDecryptSpecExample() throws Exception {
+        if (IGNORE_TESTS) {
+            return;
+        }
+        
+        final String specPlainText = "The true sign of intelligence is not knowledge but imagination.";
+        String jweContent = encryptContent(specPlainText);
+        
+        decrypt(jweContent, specPlainText);
+    }
+    
+    @Test
+    public void testEncryptDecryptJwsToken() throws Exception {
+        if (IGNORE_TESTS) {
+            return;
+        }
+        String jweContent = encryptContent(JwsCompactReaderWriterTest.ENCODED_TOKEN_SIGNED_BY_MAC);
+        decrypt(jweContent, JwsCompactReaderWriterTest.ENCODED_TOKEN_SIGNED_BY_MAC);
+    }
+    
+    private String encryptContent(String content) throws Exception {
+        RSAPublicKey publicKey = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED, RSA_PUBLIC_EXPONENT_ENCODED);
+        SecretKey key = CryptoUtils.createSecretKeySpec(CONTENT_ENCRYPTION_KEY, Algorithms.A256GCM_ALGO.getJavaName());
+        RSAJweEncryptor encryptor = new RSAJweEncryptor(publicKey, key, INIT_VECTOR);
+        return encryptor.getJweContent(content);
+    }
+    
+    private void decrypt(String jweContent, String plainContent) throws Exception {
+        
+        RSAPrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED);
+        RSAJweDecryptor decryptor = new RSAJweDecryptor(jweContent, privateKey);
+        String decryptedText = decryptor.getDecryptedContentText();
+        assertEquals(decryptedText, plainContent);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jws/JwsCompactReaderWriterTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jws/JwsCompactReaderWriterTest.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jws/JwsCompactReaderWriterTest.java
new file mode 100644
index 0000000..078895a
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jws/JwsCompactReaderWriterTest.java
@@ -0,0 +1,257 @@
+/**
+ * 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.jws;
+
+import java.security.PrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.rs.security.oauth2.jwt.Algorithms;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtClaims;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtConstants;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtHeaders;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtToken;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtTokenReaderWriter;
+import org.apache.cxf.rs.security.oauth2.jwt.JwtTokenWriter;
+import org.apache.cxf.rs.security.oauth2.jwt.jwk.JsonWebKey;
+import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility;
+import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils;
+import org.apache.cxf.rs.security.oauth2.utils.crypto.HmacUtils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class JwsCompactReaderWriterTest extends Assert {
+    
+    public static final String ENCODED_TOKEN_SIGNED_BY_MAC = 
+        "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"
+        + ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+        + ".dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
+    
+    
+    private static final String ENCODED_MAC_KEY = "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75"
+        + "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow";
+    
+    private static final String ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC = 
+        "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU"
+        + "zI1NiIsDQogImp3ayI6eyJrdHkiOiJvY3QiLA0KICJrZXlfb3BzIjpbDQogInNpZ24iLA0KICJ2ZXJpZnkiDQogXX19"
+        + ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+        + ".8cFZqb15gEDYRZqSzUu23nQnKNynru1ADByRPvmmOq8";
+    
+    private static final String RSA_MODULUS_ENCODED = "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
+        + "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
+        + "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
+        + "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
+        + "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
+        + "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ";
+    private static final String RSA_PUBLIC_EXPONENT_ENCODED = "AQAB";
+    private static final String RSA_PRIVATE_EXPONENT_ENCODED = 
+        "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
+        + "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
+        + "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
+        + "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
+        + "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
+        + "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ";
+    private static final String ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY =
+        "eyJhbGciOiJSUzI1NiJ9"
+        + "."
+        + "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
+        + "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+        + "."
+        + "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
+        + "AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
+        + "BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
+        + "0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
+        + "hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
+        + "p0igcN_IoypGlUPQGe77Rw";
+     
+    @Test
+    public void testWriteJwsSignedByMacSpecExample() throws Exception {
+        JwtHeaders headers = new JwtHeaders();
+        headers.setType(JwtConstants.TYPE_JWT);
+        headers.setAlgorithm(Algorithms.HmacSHA256.getJwtName());
+        JwsCompactProducer jws = initSpecJwtTokenWriter(headers);
+        String plain = jws.getUnsignedEncodedToken();
+        
+        byte[] mac = computeMac(plain);
+        jws.setSignatureOctets(mac);
+        
+        assertEquals(ENCODED_TOKEN_SIGNED_BY_MAC, jws.getSignedEncodedToken());
+        
+    }
+    
+    @Test
+    public void testWriteReadJwsUnsigned() throws Exception {
+        JwtHeaders headers = new JwtHeaders();
+        headers.setType(JwtConstants.TYPE_JWT);
+        headers.setAlgorithm(JwtConstants.PLAIN_TEXT_ALGO);
+        
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://jwt-idp.example.com");
+        claims.setSubject("mailto:mike@example.com");
+        claims.setAudience("https://jwt-rp.example.net");
+        claims.setNotBefore(1300815780);
+        claims.setExpiryTime(1300819380);
+        claims.setClaim("http://claims.example.com/member", true);
+        
+        JwsCompactProducer writer = new JwsCompactProducer(headers, claims);
+        String signed = writer.getSignedEncodedToken();
+        
+        JwsCompactConsumer reader = new JwsCompactConsumer(signed);
+        assertEquals(0, reader.getDecodedSignature().length);
+        
+        JwtToken token = reader.getJwtToken();
+        assertEquals(new JwtToken(headers, claims), token);
+    }
+
+    @Test
+    public void testReadJwsSignedByMacSpecExample() throws Exception {
+        JwsCompactConsumer jws = new JwsCompactConsumer(ENCODED_TOKEN_SIGNED_BY_MAC);
+        String plain = jws.getUnsignedEncodedToken();
+        byte[] mac = computeMac(plain);
+        Arrays.equals(mac, jws.getDecodedSignature());
+        JwtToken token = jws.getJwtToken();
+        JwtHeaders headers = token.getHeaders();
+        assertEquals(JwtConstants.TYPE_JWT, headers.getType());
+        assertEquals(Algorithms.HmacSHA256.getJwtName(), headers.getAlgorithm());
+        validateSpecClaim(token.getClaims());
+    }
+    
+    @Test
+    public void testWriteJwsWithJwkSignedByMac() throws Exception {
+        JsonWebKey key = new JsonWebKey();
+        key.setKeyType(JsonWebKey.KEY_TYPE_OCTET);
+        key.setKeyOperation(Arrays.asList(
+            new String[]{JsonWebKey.KEY_OPER_SIGN, JsonWebKey.KEY_OPER_VERIFY}));
+        doTestWriteJwsWithJwkSignedByMac(key);
+    }
+    
+    @Test
+    public void testWriteJwsWithJwkAsMapSignedByMac() throws Exception {
+        Map<String, Object> map = new LinkedHashMap<String, Object>();
+        map.put(JsonWebKey.KEY_TYPE, JsonWebKey.KEY_TYPE_OCTET);
+        map.put(JsonWebKey.KEY_OPERATIONS,
+                new String[]{JsonWebKey.KEY_OPER_SIGN, JsonWebKey.KEY_OPER_VERIFY});
+        doTestWriteJwsWithJwkSignedByMac(map);
+    }
+    
+    private void doTestWriteJwsWithJwkSignedByMac(Object jsonWebKey) throws Exception {
+        JwtHeaders headers = new JwtHeaders();
+        headers.setType(JwtConstants.TYPE_JWT);
+        headers.setAlgorithm(Algorithms.HmacSHA256.getJwtName());
+        
+        headers.setHeader(JwtConstants.HEADER_JSON_WEB_KEY, jsonWebKey);
+        
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("joe");
+        claims.setExpiryTime(1300819380);
+        claims.setClaim("http://example.com/is_root", Boolean.TRUE);
+        
+        JwtToken token = new JwtToken(headers, claims);
+        JwsCompactProducer jws = new JwsCompactProducer(token, getWriter());
+        
+        String plain = jws.getUnsignedEncodedToken();
+        
+        byte[] mac = computeMac(plain);
+        jws.setSignatureOctets(mac);
+        
+        assertEquals(ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC, jws.getSignedEncodedToken());
+    }
+    
+    @Test
+    public void testReadJwsWithJwkSignedByMac() throws Exception {
+        JwsCompactConsumer jws = new JwsCompactConsumer(ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC);
+        String plain = jws.getUnsignedEncodedToken();
+        byte[] mac = computeMac(plain);
+        Arrays.equals(mac, jws.getDecodedSignature());
+        JwtToken token = jws.getJwtToken();
+        JwtHeaders headers = token.getHeaders();
+        assertEquals(JwtConstants.TYPE_JWT, headers.getType());
+        assertEquals(Algorithms.HmacSHA256.getJwtName(), headers.getAlgorithm());
+        
+        JsonWebKey key = headers.getJsonWebKey();
+        assertEquals(JsonWebKey.KEY_TYPE_OCTET, key.getKeyType());
+        List<String> keyOps = key.getKeyOperation();
+        assertEquals(2, keyOps.size());
+        assertEquals(JsonWebKey.KEY_OPER_SIGN, keyOps.get(0));
+        assertEquals(JsonWebKey.KEY_OPER_VERIFY, keyOps.get(1));
+        
+        validateSpecClaim(token.getClaims());
+    }
+    
+    private void validateSpecClaim(JwtClaims claims) {
+        assertEquals("joe", claims.getIssuer());
+        assertEquals(Integer.valueOf(1300819380), claims.getExpiryTime());
+        assertEquals(Boolean.TRUE, claims.getClaim("http://example.com/is_root"));
+    }
+    
+    @Test
+    public void testWriteReadJwsSignedByPrivateKey() throws Exception {
+        JwtHeaders headers = new JwtHeaders();
+        headers.setAlgorithm(Algorithms.SHA256withRSA.getJwtName());
+        JwsCompactProducer jws = initSpecJwtTokenWriter(headers);
+        String plain = jws.getUnsignedEncodedToken();
+        
+        PrivateKey key = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED);
+        byte[] sig = CryptoUtils.signData(plain.getBytes("UTF-8"), key, 
+                                          Algorithms.SHA256withRSA.getJavaName());
+        
+        jws.setSignatureOctets(sig);
+        
+        assertEquals(ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY, jws.getSignedEncodedToken());
+    }
+    
+    @Test
+    public void testReadJwsSignedByPrivateKey() throws Exception {
+        JwsCompactConsumer jws = new JwsCompactConsumer(ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY);
+        String plain = jws.getUnsignedEncodedToken();
+        RSAPublicKey key = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED, RSA_PUBLIC_EXPONENT_ENCODED);
+        CryptoUtils.verifySignature(plain.getBytes("UTF-8"), jws.getDecodedSignature(), key, 
+                                    Algorithms.SHA256withRSA.getJavaName());
+        JwtToken token = jws.getJwtToken();
+        JwtHeaders headers = token.getHeaders();
+        assertEquals(Algorithms.SHA256withRSA.getJwtName(), headers.getAlgorithm());
+        validateSpecClaim(token.getClaims());
+    }
+    
+    private JwsCompactProducer initSpecJwtTokenWriter(JwtHeaders headers) throws Exception {
+        
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("joe");
+        claims.setExpiryTime(1300819380);
+        claims.setClaim("http://example.com/is_root", Boolean.TRUE);
+        
+        JwtToken token = new JwtToken(headers, claims);
+        return new JwsCompactProducer(token, getWriter());
+    }
+
+    private byte[] computeMac(String plain) throws Exception {
+        byte[] key = Base64UrlUtility.decode(ENCODED_MAC_KEY);
+        return HmacUtils.computeHmac(key, Algorithms.HmacSHA256.getJavaName(), plain);
+    }
+    
+    private JwtTokenWriter getWriter() {
+        JwtTokenReaderWriter jsonWriter = new JwtTokenReaderWriter();
+        jsonWriter.setFormat(true);
+        return jsonWriter;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/fd0528c0/rt/rs/security/oauth-parent/pom.xml
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/pom.xml b/rt/rs/security/oauth-parent/pom.xml
index b64a741..74e43c6 100644
--- a/rt/rs/security/oauth-parent/pom.xml
+++ b/rt/rs/security/oauth-parent/pom.xml
@@ -39,5 +39,6 @@
         <module>oauth</module>
         <module>oauth2</module>
         <module>oauth2-saml</module>
+        <module>oauth2-jwt</module>
     </modules>
 </project>


Mime
View raw message