cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject [cxf] 01/02: CXF-7568 - Support JWE PBES encryption
Date Fri, 17 Nov 2017 17:50:22 GMT
This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch 3.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 3f79dd802da29bee9061a27cc5b4c49217eed1ef
Author: Colm O hEigeartaigh <coheigea@apache.org>
AuthorDate: Fri Nov 17 17:01:19 2017 +0000

    CXF-7568 - Support JWE PBES encryption
    
    (cherry picked from commit eeae04330ebd610be9cb46891e9f05851afad2a4)
    
    # Conflicts:
    #	rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java
---
 .../cxf/rs/security/jose/common/JoseConstants.java |   9 +-
 .../security/jose/common/KeyManagementUtils.java   |   4 +-
 .../apache/cxf/rs/security/jose/jwe/JweUtils.java  | 105 ++++++++++++++-------
 .../security/jose/jwejws/JweJwsAlgorithmTest.java  |  60 ++++++++++++
 .../security/jose/jwejws/algorithms-server.xml     |  19 ++++
 5 files changed, 158 insertions(+), 39 deletions(-)

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 ed68908..bff746f 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
@@ -173,7 +173,7 @@ public final class JoseConstants {
      * 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";
-    
+
     /**
      * Include the X.509 certificate SHA-256 digest for signature in the "x5t#S256" header.
      */
@@ -241,12 +241,17 @@ public final class JoseConstants {
      * Include the X.509 certificate SHA-1 digest for encryption in the "x5t" header.
      */
     public static final String RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1 = "rs.security.encryption.include.cert.sha1";
-    
+
     /**
      * Include the X.509 certificate SHA-256 digest for encryption in the "x5t#S256" header.
      */
     public static final String RSSEC_ENCRYPTION_INCLUDE_CERT_SHA256 = "rs.security.encryption.include.cert.sha256";
 
+    /**
+     * The value to be used for the "p2c" (PBES2 count) Header Parameter. The default is
4096.
+     */
+    public static final String RSSEC_ENCRYPTION_PBES2_COUNT = "rs.security.encryption.pbes2.count";
+
     //
     // JWT specific configuration
     //
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 f9409cf..d6780bf 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
@@ -441,7 +441,7 @@ public final class KeyManagementUtils {
         if (props == null) {
             if (required) {
                 LOG.warning("Properties resource is not identified");
-                throw new JoseException();
+                throw new JoseException("Properties resource is not identified");
             }
             props = new Properties();
         }
@@ -511,7 +511,7 @@ public final class KeyManagementUtils {
         
         return null;
     }
-    
+
     public static void setSha1DigestHeader(JoseHeaders headers, Message m, Properties props)
{
         String digest = loadDigestAndEncodeX509Certificate(m, props, MessageDigestUtils.ALGO_SHA_1);
         if (digest != null) {
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 4617934..eeea701 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
@@ -44,11 +44,13 @@ import javax.crypto.SecretKey;
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.common.util.StringUtils;
 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.common.JoseConstants;
 import org.apache.cxf.rs.security.jose.common.JoseHeaders;
 import org.apache.cxf.rs.security.jose.common.JoseUtils;
 import org.apache.cxf.rs.security.jose.common.KeyManagementUtils;
+import org.apache.cxf.rs.security.jose.common.PrivateKeyPasswordProvider;
 import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils;
 import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm;
 import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm;
@@ -273,7 +275,7 @@ public final class JweUtils {
     public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm
algorithm) {
         return getContentEncryptionProvider(algorithm, false);
     }
-    public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm
algorithm, 
+    public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm
algorithm,
                                                                          boolean generateCekOnce)
{
         if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) {
             return new AesGcmContentEncryptionAlgorithm(algorithm, generateCekOnce);
@@ -376,9 +378,9 @@ public final class JweUtils {
         Message m = PhaseInterceptorChain.getCurrentMessage();
         return loadEncryptionProvider(props, m, headers);
     }
-    
+
     public static JweEncryptionProvider loadEncryptionProvider(Properties props, Message
m, JweHeaders headers) {
-    
+
         KeyEncryptionProvider keyEncryptionProvider = loadKeyEncryptionProvider(props, m,
headers);
 
         ContentAlgorithm contentAlgo = getContentEncryptionAlgorithm(m, props, null, ContentAlgorithm.A128GCM);
@@ -393,7 +395,7 @@ public final class JweUtils {
                     jwk.getAlgorithm() != null ? ContentAlgorithm.getAlgorithm(jwk.getAlgorithm())
: null,
                     contentAlgo);
                 ctEncryptionProvider = getContentEncryptionProvider(jwk, contentAlgo);
-            }         
+            }
         }
         String compression = props.getProperty(JoseConstants.RSSEC_ENCRYPTION_ZIP_ALGORITHM);
         return createJweEncryptionProvider(keyEncryptionProvider,
@@ -402,21 +404,31 @@ public final class JweUtils {
                                     compression,
                                     headers);
     }
-    
+
     public static KeyEncryptionProvider loadKeyEncryptionProvider(Properties props, Message
m, JweHeaders headers) {
-        
+
         KeyEncryptionProvider keyEncryptionProvider = null;
         KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null);
+
         if (KeyAlgorithm.DIRECT == keyAlgo) {
-            keyEncryptionProvider = new DirectKeyEncryptionAlgorithm();    
+            keyEncryptionProvider = new DirectKeyEncryptionAlgorithm();
+        } else if (keyAlgo != null && AlgorithmUtils.PBES_HS_SET.contains(keyAlgo.getJwaName()))
{
+            PrivateKeyPasswordProvider provider =
+                KeyManagementUtils.loadPasswordProvider(m, props, KeyOperation.ENCRYPT);
+            char[] password = provider != null ? provider.getPassword(props) : null;
+            if (password == null) {
+                throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE);
+            }
+            int pbes2Count = MessageUtils.getContextualInteger(m, JoseConstants.RSSEC_ENCRYPTION_PBES2_COUNT,
4096);
+            return new PbesHmacAesWrapKeyEncryptionAlgorithm(new String(password), pbes2Count,
keyAlgo, false);
         } else {
-            boolean includeCert = 
+            boolean includeCert =
                 JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT);
-            boolean includeCertSha1 = 
+            boolean includeCertSha1 =
                 JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1);
-            boolean includeCertSha256 =  
+            boolean includeCertSha256 =
                 JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA256);
