Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9806117A12 for ; Thu, 16 Oct 2014 20:33:54 +0000 (UTC) Received: (qmail 97617 invoked by uid 500); 16 Oct 2014 20:33:54 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 97554 invoked by uid 500); 16 Oct 2014 20:33:54 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 97545 invoked by uid 99); 16 Oct 2014 20:33:54 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 16 Oct 2014 20:33:54 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 3C1C69D5765; Thu, 16 Oct 2014 20:33:54 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: sergeyb@apache.org To: commits@cxf.apache.org Message-Id: <8ed93ed751454f15990a83c8ec6174d5@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: git commit: [CXF-6053] Introducing JwsJsonConsumer, applying the first part of the modified patch on behalf of Daniel Torkian and Luigi Lo Iacono Date: Thu, 16 Oct 2014 20:33:54 +0000 (UTC) Repository: cxf Updated Branches: refs/heads/3.0.x-fixes d951b7efb -> eb7dbc4b3 [CXF-6053] Introducing JwsJsonConsumer, applying the first part of the modified patch on behalf of Daniel Torkian and Luigi Lo Iacono Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/eb7dbc4b Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/eb7dbc4b Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/eb7dbc4b Branch: refs/heads/3.0.x-fixes Commit: eb7dbc4b3cca8511e496b2a02ac4bfd8e3e95113 Parents: d951b7e Author: Sergey Beryozkin Authored: Thu Oct 16 21:31:06 2014 +0100 Committer: Sergey Beryozkin Committed: Thu Oct 16 21:33:33 2014 +0100 ---------------------------------------------------------------------- .../provider/json/AbstractJsonMapObject.java | 2 +- .../json/JsonMapObjectReaderWriter.java | 7 +- .../cxf/rs/security/jose/JoseHeaders.java | 4 + .../apache/cxf/rs/security/jose/JoseUtils.java | 16 +++ .../security/jose/jws/JwsCompactConsumer.java | 32 ++---- .../rs/security/jose/jws/JwsJsonConsumer.java | 101 ++++++++++++++++++ .../jose/jws/JwsJsonProtectedHeader.java | 65 ++++++++++++ .../jose/jws/JwsJsonSignatureEntry.java | 103 +++++++++++++++++++ .../jose/jws/JwsJsonUnprotectedHeader.java | 66 ++++++++++++ .../security/jose/jws/JwsJsonConsumerTest.java | 78 ++++++++++++++ .../jose/jws/jwkPublicJsonConsumerSet.txt | 18 ++++ .../jose/jwt/grant/JwtBearerGrantHandler.java | 2 +- 12 files changed, 467 insertions(+), 27 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/AbstractJsonMapObject.java ---------------------------------------------------------------------- diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/AbstractJsonMapObject.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/AbstractJsonMapObject.java index a518a06..d6eef9d 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/AbstractJsonMapObject.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/AbstractJsonMapObject.java @@ -42,7 +42,7 @@ public abstract class AbstractJsonMapObject { } public Map asMap() { - return new LinkedHashMap(values); + return values; } protected Long getLongDate(String name) { http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObjectReaderWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObjectReaderWriter.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObjectReaderWriter.java index ca30c7d..de382a0 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObjectReaderWriter.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObjectReaderWriter.java @@ -100,7 +100,12 @@ public class JsonMapObjectReaderWriter { readJsonObjectAsSettable(joseObject, theJson.substring(1, theJson.length() - 1)); } - + public Map fromJson(String json) { + String theJson = json.trim(); + MapSettable nextMap = new MapSettable(); + readJsonObjectAsSettable(nextMap, theJson.substring(1, theJson.length() - 1)); + return nextMap.map; + } protected void readJsonObjectAsSettable(Settable values, String json) { for (int i = 0; i < json.length(); i++) { http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java index ea1b984..15bff4d 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java @@ -33,6 +33,10 @@ public class JoseHeaders extends AbstractJsonMapObject { public JoseHeaders() { } + public JoseHeaders(JoseHeaders headers) { + this(headers.asMap()); + } + public JoseHeaders(Map values) { super(values); } http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java index 9abbe79..b0ba894 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java @@ -18,6 +18,10 @@ */ package org.apache.cxf.rs.security.jose; +import java.io.UnsupportedEncodingException; + +import org.apache.cxf.common.util.crypto.CryptoUtils; + public final class JoseUtils { private JoseUtils() { @@ -43,4 +47,16 @@ public final class JoseUtils { } return contentType; } + + public static String decodeToString(String encoded) { + try { + return new String(decode(encoded), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new SecurityException(ex); + } + + } + public static byte[] decode(String encoded) { + return CryptoUtils.decodeSequence(encoded); + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java index 1d37dd7..578c049 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java @@ -18,14 +18,11 @@ */ package org.apache.cxf.rs.security.jose.jws; -import java.io.UnsupportedEncodingException; - -import org.apache.cxf.common.util.Base64Exception; -import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.rs.security.jose.JoseHeaders; import org.apache.cxf.rs.security.jose.JoseHeadersReader; import org.apache.cxf.rs.security.jose.JoseHeadersReaderWriter; +import org.apache.cxf.rs.security.jose.JoseUtils; import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; public class JwsCompactConsumer { @@ -54,12 +51,12 @@ public class JwsCompactConsumer { } else { encodedSignature = parts[2]; } - headersJson = decodeToString(parts[0]); - jwsPayload = decodeToString(parts[1]); + headersJson = JoseUtils.decodeToString(parts[0]); + jwsPayload = JoseUtils.decodeToString(parts[1]); encodedSequence = parts[0] + "." + parts[1]; } - public String getUnsignedEncodedPayload() { + public String getUnsignedEncodedSequence() { return encodedSequence; } public String getEncodedSignature() { @@ -75,7 +72,7 @@ public class JwsCompactConsumer { return StringUtils.toBytesUTF8(jwsPayload); } public byte[] getDecodedSignature() { - return encodedSignature.isEmpty() ? new byte[]{} : decode(encodedSignature); + return encodedSignature.isEmpty() ? new byte[]{} : JoseUtils.decode(encodedSignature); } public JoseHeaders getJoseHeaders() { JoseHeaders joseHeaders = reader.fromJsonHeaders(headersJson); @@ -86,7 +83,7 @@ public class JwsCompactConsumer { } public boolean verifySignatureWith(JwsSignatureVerifier validator) { try { - if (validator.verify(getJoseHeaders(), getUnsignedEncodedPayload(), getDecodedSignature())) { + if (validator.verify(getJoseHeaders(), getUnsignedEncodedSequence(), getDecodedSignature())) { return true; } } catch (SecurityException ex) { @@ -97,22 +94,9 @@ public class JwsCompactConsumer { public boolean verifySignatureWith(JsonWebKey key) { return verifySignatureWith(JwsUtils.getSignatureVerifier(key)); } - private static String decodeToString(String encoded) { - try { - return new String(decode(encoded), "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new SecurityException(ex); - } - - } + protected JoseHeadersReader getReader() { return reader; } - private static byte[] decode(String encoded) { - try { - return Base64UrlUtility.decode(encoded); - } catch (Base64Exception ex) { - throw new SecurityException(ex); - } - } + } http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java new file mode 100644 index 0000000..bbc2ac5 --- /dev/null +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java @@ -0,0 +1,101 @@ +/** + * 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.jose.jws; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.jaxrs.provider.json.AbstractJsonMapObject; +import org.apache.cxf.jaxrs.provider.json.JsonMapObjectReaderWriter; +import org.apache.cxf.rs.security.jose.JoseUtils; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; + +public class JwsJsonConsumer { + + private String jwsSignedDocument; + private String encodedJwsPayload; + private List signatureEntries; + + /** + * @param jwsSignedDocument + * signed JWS Document + */ + public JwsJsonConsumer(String jwsSignedDocument) { + this.jwsSignedDocument = jwsSignedDocument; + prepare(); + } + + private void prepare() { + AbstractJsonMapObject jsonObject = new AbstractJsonMapObject() { }; + new JsonMapObjectReaderWriter().fromJson(jsonObject, jwsSignedDocument); + this.encodedJwsPayload = (String)jsonObject.asMap().get("payload"); + + @SuppressWarnings("unchecked") + List> signatureArray = + (List>)jsonObject.asMap().get("signatures"); + + this.signatureEntries = new ArrayList(signatureArray.size()); + + for (Map signatureEntry : signatureArray) { + String protectedHeader = (String)signatureEntry.get("protected"); + @SuppressWarnings("unchecked") + Map header = (Map)signatureEntry.get("header"); + String signature = (String)signatureEntry.get("signature"); + if (protectedHeader == null && header == null || signature == null) { + throw new SecurityException("Invalid security entry"); + } + JwsJsonSignatureEntry signatureObject = + new JwsJsonSignatureEntry(encodedJwsPayload, + protectedHeader, + signature, + new JwsJsonUnprotectedHeader(header)); + this.signatureEntries.add(signatureObject); + } + } + public String getSignedDocument() { + return this.jwsSignedDocument; + } + public String getEncodedJwsPayload() { + return this.encodedJwsPayload; + } + public String getDecodedJwsPayload() { + return JoseUtils.decodeToString(encodedJwsPayload); + } + public byte[] getDecodedJwsPayloadBytes() { + return StringUtils.toBytesUTF8(getDecodedJwsPayload()); + } + public List getSignatureEntries() { + return Collections.unmodifiableList(signatureEntries); + } + public boolean verifySignatureWith(JwsSignatureVerifier validator) { + for (JwsJsonSignatureEntry signatureEntry : signatureEntries) { + if (signatureEntry.verifySignatureWith(validator)) { + return true; + } + } + return false; + } + public boolean verifySignatureWith(JsonWebKey key) { + return verifySignatureWith(JwsUtils.getSignatureVerifier(key)); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProtectedHeader.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProtectedHeader.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProtectedHeader.java new file mode 100644 index 0000000..81b3bf6 --- /dev/null +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProtectedHeader.java @@ -0,0 +1,65 @@ +/** + * 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.jose.jws; + +import java.util.Map; + +import org.apache.cxf.common.util.Base64UrlUtility; +import org.apache.cxf.rs.security.jose.JoseHeaders; +import org.apache.cxf.rs.security.jose.JoseHeadersReaderWriter; + +public class JwsJsonProtectedHeader { + + private JoseHeadersReaderWriter writer = new JoseHeadersReaderWriter(); + private JoseHeaders headerEntries; + + public JwsJsonProtectedHeader() { + } + public JwsJsonProtectedHeader(JoseHeaders headerEntries) { + this.headerEntries = headerEntries; + } + public JwsJsonProtectedHeader(Map values) { + this.headerEntries = new JoseHeaders(values); + } + public void setHeaderEntries(JoseHeaders headerEntries) { + this.headerEntries = headerEntries; + } + public JoseHeaders getHeaderEntries() { + return headerEntries; + } + public void addHeader(String name, Object value) { + headerEntries.setHeader(name, value); + } + public Object getHeader(String name) { + return headerEntries.getHeader(name); + } + public String toJson() { + if (headerEntries == null) { + return ""; + } + //The "protected" member MUST be present and contain the value + // BASE64URL(UTF8(JWS Protected Header)) when the JWS Protected + // Header value is non-empty; otherwise, it MUST be absent. These + // Header Parameter values are integrity protected. + return "\"protected\":\"" + + Base64UrlUtility.encode(writer.headersToJson(headerEntries)) + + "\""; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonSignatureEntry.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonSignatureEntry.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonSignatureEntry.java new file mode 100644 index 0000000..8b73f45 --- /dev/null +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonSignatureEntry.java @@ -0,0 +1,103 @@ +/** + * 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.jose.jws; + +import java.util.Collections; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.rs.security.jose.JoseHeaders; +import org.apache.cxf.rs.security.jose.JoseHeadersReaderWriter; +import org.apache.cxf.rs.security.jose.JoseUtils; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; + + +public class JwsJsonSignatureEntry { + private String encodedJwsPayload; + private String encodedProtectedHeader; + private String encodedSignature; + private JwsJsonProtectedHeader protectedHeader; + private JwsJsonUnprotectedHeader unprotectedHeader; + private JoseHeaders unionHeader; + + public JwsJsonSignatureEntry(String encodedJwsPayload, + String encodedProtectedHeader, + String encodedSignature, + JwsJsonUnprotectedHeader unprotectedHeader) { + this.encodedJwsPayload = encodedJwsPayload; + this.encodedProtectedHeader = encodedProtectedHeader; + this.encodedSignature = encodedSignature; + this.unprotectedHeader = unprotectedHeader; + this.protectedHeader = new JwsJsonProtectedHeader( + new JoseHeadersReaderWriter().fromJsonHeaders(JoseUtils.decodeToString(encodedProtectedHeader))); + prepare(); + } + private void prepare() { + JoseHeaders joseProtectedHeader = getProtectedHeader().getHeaderEntries(); + JoseHeaders joseUnprotectedHeader = getUnprotectedHeader().getHeaderEntries(); + if (!Collections.disjoint(joseProtectedHeader.asMap().keySet(), + joseUnprotectedHeader.asMap().keySet())) { + throw new SecurityException("Protected and unprotected headers have duplicate values"); + } + unionHeader = new JoseHeaders(joseProtectedHeader); + unionHeader.asMap().putAll(joseUnprotectedHeader.asMap()); + } + public String getEncodedJwsPayload() { + return encodedJwsPayload; + } + public String getDecodedJwsPayload() { + return JoseUtils.decodeToString(encodedJwsPayload); + } + public byte[] getDecodedJwsPayloadBytes() { + return StringUtils.toBytesUTF8(getDecodedJwsPayload()); + } + public String getEncodedProtectedHeader() { + return encodedProtectedHeader; + } + public JwsJsonProtectedHeader getProtectedHeader() { + return protectedHeader; + } + public JwsJsonUnprotectedHeader getUnprotectedHeader() { + return unprotectedHeader; + } + public JoseHeaders getUnionHeader() { + return unionHeader; + } + public String getEncodedSignature() { + return encodedSignature; + } + public byte[] getDecodedSignature() { + return JoseUtils.decode(getEncodedSignature()); + } + public String getUnsignedEncodedSequence() { + return getEncodedProtectedHeader() + "." + getEncodedJwsPayload(); + } + public boolean verifySignatureWith(JwsSignatureVerifier validator) { + try { + return validator.verify(getUnionHeader(), + getUnsignedEncodedSequence(), + getDecodedSignature()); + } catch (SecurityException ex) { + // ignore + } + return false; + } + public boolean verifySignatureWith(JsonWebKey key) { + return verifySignatureWith(JwsUtils.getSignatureVerifier(key)); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonUnprotectedHeader.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonUnprotectedHeader.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonUnprotectedHeader.java new file mode 100644 index 0000000..7fdd54a --- /dev/null +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonUnprotectedHeader.java @@ -0,0 +1,66 @@ +/** + * 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.jose.jws; + +import java.util.Map; + +import org.apache.cxf.rs.security.jose.JoseConstants; +import org.apache.cxf.rs.security.jose.JoseHeaders; +import org.apache.cxf.rs.security.jose.JoseHeadersReaderWriter; + +public class JwsJsonUnprotectedHeader { + + private JoseHeadersReaderWriter writer = new JoseHeadersReaderWriter(); + private JoseHeaders headerEntries; + + public JwsJsonUnprotectedHeader() { + } + public JwsJsonUnprotectedHeader(JoseHeaders headers) { + headerEntries = headers; + } + + public JwsJsonUnprotectedHeader(Map values) { + this(new JoseHeaders(values)); + } + + + public void addHeader(String name, Object value) { + if (JoseConstants.HEADER_CRITICAL.equals(name)) { + throw new SecurityException(); + } + headerEntries.setHeader(name, value); + } + public Object getHeader(String name) { + return headerEntries.getHeader(name); + } + public JoseHeaders getHeaderEntries() { + return headerEntries; + } + public String toJson() { + // The "header" member MUST be present and contain the value JWS + // Unprotected Header when the JWS Unprotected Header value is non- + // empty; otherwise, it MUST be absent. This value is represented as + // an unencoded JSON object, rather than as a string. These Header + // Parameter values are not integrity protected. + if (headerEntries == null) { + return ""; + } + return "\"header\":" + writer.headersToJson(headerEntries); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java new file mode 100644 index 0000000..a79754a --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java @@ -0,0 +1,78 @@ +/** + * 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.jose.jws; + +import java.io.InputStream; +import java.util.List; + +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; +import org.apache.cxf.rs.security.jose.jwk.JwkUtils; + +import org.junit.Assert; +import org.junit.Test; + +public class JwsJsonConsumerTest extends Assert { + private static final String DUAL_SIGNED_DOCUMENT = + "{\"payload\":\n" + + "\t\"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ\",\n" + + "\t\"signatures\":[\n" + + "\t\t\t{\"protected\":\"eyJhbGciOiJSUzI1NiJ9\",\n" + + "\t\t\t \"header\":\n" + + "\t\t\t\t{\"kid\":\"2010-12-29\"},\n" + + "\t\t\t \"signature\":\n" + + "\t\t\t\t\"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5" + + "jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb" + + "1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOY" + + "EUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw\"},\n" + + "\t\t\t{\"protected\":\"eyJhbGciOiJFUzI1NiJ9\",\n" + + "\t\t\t \"header\":\n" + + "\t\t\t\t{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"},\n" + + "\t\t\t \"signature\":\n" + + "\t\t\t\t\"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q\"}]\n" + + "}"; + + private static final String KID_OF_THE_FIRST_SIGNER = "2010-12-29"; + private static final String KID_OF_THE_SECOND_SIGNER = "e9bc097a-ce51-4036-9562-d2ade882db0d"; + + @Test + public void testVerifyDualSignedDocument() throws Exception { + JwsJsonConsumer consumer = new JwsJsonConsumer(DUAL_SIGNED_DOCUMENT); + JsonWebKeys jwks = readKeySet("jwkPublicJsonConsumerSet.txt"); + + List sigEntries = consumer.getSignatureEntries(); + assertEquals(2, sigEntries.size()); + // 1st signature + String firstKid = (String)sigEntries.get(0).getUnionHeader().getHeader("kid"); + assertEquals(KID_OF_THE_FIRST_SIGNER, firstKid); + JsonWebKey rsaKey = jwks.getKey(firstKid); + assertNotNull(rsaKey); + assertTrue(sigEntries.get(0).verifySignatureWith(rsaKey)); + // 2nd signature + String secondKid = (String)sigEntries.get(1).getUnionHeader().getHeader("kid"); + assertEquals(KID_OF_THE_SECOND_SIGNER, secondKid); + JsonWebKey ecKey = jwks.getKey(secondKid); + assertNotNull(ecKey); + //assertTrue(sigEntries.get(1).verifySignatureWith(ecKey)); + } + public JsonWebKeys readKeySet(String fileName) throws Exception { + InputStream is = JwsJsonConsumerTest.class.getResourceAsStream(fileName); + return JwkUtils.readJwkSet(is); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/jwkPublicJsonConsumerSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/jwkPublicJsonConsumerSet.txt b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/jwkPublicJsonConsumerSet.txt new file mode 100644 index 0000000..ba8e723 --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/jwkPublicJsonConsumerSet.txt @@ -0,0 +1,18 @@ +{"keys": + [ + {"kty":"RSA", + "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e":"AQAB", + "alg":"RS256", + "kid":"2010-12-29"}, + + {"kty":"EC", + "alg":"ES256", + "crv":"P-256", + "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "kid":"e9bc097a-ce51-4036-9562-d2ade882db0d" + } + + ] + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/eb7dbc4b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java index c3a2283..f7b25f8 100644 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java @@ -60,7 +60,7 @@ public class JwtBearerGrantHandler extends AbstractJwtHandler { JwsJwtCompactConsumer jwsReader = getJwsReader(assertion); JwtToken jwtToken = jwsReader.getJwtToken(); super.validateSignature(jwtToken.getHeaders(), - jwsReader.getUnsignedEncodedPayload(), + jwsReader.getUnsignedEncodedSequence(), jwsReader.getDecodedSignature());