ws-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject svn commit: r1040493 - in /webservices/wss4j/trunk: ./ src/org/apache/ws/security/ src/org/apache/ws/security/handler/ src/org/apache/ws/security/processor/ test/wssec/ xdocs/
Date Tue, 30 Nov 2010 12:37:27 GMT
Author: coheigea
Date: Tue Nov 30 12:37:26 2010
New Revision: 1040493

URL: http://svn.apache.org/viewvc?rev=1040493&view=rev
Log:
[WSS-255] - Add support for enforcing a Username Token password type
 - This can be done by setting WSHandlerConstants.PASSWORD_TYPE_STRICT to "true. - The default
value is false.

Added:
    webservices/wss4j/trunk/test/wssec/PasswordTypeTest.java
Modified:
    webservices/wss4j/trunk/ChangeLog.txt
    webservices/wss4j/trunk/pom.xml
    webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java
    webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java
    webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java
    webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
    webservices/wss4j/trunk/test/wssec/PackageTests.java
    webservices/wss4j/trunk/xdocs/index.xml

Modified: webservices/wss4j/trunk/ChangeLog.txt
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ChangeLog.txt?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/ChangeLog.txt (original)
+++ webservices/wss4j/trunk/ChangeLog.txt Tue Nov 30 12:37:26 2010
@@ -5,6 +5,21 @@ for a given release.  
 Portions of this report were generated using the ReleaseNotes facility
 in Jira.
 
+Release 1.5.10
+=============
+
+** Bug
+
+    * [WSS-40] - WSSecurityEngine does not support chained certificates
+
+** Improvement
+
+    * [WSS-238] - Switch to wsse:KeyIdentifier instead of wsse:Reference for SAML references
within SOAP:body EncryptedData elements.
+    * [WSS-239] - Need ability to handle password "equivalent" between WSPasswordCallback
and UsernameToken when it's binary data
+    * [WSS-247] - Upgrade to XML Security 1.4.4
+    * [WSS-253] - UsernameTokenProcessor logs the password to the log
+
+
 Release 1.5.9
 =============
 

Modified: webservices/wss4j/trunk/pom.xml
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/pom.xml?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/pom.xml (original)
+++ webservices/wss4j/trunk/pom.xml Tue Nov 30 12:37:26 2010
@@ -134,14 +134,14 @@
                         <Implementation-Title>Apache WSS4J</Implementation-Title>
                         <Implementation-Vendor>The Apache Software Foundation</Implementation-Vendor>
                         <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
-                        <Implementation-Version>${pom.version}</Implementation-Version>
+                        <Implementation-Version>${project.version}</Implementation-Version>
                         <Specification-Title>Apache WSS4J</Specification-Title>
                         <Specification-Vendor>The Apache Software Foundation</Specification-Vendor>
-                        <Specification-Version>${pom.version}</Specification-Version>
+                        <Specification-Version>${project.version}</Specification-Version>
 
                         <Export-Package>