-            boolean includeKeyId = 
+            boolean includeKeyId =
                 JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_KEY_ID);
 
             if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE)))
{
@@ -426,9 +438,9 @@ public final class JweUtils {
                                                         KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()),
                                                         getDefaultKeyAlgorithm(jwk));
                     keyEncryptionProvider = getKeyEncryptionProvider(jwk, keyAlgo);
-    
-                    boolean includePublicKey = 
-                        JoseUtils.checkBooleanProperty(headers, props, m, 
+
+                    boolean includePublicKey =
+                        JoseUtils.checkBooleanProperty(headers, props, m,
                                                        JoseConstants.RSSEC_ENCRYPTION_INCLUDE_PUBLIC_KEY);
                     if (includeCert) {
                         JwkUtils.includeCertChain(jwk, headers, keyAlgo.getJwaName());
@@ -468,10 +480,10 @@ public final class JweUtils {
         }
         headers.setKeyEncryptionAlgorithm(keyEncryptionProvider.getAlgorithm());
         return keyEncryptionProvider;
-        
+
     }
-    
-    
+
+
     public static JweDecryptionProvider loadDecryptionProvider(boolean required) {
         return loadDecryptionProvider(null, required);
     }
@@ -552,6 +564,14 @@ public final class JweUtils {
                                                         getDefaultKeyAlgorithm(jwk));
                     keyDecryptionProvider = getKeyDecryptionProvider(jwk, keyAlgo);
                 }
