You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ri...@apache.org on 2007/04/05 11:29:23 UTC

svn commit: r525777 - in /incubator/qpid/branches/M2/java: broker/distribution/src/main/assembly/ broker/src/main/java/org/apache/qpid/server/security/ broker/src/main/java/org/apache/qpid/server/security/auth/database/ broker/src/main/java/org/apache/...

Author: ritchiem
Date: Thu Apr  5 02:29:22 2007
New Revision: 525777

URL: http://svn.apache.org/viewvc?view=rev&rev=525777
Log:
Addition of CRAM-MD5-HASHED authentication. Same as CRAM-MD5 but the client uses the hash of the password rather than the original password. This allows the broker to store the hash not the original password.

Added initial tool for generation passwords.

Broker:
Renamed MD5PasswordFilePrincipalDatabase.java to Base64MD5PasswordFilePrincipalDatabase.java as that more accurately represents the file contents. 
PlainPasswordVhostFilePrincipalDatabase.java - import tidy up
PrincipalDatabaseAuthenticationManager.java - Changed to add our SASL providers at the start of the SASL list.
CRAMMD5Hashed* - New SASL mechanism that delegates to CRAM-MD5 but understands that the password to use is the hash of the users password.
JCAProvider - Removed the addProvider() line as this is done after the construction in PrincipalDatabaseAuthenticationManager.
PlainSaslServerFactory - White Space
Passwd.java - New util stub for managing passwords ala htpasswd.

Client
Added CRAM-MD5-HASHED to CallbackHandlerRegistry 
Added ClientFactory for CRAMMD5Hashed that returns the first CRAM-MD5 SaslClient.
DynamicSaslRegistrar.java - Tidied imports added new JCAProviders at the start of the Sasl lists.
DynamicSaslRegistrar.properties - Added CRAM-MD5-HASHED handler.
JCAProvider.java - as with broker stopped JCAProvider.java adding itself as the DynamicSaslRegistrar.java does this on the client.
UsernameHashedPasswordCallbackHandler.java - New callback handler that is used by CRAM-MD5-HASHED. It hashes the client's password and uses that in the CRAM-MD5 algorithm.

Added:
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/Passwd.java   (with props)
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java   (with props)
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java   (with props)
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java   (with props)
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java   (with props)
    incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java   (with props)
    incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/
    incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java   (with props)
Removed:
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/MD5PasswordFilePrincipalDatabase.java
Modified:
    incubator/qpid/branches/M2/java/broker/distribution/src/main/assembly/broker-bin.xml
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java
    incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java
    incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties
    incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java
    incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties
    incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java

Modified: incubator/qpid/branches/M2/java/broker/distribution/src/main/assembly/broker-bin.xml
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/distribution/src/main/assembly/broker-bin.xml?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/distribution/src/main/assembly/broker-bin.xml (original)
+++ incubator/qpid/branches/M2/java/broker/distribution/src/main/assembly/broker-bin.xml Thu Apr  5 02:29:22 2007
@@ -108,6 +108,12 @@
       <fileMode>473</fileMode>
     </file>
     <file>
+      <source>../bin/passwd</source>
+      <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+      <destName>passwd</destName>
+      <fileMode>473</fileMode>
+    </file>
+    <file>
       <source>../bin/qpid-server</source>
       <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
       <destName>qpid-server</destName>

