Repository: cxf Updated Branches: refs/heads/master 2fc62cf0f -> 56b99bada
Support for unencoded detached payloads in case of JWS compact Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/56b99bad Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/56b99bad Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/56b99bad Branch: refs/heads/master Commit: 56b99bada23c3b142b5d99da12c4256dda4f3dd4 Parents: 2fc62cf Author: Sergey Beryozkin <[email protected]> Authored: Sun Sep 6 18:36:35 2015 +0100 Committer: Sergey Beryozkin <[email protected]> Committed: Sun Sep 6 18:36:35 2015 +0100 ---------------------------------------------------------------------- .../security/jose/jws/JwsCompactConsumer.java | 47 +++++++++++++------- .../security/jose/jws/JwsCompactProducer.java | 38 +++++++++++----- .../jose/jws/JwsJsonSignatureEntry.java | 3 +- .../cxf/rs/security/jose/jws/JwsUtils.java | 3 ++ .../jose/cookbook/JwsJoseCookBookTest.java | 8 ++-- .../jose/jws/JwsCompactReaderWriterTest.java | 15 +++++-- 6 files changed, 78 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/56b99bad/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 4c721c5..93f6d89 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 @@ -36,14 +36,17 @@ public class JwsCompactConsumer { private String encodedSequence; private String encodedSignature; private String headersJson; + private String jwsPayload; private String decodedJwsPayload; + private JwsHeaders jwsHeaders; + private boolean detached; public JwsCompactConsumer(String encodedJws) { this(encodedJws, null, null); } - public JwsCompactConsumer(String encodedJws, String encodedDetachedPayload) { - this(encodedJws, encodedDetachedPayload, null); + public JwsCompactConsumer(String encodedJws, String detachedPayload) { + this(encodedJws, detachedPayload, null); } - protected JwsCompactConsumer(String encodedJws, String encodedDetachedPayload, JsonMapObjectReaderWriter r) { + protected JwsCompactConsumer(String encodedJws, String detachedPayload, JsonMapObjectReaderWriter r) { if (r != null) { this.reader = r; } @@ -58,17 +61,17 @@ public class JwsCompactConsumer { } else { encodedSignature = parts[2]; } - String encodedJwsPayload = parts[1]; - if (encodedDetachedPayload != null) { - if (!StringUtils.isEmpty(encodedJwsPayload)) { + jwsPayload = parts[1]; + if (detachedPayload != null) { + if (!StringUtils.isEmpty(jwsPayload)) { LOG.warning("Compact JWS includes a payload expected to be detached"); throw new JwsException(JwsException.Error.INVALID_COMPACT_JWS); } - encodedJwsPayload = encodedDetachedPayload; + detached = true; + jwsPayload = detachedPayload; } - encodedSequence = parts[0] + "." + encodedJwsPayload; + encodedSequence = parts[0] + "." + jwsPayload; headersJson = JoseUtils.decodeToString(parts[0]); - decodedJwsPayload = JoseUtils.decodeToString(encodedJwsPayload); } public String getUnsignedEncodedSequence() { return encodedSequence; @@ -80,21 +83,35 @@ public class JwsCompactConsumer { return headersJson; } public String getDecodedJwsPayload() { + if (decodedJwsPayload == null) { + if (JwsUtils.isPayloadUnencoded(jwsHeaders)) { + decodedJwsPayload = jwsPayload; + } else { + decodedJwsPayload = JoseUtils.decodeToString(jwsPayload); + } + } return decodedJwsPayload; } public byte[] getDecodedJwsPayloadBytes() { - return StringUtils.toBytesUTF8(decodedJwsPayload); + return StringUtils.toBytesUTF8(getDecodedJwsPayload()); } public byte[] getDecodedSignature() { return encodedSignature.isEmpty() ? new byte[]{} : JoseUtils.decode(encodedSignature); } public JwsHeaders getJwsHeaders() { - JsonMapObject joseHeaders = reader.fromJsonToJsonObject(headersJson); - if (joseHeaders.getUpdateCount() != null) { - LOG.warning("Duplicate headers have been detected"); - throw new JwsException(JwsException.Error.INVALID_COMPACT_JWS); + if (jwsHeaders == null) { + JsonMapObject joseHeaders = reader.fromJsonToJsonObject(headersJson); + if (joseHeaders.getUpdateCount() != null) { + LOG.warning("Duplicate headers have been detected"); + throw new JwsException(JwsException.Error.INVALID_COMPACT_JWS); + } + jwsHeaders = new JwsHeaders(joseHeaders.asMap()); + if (JwsUtils.isPayloadUnencoded(jwsHeaders) && !detached) { + LOG.warning("Only detached payload can be unencoded"); + throw new JwsException(JwsException.Error.INVALID_COMPACT_JWS); + } } - return new JwsHeaders(joseHeaders.asMap()); + return jwsHeaders; } public boolean verifySignatureWith(JwsSignatureVerifier validator) { try { http://git-wip-us.apache.org/repos/asf/cxf/blob/56b99bad/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java index 20213ea..9795c10 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java @@ -31,18 +31,30 @@ public class JwsCompactProducer { private JwsHeaders headers; private String plainJwsPayload; private String signature; + private boolean detached; public JwsCompactProducer(String plainJwsPayload) { - this(null, null, plainJwsPayload); + this(plainJwsPayload, false); + } + public JwsCompactProducer(String plainJwsPayload, boolean detached) { + this(null, null, plainJwsPayload, detached); } public JwsCompactProducer(JwsHeaders headers, String plainJwsPayload) { - this(headers, null, plainJwsPayload); + this(headers, plainJwsPayload, false); + } + public JwsCompactProducer(JwsHeaders headers, String plainJwsPayload, boolean detached) { + this(headers, null, plainJwsPayload, detached); } protected JwsCompactProducer(JwsHeaders headers, JsonMapObjectReaderWriter w, String plainJwsPayload) { + this(headers, w, plainJwsPayload, false); + } + protected JwsCompactProducer(JwsHeaders headers, JsonMapObjectReaderWriter w, String plainJwsPayload, + boolean detached) { this.headers = headers; if (w != null) { this.writer = w; } this.plainJwsPayload = plainJwsPayload; + this.detached = detached; } public JwsHeaders getJwsHeaders() { if (headers == null) { @@ -51,27 +63,31 @@ public class JwsCompactProducer { return headers; } public String getUnsignedEncodedJws() { - return getUnsignedEncodedJws(false); - } - private String getUnsignedEncodedJws(boolean detached) { checkAlgorithm(); return Base64UrlUtility.encode(writer.toJson(getJwsHeaders())) + "." + (detached ? "" : Base64UrlUtility.encode(plainJwsPayload)); } + private String getSigningInput() { + checkAlgorithm(); + boolean unencoded = JwsUtils.isPayloadUnencoded(getJwsHeaders()); + if (unencoded && !detached) { + throw new JwsException(JwsException.Error.INVALID_COMPACT_JWS); + } + return Base64UrlUtility.encode(writer.toJson(getJwsHeaders())) + + "." + + (unencoded ? plainJwsPayload : Base64UrlUtility.encode(plainJwsPayload)); + } public String getEncodedSignature() { return signature; } public String getSignedEncodedJws() { - return getSignedEncodedJws(false); - } - public String getSignedEncodedJws(boolean detached) { checkAlgorithm(); boolean noSignature = StringUtils.isEmpty(signature); if (noSignature && !isPlainText()) { throw new IllegalStateException("Signature is not available"); } - return getUnsignedEncodedJws(detached) + "." + (noSignature ? "" : signature); + return getUnsignedEncodedJws() + "." + (noSignature ? "" : signature); } public String signWith(JsonWebKey jwk) { return signWith(JwsUtils.getSignatureProvider(jwk, @@ -88,7 +104,7 @@ public class JwsCompactProducer { } public String signWith(JwsSignatureProvider signer) { - byte[] bytes = StringUtils.toBytesUTF8(getUnsignedEncodedJws()); + byte[] bytes = StringUtils.toBytesUTF8(getSigningInput()); byte[] sig = signer.sign(getJwsHeaders(), bytes); return setSignatureBytes(sig); } @@ -114,7 +130,7 @@ public class JwsCompactProducer { } private void checkAlgorithm() { if (getAlgorithm() == null) { - throw new IllegalStateException("Algorithm header is not set"); + throw new JwsException(JwsException.Error.INVALID_ALGORITHM); } } } http://git-wip-us.apache.org/repos/asf/cxf/blob/56b99bad/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 index 4a2de5f..dd2e590 100644 --- 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 @@ -77,8 +77,7 @@ public class JwsJsonSignatureEntry { return jwsPayload; } public String getDecodedJwsPayload() { - if (protectedHeader == null - || protectedHeader.getPayloadEncodingStatus() != Boolean.FALSE) { + if (protectedHeader == null || !JwsUtils.isPayloadUnencoded(protectedHeader)) { return JoseUtils.decodeToString(jwsPayload); } else { return jwsPayload; http://git-wip-us.apache.org/repos/asf/cxf/blob/56b99bad/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index 73b291e..e959e6e 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -390,4 +390,7 @@ public final class JwsUtils { RSSEC_SIGNATURE_IN_PROPS, RSSEC_SIGNATURE_PROPS); KeyManagementUtils.validateCertificateChain(props, certs); } + public static boolean isPayloadUnencoded(JwsHeaders jwsHeaders) { + return jwsHeaders.getPayloadEncodingStatus() == Boolean.FALSE; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/56b99bad/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java index f606beb..1c1bb04 100644 --- a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java @@ -508,20 +508,20 @@ public class JwsJoseCookBookTest { } @Test public void testDetachedHMACSignature() throws Exception { - JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD); + JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD, true); compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.HS256); compactProducer.getJwsHeaders().setKeyId(HMAC_KID_VALUE); JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); assertEquals(reader.toJson(compactProducer.getJwsHeaders().asMap()), HMAC_SIGNATURE_PROTECTED_HEADER_JSON); assertEquals(compactProducer.getUnsignedEncodedJws(), - HMAC_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD); + HMAC_SIGNATURE_PROTECTED_HEADER + "."); JsonWebKeys jwks = readKeySet("cookbookSecretSet.txt"); List<JsonWebKey> keys = jwks.getKeys(); JsonWebKey key = keys.get(0); compactProducer.signWith(key); - assertEquals(compactProducer.getSignedEncodedJws(true), DETACHED_HMAC_JWS); + assertEquals(compactProducer.getSignedEncodedJws(), DETACHED_HMAC_JWS); JwsCompactConsumer compactConsumer = - new JwsCompactConsumer(compactProducer.getSignedEncodedJws(true), ENCODED_PAYLOAD); + new JwsCompactConsumer(compactProducer.getSignedEncodedJws(), ENCODED_PAYLOAD); assertTrue(compactConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); http://git-wip-us.apache.org/repos/asf/cxf/blob/56b99bad/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java index 0759c61..64f04ce 100644 --- a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java @@ -42,13 +42,12 @@ import org.apache.cxf.rt.security.crypto.CryptoUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; public class JwsCompactReaderWriterTest extends Assert { public static final String TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD = - "eyJhbGciOiJIUzI1NiJ9..GsyM6AQJbQHY8aQKCbZSPJHzMRWo3HKIlcDuXof7nqs"; + "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9..GsyM6AQJbQHY8aQKCbZSPJHzMRWo3HKIlcDuXof7nqs"; public static final String UNSIGNED_PLAIN_DOCUMENT = "$.02"; public static final String ENCODED_TOKEN_SIGNED_BY_MAC = @@ -110,11 +109,19 @@ public class JwsCompactReaderWriterTest extends Assert { assertEquals(ENCODED_TOKEN_SIGNED_BY_MAC, jws.getSignedEncodedJws()); } @Test - @Ignore public void testWriteReadJwsUnencodedPayload() throws Exception { JwsHeaders headers = new JwsHeaders(SignatureAlgorithm.HS256); headers.setPayloadEncodingStatus(false); - //JwsCompactProducer producer = new JwsCompactProducer(UNSIGNED_PLAIN_DOCUMENT); + JwsCompactProducer producer = new JwsCompactProducer(headers, + UNSIGNED_PLAIN_DOCUMENT, + true); + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)); + assertEquals(TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD, producer.getSignedEncodedJws()); + JwsCompactConsumer consumer = + new JwsCompactConsumer(TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD, UNSIGNED_PLAIN_DOCUMENT); + + assertTrue(consumer.verifySignatureWith( + new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, SignatureAlgorithm.HS256))); } @Test public void testWriteReadJwsUnsigned() throws Exception {