+            } else if (keyAlgo != null && AlgorithmUtils.PBES_HS_SET.contains(keyAlgo.getJwaName()))
{
+                PrivateKeyPasswordProvider provider =
+                    KeyManagementUtils.loadPasswordProvider(m, props, KeyOperation.DECRYPT);
+                char[] password = provider != null ? provider.getPassword(props) : null;
+                if (password == null) {
+                    throw new JweException(JweException.Error.KEY_DECRYPTION_FAILURE);
+                }
+                keyDecryptionProvider = new PbesHmacAesWrapKeyDecryptionAlgorithm(new String(password));
             } else {
                 PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.DECRYPT);
                 if (keyAlgo == null) {
@@ -563,6 +583,7 @@ public final class JweUtils {
         return createJweDecryptionProvider(keyDecryptionProvider, ctDecryptionKey, 
                                            contentAlgo);
     }
+
     public static JweEncryptionProvider createJweEncryptionProvider(PublicKey key,
                                                                     KeyAlgorithm keyAlgo,
                                                                     ContentAlgorithm contentEncryptionAlgo)
{
@@ -619,7 +640,7 @@ public final class JweUtils {
                 contentEncryptionAlgo.getJwaName(), compression, null);
         return createJweEncryptionProvider(keyEncryptionProvider, headers);
     }
-    
+
     public static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider
keyEncryptionProvider,
                                                                     JweHeaders headers) {
         return createJweEncryptionProvider(keyEncryptionProvider, headers, false);
@@ -672,24 +693,25 @@ public final class JweUtils {
                           JwkUtils.toECPublicKey(peerPublicKey),
                           partyUInfo, partyVInfo, algoName, algoKeyBitLen);
     }
-    public static byte[] getECDHKey(ECPrivateKey privateKey, 
+
+    public static byte[] getECDHKey(ECPrivateKey privateKey,
                                     ECPublicKey peerPublicKey,
                                     byte[] partyUInfo,
                                     byte[] partyVInfo,
                                     String algoName,
                                     int algoKeyBitLen) { 
         // Validate the peerPublicKey first
-        
-        // Credits: 
+
+        // Credits:
         // https://neilmadden.wordpress.com/2017/05/17/so-how-do-you-validate-nist-ecdh-public-keys/
-        // https://blogs.adobe.com/security/2017/03/critical-vulnerability-uncovered-in-json-encryption.html

-        
-        // Step 1: Verify public key is not point at infinity. 
+        // https://blogs.adobe.com/security/2017/03/critical-vulnerability-uncovered-in-json-encryption.html
+
+        // Step 1: Verify public key is not point at infinity.
         if (ECPoint.POINT_INFINITY.equals(peerPublicKey.getW())) {
             throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE);
         }
         EllipticCurve curve = peerPublicKey.getParams().getCurve();
-        
+
         final BigInteger x = peerPublicKey.getW().getAffineX();
         final BigInteger y = peerPublicKey.getW().getAffineY();
         final BigInteger p = ((ECFieldFp) curve.getField()).getP();
@@ -708,7 +730,7 @@ public final class JweUtils {
         if (!ySquared.equals(xCubedPlusAXPlusB)) {
             throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE);
         }
-        
+
         // Step 4: Verify that nQ = 0, where n is the order of the curve and Q is the public
key.
         // As per http://www.secg.org/sec1-v2.pdf section 3.2.2:
         // "In Step 4, it may not be necessary to compute the point nQ. For example, if h
= 1, then nQ = O is implied
@@ -719,7 +741,7 @@ public final class JweUtils {
         }
 
         // Finally calculate the derived key
-        
+
         byte[] keyZ = generateKeyZ(privateKey, peerPublicKey);
         return calculateDerivedKey(keyZ, algoName, partyUInfo, partyVInfo, algoKeyBitLen);
     }
@@ -810,7 +832,7 @@ public final class JweUtils {
         }
         return headers;
     }