Added: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/Passwd.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/Passwd.java?view=auto&rev=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/Passwd.java (added)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/Passwd.java Thu Apr  5 02:29:22 2007
@@ -0,0 +1,81 @@
+/*
+ *  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 org.apache.qpid.server.security;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.DigestException;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class Passwd
+{
+    public static void main(String args[]) throws NoSuchAlgorithmException, DigestException, IOException
+    {
+        if (args.length != 2)
+        {
+            System.out.println("Passwd <username> <password>");
+            System.exit(0);
+        }
+
+        byte[] data = args[1].getBytes("utf-8");
+
+        MessageDigest md = MessageDigest.getInstance("MD5");
+
+        for (byte b : data)
+        {
+            md.update(b);
+        }
+
+        byte[] digest = md.digest();
+
+        Base64 b64 = new Base64();
+
+        byte[] encoded = b64.encode(digest);
+
+        output(args[0], encoded);
+    }
+
+    private static void output(String user, byte[] encoded) throws IOException
+    {
+
+//        File passwdFile = new File("qpid.passwd");
+
+        PrintStream ps = new PrintStream(System.out);
+
+        user += ":";
+        ps.write(user.getBytes("utf-8"));
+
+        for (byte b : encoded)
+        {
+            ps.write(b);
+        }
+
+        ps.println();
+
+        ps.flush();
+        ps.close();
+    }
+}

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/Passwd.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/Passwd.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java?view=auto&rev=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java (added)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java Thu Apr  5 02:29:22 2007
@@ -0,0 +1,212 @@
+/*
+ *  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 org.apache.qpid.server.security.auth.database;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser;
+import org.apache.commons.codec.binary.Base64;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.regex.Pattern;
+import java.util.Map;
+import java.util.HashMap;
+import java.security.Principal;
+
+/**
+ * Represents a user database where the account information is stored in a simple flat file.
+ *
+ * The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn
+ *
+ * where a carriage return separates each username/password pair. Passwords are assumed to be in plain text.
+ */
+public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
+{
+    private static final Logger _logger = Logger.getLogger(Base64MD5PasswordFilePrincipalDatabase.class);
+
+    private File _passwordFile;
+
+    private Pattern _regexp = Pattern.compile(":");
+
+    private Map<String, AuthenticationProviderInitialiser> _saslServers;
+
+    public Base64MD5PasswordFilePrincipalDatabase()
+    {
+        _saslServers = new HashMap<String, AuthenticationProviderInitialiser>();
+
+        /**
+         *  Create Authenticators for MD5 Password file.
+         */
+
+        // Accept Plain incomming and hash it for comparison to the file.
+        CRAMMD5HashedInitialiser cram = new CRAMMD5HashedInitialiser();
+        cram.initialise(this);
+        _saslServers.put(cram.getMechanismName(), cram);
+    }
+
+    public void setPasswordFile(String passwordFile) throws FileNotFoundException
+    {
+        File f = new File(passwordFile);
+        _logger.info("PasswordFilePrincipalDatabase using file " + f.getAbsolutePath());
+        _passwordFile = f;
+        if (!f.exists())
+        {
+            throw new FileNotFoundException("Cannot find password file " + f);
+        }
+        if (!f.canRead())
+        {
+            throw new FileNotFoundException("Cannot read password file " + f +
+                                            ". Check permissions.");
+        }
+    }
+
+    public void setPassword(Principal principal, PasswordCallback callback) throws IOException,
+                                                                                   AccountNotFoundException
+    {
+        if (_passwordFile == null)
+        {
+            throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation");
+        }
+        if (principal == null)
+        {
+            throw new IllegalArgumentException("principal must not be null");
+        }
+        char[] pwd = lookupPassword(principal.getName());
+        if (pwd != null)
+        {
+            callback.setPassword(pwd);
+        }
+        else
+        {
+            throw new AccountNotFoundException("No account found for principal " + principal);
+        }
+    }
+
+    public boolean verifyPassword(Principal principal, char[] password) throws AccountNotFoundException
+    {
+        try
+        {
+            char[] pwd = lookupPassword(principal.getName());
+            return compareCharArray(pwd, password);
+        }
+        catch (IOException e)
+        {
+            return false;
+        }
+    }
+
+    public Map<String, AuthenticationProviderInitialiser> getMechanisms()
+    {
+        return _saslServers;
+    }
+
+    private boolean compareCharArray(char[] a, char[] b)
+    {
+        boolean equal = false;
+        if (a.length == b.length)
+        {
+            equal = true;
+            int index = 0;
+            while (equal && index < a.length)
+            {
+                equal = a[index] == b[index];
+                index++;
+            }
+        }
+        return equal;
+    }
+
+
+    /**
+     * Looks up the password for a specified user in the password file. Note this code is <b>not</b> secure since it
+     * creates strings of passwords. It should be modified to create only char arrays which get nulled out.
+     *
+     * @param name
+     *
+     * @return
+     *
+     * @throws java.io.IOException
+     */
+    private char[] lookupPassword(String name) throws IOException
+    {
+        BufferedReader reader = null;
+        byte[] passwd = null;
+        try
+        {
+            reader = new BufferedReader(new FileReader(_passwordFile));
+            String line;
+
+            while ((line = reader.readLine()) != null)
+            {
+                String[] result = _regexp.split(line);
+                if (result == null || result.length < 2)
+                {
+                    continue;
+                }
+
+                if (name.equals(result[0]))
+                {
+
+
+                    char[] raw = result[1].toCharArray();
+
+                    byte[] encoded = new byte[result[1].length() + 1];
+
+                    int index = 0;
+                    for (char c : raw)
+                    {
+                        index++;
+                        encoded[index] = (byte) c;
+                    }
+
+                    Base64 b64 = new Base64();
+                    byte[] decoded = b64.decode(encoded);
+
+
+                    char[] hashedPassword = new char[decoded.length + 1];
+
+                    index = 0;
+                    for (byte c : decoded)
+                    {
+                        index++;
+                        hashedPassword[index] = (char) c;
+                    }
+
+                    return hashedPassword;
+                }
+            }
+            return null;
+        }
+        finally
+        {
+            if (reader != null)
+            {
+                reader.close();
+            }
+        }
+    }
+}

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java (original)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java Thu Apr  5 02:29:22 2007
@@ -20,27 +20,15 @@
  */
 package org.apache.qpid.server.security.auth.database;
 
