Repository: cxf Updated Branches: refs/heads/master 96ed80508 -> 5e919271e
Prototyping the code for supporting passing public keys/certs in the jose headers Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/5e919271 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/5e919271 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/5e919271 Branch: refs/heads/master Commit: 5e919271e2e92f9845d6454b37ab0b86cfc66591 Parents: 96ed805 Author: Sergey Beryozkin <sberyoz...@talend.com> Authored: Mon Feb 2 22:11:44 2015 +0000 Committer: Sergey Beryozkin <sberyoz...@talend.com> Committed: Mon Feb 2 22:11:44 2015 +0000 ---------------------------------------------------------------------- .../jose/jaxrs/AbstractJweDecryptingFilter.java | 10 +-- .../jose/jaxrs/AbstractJwsReaderProvider.java | 5 +- .../jose/jaxrs/AbstractJwsWriterProvider.java | 4 +- .../jose/jaxrs/JweWriterInterceptor.java | 20 +++--- .../jose/jaxrs/JwsClientResponseFilter.java | 2 +- .../jose/jaxrs/JwsContainerRequestFilter.java | 2 +- .../jose/jaxrs/JwtJwsAuthenticationFilter.java | 2 +- .../security/jose/jaxrs/KeyManagementUtils.java | 65 +++++++++++++++++-- .../cxf/rs/security/jose/jwe/JweUtils.java | 63 +++++++++++++----- .../cxf/rs/security/jose/jwk/JwkUtils.java | 13 ++++ .../cxf/rs/security/jose/jws/JwsUtils.java | 67 +++++++++++++++----- .../jaxrs/security/jwt/JAXRSJweJwsTest.java | 8 +++ .../cxf/systest/jaxrs/security/jwt/server.xml | 19 ++++++ .../security/alice.rs.storeonly.properties | 19 ++++++ .../jaxrs/security/bob.rs.storeonly.properties | 21 ++++++ 15 files changed, 258 insertions(+), 62 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java index 03c024e..0d7d915 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.rs.security.jose.jwe.JweCompactConsumer; import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput; import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; import org.apache.cxf.rs.security.jose.jwe.JweHeaders; @@ -31,8 +32,9 @@ public class AbstractJweDecryptingFilter { private JweDecryptionProvider decryption; private String defaultMediaType; protected JweDecryptionOutput decrypt(InputStream is) throws IOException { - JweDecryptionProvider theDecryptor = getInitializedDecryptionProvider(); - JweDecryptionOutput out = theDecryptor.decrypt(new String(IOUtils.readBytesFromStream(is), "UTF-8")); + JweCompactConsumer jwe = new JweCompactConsumer(new String(IOUtils.readBytesFromStream(is), "UTF-8")); + JweDecryptionProvider theDecryptor = getInitializedDecryptionProvider(jwe.getJweHeaders()); + JweDecryptionOutput out = new JweDecryptionOutput(jwe.getJweHeaders(), jwe.getDecryptedContent(theDecryptor)); validateHeaders(out.getHeaders()); return out; } @@ -43,11 +45,11 @@ public class AbstractJweDecryptingFilter { public void setDecryptionProvider(JweDecryptionProvider decryptor) { this.decryption = decryptor; } - protected JweDecryptionProvider getInitializedDecryptionProvider() { + protected JweDecryptionProvider getInitializedDecryptionProvider(JweHeaders headers) { if (decryption != null) { return decryption; } - return JweUtils.loadDecryptionProvider(true); + return JweUtils.loadDecryptionProvider(headers, true); } public String getDefaultMediaType() { return defaultMediaType; http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java index 441f4bb..90d6c74 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.rs.security.jose.jaxrs; +import org.apache.cxf.rs.security.jose.JoseHeaders; import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; import org.apache.cxf.rs.security.jose.jws.JwsUtils; @@ -29,11 +30,11 @@ public class AbstractJwsReaderProvider { this.sigVerifier = signatureVerifier; } - protected JwsSignatureVerifier getInitializedSigVerifier() { + protected JwsSignatureVerifier getInitializedSigVerifier(JoseHeaders headers) { if (sigVerifier != null) { return sigVerifier; } - return JwsUtils.loadSignatureVerifier(true); + return JwsUtils.loadSignatureVerifier(headers, true); } public String getDefaultMediaType() { http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java index 1fc5bdc..02f5390 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java @@ -42,9 +42,7 @@ public class AbstractJwsWriterProvider { if (sigProvider != null) { return sigProvider; } - JwsSignatureProvider theSigProvider = JwsUtils.loadSignatureProvider(true); - headers.setAlgorithm(theSigProvider.getAlgorithm()); - return theSigProvider; + return JwsUtils.loadSignatureProvider(headers, true); } protected void setRequestContextProperty(JoseHeaders headers) { JoseUtils.setJoseContextProperty(headers); http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java index cc28c63..36a2c51 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java @@ -21,7 +21,6 @@ package org.apache.cxf.rs.security.jose.jaxrs; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.Collections; import java.util.zip.DeflaterOutputStream; import javax.annotation.Priority; @@ -54,8 +53,8 @@ public class JweWriterInterceptor implements WriterInterceptor { return; } OutputStream actualOs = ctx.getOutputStream(); - - JweEncryptionProvider theEncryptionProvider = getInitializedEncryptionProvider(); + JweHeaders jweHeaders = new JweHeaders(); + JweEncryptionProvider theEncryptionProvider = getInitializedEncryptionProvider(jweHeaders); String ctString = null; MediaType contentMediaType = ctx.getMediaType(); @@ -66,9 +65,12 @@ public class JweWriterInterceptor implements WriterInterceptor { ctString = JAXRSUtils.mediaTypeToString(contentMediaType); } } + if (ctString != null) { + jweHeaders.setContentType(ctString); + } if (useJweOutputStream) { - JweEncryptionState encryption = theEncryptionProvider.createJweEncryptionState(toJweHeaders(ctString)); + JweEncryptionState encryption = theEncryptionProvider.createJweEncryptionState(jweHeaders); try { JweCompactProducer.startJweContent(actualOs, encryption.getHeaders(), @@ -93,7 +95,7 @@ public class JweWriterInterceptor implements WriterInterceptor { CachedOutputStream cos = new CachedOutputStream(); ctx.setOutputStream(cos); ctx.proceed(); - String jweContent = theEncryptionProvider.encrypt(cos.getBytes(), toJweHeaders(ctString)); + String jweContent = theEncryptionProvider.encrypt(cos.getBytes(), jweHeaders); setJoseMediaType(ctx); IOUtils.copy(new ByteArrayInputStream(StringUtils.toBytesUTF8(jweContent)), actualOs); @@ -106,11 +108,11 @@ public class JweWriterInterceptor implements WriterInterceptor { ctx.setMediaType(joseMediaType); } - protected JweEncryptionProvider getInitializedEncryptionProvider() { + protected JweEncryptionProvider getInitializedEncryptionProvider(JweHeaders headers) { if (encryptionProvider != null) { return encryptionProvider; } - return JweUtils.loadEncryptionProvider(true); + return JweUtils.loadEncryptionProvider(headers, true); } public void setUseJweOutputStream(boolean useJweOutputStream) { @@ -120,7 +122,5 @@ public class JweWriterInterceptor implements WriterInterceptor { public void setEncryptionProvider(JweEncryptionProvider encryptionProvider) { this.encryptionProvider = encryptionProvider; } - private static JweHeaders toJweHeaders(String ct) { - return new JweHeaders(Collections.<String, Object>singletonMap(JoseConstants.HEADER_CONTENT_TYPE, ct)); - } + } http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsClientResponseFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsClientResponseFilter.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsClientResponseFilter.java index 840c971..46a5813 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsClientResponseFilter.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsClientResponseFilter.java @@ -35,8 +35,8 @@ import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; public class JwsClientResponseFilter extends AbstractJwsReaderProvider implements ClientResponseFilter { @Override public void filter(ClientRequestContext req, ClientResponseContext res) throws IOException { - JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(); JwsCompactConsumer p = new JwsCompactConsumer(IOUtils.readStringFromStream(res.getEntityStream())); + JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(p.getJoseHeaders()); if (!p.verifySignatureWith(theSigVerifier)) { throw new SecurityException(); } http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java index 6164b8f..6eb15e4 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java @@ -41,8 +41,8 @@ public class JwsContainerRequestFilter extends AbstractJwsReaderProvider impleme if (HttpMethod.GET.equals(context.getMethod())) { return; } - JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(); JwsCompactConsumer p = new JwsCompactConsumer(IOUtils.readStringFromStream(context.getEntityStream())); + JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(p.getJoseHeaders()); if (!p.verifySignatureWith(theSigVerifier)) { context.abortWith(JAXRSUtils.toResponse(400)); return; http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtJwsAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtJwsAuthenticationFilter.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtJwsAuthenticationFilter.java index 68d222f..bf1754c 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtJwsAuthenticationFilter.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtJwsAuthenticationFilter.java @@ -47,8 +47,8 @@ public class JwtJwsAuthenticationFilter extends AbstractJwsReaderProvider implem throw new SecurityException(); } - JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(); JwsJwtCompactConsumer p = new JwsJwtCompactConsumer(schemeData[1]); + JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(p.getJoseHeaders()); if (!p.verifySignatureWith(theSigVerifier)) { context.abortWith(JAXRSUtils.toResponse(400)); return; http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/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 58869d8..a798dec 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 @@ -24,8 +24,12 @@ import java.security.KeyStore; import java.security.Principal; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; import java.util.List; import java.util.Properties; @@ -54,9 +58,31 @@ public final class KeyManagementUtils { public static final String RSSEC_SIG_KEY_PSWD_PROVIDER = "rs.security.signature.key.password.provider"; public static final String RSSEC_DECRYPT_KEY_PSWD_PROVIDER = "rs.security.decryption.key.password.provider"; public static final String RSSEC_DEFAULT_ALGORITHMS = "rs.security.default.algorithms"; + public static final String RSSEC_REPORT_KEY_PROP = "rs.security.report.public.key"; private KeyManagementUtils() { } + public static List<String> loadAndEncodeX509CertificateOrChain(Message m, Properties props) { + X509Certificate[] chain = loadX509CertificateOrChain(m, props); + return encodeX509CertificateChain(chain); + } + public static X509Certificate[] loadX509CertificateOrChain(Message m, Properties props) { + KeyStore keyStore = KeyManagementUtils.loadPersistKeyStore(m, props); + String alias = props.getProperty(RSSEC_KEY_STORE_ALIAS); + return loadX509CertificateOrChain(keyStore, alias); + } + private static X509Certificate[] loadX509CertificateOrChain(KeyStore keyStore, String alias) { + try { + Certificate[] certs = keyStore.getCertificateChain(alias); + if (certs != null) { + return Arrays.copyOf(certs, certs.length, X509Certificate[].class); + } else { + return new X509Certificate[]{(X509Certificate)CryptoUtils.loadCertificate(keyStore, alias)}; + } + } catch (Exception ex) { + throw new SecurityException(ex); + } + } public static PublicKey loadPublicKey(Message m, Properties props) { KeyStore keyStore = KeyManagementUtils.loadPersistKeyStore(m, props); @@ -89,13 +115,14 @@ public final class KeyManagementUtils { Properties props, Bus bus, PrivateKeyPasswordProvider provider, - String keyOper) { + String keyOper, + String alias) { String keyPswd = props.getProperty(RSSEC_KEY_PSWD); - String alias = getKeyId(m, props, RSSEC_KEY_STORE_ALIAS, keyOper); + String theAlias = alias != null ? alias : getKeyId(m, props, RSSEC_KEY_STORE_ALIAS, keyOper); char[] keyPswdChars = provider != null ? provider.getPassword(props) : keyPswd != null ? keyPswd.toCharArray() : null; - return CryptoUtils.loadPrivateKey(keyStore, keyPswdChars, alias); + return CryptoUtils.loadPrivateKey(keyStore, keyPswdChars, theAlias); } public static PrivateKey loadPrivateKey(Message m, String keyStoreLocProp, String keyOper) { @@ -148,9 +175,13 @@ public final class KeyManagementUtils { return cb; } - public static PrivateKey loadPrivateKey(Message m, Properties props, String keyOper) { - Bus bus = m.getExchange().getBus(); + public static RSAPrivateKey loadPrivateKey(Message m, Properties props, String keyOper) { KeyStore keyStore = loadPersistKeyStore(m, props); + return (RSAPrivateKey)loadPrivateKey(keyStore, m, props, keyOper, null); + } + private static RSAPrivateKey loadPrivateKey(KeyStore keyStore, Message m, Properties props, String keyOper, + String alias) { + Bus bus = m.getExchange().getBus(); PrivateKeyPasswordProvider cb = loadPasswordProvider(m, props, keyOper); if (cb != null && m.getExchange().getInMessage() != null) { SecurityContext sc = m.getExchange().getInMessage().get(SecurityContext.class); @@ -161,7 +192,7 @@ public final class KeyManagementUtils { } } } - return loadPrivateKey(keyStore, m, props, bus, cb, keyOper); + return (RSAPrivateKey)loadPrivateKey(keyStore, m, props, bus, cb, keyOper, alias); } public static KeyStore loadPersistKeyStore(Message m, Properties props) { KeyStore keyStore = (KeyStore)m.getExchange().get(props.get(RSSEC_KEY_STORE_FILE)); @@ -182,7 +213,9 @@ public final class KeyManagementUtils { throw new SecurityException(ex); } } - + public static List<String> encodeX509CertificateChain(X509Certificate[] chain) { + return encodeX509CertificateChain(Arrays.asList(chain)); + } public static List<String> encodeX509CertificateChain(List<X509Certificate> chain) { List<String> encodedChain = new ArrayList<String>(chain.size()); for (X509Certificate cert : chain) { @@ -252,4 +285,22 @@ public final class KeyManagementUtils { } return props; } + public static RSAPrivateKey loadPrivateKey(Message m, Properties props, + List<X509Certificate> inCert, String keyOper) { + KeyStore keyStore = loadPersistKeyStore(m, props); + try { + Object[] inCertArray = inCert.toArray(); + // perhaps inCert properties can be optionally used as aliases + for (Enumeration<String> e = keyStore.aliases(); e.hasMoreElements();) { + String alias = e.nextElement(); + X509Certificate[] chain = loadX509CertificateOrChain(keyStore, alias); + if (chain != null && Arrays.equals(chain, inCertArray)) { + return loadPrivateKey(keyStore, m, props, keyOper, alias); + } + } + } catch (Exception ex) { + throw new SecurityException(ex); + } + return null; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index 62ecbb0..ae820f6 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -19,12 +19,14 @@ package org.apache.cxf.rs.security.jose.jwe; import java.nio.ByteBuffer; +import java.security.cert.X509Certificate; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Properties; import javax.crypto.KeyAgreement; @@ -34,6 +36,7 @@ import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.common.util.crypto.MessageDigestUtils; import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; import org.apache.cxf.rs.security.jose.JoseConstants; import org.apache.cxf.rs.security.jose.JoseHeaders; import org.apache.cxf.rs.security.jose.JoseUtils; @@ -49,6 +52,7 @@ public final class JweUtils { private static final String RSSEC_ENCRYPTION_OUT_PROPS = "rs.security.encryption.out.properties"; private static final String RSSEC_ENCRYPTION_IN_PROPS = "rs.security.encryption.in.properties"; private static final String RSSEC_ENCRYPTION_PROPS = "rs.security.encryption.properties"; + private static final String RSSEC_ENCRYPTION_REPORT_KEY_PROP = "rs.security.jwe.report.public.key"; private JweUtils() { @@ -221,16 +225,21 @@ public final class JweUtils { getContentDecryptionAlgorithm(key.getAlgorithm())); } public static JweEncryptionProvider loadEncryptionProvider(boolean required) { - return loadEncryptionProvider(JAXRSUtils.getCurrentMessage(), required); + return loadEncryptionProvider(null, required); } - public static JweEncryptionProvider loadEncryptionProvider(Message m, boolean required) { - + public static JweEncryptionProvider loadEncryptionProvider(JweHeaders headers, boolean required) { + Message m = JAXRSUtils.getCurrentMessage(); Properties props = KeyManagementUtils.loadStoreProperties(m, required, RSSEC_ENCRYPTION_OUT_PROPS, RSSEC_ENCRYPTION_PROPS); if (props == null) { return null; } + boolean reportPublicKey = + headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, RSSEC_ENCRYPTION_REPORT_KEY_PROP, + KeyManagementUtils.RSSEC_REPORT_KEY_PROP)); + KeyEncryptionAlgorithm keyEncryptionProvider = null; String keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, null, null); String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); @@ -244,11 +253,18 @@ public final class JweUtils { ctEncryptionProvider = getContentEncryptionAlgorithm(jwk, contentEncryptionAlgo); } else { keyEncryptionProvider = getKeyEncryptionAlgorithm(jwk, keyEncryptionAlgo); + if (reportPublicKey) { + JwkUtils.setPublicKeyInfo(jwk, headers, keyEncryptionAlgo); + } } } else { keyEncryptionProvider = getRSAKeyEncryptionAlgorithm( (RSAPublicKey)KeyManagementUtils.loadPublicKey(m, props), keyEncryptionAlgo); + if (reportPublicKey) { + headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); + } + } return createJweEncryptionProvider(keyEncryptionProvider, ctEncryptionProvider, @@ -256,32 +272,45 @@ public final class JweUtils { props.getProperty(JSON_WEB_ENCRYPTION_ZIP_ALGO_PROP)); } public static JweDecryptionProvider loadDecryptionProvider(boolean required) { - return loadDecryptionProvider(JAXRSUtils.getCurrentMessage(), required); + return loadDecryptionProvider(null, required); } - public static JweDecryptionProvider loadDecryptionProvider(Message m, boolean required) { + public static JweDecryptionProvider loadDecryptionProvider(JweHeaders inHeaders, boolean required) { + Message m = JAXRSUtils.getCurrentMessage(); Properties props = KeyManagementUtils.loadStoreProperties(m, required, RSSEC_ENCRYPTION_IN_PROPS, RSSEC_ENCRYPTION_PROPS); if (props == null) { return null; } + KeyDecryptionAlgorithm keyDecryptionProvider = null; String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); SecretKey ctDecryptionKey = null; String keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, null, null); - if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) { - JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_DECRYPT); - keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, jwk.getAlgorithm(), - getDefaultKeyAlgo(jwk)); - if ("direct".equals(keyEncryptionAlgo)) { - contentEncryptionAlgo = getContentEncryptionAlgo(m, props, contentEncryptionAlgo); - ctDecryptionKey = getContentDecryptionSecretKey(jwk, contentEncryptionAlgo); + if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { + //TODO: validate incoming public keys or certificates + //TODO: optionally validate inHeaders.getAlgorithm against a property in props + // Supporting loading a private key via a certificate for now + List<X509Certificate> chain = KeyManagementUtils.toX509CertificateChain(inHeaders.getX509Chain()); + RSAPrivateKey privateKey = + KeyManagementUtils.loadPrivateKey(m, props, chain, JsonWebKey.KEY_OPER_DECRYPT); + contentEncryptionAlgo = inHeaders.getContentEncryptionAlgorithm(); + keyDecryptionProvider = getRSAKeyDecryptionAlgorithm(privateKey, inHeaders.getKeyEncryptionAlgorithm()); + } else { + if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) { + JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_DECRYPT); + keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, jwk.getAlgorithm(), + getDefaultKeyAlgo(jwk)); + if ("direct".equals(keyEncryptionAlgo)) { + contentEncryptionAlgo = getContentEncryptionAlgo(m, props, contentEncryptionAlgo); + ctDecryptionKey = getContentDecryptionSecretKey(jwk, contentEncryptionAlgo); + } else { + keyDecryptionProvider = getKeyDecryptionAlgorithm(jwk, keyEncryptionAlgo); + } } else { - keyDecryptionProvider = getKeyDecryptionAlgorithm(jwk, keyEncryptionAlgo); + keyDecryptionProvider = getRSAKeyDecryptionAlgorithm( + (RSAPrivateKey)KeyManagementUtils.loadPrivateKey( + m, props, JsonWebKey.KEY_OPER_DECRYPT), keyEncryptionAlgo); } - } else { - keyDecryptionProvider = getRSAKeyDecryptionAlgorithm( - (RSAPrivateKey)KeyManagementUtils.loadPrivateKey( - m, props, JsonWebKey.KEY_OPER_DECRYPT), keyEncryptionAlgo); } return createJweDecryptionProvider(keyDecryptionProvider, ctDecryptionKey, contentEncryptionAlgo); } http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/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 2f6cf77..911ddda 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 @@ -38,10 +38,12 @@ import org.apache.cxf.Bus; import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.common.util.crypto.CryptoUtils; +import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.jaxrs.utils.ResourceUtils; import org.apache.cxf.message.Message; import org.apache.cxf.rs.security.jose.JoseConstants; +import org.apache.cxf.rs.security.jose.JoseHeaders; import org.apache.cxf.rs.security.jose.JoseUtils; import org.apache.cxf.rs.security.jose.jaxrs.KeyManagementUtils; import org.apache.cxf.rs.security.jose.jaxrs.PrivateKeyPasswordProvider; @@ -438,4 +440,15 @@ public final class JwkUtils { private static JweHeaders toJweHeaders(String ct) { return new JweHeaders(Collections.<String, Object>singletonMap(JoseConstants.HEADER_CONTENT_TYPE, ct)); } + public static void setPublicKeyInfo(JsonWebKey jwk, JoseHeaders headers, String algo) { + if (JsonWebKey.KEY_TYPE_RSA.equals(jwk.getKeyType())) { + List<String> chain = CastUtils.cast((List<?>)jwk.getProperty("x5c")); + if (chain != null) { + headers.setX509Chain(chain); + } else { + headers.setJsonWebKey( + JwkUtils.fromRSAPublicKey(JwkUtils.toRSAPublicKey(jwk), algo)); + } + } + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/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 53ac53e..1e8e6d4 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 @@ -18,6 +18,7 @@ */ package org.apache.cxf.rs.security.jose.jws; +import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; @@ -31,6 +32,7 @@ import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.jaxrs.utils.ResourceUtils; import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; import org.apache.cxf.rs.security.jose.JoseConstants; import org.apache.cxf.rs.security.jose.JoseHeaders; import org.apache.cxf.rs.security.jose.JoseUtils; @@ -44,6 +46,7 @@ public final class JwsUtils { private static final String RSSEC_SIGNATURE_OUT_PROPS = "rs.security.signature.out.properties"; private static final String RSSEC_SIGNATURE_IN_PROPS = "rs.security.signature.in.properties"; private static final String RSSEC_SIGNATURE_PROPS = "rs.security.signature.properties"; + private static final String JSON_WEB_SIGNATURE_REPORT_KEY_PROP = "rs.security.jws.report.public.key"; private JwsUtils() { } @@ -132,22 +135,36 @@ public final class JwsUtils { return map; } public static JwsSignatureProvider loadSignatureProvider(boolean required) { - return loadSignatureProvider(JAXRSUtils.getCurrentMessage(), required); + return loadSignatureProvider(null, required); } - public static JwsSignatureProvider loadSignatureProvider(Message m, boolean required) { + public static JwsSignatureProvider loadSignatureProvider(JoseHeaders headers, boolean required) { + Message m = JAXRSUtils.getCurrentMessage(); Properties props = KeyManagementUtils.loadStoreProperties(m, required, RSSEC_SIGNATURE_OUT_PROPS, RSSEC_SIGNATURE_PROPS); if (props == null) { return null; } - return loadSignatureProvider(m, props, false); + JwsSignatureProvider theSigProvider = loadSignatureProvider(m, props, headers, false); + if (headers != null) { + headers.setAlgorithm(theSigProvider.getAlgorithm()); + } + return theSigProvider; } public static JwsSignatureVerifier loadSignatureVerifier(boolean required) { - return loadSignatureVerifier(JAXRSUtils.getCurrentMessage(), required); + return loadSignatureVerifier(null, required); + } + public static JwsSignatureVerifier loadSignatureVerifier(JoseHeaders headers, boolean required) { + Message m = JAXRSUtils.getCurrentMessage(); + Properties props = KeyManagementUtils.loadStoreProperties(m, required, + RSSEC_SIGNATURE_IN_PROPS, RSSEC_SIGNATURE_PROPS); + if (props == null) { + return null; + } + return loadSignatureVerifier(m, props, headers, false); } public static List<JwsSignatureProvider> loadSignatureProviders(String propLoc, Message m) { Properties props = loadProperties(m, propLoc); - JwsSignatureProvider theSigProvider = loadSignatureProvider(m, props, true); + JwsSignatureProvider theSigProvider = loadSignatureProvider(m, props, null, true); if (theSigProvider != null) { return Collections.singletonList(theSigProvider); } @@ -166,18 +183,10 @@ public final class JwsUtils { } return theSigProviders; } - public static JwsSignatureVerifier loadSignatureVerifier(Message m, boolean required) { - Properties props = KeyManagementUtils.loadStoreProperties(m, required, - RSSEC_SIGNATURE_IN_PROPS, RSSEC_SIGNATURE_PROPS); - if (props == null) { - return null; - } - return loadSignatureVerifier(m, props, false); - } public static List<JwsSignatureVerifier> loadSignatureVerifiers(String propLoc, Message m) { Properties props = loadProperties(m, propLoc); - JwsSignatureVerifier theVerifier = loadSignatureVerifier(m, props, true); + JwsSignatureVerifier theVerifier = loadSignatureVerifier(m, props, null, true); if (theVerifier != null) { return Collections.singletonList(theVerifier); } @@ -200,30 +209,56 @@ public final class JwsUtils { //TODO: validate JWS specific constraints return JoseUtils.validateCriticalHeaders(headers); } - private static JwsSignatureProvider loadSignatureProvider(Message m, Properties props, + private static JwsSignatureProvider loadSignatureProvider(Message m, + Properties props, + JoseHeaders headers, boolean ignoreNullProvider) { JwsSignatureProvider theSigProvider = null; String rsaSignatureAlgo = null; + boolean reportPublicKey = + headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JSON_WEB_SIGNATURE_REPORT_KEY_PROP, + KeyManagementUtils.RSSEC_REPORT_KEY_PROP)); if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_SIGN); if (jwk != null) { rsaSignatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); theSigProvider = JwsUtils.getSignatureProvider(jwk, rsaSignatureAlgo); + if (reportPublicKey) { + JwkUtils.setPublicKeyInfo(jwk, headers, rsaSignatureAlgo); + } } } else { rsaSignatureAlgo = getSignatureAlgo(m, props, null, null); RSAPrivateKey pk = (RSAPrivateKey)KeyManagementUtils.loadPrivateKey(m, props, JsonWebKey.KEY_OPER_SIGN); theSigProvider = getRSAKeySignatureProvider(pk, rsaSignatureAlgo); + if (reportPublicKey) { + headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); + } } if (theSigProvider == null && !ignoreNullProvider) { throw new SecurityException(); } return theSigProvider; } - private static JwsSignatureVerifier loadSignatureVerifier(Message m, Properties props, + private static JwsSignatureVerifier loadSignatureVerifier(Message m, + Properties props, + JoseHeaders inHeaders, boolean ignoreNullVerifier) { JwsSignatureVerifier theVerifier = null; + if (inHeaders != null) { + //TODO: validate incoming public keys or certificates + //TODO: optionally validate inHeaders.getAlgorithm against a property in props + if (inHeaders.getHeader(JoseConstants.HEADER_JSON_WEB_KEY) != null) { + JsonWebKey publicJwk = inHeaders.getJsonWebKey(); + return getSignatureVerifier(publicJwk, inHeaders.getAlgorithm()); + } else if (inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { + List<X509Certificate> chain = KeyManagementUtils.toX509CertificateChain(inHeaders.getX509Chain()); + return getRSAKeySignatureVerifier((RSAPublicKey)chain.get(0).getPublicKey(), inHeaders.getAlgorithm()); + } + } + String rsaSignatureAlgo = null; if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_VERIFY); http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/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 cd113ae..ba1c033 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 @@ -220,6 +220,14 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase { assertEquals("book", text); } @Test + public void testJweRsaJwsRsaCertInHeaders() throws Exception { + String address = "https://localhost:" + PORT + "/jwejwsrsaCertInHeaders"; + BookStore bs = createJweJwsBookStore(address, null, null); + WebClient.getConfig(bs).getRequestContext().put("rs.security.report.public.key", "true"); + String text = bs.echoText("book"); + assertEquals("book", text); + } + @Test public void testJweRsaJwsPlainTextHMac() throws Exception { String address = "https://localhost:" + PORT + "/jwejwshmac"; HmacJwsSignatureProvider hmacProvider = http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/server.xml ---------------------------------------------------------------------- diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/server.xml b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/server.xml index ce9a491..155cf69 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/server.xml +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/server.xml @@ -114,6 +114,25 @@ under the License. <entry key="rs.security.default.algorithms" value="true"/> </jaxrs:properties> </jaxrs:server> + <jaxrs:server address="https://localhost:${testutil.ports.jaxrs-jwt}/jwejwsrsaCertInHeaders"> + <jaxrs:serviceBeans> + <ref bean="serviceBean"/> + </jaxrs:serviceBeans> + <jaxrs:providers> + <ref bean="jweInFilter"/> + <ref bean="jweOutFilter"/> + <ref bean="jwsInFilter"/> + <ref bean="jwsOutFilter"/> + </jaxrs:providers> + <jaxrs:properties> + <entry key="rs.security.encryption.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.storeonly.properties"/> + <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.storeonly.properties"/> + <entry key="rs.security.encryption.out.properties" value="org/apache/cxf/systest/jaxrs/security/bob.rs.properties"/> + <entry key="rs.security.signature.out.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> + <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> + <entry key="rs.security.decryption.key.password.provider" value-ref="keyPasswordProvider"/> + </jaxrs:properties> + </jaxrs:server> <bean id="jackson" class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/> <jaxrs:server address="https://localhost:${testutil.ports.jaxrs-jwt}/jwejwkrsa"> <jaxrs:serviceBeans> http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/alice.rs.storeonly.properties ---------------------------------------------------------------------- diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/alice.rs.storeonly.properties b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/alice.rs.storeonly.properties new file mode 100644 index 0000000..3dd7174 --- /dev/null +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/alice.rs.storeonly.properties @@ -0,0 +1,19 @@ +# 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. +rs.security.keystore.type=jks +rs.security.keystore.password=password +rs.security.keystore.file=org/apache/cxf/systest/jaxrs/security/certs/alice.jks http://git-wip-us.apache.org/repos/asf/cxf/blob/5e919271/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/bob.rs.storeonly.properties ---------------------------------------------------------------------- diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/bob.rs.storeonly.properties b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/bob.rs.storeonly.properties new file mode 100644 index 0000000..9f69a3d --- /dev/null +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/bob.rs.storeonly.properties @@ -0,0 +1,21 @@ +# +# 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. +# +rs.security.keystore.type=jks +rs.security.keystore.password=password +rs.security.keystore.file=org/apache/cxf/systest/jaxrs/security/certs/bob.jks