-    
+
     private static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider
keyEncryptionProvider,
                                                                      ContentEncryptionProvider
ctEncryptionProvider,
                                                                      ContentAlgorithm contentEncryptionAlgo,
@@ -852,7 +874,7 @@ public final class JweUtils {
     public static KeyAlgorithm getKeyEncryptionAlgorithm(Properties props, KeyAlgorithm defaultAlgo)
{
         return getKeyEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props,
defaultAlgo);
     }
-    public static KeyAlgorithm getKeyEncryptionAlgorithm(Message m, Properties props, KeyAlgorithm
defaultAlgo) {    
+    public static KeyAlgorithm getKeyEncryptionAlgorithm(Message m, Properties props, KeyAlgorithm
defaultAlgo) {
         String algo = KeyManagementUtils.getKeyAlgorithm(m,
                                                   props,
                                                   JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM,
@@ -879,7 +901,7 @@ public final class JweUtils {
         return algo;
     }
     public static ContentAlgorithm getContentEncryptionAlgorithm(Properties props) {
-        return getContentEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props,
null); 
+        return getContentEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props,
null);
     }
     public static ContentAlgorithm getContentEncryptionAlgorithm(Properties props,
                                                                  ContentAlgorithm defaultAlgo)
{
@@ -913,15 +935,28 @@ public final class JweUtils {
     }
     public static Properties loadEncryptionInProperties(boolean required) {
         Message m = PhaseInterceptorChain.getCurrentMessage();
-        return KeyManagementUtils.loadStoreProperties(m, required, 
-                                                      JoseConstants.RSSEC_ENCRYPTION_IN_PROPS,

+        String keyEncryptionAlgorithm =
+            (String)m.getContextualProperty(JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM);
+        if (keyEncryptionAlgorithm != null && AlgorithmUtils.PBES_HS_SET.contains(keyEncryptionAlgorithm))
{
+            // We don't need to load the keystore properties for the PBES case
+            required = false;
+        }
+        return KeyManagementUtils.loadStoreProperties(m, required,
+                                                      JoseConstants.RSSEC_ENCRYPTION_IN_PROPS,
                                                       JoseConstants.RSSEC_ENCRYPTION_PROPS);
         
     }
     public static Properties loadEncryptionOutProperties(boolean required) {
         Message m = PhaseInterceptorChain.getCurrentMessage();
-        return KeyManagementUtils.loadStoreProperties(m, required, 
-                                                      JoseConstants.RSSEC_ENCRYPTION_OUT_PROPS,

+        String keyEncryptionAlgorithm =
+            (String)m.getContextualProperty(JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM);
+        if (keyEncryptionAlgorithm != null && AlgorithmUtils.PBES_HS_SET.contains(keyEncryptionAlgorithm))
{
+            // We don't need to load the keystore properties for the PBES case
+            required = false;
+        }
+
+        return KeyManagementUtils.loadStoreProperties(m, required,
+                                                      JoseConstants.RSSEC_ENCRYPTION_OUT_PROPS,
                                                       JoseConstants.RSSEC_ENCRYPTION_PROPS);
         
     }
@@ -949,7 +984,7 @@ public final class JweUtils {
             return new JsonWebKeys(jwk);
         }
     }
-    
+
     public static Properties loadJweProperties(Message m, String propLoc) {
         try {
             return JoseUtils.loadProperties(propLoc, m.getExchange().getBus());
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java
b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java
index 7762317..2096a4f 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java
@@ -313,6 +313,66 @@ public class JweJwsAlgorithmTest extends AbstractBusClientServerTestBase
{
         assertNotEquals(response.getStatus(), 200);
     }
 
+    @org.junit.Test
+    public void testEncryptionPBES() throws Exception {
+
+        URL busFile = JweJwsAlgorithmTest.class.getResource("client.xml");
+
+        List<Object> providers = new ArrayList<>();
+        providers.add(new JacksonJsonProvider());
+        providers.add(new JweWriterInterceptor());
+
+        String address = "http://localhost:" + PORT + "/jwepbes/bookstore/books";
+        WebClient client =
+            WebClient.create(address, providers, busFile.toString());
+        client.type("application/json").accept("application/json");
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("rs.security.encryption.content.algorithm", "A128GCM");
+        properties.put("rs.security.encryption.key.algorithm", "PBES2-HS256+A128KW");
+        String password = "123456789123456789";
+        properties.put("rs.security.key.password.provider", new PrivateKeyPasswordProviderImpl(password));
+        WebClient.getConfig(client).getRequestContext().putAll(properties);
+
+        Response response = client.post(new Book("book", 123L));
+        assertEquals(response.getStatus(), 200);
+
+        Book returnedBook = response.readEntity(Book.class);
+        assertEquals(returnedBook.getName(), "book");
+        assertEquals(returnedBook.getId(), 123L);
+    }
+
+    @org.junit.Test
+    public void testEncryptionPBESDifferentCount() throws Exception {
+
+        URL busFile = JweJwsAlgorithmTest.class.getResource("client.xml");
+
+        List<Object> providers = new ArrayList<>();
+        providers.add(new JacksonJsonProvider());
+        providers.add(new JweWriterInterceptor());
+
+        String address = "http://localhost:" + PORT + "/jwepbes/bookstore/books";
+        WebClient client =
+            WebClient.create(address, providers, busFile.toString());
+        client.type("application/json").accept("application/json");
+
+        Map<String, Object> properties = new HashMap<>();
+        String password = "123456789123456789";
+        properties.put("rs.security.encryption.content.algorithm", "A128GCM");
+        properties.put("rs.security.encryption.key.algorithm", "PBES2-HS256+A128KW");
+        properties.put("rs.security.key.password.provider", new PrivateKeyPasswordProviderImpl(password));
+        properties.put("rs.security.encryption.pbes2.count", "1000");
+        WebClient.getConfig(client).getRequestContext().putAll(properties);
+
+        Response response = client.post(new Book("book", 123L));
+        assertEquals(response.getStatus(), 200);
+
+        Book returnedBook = response.readEntity(Book.class);
+        assertEquals(returnedBook.getName(), "book");
+        assertEquals(returnedBook.getId(), 123L);
+    }
+
+
     //
     // Signature tests
     //
diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml
b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml
index 08fbb88..5c875a2 100644
--- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml
+++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml
@@ -61,6 +61,25 @@ under the License.
        </jaxrs:properties>
    </jaxrs:server>
    
+   <bean id="pbesKeyProvider" class="org.apache.cxf.systest.jaxrs.security.jose.jwejws.PrivateKeyPasswordProviderImpl">
+       <constructor-arg>
+           <value>123456789123456789</value> 
+       </constructor-arg>
+   </bean>
+   
+   <jaxrs:server address="http://localhost:${testutil.ports.jaxrs-jwejws-algorithms}/jwepbes">
+       <jaxrs:serviceBeans>
+          <ref bean="serviceBean"/>
+       </jaxrs:serviceBeans>
+       <jaxrs:providers>
+          <ref bean="jweInFilter"/>
+       </jaxrs:providers>
+       <jaxrs:properties>
+            <entry key="rs.security.encryption.key.algorithm" value="PBES2-HS256+A128KW"/>
+            <entry key="rs.security.key.password.provider" value-ref="pbesKeyProvider"/>
+       </jaxrs:properties>
+   </jaxrs:server>
+   
     <bean id="jwsInFilter" class="org.apache.cxf.rs.security.jose.jaxrs.JwsContainerRequestFilter"/>
     
     <jaxrs:server address="http://localhost:${testutil.ports.jaxrs-jwejws-algorithms}/jws">

-- 
To stop receiving notification emails like this one, please contact
"commits@cxf.apache.org" <commits@cxf.apache.org>.

Mime
View raw message