-import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
-import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
-import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser;
-import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser;
 import org.apache.qpid.server.security.access.AccessManager;
 import org.apache.qpid.server.security.access.AccessResult;
 import org.apache.qpid.server.security.access.Accessable;
 import org.apache.qpid.server.virtualhost.VirtualHost;
 import org.apache.log4j.Logger;
 
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.AccountNotFoundException;
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.BufferedReader;
 import java.io.FileReader;
-import java.util.regex.Pattern;
-import java.util.Map;
-import java.util.HashMap;
-import java.security.Principal;
 
 /**
  * Represents a user database where the account information is stored in a simple flat file.

Modified: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java (original)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java Thu Apr  5 02:29:22 2007
@@ -108,11 +108,15 @@
 
         if (providerMap.size() > 0)
         {
-            Security.addProvider(new JCAProvider(providerMap));
+            // Ensure we are used before the defaults
+            if (Security.insertProviderAt(new JCAProvider(providerMap), 1) == -1)
+            {
+                _logger.warn("Unable to set order of providers.");
+            }
         }
         else
         {
-            _logger.warn("No SASL providers availble.");
+            _logger.warn("No additional SASL providers registered.");
         }
 
     }
@@ -148,21 +152,20 @@
     {
         if (database == null || database.getMechanisms().size() == 0)
         {
-            _logger.warn("");
+            _logger.warn("No Database or no mechanisms to initialise authentication");
             return;
         }
 
-        for (AuthenticationProviderInitialiser mechanism : database.getMechanisms().values())
+        for (Map.Entry<String, AuthenticationProviderInitialiser> mechanism : database.getMechanisms().entrySet())
         {
-            initialiseAuthenticationMechanism(mechanism, providerMap);
+            initialiseAuthenticationMechanism(mechanism.getKey(), mechanism.getValue(), providerMap);
         }
     }
 
-    private void initialiseAuthenticationMechanism(AuthenticationProviderInitialiser initialiser,
+    private void initialiseAuthenticationMechanism(String mechanism, AuthenticationProviderInitialiser initialiser,
                                                    Map<String, Class<? extends SaslServerFactory>> providerMap)
             throws Exception
     {
-        String mechanism = initialiser.getMechanismName();
         if (_mechanisms == null)
         {
             _mechanisms = mechanism;

Modified: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java (original)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java Thu Apr  5 02:29:22 2007
@@ -33,7 +33,7 @@
         super("AMQSASLProvider", 1.0, "A JCA provider that registers all " +
               "AMQ SASL providers that want to be registered");
         register(providerMap);
-        Security.addProvider(this);
+        //Security.addProvider(this);
     }
 
     private void register(Map<String, Class<? extends SaslServerFactory>> providerMap)

Added: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java?view=auto&rev=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java (added)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java Thu Apr  5 02:29:22 2007
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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 org.apache.qpid.server.security.auth.sasl.crammd5;
+
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+
+import javax.security.sasl.SaslServerFactory;
+import java.util.Map;
+
+public class CRAMMD5HashedInitialiser extends UsernamePasswordInitialiser
+{
+    public String getMechanismName()
+    {
+        return CRAMMD5HashedSaslServer.MECHANISM;
+    }
+
+    public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+    {
+        return CRAMMD5HashedServerFactory.class;
+    }
+
+    public void initialise(PrincipalDatabase passwordFile)
+    {
+        super.initialise(passwordFile);
+    }
+
+    public Map<String, ?> getProperties()
+    {
+        return null;
+    }
+}

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java?view=auto&rev=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java (added)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java Thu Apr  5 02:29:22 2007
@@ -0,0 +1,105 @@
+/*
+ *  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 org.apache.qpid.server.security.auth.sasl.crammd5;
+
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslServerFactory;
+import javax.security.auth.callback.CallbackHandler;
+import java.util.Enumeration;
+import java.util.Map;
+
+public class CRAMMD5HashedSaslServer implements SaslServer
+{
+    public static final String MECHANISM = "CRAM-MD5-HASHED";
+
+    private SaslServer _realServer;
+
+    public CRAMMD5HashedSaslServer(String mechanism, String protocol, String serverName, Map<String, ?> props,
+                                   CallbackHandler cbh) throws SaslException
+    {
+        Enumeration factories = Sasl.getSaslServerFactories();
+
+        while (factories.hasMoreElements())
+        {
+            SaslServerFactory factory = (SaslServerFactory) factories.nextElement();
+
+            if (factory instanceof CRAMMD5HashedServerFactory)
+            {
+                continue;
+            }
+            
+            String[] mechs = factory.getMechanismNames(props);
+
+            for (String mech : mechs)
+            {
+                if (mech.equals("CRAM-MD5"))
+                {
+                    _realServer = factory.createSaslServer("CRAM-MD5", protocol, serverName, props, cbh);
+                    return;
+                }
+            }
+        }
+
+        throw new RuntimeException("No default SaslServer found for mechanism:" + "CRAM-MD5");
+    }
+
+    public String getMechanismName()
+    {
+        return MECHANISM;
+    }
+
+    public byte[] evaluateResponse(byte[] response) throws SaslException
+    {
+        return _realServer.evaluateResponse(response);
+    }
+
+    public boolean isComplete()
+    {
+        return _realServer.isComplete();
+    }
+
+    public String getAuthorizationID()
+    {
+        return _realServer.getAuthorizationID();
+    }
+
+    public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+    {
+        return _realServer.unwrap(incoming, offset, len);
+    }
+
+    public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+    {
+        return _realServer.wrap(outgoing, offset, len);
+    }
+
+    public Object getNegotiatedProperty(String propName)
+    {
+        return _realServer.getNegotiatedProperty(propName);
+    }
+
+    public void dispose() throws SaslException
+    {
+        _realServer.dispose();
+    }
+}

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java?view=auto&rev=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java (added)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java Thu Apr  5 02:29:22 2007
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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 org.apache.qpid.server.security.auth.sasl.crammd5;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+
+public class CRAMMD5HashedServerFactory implements SaslServerFactory
+{
+    public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map<String, ?> props,
+                                       CallbackHandler cbh) throws SaslException
+    {
+        if (mechanism.equals(CRAMMD5HashedSaslServer.MECHANISM))
+        {
+            return new CRAMMD5HashedSaslServer(mechanism, protocol, serverName, props, cbh);
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    public String[] getMechanismNames(Map props)
+    {
+        if (props != null)
+        {
+            if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+                props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+                props.containsKey(Sasl.POLICY_NOACTIVE))
+            {
+                // returned array must be non null according to interface documentation
+                return new String[0];
+            }
+        }
+
+        return new String[]{CRAMMD5HashedSaslServer.MECHANISM};
+    }
+}

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java (original)
+++ incubator/qpid/branches/M2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java Thu Apr  5 02:29:22 2007
@@ -29,7 +29,7 @@
 import javax.security.sasl.SaslServerFactory;
 
 public class PlainSaslServerFactory implements SaslServerFactory
-{    
+{
     public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props,
                                        CallbackHandler cbh) throws SaslException
     {

Modified: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties (original)
+++ incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties Thu Apr  5 02:29:22 2007
@@ -16,5 +16,6 @@
 # specific language governing permissions and limitations
 # under the License.
 #
+CallbackHandler.CRAM-MD5-HASHED=org.apache.qpid.client.security.UsernameHashedPasswordCallbackHandler
 CallbackHandler.CRAM-MD5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
 CallbackHandler.PLAIN=org.apache.qpid.client.security.UsernamePasswordCallbackHandler

Modified: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java (original)
+++ incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java Thu Apr  5 02:29:22 2007
@@ -20,10 +20,6 @@
  */
 package org.apache.qpid.client.security;
 
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.Security;
@@ -34,6 +30,7 @@
 
 import javax.security.sasl.SaslClientFactory;
 
