You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2014/10/31 16:38:01 UTC
git commit: [CXF-6053] Prototyping JwSJsonOutputStream and supporting
multiple key aliases in a single properties file
Repository: cxf
Updated Branches:
refs/heads/master 56c0db051 -> d20d51801
[CXF-6053] Prototyping JwSJsonOutputStream and supporting multiple key aliases in a single properties file
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/d20d5180
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/d20d5180
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/d20d5180
Branch: refs/heads/master
Commit: d20d518014f1df8bf6d3befa0af2ebc378a8d488
Parents: 56c0db0
Author: Sergey Beryozkin <sb...@talend.com>
Authored: Fri Oct 31 15:37:43 2014 +0000
Committer: Sergey Beryozkin <sb...@talend.com>
Committed: Fri Oct 31 15:37:43 2014 +0000
----------------------------------------------------------------------
.../cxf/common/util/Base64OutputStream.java | 4 +-
.../jaxrs/AbstractJwsJsonReaderProvider.java | 6 +-
.../jaxrs/AbstractJwsJsonWriterProvider.java | 8 +-
.../jose/jaxrs/JwsJsonWriterInterceptor.java | 63 +++++++++---
.../security/jose/jaxrs/KeyManagementUtils.java | 1 +
.../cxf/rs/security/jose/jwk/JwkUtils.java | 75 ++++++++++----
.../security/jose/jws/JwsJsonOutputStream.java | 103 +++++++++++++++++++
.../jose/jws/JwsJsonProtectedHeader.java | 5 +-
.../cxf/rs/security/jose/jws/JwsUtils.java | 76 +++++++++++---
.../systest/jaxrs/security/jwt/BookStore.java | 10 ++
.../jaxrs/security/jwt/JAXRSJweJwsTest.java | 21 +++-
.../jaxrs/security/jwt/JAXRSJwsJsonTest.java | 27 +++--
12 files changed, 335 insertions(+), 64 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java b/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java
index 6ba8e95..61724d8 100644
--- a/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java
+++ b/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java
@@ -65,14 +65,14 @@ public class Base64OutputStream extends FilterOutputStream {
@Override
public void flush() throws IOException {
- if (flushed) {
+ if (flushed || lastChunk == null) {
return;
}
try {
Base64Utility.encodeAndStream(lastChunk, 0, lastChunk.length, urlSafe, out);
lastChunk = null;
} catch (Exception ex) {
- throw new SecurityException();
+ throw new IOException(ex);
}
flushed = true;
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonReaderProvider.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonReaderProvider.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonReaderProvider.java
index bf2bf2f..7272df9 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonReaderProvider.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonReaderProvider.java
@@ -18,6 +18,7 @@
*/
package org.apache.cxf.rs.security.jose.jaxrs;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -55,13 +56,14 @@ public class AbstractJwsJsonReaderProvider {
}
List<String> propLocs = null;
if (propLocsProp instanceof String) {
- propLocs = Collections.singletonList((String)propLocsProp);
+ String[] props = ((String)propLocsProp).split(",");
+ propLocs = Arrays.asList(props);
} else {
propLocs = CastUtils.cast((List<?>)propLocsProp);
}
List<JwsSignatureVerifier> theSigVerifiers = new LinkedList<JwsSignatureVerifier>();
for (String propLoc : propLocs) {
- theSigVerifiers.add(JwsUtils.loadSignatureVerifier(propLoc, m));
+ theSigVerifiers.addAll(JwsUtils.loadSignatureVerifiers(propLoc, m));
}
return theSigVerifiers;
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonWriterProvider.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonWriterProvider.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonWriterProvider.java
index 6aa9695..db10c62 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonWriterProvider.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsJsonWriterProvider.java
@@ -21,6 +21,7 @@ package org.apache.cxf.rs.security.jose.jaxrs;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -59,14 +60,15 @@ public class AbstractJwsJsonWriterProvider {
throw new SecurityException();
}
List<String> propLocs = null;
- if (propLocsProp instanceof String) {
- propLocs = Collections.singletonList((String)propLocsProp);
+ if (propLocsProp instanceof String) {
+ String[] props = ((String)propLocsProp).split(",");
+ propLocs = Arrays.asList(props);
} else {
propLocs = CastUtils.cast((List<?>)propLocsProp);
}
List<JwsSignatureProvider> theSigProviders = new LinkedList<JwsSignatureProvider>();
for (String propLoc : propLocs) {
- theSigProviders.add(JwsUtils.loadSignatureProvider(propLoc, m));
+ theSigProviders.addAll(JwsUtils.loadSignatureProviders(propLoc, m));
}
return theSigProviders;
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java
index e812e59..1417cf0 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java
@@ -20,6 +20,7 @@ package org.apache.cxf.rs.security.jose.jaxrs;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.List;
import javax.annotation.Priority;
@@ -28,42 +29,76 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
+import org.apache.cxf.common.util.Base64UrlOutputStream;
+import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.rs.security.jose.JoseConstants;
import org.apache.cxf.rs.security.jose.JoseHeaders;
+import org.apache.cxf.rs.security.jose.jws.JwsJsonOutputStream;
import org.apache.cxf.rs.security.jose.jws.JwsJsonProducer;
import org.apache.cxf.rs.security.jose.jws.JwsJsonProtectedHeader;
+import org.apache.cxf.rs.security.jose.jws.JwsSignature;
import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
@Priority(Priorities.JWS_WRITE_PRIORITY)
public class JwsJsonWriterInterceptor extends AbstractJwsJsonWriterProvider implements WriterInterceptor {
private boolean contentTypeRequired = true;
+ private boolean useJwsOutputStream;
@Override
public void aroundWriteTo(WriterInterceptorContext ctx) throws IOException, WebApplicationException {
List<JwsSignatureProvider> sigProviders = getInitializedSigProviders();
OutputStream actualOs = ctx.getOutputStream();
- CachedOutputStream cos = new CachedOutputStream();
- ctx.setOutputStream(cos);
- ctx.proceed();
- JwsJsonProducer p = new JwsJsonProducer(new String(cos.getBytes(), "UTF-8"));
- for (JwsSignatureProvider signer : sigProviders) {
- JoseHeaders headers = new JoseHeaders();
- headers.setAlgorithm(signer.getAlgorithm());
- setContentTypeIfNeeded(headers, ctx);
- //TODO: support setting public JWK kid property as the unprotected header;
- // the property would have to be associated with the individual signer
- p.signWith(signer, new JwsJsonProtectedHeader(headers), null);
+ if (useJwsOutputStream) {
+ List<String> protectedHeaders = new ArrayList<String>(sigProviders.size());
+ List<JwsSignature> signatures = new ArrayList<JwsSignature>(sigProviders.size());
+ for (JwsSignatureProvider signer : sigProviders) {
+ JwsJsonProtectedHeader protectedHeader = prepareProtectedHeader(ctx, signer);
+ String encoded = protectedHeader.getEncodedHeaderEntries();
+ protectedHeaders.add(encoded);
+ JwsSignature signature = signer.createJwsSignature(protectedHeader.getHeaderEntries());
+ byte[] start = StringUtils.toBytesUTF8(encoded + ".");
+ signature.update(start, 0, start.length);
+ signatures.add(signature);
+ }
+ ctx.setMediaType(JAXRSUtils.toMediaType(JoseConstants.MEDIA_TYPE_JOSE_JSON));
+ actualOs.write(StringUtils.toBytesUTF8("{\"payload\":\""));
+ JwsJsonOutputStream jwsStream = new JwsJsonOutputStream(actualOs, protectedHeaders, signatures);
+ Base64UrlOutputStream base64Stream = new Base64UrlOutputStream(jwsStream);
+ ctx.setOutputStream(base64Stream);
+ ctx.proceed();
+ base64Stream.flush();
+ jwsStream.flush();
+ } else {
+ CachedOutputStream cos = new CachedOutputStream();
+ ctx.setOutputStream(cos);
+ ctx.proceed();
+ JwsJsonProducer p = new JwsJsonProducer(new String(cos.getBytes(), "UTF-8"));
+ for (JwsSignatureProvider signer : sigProviders) {
+ JwsJsonProtectedHeader protectedHeader = prepareProtectedHeader(ctx, signer);
+ p.signWith(signer, protectedHeader, null);
+ }
+ ctx.setMediaType(JAXRSUtils.toMediaType(JoseConstants.MEDIA_TYPE_JOSE_JSON));
+ writeJws(p, actualOs);
}
- ctx.setMediaType(JAXRSUtils.toMediaType(JoseConstants.MEDIA_TYPE_JOSE_JSON));
- writeJws(p, actualOs);
+
+ }
+
+ private JwsJsonProtectedHeader prepareProtectedHeader(WriterInterceptorContext ctx,
+ JwsSignatureProvider signer) {
+ JoseHeaders headers = new JoseHeaders();
+ headers.setAlgorithm(signer.getAlgorithm());
+ setContentTypeIfNeeded(headers, ctx);
+ return new JwsJsonProtectedHeader(headers);
}
public void setContentTypeRequired(boolean contentTypeRequired) {
this.contentTypeRequired = contentTypeRequired;
}
-
+ public void setUseJwsJsonOutputStream(boolean useJwsJsonOutputStream) {
+ this.useJwsOutputStream = useJwsJsonOutputStream;
+ }
private void setContentTypeIfNeeded(JoseHeaders headers, WriterInterceptorContext ctx) {
if (contentTypeRequired) {
MediaType mt = ctx.getMediaType();
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/KeyManagementUtils.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/KeyManagementUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/KeyManagementUtils.java
index 369e072..88fcb3a 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/KeyManagementUtils.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/KeyManagementUtils.java
@@ -42,6 +42,7 @@ public final class KeyManagementUtils {
public static final String RSSEC_KEY_STORE_PSWD = "rs.security.keystore.password";
public static final String RSSEC_KEY_PSWD = "rs.security.key.password";
public static final String RSSEC_KEY_STORE_ALIAS = "rs.security.keystore.alias";
+ public static final String RSSEC_KEY_STORE_ALIASES = "rs.security.keystore.aliases";
public static final String RSSEC_KEY_STORE_FILE = "rs.security.keystore.file";
public static final String RSSEC_PRINCIPAL_NAME = "rs.security.principal.name";
public static final String RSSEC_KEY_PSWD_PROVIDER = "rs.security.key.password.provider";
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
index ac6654f..883a2b4 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java
@@ -25,6 +25,7 @@ import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
@@ -199,40 +200,78 @@ public final class JwkUtils {
public static JsonWebKey loadJsonWebKey(Message m, Properties props, String keyOper) {
return loadJsonWebKey(m, props, keyOper, new DefaultJwkReaderWriter());
}
+
public static JsonWebKey loadJsonWebKey(Message m, Properties props, String keyOper, JwkReaderWriter reader) {
- PrivateKeyPasswordProvider cb =
- (PrivateKeyPasswordProvider)m.getContextualProperty(KeyManagementUtils.RSSEC_KEY_PSWD_PROVIDER);
- if (cb == null && keyOper != null) {
- String propName = keyOper.equals(JsonWebKey.KEY_OPER_SIGN) ? KeyManagementUtils.RSSEC_SIG_KEY_PSWD_PROVIDER
- : keyOper.equals(JsonWebKey.KEY_OPER_ENCRYPT)
- ? KeyManagementUtils.RSSEC_DECRYPT_KEY_PSWD_PROVIDER : null;
- if (propName != null) {
- cb = (PrivateKeyPasswordProvider)m.getContextualProperty(propName);
+ PrivateKeyPasswordProvider cb = loadPasswordProvider(m, props, keyOper);
+ JsonWebKeys jwkSet = loadJwkSet(m, props, cb, reader);
+ String kid = getKeyId(props, KeyManagementUtils.RSSEC_KEY_STORE_ALIAS, keyOper);
+ if (kid != null) {
+ return jwkSet.getKey(kid);
+ } else if (keyOper != null) {
+ List<JsonWebKey> keys = jwkSet.getKeyUseMap().get(keyOper);
+ if (keys != null && keys.size() == 1) {
+ return keys.get(0);
}
}
+ return null;
+ }
+ public static List<JsonWebKey> loadJsonWebKeys(Message m, Properties props, String keyOper) {
+ return loadJsonWebKeys(m, props, keyOper, new DefaultJwkReaderWriter());
+ }
+
+ public static List<JsonWebKey> loadJsonWebKeys(Message m, Properties props, String keyOper,
+ JwkReaderWriter reader) {
+ PrivateKeyPasswordProvider cb = loadPasswordProvider(m, props, keyOper);
JsonWebKeys jwkSet = loadJwkSet(m, props, cb, reader);
- String kid = props.getProperty(KeyManagementUtils.RSSEC_KEY_STORE_ALIAS);
+ String kid = getKeyId(props, KeyManagementUtils.RSSEC_KEY_STORE_ALIAS, keyOper);
+ if (kid != null) {
+ return Collections.singletonList(jwkSet.getKey(kid));
+ }
+ String kids = getKeyId(props, KeyManagementUtils.RSSEC_KEY_STORE_ALIASES, keyOper);
+ if (kids != null) {
+ String[] values = kids.split(",");
+ List<JsonWebKey> keys = new ArrayList<JsonWebKey>(values.length);
+ for (String value : values) {
+ keys.add(jwkSet.getKey(value));
+ }
+ return keys;
+ }
+ if (keyOper != null) {
+ List<JsonWebKey> keys = jwkSet.getKeyUseMap().get(keyOper);
+ if (keys != null && keys.size() == 1) {
+ return Collections.singletonList(keys.get(0));
+ }
+ }
+ return null;
+ }
+ private static String getKeyId(Properties props, String propertyName, String keyOper) {
+ String kid = props.getProperty(propertyName);
if (kid == null && keyOper != null) {
String keyIdProp = null;
if (keyOper.equals(JsonWebKey.KEY_OPER_ENCRYPT)) {
- keyIdProp = KeyManagementUtils.RSSEC_KEY_STORE_ALIAS + ".jwe";
+ keyIdProp = propertyName + ".jwe";
} else if (keyOper.equals(JsonWebKey.KEY_OPER_SIGN)
|| keyOper.equals(JsonWebKey.KEY_OPER_VERIFY)) {
- keyIdProp = KeyManagementUtils.RSSEC_KEY_STORE_ALIAS + ".jws";
+ keyIdProp = propertyName + ".jws";
}
if (keyIdProp != null) {
kid = props.getProperty(keyIdProp);
}
}
- if (kid != null) {
- return jwkSet.getKey(kid);
- } else if (keyOper != null) {
- List<JsonWebKey> keys = jwkSet.getKeyUseMap().get(keyOper);
- if (keys != null && keys.size() == 1) {
- return keys.get(0);
+ return kid;
+ }
+ public static PrivateKeyPasswordProvider loadPasswordProvider(Message m, Properties props, String keyOper) {
+ PrivateKeyPasswordProvider cb =
+ (PrivateKeyPasswordProvider)m.getContextualProperty(KeyManagementUtils.RSSEC_KEY_PSWD_PROVIDER);
+ if (cb == null && keyOper != null) {
+ String propName = keyOper.equals(JsonWebKey.KEY_OPER_SIGN) ? KeyManagementUtils.RSSEC_SIG_KEY_PSWD_PROVIDER
+ : keyOper.equals(JsonWebKey.KEY_OPER_ENCRYPT)
+ ? KeyManagementUtils.RSSEC_DECRYPT_KEY_PSWD_PROVIDER : null;
+ if (propName != null) {
+ cb = (PrivateKeyPasswordProvider)m.getContextualProperty(propName);
}
}
- return null;
+ return cb;
}
public static RSAPublicKey toRSAPublicKey(JsonWebKey jwk) {
String encodedModulus = (String)jwk.getProperty(JsonWebKey.RSA_MODULUS);
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonOutputStream.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonOutputStream.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonOutputStream.java
new file mode 100644
index 0000000..7fc1007
--- /dev/null
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonOutputStream.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.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cxf.common.util.Base64UrlUtility;
+import org.apache.cxf.common.util.StringUtils;
+
+public class JwsJsonOutputStream extends FilterOutputStream {
+ private boolean flushed;
+ private List<String> protectedHeaders;
+ private List<JwsSignature> signatures;
+ private ExecutorService executor;
+ public JwsJsonOutputStream(OutputStream out,
+ List<String> protectedHeaders,
+ List<JwsSignature> signatures) {
+ super(out);
+ this.protectedHeaders = protectedHeaders;
+ this.signatures = signatures;
+ // This can be further optimized by having a dedicated thread per signature,
+ // can make a difference if a number of signatures to be created is > 2
+ this.executor = Executors.newSingleThreadExecutor();
+ }
+
+ @Override
+ public void write(int value) throws IOException {
+ byte[] bytes = ByteBuffer.allocate(Integer.SIZE / 8).putInt(value).array();
+ write(bytes, 0, bytes.length);
+ }
+
+ @Override
+ public void write(final byte b[], final int off, final int len) throws IOException {
+ //TODO: Review if it is at least theoretically possible that a given b[] region
+ // can be modified in a subsequent write which might affect the signature calculation
+ executor.execute(new Runnable() {
+ public void run() {
+ for (JwsSignature signature : signatures) {
+ try {
+ signature.update(b, off, len);
+ } catch (Throwable ex) {
+ throw new SecurityException();
+ }
+ }
+ }
+ });
+ out.write(b, off, len);
+ }
+ @Override
+ public void flush() throws IOException {
+ if (flushed) {
+ return;
+ }
+ out.write(StringUtils.toBytesUTF8("\",\"signatures\":["));
+ try {
+ shutdownExecutor();
+ for (int i = 0; i < signatures.size(); i++) {
+ if (i > 0) {
+ out.write(new byte[]{','});
+ }
+ out.write(StringUtils.toBytesUTF8("{\"protected\":\""
+ + protectedHeaders.get(i)
+ + "\",\"signature\":\""));
+ byte[] sign = signatures.get(i).sign();
+ Base64UrlUtility.encodeAndStream(sign, 0, sign.length, out);
+ out.write(StringUtils.toBytesUTF8("\"}"));
+ }
+ } catch (Exception ex) {
+ throw new SecurityException();
+ }
+ out.write(StringUtils.toBytesUTF8("]}"));
+ flushed = true;
+ }
+ private void shutdownExecutor() throws Exception {
+ executor.shutdown();
+ while (!executor.isTerminated()) {
+ executor.awaitTermination(1, TimeUnit.MILLISECONDS);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/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
index 05cecdb..dd94b25 100644
--- 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
@@ -49,8 +49,11 @@ public class JwsJsonProtectedHeader {
public Object getHeader(String name) {
return headerEntries.getHeader(name);
}
+ public String toJson() {
+ return writer.headersToJson(headerEntries);
+ }
public String getEncodedHeaderEntries() {
- return Base64UrlUtility.encode(writer.headersToJson(headerEntries));
+ return Base64UrlUtility.encode(toJson());
}
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/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 c9741a2..21cb1e1 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
@@ -20,6 +20,8 @@ package org.apache.cxf.rs.security.jose.jws;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Properties;
@@ -84,12 +86,10 @@ public final class JwsUtils {
return map;
}
public static JwsSignatureProvider loadSignatureProvider(String propLoc, Message m) {
- Properties props = null;
- try {
- props = ResourceUtils.loadProperties(propLoc, m.getExchange().getBus());
- } catch (Exception ex) {
- throw new SecurityException(ex);
- }
+ return loadSignatureProvider(propLoc, m, false);
+ }
+ private static JwsSignatureProvider loadSignatureProvider(String propLoc, Message m, boolean ignoreNullProvider) {
+ Properties props = loadProperties(m, propLoc);
JwsSignatureProvider theSigProvider = null;
String rsaSignatureAlgo = null;
if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) {
@@ -102,18 +102,37 @@ public final class JwsUtils {
KeyManagementUtils.RSSEC_SIG_KEY_PSWD_PROVIDER);
theSigProvider = new PrivateKeyJwsSignatureProvider(pk, rsaSignatureAlgo);
}
- if (theSigProvider == null) {
+ if (theSigProvider == null && !ignoreNullProvider) {
throw new SecurityException();
}
return theSigProvider;
}
- public static JwsSignatureVerifier loadSignatureVerifier(String propLoc, Message m) {
- Properties props = null;
- try {
- props = ResourceUtils.loadProperties(propLoc, m.getExchange().getBus());
- } catch (Exception ex) {
- throw new SecurityException(ex);
+ public static List<JwsSignatureProvider> loadSignatureProviders(String propLoc, Message m) {
+ Properties props = loadProperties(m, propLoc);
+ JwsSignatureProvider theSigProvider = loadSignatureProvider(propLoc, m, true);
+ if (theSigProvider != null) {
+ return Collections.singletonList(theSigProvider);
+ }
+ List<JwsSignatureProvider> theSigProviders = null;
+ if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) {
+ List<JsonWebKey> jwks = JwkUtils.loadJsonWebKeys(m, props, JsonWebKey.KEY_OPER_SIGN);
+ if (jwks != null) {
+ theSigProviders = new ArrayList<JwsSignatureProvider>(jwks.size());
+ for (JsonWebKey jwk : jwks) {
+ theSigProviders.add(JwsUtils.getSignatureProvider(jwk));
+ }
+ }
}
+ if (theSigProviders == null) {
+ throw new SecurityException();
+ }
+ return theSigProviders;
+ }
+ public static JwsSignatureVerifier loadSignatureVerifier(String propLoc, Message m) {
+ return loadSignatureVerifier(propLoc, m, false);
+ }
+ public static JwsSignatureVerifier loadSignatureVerifier(String propLoc, Message m, boolean ignoreNullVerifier) {
+ Properties props = loadProperties(m, propLoc);
JwsSignatureVerifier theVerifier = null;
String rsaSignatureAlgo = null;
if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) {
@@ -126,8 +145,39 @@ public final class JwsUtils {
theVerifier = new PublicKeyJwsSignatureVerifier(
(RSAPublicKey)KeyManagementUtils.loadPublicKey(m, props), rsaSignatureAlgo);
}
+ if (theVerifier == null && !ignoreNullVerifier) {
+ throw new SecurityException();
+ }
return theVerifier;
}
+ public static List<JwsSignatureVerifier> loadSignatureVerifiers(String propLoc, Message m) {
+ Properties props = loadProperties(m, propLoc);
+ JwsSignatureVerifier theVerifier = loadSignatureVerifier(propLoc, m, true);
+ if (theVerifier != null) {
+ return Collections.singletonList(theVerifier);
+ }
+ List<JwsSignatureVerifier> theVerifiers = null;
+ if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) {
+ List<JsonWebKey> jwks = JwkUtils.loadJsonWebKeys(m, props, JsonWebKey.KEY_OPER_SIGN);
+ if (jwks != null) {
+ theVerifiers = new ArrayList<JwsSignatureVerifier>(jwks.size());
+ for (JsonWebKey jwk : jwks) {
+ theVerifiers.add(JwsUtils.getSignatureVerifier(jwk));
+ }
+ }
+ }
+ if (theVerifiers == null) {
+ throw new SecurityException();
+ }
+ return theVerifiers;
+ }
+ private static Properties loadProperties(Message m, String propLoc) {
+ try {
+ return ResourceUtils.loadProperties(propLoc, m.getExchange().getBus());
+ } catch (Exception ex) {
+ throw new SecurityException(ex);
+ }
+ }
private static String getSignatureAlgo(Properties props, String algo) {
return algo == null ? props.getProperty(JSON_WEB_SIGNATURE_ALGO_PROP) : algo;
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/BookStore.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/BookStore.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/BookStore.java
index 0bc010e..b56ce36 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/BookStore.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/BookStore.java
@@ -25,6 +25,8 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import org.apache.cxf.systest.jaxrs.security.Book;
+
@Path("/bookstore")
public class BookStore {
@@ -39,6 +41,14 @@ public class BookStore {
return text;
}
+ @POST
+ @Path("/books")
+ @Produces("application/json")
+ @Consumes("application/json")
+ public Book echoBook(Book book) {
+ return book;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
index 740c6a8..119aa36 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
@@ -42,6 +42,7 @@ import org.apache.cxf.rs.security.jose.jwe.AesWrapKeyDecryptionAlgorithm;
import org.apache.cxf.rs.security.jose.jwe.AesWrapKeyEncryptionAlgorithm;
import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider;
import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
+import org.apache.cxf.systest.jaxrs.security.Book;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -78,8 +79,21 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase {
Security.removeProvider(BouncyCastleProvider.class.getName());
}
@Test
- public void testJweJwkRSA() throws Exception {
+ public void testJweJwkPlainTextRSA() throws Exception {
String address = "https://localhost:" + PORT + "/jwejwkrsa";
+ BookStore bs = createBookStore(address);
+ String text = bs.echoText("book");
+ assertEquals("book", text);
+ }
+ @Test
+ public void testJweJwkBookBeanRSA() throws Exception {
+ String address = "https://localhost:" + PORT + "/jwejwkrsa";
+ BookStore bs = createBookStore(address);
+ Book book = bs.echoBook(new Book("book", 123L));
+ assertEquals("book", book.getName());
+ assertEquals(123L, book.getId());
+ }
+ private BookStore createBookStore(String address) throws Exception {
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
SpringBusFactory bf = new SpringBusFactory();
URL busFile = JAXRSJweJwsTest.class.getResource("client.xml");
@@ -97,10 +111,9 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase {
"org/apache/cxf/systest/jaxrs/security/bob.jwk.properties");
bean.getProperties(true).put("rs.security.encryption.in.properties",
"org/apache/cxf/systest/jaxrs/security/alice.jwk.properties");
- BookStore bs = bean.create(BookStore.class);
- String text = bs.echoText("book");
- assertEquals("book", text);
+ return bean.create(BookStore.class);
}
+
@Test
public void testJweJwkAesWrap() throws Exception {
String address = "https://localhost:" + PORT + "/jwejwkaeswrap";
http://git-wip-us.apache.org/repos/asf/cxf/blob/d20d5180/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJwsJsonTest.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJwsJsonTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJwsJsonTest.java
index 892ca40..f515da1 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJwsJsonTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJwsJsonTest.java
@@ -28,6 +28,7 @@ import org.apache.cxf.bus.spring.SpringBusFactory;
import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
import org.apache.cxf.rs.security.jose.jaxrs.JwsJsonClientResponseFilter;
import org.apache.cxf.rs.security.jose.jaxrs.JwsJsonWriterInterceptor;
+import org.apache.cxf.systest.jaxrs.security.Book;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.junit.BeforeClass;
@@ -43,8 +44,21 @@ public class JAXRSJwsJsonTest extends AbstractBusClientServerTestBase {
}
@Test
- public void testJwsJwkHMac() throws Exception {
+ public void testJwsJsonPlainTextHmac() throws Exception {
String address = "https://localhost:" + PORT + "/jwsjsonhmac";
+ BookStore bs = createBookStore(address, "org/apache/cxf/systest/jaxrs/security/secret.jwk.properties");
+ String text = bs.echoText("book");
+ assertEquals("book", text);
+ }
+ @Test
+ public void testJweJsonBookBeanHmac() throws Exception {
+ String address = "https://localhost:" + PORT + "/jwsjsonhmac";
+ BookStore bs = createBookStore(address, "org/apache/cxf/systest/jaxrs/security/secret.jwk.properties");
+ Book book = bs.echoBook(new Book("book", 123L));
+ assertEquals("book", book.getName());
+ assertEquals(123L, book.getId());
+ }
+ private BookStore createBookStore(String address, String properties) throws Exception {
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
SpringBusFactory bf = new SpringBusFactory();
URL busFile = JAXRSJwsJsonTest.class.getResource("client.xml");
@@ -53,14 +67,13 @@ public class JAXRSJwsJsonTest extends AbstractBusClientServerTestBase {
bean.setServiceClass(BookStore.class);
bean.setAddress(address);
List<Object> providers = new LinkedList<Object>();
- providers.add(new JwsJsonWriterInterceptor());
+ JwsJsonWriterInterceptor writer = new JwsJsonWriterInterceptor();
+ writer.setUseJwsJsonOutputStream(true);
+ providers.add(writer);
providers.add(new JwsJsonClientResponseFilter());
bean.setProviders(providers);
- bean.getProperties(true).put("rs.security.signature.list.properties",
- "org/apache/cxf/systest/jaxrs/security/secret.jwk.properties");
- BookStore bs = bean.create(BookStore.class);
- String text = bs.echoText("book");
- assertEquals("book", text);
+ bean.getProperties(true).put("rs.security.signature.list.properties", properties);
+ return bean.create(BookStore.class);
}
}