-                              org.apache.ws.security.*;version="${pom.version}",
-                              org.apache.ws.axis.security.*;version="${pom.version}",
+                              org.apache.ws.security.*;version="${project.version}",
+                              org.apache.ws.axis.security.*;version="${project.version}",
                         </Export-Package>
                         <Import-Package>
                               !org.apache.ws.security.*,

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/WSSConfig.java Tue Nov 30 12:37:26
2010
@@ -192,6 +192,12 @@ public class WSSConfig {
     protected boolean timeStampStrict = true;
     
     /**
+     * If this value is not null, then username token handling will throw an 
+     * exception if the password type of the Username Token does not match this value
+     */
+    protected String requiredPasswordType = null;
+    
+    /**
      * The time in seconds between creation and expiry for a Timestamp. The default
      * is 300 seconds (5 minutes).
      */
@@ -439,6 +445,21 @@ public class WSSConfig {
     }
     
     /**
+     * @return the required password type when processing a UsernameToken
+     */
+    public String getRequiredPasswordType() {
+        return requiredPasswordType;
+    }
+
+    /**
+     * @param requiredPasswordType The required password type when processing
+     * a Username Token.
+     */
+    public void setRequiredPasswordType(String requiredPasswordType) {
+        this.requiredPasswordType = requiredPasswordType;
+    }
+    
+    /**
      * @return Returns the TTL of a Timestamp in seconds
      */
     public int getTimeStampTTL() {

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandler.java Tue Nov 30 12:37:26
2010
@@ -242,8 +242,6 @@ public abstract class WSHandler {
         }
     }
 
-
-
     protected void doReceiverAction(int doAction, RequestData reqData)
         throws WSSecurityException {
 
@@ -256,6 +254,10 @@ public abstract class WSHandler {
             enableSigConf || ((doAction & WSConstants.SC) != 0)
         );
         wssConfig.setTimeStampStrict(decodeTimestampStrict(reqData));
+        if (decodePasswordTypeStrict(reqData)) {
+            String passwordType = decodePasswordType(reqData);
+            wssConfig.setRequiredPasswordType(passwordType);
+        }
         wssConfig.setTimeStampTTL(decodeTimeToLive(reqData));
         wssConfig.setHandleCustomPasswordTypes(decodeCustomPasswordTypes(reqData));
         wssConfig.setPasswordsAreEncoded(decodeUseEncodedPasswords(reqData));
@@ -504,7 +506,7 @@ public abstract class WSHandler {
     protected void decodeUTParameter(RequestData reqData) 
         throws WSSecurityException {
         Object mc = reqData.getMsgContext();
-
+        
         String type = getString(WSHandlerConstants.PASSWORD_TYPE, mc);
         if (type != null) {
             if (WSConstants.PW_TEXT.equals(type)) {
@@ -512,13 +514,12 @@ public abstract class WSHandler {
             } else if (WSConstants.PW_DIGEST.equals(type)) {
                 reqData.setPwType(WSConstants.PASSWORD_DIGEST);
             } else if (WSConstants.PW_NONE.equals(type)) {
-                // No password requested.
                 reqData.setPwType(null);
             } else {
                 throw new WSSecurityException("Unknown password type encoding: " + type);
             }
         }
-
+        
         String add = getString(WSHandlerConstants.ADD_UT_ELEMENTS, mc);
         if (add != null) {
             reqData.setUtElements(StringUtil.split(add, ' '));
@@ -678,6 +679,18 @@ public abstract class WSHandler {
         return ttl_i;
     }
     
+    protected String decodePasswordType(RequestData reqData) throws WSSecurityException {
+        String type = getString(WSHandlerConstants.PASSWORD_TYPE, reqData.getMsgContext());
+        if (type != null) {
+            if (WSConstants.PW_TEXT.equals(type)) {
+                return WSConstants.PASSWORD_TEXT;
+            } else if (WSConstants.PW_DIGEST.equals(type)) {
+                return WSConstants.PASSWORD_DIGEST;
+            }
+        }
+        return null;
+    }
+    
     protected boolean decodeMustUnderstand(RequestData reqData) 
         throws WSSecurityException {
         return decodeBooleanConfigValue(
@@ -730,6 +743,13 @@ public abstract class WSHandler {
         );
     }
     
+    protected boolean decodePasswordTypeStrict(RequestData reqData) 
+        throws WSSecurityException {
+        return decodeBooleanConfigValue(
+            reqData, WSHandlerConstants.PASSWORD_TYPE_STRICT, false
+        );
+    }
+    
     protected boolean decodeUseSingleCertificate(RequestData reqData) 
         throws WSSecurityException {
         return decodeBooleanConfigValue(

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/handler/WSHandlerConstants.java Tue
Nov 30 12:37:26 2010
@@ -499,6 +499,15 @@ public class WSHandlerConstants {
     public static final String HANDLE_CUSTOM_PASSWORD_TYPES = "handleCustomPasswordTypes";
     
     /**
+     * Set the value of this parameter to true to enable strict Username Token password type
+     * handling (default is false).
+     * 
+     * If this parameter is set to true, it throws an exception if the password type of 
+     * the Username Token does not match that of the configured PASSWORD_TYPE parameter.
+     */
+    public static final String PASSWORD_TYPE_STRICT = "passwordTypeStrict";
+    
+    /**
      * This variable controls whether (wsse) namespace qualified password types are
      * accepted when processing UsernameTokens.
      * 

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
(original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java
Tue Nov 30 12:37:26 2010
@@ -48,6 +48,7 @@ public class UsernameTokenProcessor impl
     private boolean handleCustomPasswordTypes;
     private boolean allowNamespaceQualifiedPasswordTypes;
     private boolean passwordsAreEncoded;
+    private WSSConfig wssConfig;
     
     public void handleToken(Element elem, Crypto crypto, Crypto decCrypto, CallbackHandler
cb, 
         WSDocInfo wsDocInfo, List<WSSecurityEngineResult> returnResults, WSSConfig
wsc) throws WSSecurityException {
@@ -57,6 +58,7 @@ public class UsernameTokenProcessor impl
         handleCustomPasswordTypes = wsc.getHandleCustomPasswordTypes();
         allowNamespaceQualifiedPasswordTypes = wsc.getAllowNamespaceQualifiedPasswordTypes();
         passwordsAreEncoded = wsc.getPasswordsAreEncoded();
+        wssConfig = wsc;
         
         Principal lastPrincipalFound = handleUsernameToken(elem, cb);
         returnResults.add(
@@ -103,6 +105,15 @@ public class UsernameTokenProcessor impl
             log.debug("UsernameToken user " + user);
             log.debug("UsernameToken password type " + pwType);
         }
+        
+        String requiredPasswordType = wssConfig.getRequiredPasswordType();
+        if (requiredPasswordType != null && !requiredPasswordType.equals(pwType))
{
+            if (log.isDebugEnabled()) {
+                log.debug("Authentication failed as the received password type does not "

+                    + "match the required password type of: " + requiredPasswordType);
+            }
+            throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
+        }
         //
         // If the UsernameToken is hashed or plaintext, then retrieve the password from the
         // callback handler and compare directly. If the UsernameToken is of some unknown
type,

Modified: webservices/wss4j/trunk/test/wssec/PackageTests.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/test/wssec/PackageTests.java?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/test/wssec/PackageTests.java (original)
+++ webservices/wss4j/trunk/test/wssec/PackageTests.java Tue Nov 30 12:37:26 2010
@@ -79,6 +79,7 @@ public class PackageTests extends TestCa
         suite.addTestSuite(TestWSSecurityWSS199.class);
         suite.addTestSuite(TestWSSecurityWSS234.class);
         suite.addTestSuite(TestWSSecurityWSS245.class);
+        suite.addTestSuite(PasswordTypeTest.class);
         
         return suite;
     }

Added: webservices/wss4j/trunk/test/wssec/PasswordTypeTest.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/test/wssec/PasswordTypeTest.java?rev=1040493&view=auto
==============================================================================
--- webservices/wss4j/trunk/test/wssec/PasswordTypeTest.java (added)
+++ webservices/wss4j/trunk/test/wssec/PasswordTypeTest.java Tue Nov 30 12:37:26 2010
@@ -0,0 +1,247 @@
+/**
+ * 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 wssec;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityEngine;
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSSConfig;
+import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.handler.WSHandlerConstants;
+import org.apache.ws.security.message.WSSecUsernameToken;
+import org.apache.ws.security.message.WSSecHeader;
+import org.w3c.dom.Document;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import java.io.IOException;
+
+/**
+ * This is a test for processing a Username Token to enforce either a plaintext or digest
+ * password type. See WSS-255.
+ */
+public class PasswordTypeTest extends TestCase implements CallbackHandler {
+    private static final Log LOG = LogFactory.getLog(PasswordTypeTest.class);
+    private static final String SOAPMSG = 
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 
+        + "<SOAP-ENV:Envelope "
+        +   "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+        +   "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+        +   "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" 
+        +   "<SOAP-ENV:Body>" 
+        +       "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">" 
+        +           "<value xmlns=\"\">15</value>" 
+        +       "</add>" 
+        +   "</SOAP-ENV:Body>" 
+        + "</SOAP-ENV:Envelope>";
+
+    /**
+     * TestWSSecurity constructor
+     * 
+     * @param name name of the test
+     */
+    public PasswordTypeTest(String name) {
+        super(name);
+    }
+
+    /**
+     * JUnit suite
+     * 
+     * @return a junit test suite
+     */
+    public static Test suite() {
+        return new TestSuite(PasswordTypeTest.class);
+    }
+
+
+    /**
+     * Test that adds a UserNameToken with password Digest to a WS-Security envelope
+     */
+    public void testPasswordDigest() throws Exception {
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setUserInfo("wernerd", "verySecret");
+        Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        Document signedDoc = builder.build(doc, secHeader);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Message with UserNameToken PW Digest:");
+            String outputString = 
+                org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
+            LOG.debug(outputString);
+        }
+        WSSecurityEngine secEngine = new WSSecurityEngine();
+        WSSConfig wssConfig = WSSConfig.getNewInstance();
+        
+        //
+        // It should pass with PASSWORD_DIGEST
+        //
+        wssConfig.setRequiredPasswordType(WSConstants.PASSWORD_DIGEST);
+        secEngine.setWssConfig(wssConfig);
+        secEngine.processSecurityHeader(doc, null, this, null);
+        
+        //
+        // It should pass with null
+        //
+        wssConfig.setRequiredPasswordType(null);
+        secEngine.setWssConfig(wssConfig);
+        secEngine.processSecurityHeader(doc, null, this, null);
+        
+        //
+        // It should fail with PASSWORD_TEXT
+        //
+        try {
+            wssConfig.setRequiredPasswordType(WSConstants.PASSWORD_TEXT);
+            secEngine.setWssConfig(wssConfig);
+            secEngine.processSecurityHeader(doc, null, this, null);
+            fail("Expected failure on the wrong password type");
+        } catch (WSSecurityException ex) {
+            assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
+            // expected
+        }
+    }
+    
+    /**
+     * Test that adds a UserNameToken with password text to a WS-Security envelope
+     */
+    public void testUsernameTokenText() throws Exception {
+        WSSecUsernameToken builder = new WSSecUsernameToken();
+        builder.setPasswordType(WSConstants.PASSWORD_TEXT);
+        builder.setUserInfo("wernerd", "verySecret");
+        Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
+        WSSecHeader secHeader = new WSSecHeader();
+        secHeader.insertSecurityHeader(doc);
+        Document signedDoc = builder.build(doc, secHeader);
+        
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Message with UserNameToken PW Text:");
+            String outputString = 
+                org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);
+            LOG.debug(outputString);
+        }
+        WSSecurityEngine secEngine = new WSSecurityEngine();
+        WSSConfig wssConfig = WSSConfig.getNewInstance();
+        
+        //
+        // It should pass with PASSWORD_TEXT
+        //
+        wssConfig.setRequiredPasswordType(WSConstants.PASSWORD_TEXT);
+        secEngine.setWssConfig(wssConfig);
+        secEngine.processSecurityHeader(doc, null, this, null);
+        
+        //
+        // It should pass with null
+        //
+        wssConfig.setRequiredPasswordType(null);
+        secEngine.setWssConfig(wssConfig);
+        secEngine.processSecurityHeader(doc, null, this, null);
+        
+        //
+        // It should fail with PASSWORD_DIGEST
+        //
+        try {
+            wssConfig.setRequiredPasswordType(WSConstants.PASSWORD_DIGEST);
+            secEngine.setWssConfig(wssConfig);
+            secEngine.processSecurityHeader(doc, null, this, null);
+            fail("Expected failure on the wrong password type");
+        } catch (WSSecurityException ex) {
+            assertTrue(ex.getErrorCode() == WSSecurityException.FAILED_AUTHENTICATION);
+            // expected
+        }
+        
+    }
+    
+    /**
+     * Test that adds a UserNameToken via WSHandler
+     */
+    public void testUsernameTokenWSHandler() throws Exception {
+        MyHandler handler = new MyHandler();
+        Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
+        
+        RequestData reqData = new RequestData();
+        java.util.Map<String, Object> config = new java.util.TreeMap<String, Object>();
+        config.put("password", "verySecret");
+        config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
+        reqData.setUsername("wernerd");
+        reqData.setMsgContext(config);
+        
+        java.util.List<Integer> actions = new java.util.Vector<Integer>();
+        actions.add(new Integer(WSConstants.UT));
+        
+        handler.send(WSConstants.UT, doc, reqData, actions, true);
+        
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Username Token via WSHandler");
+            String outputString = 
+                org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
+            LOG.debug(outputString);
+        }
+        
+        //
+        // It should pass even on a different password type, as we haven't set the
+        // processing to be strict
+        //
+        config.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
+        reqData.setMsgContext(config);
+        handler.receive(WSConstants.UT, reqData);
+        WSSecurityEngine secEngine = new WSSecurityEngine();
+        secEngine.setWssConfig(reqData.getWssConfig());
+        secEngine.processSecurityHeader(doc, null, this, null);
+        
+        //
+        // It should fail on strict password type processing
+        //
+        config.put(WSHandlerConstants.PASSWORD_TYPE_STRICT, "true");
+        reqData.setMsgContext(config);
+        handler.receive(WSConstants.UT, reqData);
+        try {
+            secEngine.processSecurityHeader(doc, null, this, null);
+            fail("Expected failure on the wrong password type");
+        } catch (WSSecurityException ex) {
+            // expected
+        }
+    }
+    
+
+    public void handle(Callback[] callbacks)
+        throws IOException, UnsupportedCallbackException {
+        for (int i = 0; i < callbacks.length; i++) {
+            if (callbacks[i] instanceof WSPasswordCallback) {
+                WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
+                if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN
+                    && "wernerd".equals(pc.getIdentifier())) {
+                    pc.setPassword("verySecret");
+                } else {
+                    throw new IOException("Authentication failed");
+                }
+            } else {
+                throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
+            }
+        }
+    }
+}

Modified: webservices/wss4j/trunk/xdocs/index.xml
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/xdocs/index.xml?rev=1040493&r1=1040492&r2=1040493&view=diff
==============================================================================
--- webservices/wss4j/trunk/xdocs/index.xml (original)
+++ webservices/wss4j/trunk/xdocs/index.xml Tue Nov 30 12:37:26 2010
@@ -59,7 +59,7 @@ Certificate Token Profile 1.1</a>
                 <p style="margin-left: 40px;">You can download the latest version of
WSS4J at the following URL:<br/>
                     <a href="http://www.apache.org/dyn/closer.cgi/ws/wss4j/">http://www.apache.org/dyn/closer.cgi/ws/wss4j/</a>
                 </p>
-                <p style="margin-left: 40px;">The latest release of WSS4J is version
1.5.9.
+                <p style="margin-left: 40px;">The latest release of WSS4J is version
1.5.10.
                 </p>
             </subsection>
             <subsection name="WS-Security Features">



Mime
View raw message