+
 import org.apache.log4j.Logger;
 
 import org.apache.qpid.util.FileUtils;
@@ -50,14 +47,11 @@
  * mechanism=fully.qualified.class.name
  * </pre>
  *
- * <p/>Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a
- * class that implements javax.security.sasl.SaslClientFactory and provides the specified mechanism.
+ * <p/>Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a class that
+ * implements javax.security.sasl.SaslClientFactory and provides the specified mechanism.
  *
- * <p><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Parse SASL mechanism properties.
- * <tr><td> Create and register security provider for SASL mechanisms.
- * </table>
+ * <p><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Parse SASL
+ * mechanism properties. <tr><td> Create and register security provider for SASL mechanisms. </table>
  */
 public class DynamicSaslRegistrar
 {
@@ -69,10 +63,7 @@
     /** The default name of the SASL properties file resource. */
     public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/DynamicSaslRegistrar.properties";
 
-    /**
-     * Reads the properties file, and creates a dynamic security provider to register the SASL implementations
-     * with.
-     */
+    /** Reads the properties file, and creates a dynamic security provider to register the SASL implementations with. */
     public static void registerSaslProviders()
     {
         _logger.debug("public static void registerSaslProviders(): called");
@@ -80,8 +71,8 @@
         // Open the SASL properties file, using the default name is one is not specified.
         String filename = System.getProperty(FILE_PROPERTY);
         InputStream is =
-            FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME,
-                                                DynamicSaslRegistrar.class.getClassLoader());
+                FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME,
+                                                    DynamicSaslRegistrar.class.getClassLoader());
 
         try
         {
@@ -94,7 +85,7 @@
 
             if (factories.size() > 0)
             {
-                Security.addProvider(new JCAProvider(factories));
+                Security.insertProviderAt(new JCAProvider(factories), 0);
                 _logger.debug("Dynamic SASL provider added as a security provider");
             }
         }
