Introduce new configuration constants to reference keys/certs/ids etc. in the header
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/7fc812de Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/7fc812de Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/7fc812de Branch: refs/heads/3.0.x-fixes Commit: 7fc812decc8ed0c6d6058d463727de69b53f8bb2 Parents: 98d0065 Author: Colm O hEigeartaigh <[email protected]> Authored: Thu Oct 22 15:17:38 2015 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Thu Oct 22 15:23:27 2015 +0100 ---------------------------------------------------------------------- .../rs/security/jose/common/JoseConstants.java | 64 +++++++++++++++++--- .../jose/common/KeyManagementUtils.java | 27 +++++++++ .../cxf/rs/security/jose/jwe/JweUtils.java | 47 ++++++++++---- .../cxf/rs/security/jose/jwk/JwkUtils.java | 25 ++++---- .../cxf/rs/security/jose/jws/JwsUtils.java | 46 +++++++++++--- .../jaxrs/security/jwt/JAXRSJweJwsTest.java | 14 ++--- 6 files changed, 172 insertions(+), 51 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/7fc812de/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java index ef65982..604155f 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java @@ -53,7 +53,7 @@ public final class JoseConstants { // // - // Shared Keys/keystore configuration + // Shared configuration // /** @@ -100,10 +100,28 @@ public final class JoseConstants { public static final String RSSEC_KEY_PSWD_PROVIDER = "rs.security.key.password.provider"; /** + * Include the JWK public key (for signature or encryption) in the "jwk" header. + */ + public static final String RSSEC_INCLUDE_PUBLIC_KEY = "rs.security.include.public.key"; + + /** + * Include the X.509 certificate (for signature or encryption) in the "x5c" header. + */ + public static final String RSSEC_INCLUDE_CERT = "rs.security.include.cert"; + + /** + * Include the JWK key id (for signature or encryption) in the "kid" header. + */ + public static final String RSSEC_INCLUDE_KEY_ID = "rs.security.include.key.id"; + + /** + * Include the X.509 certificate SHA-1 digest (for signature or encryption) in the "x5t" header. + */ + public static final String RSSEC_INCLUDE_CERT_SHA1 = "rs.security.include.cert.sha1"; + + /** * TODO documentation for these */ - public static final String RSSEC_REPORT_KEY_PROP = "rs.security.report.public.key"; - public static final String RSSEC_REPORT_KEY_ID_PROP = "rs.security.report.public.key.id"; public static final String RSSEC_ACCEPT_PUBLIC_KEY_PROP = "rs.security.accept.public.key.properties"; public static final String RSSEC_KEY_STORE_JWKSET = "rs.security.keystore.jwkset"; public static final String RSSEC_KEY_STORE_JWKKEY = "rs.security.keystore.jwkkey"; @@ -164,10 +182,24 @@ public final class JoseConstants { public static final String RSSEC_SIGNATURE_LIST_PROPS = "rs.security.signature.list.properties"; /** - * TODO documentation for these + * Include the JWK public key for signature in the "jwk" header. + */ + public static final String RSSEC_SIGNATURE_INCLUDE_PUBLIC_KEY = "rs.security.signature.include.public.key"; + + /** + * Include the X.509 certificate for signaturein the "x5c" header. */ - public static final String RSSEC_SIGNATURE_REPORT_KEY_PROP = "rs.security.signature.report.public.key"; - public static final String RSSEC_SIGNATURE_REPORT_KEY_ID_PROP = "rs.security.signature.report.public.key.id"; + public static final String RSSEC_SIGNATURE_INCLUDE_CERT = "rs.security.signature.include.cert"; + + /** + * Include the JWK key id for signature in the "kid" header. + */ + public static final String RSSEC_SIGNATURE_INCLUDE_KEY_ID = "rs.security.signature.include.key.id"; + + /** + * Include the X.509 certificate SHA-1 digest for signature in the "x5t" header. + */ + public static final String RSSEC_SIGNATURE_INCLUDE_CERT_SHA1 = "rs.security.signature.include.cert.sha1"; // // JWE specific Configuration @@ -232,10 +264,24 @@ public final class JoseConstants { public static final String RSSEC_ENCRYPTION_PROPS = "rs.security.encryption.properties"; /** - * TODO documentation for these + * Include the JWK public key for encryption in the "jwk" header. + */ + public static final String RSSEC_ENCRYPTION_INCLUDE_PUBLIC_KEY = "rs.security.encryption.include.public.key"; + + /** + * Include the X.509 certificate for encryption the "x5c" header. + */ + public static final String RSSEC_ENCRYPTION_INCLUDE_CERT = "rs.security.encryption.include.cert"; + + /** + * Include the JWK key id for encryption in the "kid" header. + */ + public static final String RSSEC_ENCRYPTION_INCLUDE_KEY_ID = "rs.security.encryption.include.key.id"; + + /** + * Include the X.509 certificate SHA-1 digest for encryption in the "x5t" header. */ - public static final String RSSEC_ENCRYPTION_REPORT_KEY_PROP = "rs.security.encryption.report.public.key"; - public static final String RSSEC_ENCRYPTION_REPORT_KEY_ID_PROP = "rs.security.encryption.report.public.key.id"; + public static final String RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1 = "rs.security.encryption.include.cert.sha1"; // // JWT specific configuration http://git-wip-us.apache.org/repos/asf/cxf/blob/7fc812de/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java index 0c32919..04b56b4 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java @@ -21,6 +21,7 @@ package org.apache.cxf.rs.security.jose.common; import java.io.InputStream; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.CertPath; @@ -29,6 +30,7 @@ import java.security.cert.CertPathBuilderResult; import java.security.cert.CertPathValidator; import java.security.cert.CertStore; import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; import java.security.cert.X509CertSelector; @@ -38,15 +40,18 @@ import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.Properties; +import java.util.logging.Level; import java.util.logging.Logger; import org.apache.cxf.Bus; import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageUtils; import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.jose.jwk.KeyOperation; import org.apache.cxf.rt.security.crypto.CryptoUtils; +import org.apache.cxf.rt.security.crypto.MessageDigestUtils; /** * Encryption helpers @@ -56,10 +61,32 @@ public final class KeyManagementUtils { private KeyManagementUtils() { } + public static List<String> loadAndEncodeX509CertificateOrChain(Message m, Properties props) { X509Certificate[] chain = loadX509CertificateOrChain(m, props); return encodeX509CertificateChain(chain); } + + public static String loadDigestAndEncodeX509Certificate(Message m, Properties props) { + X509Certificate[] certs = loadX509CertificateOrChain(m, props); + if (certs != null && certs.length > 0) { + try { + byte[] digest = + MessageDigestUtils.createDigest(certs[0].getEncoded(), + MessageDigestUtils.ALGO_SHA_1); + return Base64UrlUtility.encode(digest); + } catch (NoSuchAlgorithmException ex) { + LOG.log(Level.FINE, "Error creating digest", ex); + throw new JoseException(ex); + } catch (CertificateEncodingException ex) { + LOG.log(Level.FINE, "Error creating digest", ex); + throw new JoseException(ex); + } + } + + return null; + } + public static X509Certificate[] loadX509CertificateOrChain(Message m, Properties props) { KeyStore keyStore = KeyManagementUtils.loadPersistKeyStore(m, props); String alias = props.getProperty(JoseConstants.RSSEC_KEY_STORE_ALIAS); http://git-wip-us.apache.org/repos/asf/cxf/blob/7fc812de/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index 5e6aece..0d2e50d 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -281,14 +281,13 @@ public final class JweUtils { return null; } - boolean reportPublicKey = + boolean includeCert = headers != null && MessageUtils.isTrue( - MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_ENCRYPTION_REPORT_KEY_PROP, - JoseConstants.RSSEC_REPORT_KEY_PROP)); - boolean reportPublicKeyId = - headers != null && MessageUtils.isTrue( - MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_ENCRYPTION_REPORT_KEY_ID_PROP, - JoseConstants.RSSEC_REPORT_KEY_ID_PROP)); + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT, + JoseConstants.RSSEC_INCLUDE_CERT)); + boolean includeCertSha1 = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1, + JoseConstants.RSSEC_INCLUDE_CERT_SHA1)); KeyEncryptionProvider keyEncryptionProvider = null; String keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, null, null); @@ -304,19 +303,43 @@ public final class JweUtils { keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); keyEncryptionProvider = getKeyEncryptionProvider(jwk, keyAlgo); - if (reportPublicKey || reportPublicKeyId) { - JwkUtils.setPublicKeyInfo(jwk, headers, keyEncryptionAlgo, - reportPublicKey, reportPublicKeyId); + + boolean includePublicKey = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_PUBLIC_KEY, + JoseConstants.RSSEC_INCLUDE_PUBLIC_KEY)); + boolean includeKeyId = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_KEY_ID, + JoseConstants.RSSEC_INCLUDE_KEY_ID)); + + if (includeCert) { + JwkUtils.includeCertChain(jwk, headers, keyEncryptionAlgo); + } + if (includeCertSha1 && headers != null) { + String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); + if (digest != null) { + headers.setX509Thumbprint(digest); + } + } + if (includePublicKey) { + JwkUtils.includePublicKey(jwk, headers, keyEncryptionAlgo); + } + if (includeKeyId && jwk.getKeyId() != null && headers != null) { + headers.setKeyId(jwk.getKeyId()); } } } else { keyEncryptionProvider = getPublicKeyEncryptionProvider( KeyManagementUtils.loadPublicKey(m, props), keyAlgo); - if (reportPublicKey) { + if (includeCert) { headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); } - + if (includeCertSha1 && headers != null) { + String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); + if (digest != null) { + headers.setX509Thumbprint(digest); + } + } } String compression = props.getProperty(JoseConstants.RSSEC_ENCRYPTION_ZIP_ALGORITHM); http://git-wip-us.apache.org/repos/asf/cxf/blob/7fc812de/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index cb76567..5ab72e0 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -508,24 +508,23 @@ 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, - boolean reportPublicKey, boolean reportPublicKeyId) { - if (reportPublicKey && KeyType.RSA.equals(jwk.getKeyType())) { + + public static void includeCertChain(JsonWebKey jwk, JoseHeaders headers, String algo) { + if (KeyType.RSA.equals(jwk.getKeyType())) { List<String> chain = CastUtils.cast((List<?>)jwk.getProperty("x5c")); - //TODO: if needed the chain can be reported as part of a 'jwk' property if (chain != null) { headers.setX509Chain(chain); - } else { - JsonWebKey jwkPublic = JwkUtils.fromRSAPublicKey(JwkUtils.toRSAPublicKey(jwk), algo); - if (reportPublicKeyId && jwk.getKeyId() != null) { - jwkPublic.setKeyId(jwk.getKeyId()); - } - headers.setJsonWebKey(jwkPublic); } } - if (reportPublicKeyId && jwk.getKeyId() != null) { - headers.setKeyId(jwk.getKeyId()); + } + + public static void includePublicKey(JsonWebKey jwk, JoseHeaders headers, String algo) { + if (KeyType.RSA.equals(jwk.getKeyType())) { + JsonWebKey jwkPublic = JwkUtils.fromRSAPublicKey(JwkUtils.toRSAPublicKey(jwk), algo); + if (jwk.getKeyId() != null) { + jwkPublic.setKeyId(jwk.getKeyId()); + } + headers.setJsonWebKey(jwkPublic); } - } } http://git-wip-us.apache.org/repos/asf/cxf/blob/7fc812de/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index 958c52e..f5db51b 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -267,20 +267,40 @@ public final class JwsUtils { boolean ignoreNullProvider) { JwsSignatureProvider theSigProvider = null; - boolean reportPublicKey = headers != null && MessageUtils.isTrue( - MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_REPORT_KEY_PROP, - JoseConstants.RSSEC_REPORT_KEY_PROP)); - boolean reportPublicKeyId = - headers != null && MessageUtils.isTrue( - MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_REPORT_KEY_ID_PROP, - JoseConstants.RSSEC_REPORT_KEY_ID_PROP)); + boolean includeCert = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_SIGNATURE_INCLUDE_CERT, + JoseConstants.RSSEC_INCLUDE_CERT)); + boolean includeCertSha1 = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_SIGNATURE_INCLUDE_CERT_SHA1, + JoseConstants.RSSEC_INCLUDE_CERT_SHA1)); + if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.SIGN); if (jwk != null) { String signatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); theSigProvider = JwsUtils.getSignatureProvider(jwk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); - if (reportPublicKey || reportPublicKeyId) { - JwkUtils.setPublicKeyInfo(jwk, headers, signatureAlgo, reportPublicKey, reportPublicKeyId); + + boolean includePublicKey = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_SIGNATURE_INCLUDE_PUBLIC_KEY, + JoseConstants.RSSEC_INCLUDE_PUBLIC_KEY)); + boolean includeKeyId = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_SIGNATURE_INCLUDE_KEY_ID, + JoseConstants.RSSEC_INCLUDE_KEY_ID)); + + if (includeCert) { + JwkUtils.includeCertChain(jwk, headers, signatureAlgo); + } + if (includeCertSha1 && headers != null) { + String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); + if (digest != null) { + headers.setX509Thumbprint(digest); + } + } + if (includePublicKey) { + JwkUtils.includePublicKey(jwk, headers, signatureAlgo); + } + if (includeKeyId && jwk.getKeyId() != null && headers != null) { + headers.setKeyId(jwk.getKeyId()); } } } else { @@ -288,9 +308,15 @@ public final class JwsUtils { PrivateKey pk = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.SIGN); theSigProvider = getPrivateKeySignatureProvider(pk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); - if (reportPublicKey) { + if (includeCert && headers != null) { headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); } + if (includeCertSha1 && headers != null) { + String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); + if (digest != null) { + headers.setX509Thumbprint(digest); + } + } } if (theSigProvider == null && !ignoreNullProvider) { LOG.warning("Provider is not available"); http://git-wip-us.apache.org/repos/asf/cxf/blob/7fc812de/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 f0dd763..c3e9c7f 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 @@ -243,7 +243,7 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase { } String address = "https://localhost:" + PORT + "/jwejwsrsaCertInHeaders"; BookStore bs = createJweJwsBookStore(address, null, null); - WebClient.getConfig(bs).getRequestContext().put("rs.security.report.public.key", "true"); + WebClient.getConfig(bs).getRequestContext().put("rs.security.include.cert", "true"); String text = bs.echoText("book"); assertEquals("book", text); } @@ -349,8 +349,8 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase { doTestJwsJwkRSA("https://localhost:" + PORT + "/jwsjwkrsa", false, true); } private void doTestJwsJwkRSA(String address, - boolean reportPublicKey, - boolean reportPublicKeyId) throws Exception { + boolean includePublicKey, + boolean includeKeyId) throws Exception { JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); SpringBusFactory bf = new SpringBusFactory(); URL busFile = JAXRSJweJwsTest.class.getResource("client.xml"); @@ -368,11 +368,11 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase { "org/apache/cxf/systest/jaxrs/security/alice.jwk.properties"); bean.getProperties(true).put("rs.security.signature.in.properties", "org/apache/cxf/systest/jaxrs/security/bob.jwk.properties"); - if (reportPublicKey) { - bean.getProperties(true).put("rs.security.report.public.key", true); + if (includePublicKey) { + bean.getProperties(true).put("rs.security.include.public.key", true); } - if (reportPublicKeyId) { - bean.getProperties(true).put("rs.security.report.public.key.id", true); + if (includeKeyId) { + bean.getProperties(true).put("rs.security.include.key.id", true); } BookStore bs = bean.create(BookStore.class); String text = bs.echoText("book");
