You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fx-dev@ws.apache.org by "Ruchith Udayanga Fernando (JIRA)" <ji...@apache.org> on 2006/11/27 14:04:27 UTC

[jira] Updated: (WSS-59) Crypto.properties doesn't allow encoded password for keystore - WSS4J1.5

     [ http://issues.apache.org/jira/browse/WSS-59?page=all ]

Ruchith Udayanga Fernando updated WSS-59:
-----------------------------------------

    Priority: Minor  (was: Blocker)

Soumadeep ... shall we leave Merlin as it is, since one can implement the Crypto interface to secure the keystore password in any desired way.

IMHO this is not a blocker. Hence reducing priority.

Thanks,
Ruchith

> Crypto.properties doesn't allow encoded password for keystore - WSS4J1.5
> ------------------------------------------------------------------------
>
>                 Key: WSS-59
>                 URL: http://issues.apache.org/jira/browse/WSS-59
>             Project: WSS4J
>          Issue Type: New Feature
>         Environment: ALL
>            Reporter: Soumadeep
>         Assigned To: Davanum Srinivas
>            Priority: Minor
>
> Crypto.properties file doesn't allow encoded passwords for keystore and alias. The plain text password would be unacceptable for most of the enterprises.
> The load method of AbstractCrypto.java file actually picks up the passwords from the crypto.properties file. I have made the required changes and am putting it below. This only accounts for the keystore password as the alias password would be usage specific. Please check the two files crypto.properties and AbstractCrypto.java. The files are from the wss4j main trunk - svn
> Thanks
> Soumadeep 
> Crypto.properties
> =============
> org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
> org.apache.ws.security.crypto.merlin.keystore.type=jks
> // This property has been added
> org.apache.ws.security.crypto.merlin.keystore.base64.encoded=false
> org.apache.ws.security.crypto.merlin.keystore.password=soumadeep
> org.apache.ws.security.crypto.merlin.keystore.alias=soumadeep
> org.apache.ws.security.crypto.merlin.alias.password=soumadeep
> org.apache.ws.security.crypto.merlin.file=resources/soumadeep.jks
> AbstractCrypto.java
> ===============
> /*
>  * Copyright  2003-2004 The Apache Software Foundation.
>  *
>  *  Licensed under the Apache License, Version 2.0 (the "License");
>  *  you may not use this file except in compliance with the License.
>  *  You may obtain a copy of the License at
>  *
>  *      http://www.apache.org/licenses/LICENSE-2.0
>  *
>  *  Unless required by applicable law or agreed to in writing, software
>  *  distributed under the License is distributed on an "AS IS" BASIS,
>  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>  *  See the License for the specific language governing permissions and
>  *  limitations under the License.
>  *
>  */
> package org.apache.ws.security.components.crypto;
> import org.apache.commons.logging.Log;
> import org.apache.commons.logging.LogFactory;
> import org.apache.ws.security.WSSecurityException;
> import org.apache.ws.security.util.Base64;
> import org.apache.ws.security.util.Loader;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.io.InputStream;
> import java.math.BigInteger;
> import java.security.GeneralSecurityException;
> import java.security.Key;
> import java.security.KeyStore;
> import java.security.KeyStoreException;
> import java.security.MessageDigest;
> import java.security.NoSuchAlgorithmException;
> import java.security.NoSuchProviderException;
> import java.security.PrivateKey;
> import java.security.PublicKey;
> import java.security.cert.Certificate;
> import java.security.cert.CertificateEncodingException;
> import java.security.cert.CertificateException;
> import java.security.cert.CertificateFactory;
> import java.security.cert.X509Certificate;
> import java.security.interfaces.RSAPublicKey;
> import java.util.Arrays;
> import java.util.Enumeration;
> import java.util.Properties;
> import java.util.Vector;
> /**
>  * Created by IntelliJ IDEA.
>  * User: dims
>  * Date: Sep 15, 2005
>  * Time: 9:50:40 AM
>  * To change this template use File | Settings | File Templates.
>  */
> public abstract class AbstractCrypto implements Crypto {
>     private static Log log = LogFactory.getLog(AbstractCrypto.class);
>     protected static CertificateFactory certFact;
>     protected Properties properties = null;
>     protected KeyStore keystore = null;
>     static String SKI_OID = "2.5.29.14";
>     /**
>      * Constructor
>      *
>      * @param properties
>      */
>     public AbstractCrypto(Properties properties) throws CredentialException, IOException {
>     	this(properties,AbstractCrypto.class.getClassLoader());
>     }
>     /**
>      * This allows providing a custom class loader to load the resources, etc
>      * @param properties
>      * @param loader
>      * @throws CredentialException
>      * @throws IOException
>      */
>     public AbstractCrypto(Properties properties, ClassLoader loader) throws CredentialException, IOException {
>         /*
>         * if no properties .. just return an instance, the rest will be
>         * done later or this instance is just used to handle certificate
>         * conversions in this implementatio
>         */
>         if (properties == null) {
>             return;
>         }
>         this.properties = properties;
>         String location = this.properties.getProperty("org.apache.ws.security.crypto.merlin.file");
> 		InputStream is = null;
> 		java.net.URL url = Loader.getResource(loader, location);
> 		if(url != null) {
> 			is =  url.openStream();
> 		} else {
> 			is = new java.io.FileInputStream(location);
> 		}
>         /**
>          * If we don't find it, then look on the file system.
>          */
>         if (is == null) {
>             try {
>                 is = new FileInputStream(location);
>             } catch (Exception e) {
>                 throw new CredentialException(3, "proxyNotFound", new Object[]{location});
>             }
>         }
>         /**
>          * Load the keystore
>          */
>         try {
>             load(is);
>         } finally {
>             is.close();
>         }
>     }
>     
>     /**
>      * Singleton certificate factory for this Crypto instance.
>      * <p/>
>      *
>      * @return Returns a <code>CertificateFactory</code> to construct
>      *         X509 certficates
>      * @throws org.apache.ws.security.WSSecurityException
>      *
>      */
>     public synchronized CertificateFactory getCertificateFactory() throws WSSecurityException {
>         if (certFact == null) {
>             try {
>                 String provider = properties.getProperty("org.apache.ws.security.crypto.merlin.cert.provider");
>                 if (provider == null || provider.length() == 0) {
>                     certFact = CertificateFactory.getInstance("X.509");
>                 } else {
>                     certFact = CertificateFactory.getInstance("X.509", provider);
>                 }
>             } catch (CertificateException e) {
>                 throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
>                         "unsupportedCertType");
>             } catch (NoSuchProviderException e) {
>                 throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
>                         "noSecProvider");
>             }
>         }
>         return certFact;
>     }
>     /**
>      * load a X509Certificate from the input stream.
>      * <p/>
>      *
>      * @param in The <code>InputStream</code> array containg the X509 data
>      * @return Returns a X509 certificate
>      * @throws org.apache.ws.security.WSSecurityException
>      *
>      */
>     public X509Certificate loadCertificate(InputStream in) throws WSSecurityException {
>         X509Certificate cert = null;
>         try {
>             cert =
>                     (X509Certificate) getCertificateFactory().generateCertificate(in);
>         } catch (CertificateException e) {
>             throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
>                     "parseError");
>         }
>         return cert;
>     }
>     /**
>      * Gets the private key identified by <code>alias</> and <code>password</code>.
>      * <p/>
>      *
>      * @param alias    The alias (<code>KeyStore</code>) of the key owner
>      * @param password The password needed to access the private key
>      * @return The private key
>      * @throws Exception
>      */
>     public PrivateKey getPrivateKey(String alias, String password) throws Exception {
>         if (alias == null) {
>             throw new Exception("alias is null");
>         }
>         boolean b = keystore.isKeyEntry(alias);
>         if (!b) {
>             log.error("Cannot find key for alias: " + alias);
>             throw new Exception("Cannot find key for alias: " + alias);
>         }
>         Key keyTmp = keystore.getKey(alias, password.toCharArray());
>         if (!(keyTmp instanceof PrivateKey)) {
>             throw new Exception("Key is not a private key, alias: " + alias);
>         }
>         return (PrivateKey) keyTmp;
>     }
>     private Vector splitAndTrim(String inString) {
>         X509NameTokenizer nmTokens = new X509NameTokenizer(inString);
>         Vector vr = new Vector();
>         while (nmTokens.hasMoreTokens()) {
>             vr.add(nmTokens.nextToken());
>         }
>         java.util.Collections.sort(vr);
>         return vr;
>     }
>     /**
>      * Lookup a X509 Certificate in the keystore according to a given
>      * the issuer of a Certficate.
>      * <p/>
>      * The search gets all alias names of the keystore and gets the certificate chain
>      * for each alias. Then the Issuer fo each certificate of the chain
>      * is compared with the parameters.
>      *
>      * @param issuer The issuer's name for the certificate
>      * @return alias name of the certificate that matches the issuer name
>      *         or null if no such certificate was found.
>      */
>     public String getAliasForX509Cert(String issuer)
>             throws WSSecurityException {
>         return getAliasForX509Cert(issuer, null, false);
>     }
>     /**
>      * Lookup a X509 Certificate in the keystore according to a given serial number and
>      * the issuer of a Certficate.
>      * <p/>
>      * The search gets all alias names of the keystore and gets the certificate chain
>      * for each alias. Then the SerialNumber and Issuer fo each certificate of the chain
>      * is compared with the parameters.
>      *
>      * @param issuer       The issuer's name for the certificate
>      * @param serialNumber The serial number of the certificate from the named issuer
>      * @return alias name of the certificate that matches serialNumber and issuer name
>      *         or null if no such certificate was found.
>      */
>     public String getAliasForX509Cert(String issuer, BigInteger serialNumber)
>             throws WSSecurityException {
>         return getAliasForX509Cert(issuer, serialNumber, true);
>     }
>     /*
>     * need to check if "getCertificateChain" also finds certificates that are
>     * used for enryption only, i.e. they may not be signed by a CA
>     * Otherwise we must define a restriction how to use certificate:
>     * each certificate must be signed by a CA or is a self signed Certificate
>     * (this should work as well).
>     * --- remains to be tested in several ways --
>     */
>     private String getAliasForX509Cert(String issuer, BigInteger serialNumber,
>                                        boolean useSerialNumber)
>             throws WSSecurityException {
>         Vector issuerRDN = splitAndTrim(issuer);
>         X509Certificate x509cert = null;
>         Vector certRDN = null;
>         Certificate cert = null;
>         try {
>             for (Enumeration e = keystore.aliases(); e.hasMoreElements();) {
>                 String alias = (String) e.nextElement();
>                 Certificate[] certs = keystore.getCertificateChain(alias);
>                 if (certs == null || certs.length == 0) {
>                     // no cert chain, so lets check if getCertificate gives us a  result.
>                     cert = keystore.getCertificate(alias);
>                     if (cert == null) {
>                         return null;
>                     }
>                 } else {
>                     cert = certs[0];
>                 }
>                 if (!(cert instanceof X509Certificate)) {
>                     continue;
>                 }
>                 x509cert = (X509Certificate) cert;
>                 if (!useSerialNumber ||
>                         useSerialNumber && x509cert.getSerialNumber().compareTo(serialNumber) == 0) {
>                     certRDN = splitAndTrim(x509cert.getIssuerDN().getName());
>                     if (certRDN.equals(issuerRDN)) {
>                         return alias;
>                     }
>                 }
>             }
>         } catch (KeyStoreException e) {
>             throw new WSSecurityException(WSSecurityException.FAILURE,
>                     "keystore");
>         }
>         return null;
>     }
>     /**
>      * Lookup a X509 Certificate in the keystore according to a given
>      * SubjectKeyIdentifier.
>      * <p/>
>      * The search gets all alias names of the keystore and gets the certificate chain
>      * or certificate for each alias. Then the SKI for each user certificate
>      * is compared with the SKI parameter.
>      *
>      * @param skiBytes The SKI info bytes
>      * @return alias name of the certificate that matches serialNumber and issuer name
>      *         or null if no such certificate was found.
>      * @throws org.apache.ws.security.WSSecurityException
>      *          if problems during keystore handling or wrong certificate (no SKI data)
>      */
>     public String getAliasForX509Cert(byte[] skiBytes) throws WSSecurityException {
>         Certificate cert = null;
>         boolean found = false;
>         try {
>             for (Enumeration e = keystore.aliases(); e.hasMoreElements();) {
>                 String alias = (String) e.nextElement();
>                 Certificate[] certs = keystore.getCertificateChain(alias);
>                 if (certs == null || certs.length == 0) {
>                     // no cert chain, so lets check if getCertificate gives us a  result.
>                     cert = keystore.getCertificate(alias);
>                     if (cert == null) {
>                         return null;
>                     }
>                 } else {
>                     cert = certs[0];
>                 }
>                 if (!(cert instanceof X509Certificate)) {
>                     continue;
>                 }
>                 byte[] data = getSKIBytesFromCert((X509Certificate) cert);
>                 if (data.length != skiBytes.length) {
>                     continue;
>                 }
>                 if (Arrays.equals(data, skiBytes)) {
>                     return alias;
>                 }
>             }
>         } catch (KeyStoreException e) {
>             throw new WSSecurityException(WSSecurityException.FAILURE,
>                     "keystore");
>         }
>         return null;
>     }
>     /**
>      * Return a X509 Certificate alias in the keystore according to a given Certificate
>      * <p/>
>      *
>      * @param cert The certificate to lookup
>      * @return alias name of the certificate that matches the given certificate
>      *         or null if no such certificate was found.
>      */
> /*
>      * See comment above
>      */
>     public String getAliasForX509Cert(Certificate cert) throws WSSecurityException {
>         try {
>             String alias = keystore.getCertificateAlias(cert);
>             if (alias != null)
>                 return alias;
>             // Use brute force search
>             Enumeration e = keystore.aliases();
>             while (e.hasMoreElements()) {
>                 alias = (String) e.nextElement();
>                 X509Certificate cert2 = (X509Certificate) keystore.getCertificate(alias);
>                 if (cert2.equals(cert)) {
>                     return alias;
>                 }
>             }
>         } catch (KeyStoreException e) {
>             throw new WSSecurityException(WSSecurityException.FAILURE,
>                     "keystore");
>         }
>         return null;
>     }
>     /**
>      * Retrieves the alias name of the default certificate which has been
>      * specified as a property. This should be the certificate that is used for
>      * signature and encryption. This alias corresponds to the certificate that
>      * should be used whenever KeyInfo is not poresent in a signed or
>      * an encrypted message. May return null.
>      *
>      * @return alias name of the default X509 certificate
>      */
>     public String getDefaultX509Alias() {
>         if (properties == null) {
>             return null;
>         }
>         return properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.alias");
>     }
>     /**
>      * Gets the list of certificates for a given alias.
>      * <p/>
>      *
>      * @param alias Lookup certificate chain for this alias
>      * @return Array of X509 certificates for this alias name, or
>      *         null if this alias does not exist in the keystore
>      */
>     public X509Certificate[] getCertificates(String alias) throws WSSecurityException {
>         Certificate[] certs = null;
>         try {
>             certs = keystore.getCertificateChain(alias);
>             if (certs == null || certs.length == 0) {
>                 // no cert chain, so lets check if getCertificate gives us a  result.
>                 Certificate cert = keystore.getCertificate(alias);
>                 if (cert == null) {
>                     return null;
>                 }
>                 certs = new Certificate[]{cert};
>             }
>         } catch (KeyStoreException e) {
>             throw new WSSecurityException(WSSecurityException.FAILURE,
>                     "keystore");
>         }
>         X509Certificate[] x509certs = new X509Certificate[certs.length];
>         for (int i = 0; i < certs.length; i++) {
>             x509certs[i] = (X509Certificate) certs[i];
>         }
>         return x509certs;
>     }
>     /**
>      * Lookup a X509 Certificate in the keystore according to a given
>      * Thumbprint.
>      * <p/>
>      * The search gets all alias names of the keystore, then reads the certificate chain
>      * or certificate for each alias. Then the thumbprint for each user certificate
>      * is compared with the thumbprint parameter.
>      *
>      * @param thumb The SHA1 thumbprint info bytes
>      * @return alias name of the certificate that matches the thumbprint
>      *         or null if no such certificate was found.
>      * @throws org.apache.ws.security.WSSecurityException
>      *          if problems during keystore handling or wrong certificate
>      */
>     public String getAliasForX509CertThumb(byte[] thumb) throws WSSecurityException {
>         Certificate cert = null;
>         MessageDigest sha = null;
>         try {
>             sha = MessageDigest.getInstance("SHA-1");
>         } catch (NoSuchAlgorithmException e1) {
>             throw new WSSecurityException(
>                     0,
>                     "noSHA1availabe");
>         }
>         try {
>             for (Enumeration e = keystore.aliases(); e.hasMoreElements();) {
>                 String alias = (String) e.nextElement();
>                 Certificate[] certs = keystore.getCertificateChain(alias);
>                 if (certs == null || certs.length == 0) {
>                     // no cert chain, so lets check if getCertificate gives us a  result.
>                     cert = keystore.getCertificate(alias);
>                     if (cert == null) {
>                         return null;
>                     }
>                 } else {
>                     cert = certs[0];
>                 }
>                 if (!(cert instanceof X509Certificate)) {
>                     continue;
>                 }
>                 sha.reset();
>                 try {
>                     sha.update(cert.getEncoded());
>                 } catch (CertificateEncodingException e1) {
>                     throw new WSSecurityException(
>                             WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
>                             "encodeError");
>                 }
>                 byte[] data = sha.digest();
>                 if (Arrays.equals(data, thumb)) {
>                     return alias;
>                 }
>             }
>         } catch (KeyStoreException e) {
>             throw new WSSecurityException(WSSecurityException.FAILURE,
>                     "keystore");
>         }
>         return null;
>     }
>     /**
>      * A Hook for subclasses to set the keystore without having to
>      * load it from an <code>InputStream</code>.
>      *
>      * @param ks existing keystore
>      */
>     public void setKeyStore(KeyStore ks) {
>         keystore = ks;
>     }
>     /**
>      * Loads the the keystore from an <code>InputStream </code>.
>      * <p/>
>      *
>      * @param input <code>InputStream</code> to read from
>      * @throws CredentialException
>      */
>     public void load(InputStream input) throws CredentialException {
>         if (input == null) {
>             throw new IllegalArgumentException("input stream cannot be null");
>         }
>         try {
>             String provider = properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.provider");
>             if (provider == null || provider.length() == 0) {
>                 keystore = KeyStore.getInstance
>                         (properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.type",
>                                 KeyStore.getDefaultType()));
>             } else {
>                 keystore = KeyStore.getInstance
>                         (properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.type",
>                                 KeyStore.getDefaultType()), provider);
>             }
> 			String password = null;
> 			String base64Encoded = properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.base64.encoded","false");
> 			password = properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.password",
>                         "security");
> 			if(base64Encoded.equalsIgnoreCase("true")){
>             	password = new String(Base64.decode(password));
>             }
> 			
>             keystore.load(input, (password == null || password.length() == 0) ? new char[0] : password.toCharArray());
>         } catch (IOException e) {
>             e.printStackTrace();
>             throw new CredentialException(3, "ioError00", e);
>         } catch (GeneralSecurityException e) {
>             e.printStackTrace();
>             throw new CredentialException(3, "secError00", e);
>         } catch (Exception e) {
>             e.printStackTrace();
>             throw new CredentialException(-1, "error00", e);
>         }
>     }
>     /**
>      * Reads the SubjectKeyIdentifier information from the certificate.
>      * <p/>
>      * If the the certificate does not contain a SKI extension then
>      * try to compute the SKI according to RFC3280 using the
>      * SHA-1 hash value of the public key. The second method described
>      * in RFC3280 is not support. Also only RSA public keys are supported.
>      * If we cannot compute the SKI throw a WSSecurityException.
>      *
>      * @param cert The certificate to read SKI
>      * @return The byte array conating the binary SKI data
>      */
>     public byte[] getSKIBytesFromCert(X509Certificate cert)
>             throws WSSecurityException {
>         /*
>            * Gets the DER-encoded OCTET string for the extension value (extnValue)
>            * identified by the passed-in oid String. The oid string is represented
>            * by a set of positive whole numbers separated by periods.
>            */
>         byte[] derEncodedValue = cert.getExtensionValue(SKI_OID);
>         if (cert.getVersion() < 3 || derEncodedValue == null) {
>             PublicKey key = cert.getPublicKey();
>             if (!(key instanceof RSAPublicKey)) {
>                 throw new WSSecurityException(
>                         1,
>                         "noSKIHandling",
>                         new Object[]{"Support for RSA key only"});
>             }
>             byte[] encoded = key.getEncoded();
>             // remove 22-byte algorithm ID and header
>             byte[] value = new byte[encoded.length - 22];
>             System.arraycopy(encoded, 22, value, 0, value.length);
>             MessageDigest sha;
>             try {
>                 sha = MessageDigest.getInstance("SHA-1");
>             } catch (NoSuchAlgorithmException ex) {
>                 throw new WSSecurityException(
>                         1,
>                         "noSKIHandling",
>                         new Object[]{"Wrong certificate version (<3) and no SHA1 message digest availabe"});
>             }
>             sha.reset();
>             sha.update(value);
>             return sha.digest();
>         }
>         /**
>          * Strip away first four bytes from the DerValue (tag and length of
>          * ExtensionValue OCTET STRING and KeyIdentifier OCTET STRING)
>          */
>         byte abyte0[] = new byte[derEncodedValue.length - 4];
>         System.arraycopy(derEncodedValue, 4, abyte0, 0, abyte0.length);
>         return abyte0;
>     }
>     public KeyStore getKeyStore() {
>         return this.keystore;
>     }
>     /**
>      * Lookup X509 Certificates in the keystore according to a given DN of the subject of the certificate
>      * <p/>
>      * The search gets all alias names of the keystore and gets the certificate (chain)
>      * for each alias. Then the DN of the certificate is compared with the parameters.
>      *
>      * @param subjectDN The DN of subject to look for in the keystore
>      * @return Vector with all alias of certificates with the same DN as given in the parameters
>      * @throws org.apache.ws.security.WSSecurityException
>      *
>      */
>     public String[] getAliasesForDN(String subjectDN) throws WSSecurityException {
>         // Store the aliases found
>         Vector aliases = new Vector();
>         Certificate cert = null;
>         // The DN to search the keystore for
>         Vector subjectRDN = splitAndTrim(subjectDN);
>         // Look at every certificate in the keystore
>         try {
>             for (Enumeration e = keystore.aliases(); e.hasMoreElements();) {
>                 String alias = (String) e.nextElement();
>                 Certificate[] certs = keystore.getCertificateChain(alias);
>                 if (certs == null || certs.length == 0) {
>                     // no cert chain, so lets check if getCertificate gives us a  result.
>                     cert = keystore.getCertificate(alias);
>                     if (cert == null) {
>                         return null;
>                     }
>                     certs = new Certificate[]{cert};
>                 } else {
>                     cert = certs[0];
>                 }
>                 if (cert instanceof X509Certificate) {
>                     Vector foundRDN = splitAndTrim(((X509Certificate) cert).getSubjectDN().getName());
>                     if (subjectRDN.equals(foundRDN)) {
>                         aliases.add(alias);
>                     }
>                 }
>             }
>         } catch (KeyStoreException e) {
>             throw new WSSecurityException(WSSecurityException.FAILURE,
>                     "keystore");
>         }
>         // Convert the vector into an array
>         String[] result = new String[aliases.size()];
>         for (int i = 0; i < aliases.size(); i++)
>             result[i] = (String) aliases.elementAt(i);
>         return result;
>     }
> }

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

---------------------------------------------------------------------
To unsubscribe, e-mail: wss4j-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: wss4j-dev-help@ws.apache.org