@@ -170,15 +161,15 @@
      * @return A map from SASL mechanism names to implementing client factory classes.
      *
      * @todo Why tree map here? Do really want mechanisms in alphabetical order? Seems more likely that the declared
-     *       order of the mechanisms is intended to be preserved, so that they are registered in the declared order
-     *       of preference. Consider LinkedHashMap instead.
+     * order of the mechanisms is intended to be preserved, so that they are registered in the declared order of
+     * preference. Consider LinkedHashMap instead.
      */
     private static Map<String, Class<? extends SaslClientFactory>> parseProperties(Properties props)
     {
         Enumeration e = props.propertyNames();
 
         TreeMap<String, Class<? extends SaslClientFactory>> factoriesToRegister =
-            new TreeMap<String, Class<? extends SaslClientFactory>>();
+                new TreeMap<String, Class<? extends SaslClientFactory>>();
 
         while (e.hasMoreElements())
         {

Modified: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties (original)
+++ incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties Thu Apr  5 02:29:22 2007
@@ -17,3 +17,4 @@
 # under the License.
 #
 AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory
+CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory

Modified: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java?view=diff&rev=525777&r1=525776&r2=525777
==============================================================================
--- incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java (original)
+++ incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java Thu Apr  5 02:29:22 2007
@@ -52,7 +52,7 @@
         super("AMQSASLProvider", 1.0, "A JCA provider that registers all "
               + "AMQ SASL providers that want to be registered");
         register(providerMap);
-        Security.addProvider(this);
+//        Security.addProvider(this);
     }
 
     /**

Added: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java?view=auto&rev=525777
==============================================================================
--- incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java (added)
+++ incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java Thu Apr  5 02:29:22 2007
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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 org.apache.qpid.client.security;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.RealmCallback;
+
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.log4j.Logger;
+import com.sun.crypto.provider.HmacMD5;
+
+public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler
+{
+    private static final Logger _logger = Logger.getLogger(UsernameHashedPasswordCallbackHandler.class);
+
+
+    private AMQProtocolSession _protocolSession;
+
+    public void initialise(AMQProtocolSession protocolSession)
+    {
+        _protocolSession = protocolSession;
+    }
+
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+    {
+        for (int i = 0; i < callbacks.length; i++)
+        {
+            Callback cb = callbacks[i];
+            if (cb instanceof NameCallback)
+            {
+                ((NameCallback) cb).setName(_protocolSession.getUsername());
+            }
+            else if (cb instanceof PasswordCallback)
+            {
+
+                try
+                {
+                    ((PasswordCallback) cb).setPassword(getHash(_protocolSession.getPassword()));
+                }
+                catch (Exception e)
+                {
+                    throw new UnsupportedCallbackException(cb);
+                }
+            }
+            else
+            {
+                throw new UnsupportedCallbackException(cb);
+            }
+        }
+    }
+
+    private char[] getHash(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException
+    {
+
+        byte[] data = text.getBytes("utf-8");
+
+        MessageDigest md = MessageDigest.getInstance("MD5");
+
+        for (byte b : data)
+        {
+            md.update(b);
+        }
+
+        byte[] digest = md.digest();
+
+        char[] hash = new char[digest.length + 1];
+
+        int index = 0;
+        for (byte b : digest)
+        {
+            index++;
+            hash[index] = (char) b;
+        }
+
+        return hash;
+    }
+}

Propchange: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java?view=auto&rev=525777
==============================================================================
--- incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java (added)
+++ incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java Thu Apr  5 02:29:22 2007
@@ -0,0 +1,72 @@
+/*
+ *  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 org.apache.qpid.client.security.crammd5hashed;
+
+import org.apache.qpid.client.security.amqplain.AmqPlainSaslClient;
+
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.Sasl;
+import javax.security.auth.callback.CallbackHandler;
+import java.util.Map;
+import java.security.Security;
+
+public class CRAMMD5HashedSaslClientFactory implements SaslClientFactory
+{
+    /** The name of this mechanism */
+    public static final String MECHANISM = "CRAM-MD5-HASHED";
+
+
+    public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, Map<String, ?> props, CallbackHandler cbh) throws SaslException
+    {
+        for (int i = 0; i < mechanisms.length; i++)
+        {
+            if (mechanisms[i].equals(MECHANISM))
+            {
+                if (cbh == null)
+                {
+                    throw new SaslException("CallbackHandler must not be null");
+                }
+
+                String[] mechs = {"CRAM-MD5"};
+                return Sasl.createSaslClient(mechs, authorizationId, protocol, serverName, props, cbh);
+            }
+        }
+        return null;
+    }
+
+    public String[] getMechanismNames(Map props)
+    {
+        if (props != null)
+        {
+            if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+                props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+                props.containsKey(Sasl.POLICY_NOACTIVE))
+            {
+                // returned array must be non null according to interface documentation
+                return new String[0];
+            }
+        }
+
+        return new String[]{MECHANISM};
+    }
+}

Propchange: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/branches/M2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date