Package mx4j.tools.remote

Source Code of mx4j.tools.remote.PasswordAuthenticator

/*
* Copyright (C) The MX4J Contributors.
* All rights reserved.
*
* This software is distributed under the terms of the MX4J License version 1.0.
* See the terms of the MX4J License in the documentation provided with this software.
*/

package mx4j.tools.remote;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.management.remote.JMXAuthenticator;
import javax.management.remote.JMXPrincipal;
import javax.security.auth.Subject;

import mx4j.util.Base64Codec;

/**
* Implementation of the JMXAuthenticator interface to be used on server side
* to secure access to {@link javax.management.remote.JMXConnectorServer JMXConnectorServer}s. <br/>
* Usage:
* <pre>
* JMXAuthenticator authenticator = new PasswordAuthenticator(new File("users.properties"));
* Map environment = new HashMap();
* environment.put(JMXConnectorServer.AUTHENTICATOR, authenticator);
* JMXServiceURL address = new JMXServiceURL("rmi", "localhost", 0);
* MBeanServer server = ...;
* JMXConnectorServer cntorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, environment, server);
* </pre>
* The format of the users.properties file is that of a standard properties file: <br/>
* &lt;user&gt;=&lt;password&gt;<br/>
* where &lt;password&gt; can be stored in 2 ways:
* <ul>
* <li>Clear text: the password is written in clear text</li>
* <li>Obfuscated text: the password is obfuscated</li>
* </ul>
* The obfuscated form can be obtained running this class as a main class:
* <pre>
* java -cp mx4j-remote.jar mx4j.tools.remote.PasswordAuthenticator
* </pre>
* and following the instructions printed on the console. The output will be a string that should be
* copy/pasted as the password into the properties file.<br/>
* The obfuscated password is obtained by digesting the clear text password using a
* {@link java.security.MessageDigest} algorithm, and then by Base64-encoding the resulting bytes.<br/>
* <br/>
* On client side, you are allowed to connect to a server side secured with the PasswordAuthenticator
* only if you provide the correct credentials:
* <pre>
* String[] credentials = new String[2];
* // The user will travel as clear text
* credentials[0] = "user";
* // You may send the password in clear text, but it's better to obfuscate it
* credentials[1] = PasswordAuthenticator.obfuscatePassword("password");
* Map environment = new HashMap();
* environment.put(JMXConnector.CREDENTIALS, credentials);
* JMXServiceURL address = ...;
* JMXConnector cntor = JMXConnectorFactory.connect(address, environment);
* </pre>
* Note that {@link #obfuscatePassword(java.lang.String,java.lang.String) obfuscating} the passwords only works if the server side has been
* setup with the PasswordAuthenticator.
* However, the PasswordAuthenticator can be used with other JSR 160 implementations, such as Sun's reference
* implementation.
*
* @version $Revision: 1.3 $
*/
public class PasswordAuthenticator implements JMXAuthenticator
{
   private static final String LEFT_DELIMITER = "OBF(";
   private static final String RIGHT_DELIMITER = "):";

   /**
    * Runs this class as main class to obfuscate passwords.
    * When no arguments are provided, it prints out the usage.
    *
    * @see #obfuscatePassword(java.lang.String,java.lang.String)
    */
   public static void main(String[] args) throws Exception
   {
      if (args.length == 1)
      {
         if (!"-help".equals(args[0]))
         {
            printPassword("MD5", args[0]);
            return;
         }
      }
      else if (args.length == 3)
      {
         if ("-alg".equals(args[0]))
         {
            printPassword(args[1], args[2]);
            return;
         }
      }
      printUsage();
   }

   private static void printPassword(String algorithm, String input)
   {
      String password = obfuscatePassword(input, algorithm);
      System.out.println(password);
   }

   private static void printUsage()
   {
      System.out.println();
      System.out.println("Usage: java -cp <lib>/mx4j-tools.jar mx4j.tools.remote.PasswordAuthenticator <options> <password>");
      System.out.println("Where <options> is one of the following:");
      System.out.println("   -help                     Prints this message");
      System.out.println("   -alg <digest algorithm>   Specifies the digest algorithm (default is MD5)");
      System.out.println();
   }

   /**
    * Obfuscates the given password using MD5 as digest algorithm
    *
    * @see #obfuscatePassword(java.lang.String,java.lang.String)
    */
   public static String obfuscatePassword(String password)
   {
      return obfuscatePassword(password, "MD5");
   }

   /**
    * Obfuscates the given password using the given digest algorithm.<br/>
    * Obfuscation consists of 2 steps: first the clear text password is {@link java.security.MessageDigest#digest digested}
    * using the specified algorithm, then the resulting bytes are Base64-encoded.<br/>
    * For example, the obfuscated version of the password "password" is "OBF(MD5):X03MO1qnZdYdgyfeuILPmQ=="
    * or "OBF(SHA-1):W6ph5Mm5Pz8GgiULbPgzG37mj9g=". <br/>
    * OBF stands for "obfuscated", in parenthesis the algorithm used to digest the password.
    */
   public static String obfuscatePassword(String password, String algorithm)
   {
      try
      {
         MessageDigest digest = MessageDigest.getInstance(algorithm);
         byte[] digestedBytes = digest.digest(password.getBytes());
         byte[] obfuscatedBytes = Base64Codec.encodeBase64(digestedBytes);
         return LEFT_DELIMITER + algorithm + RIGHT_DELIMITER + new String(obfuscatedBytes);
      }
      catch (NoSuchAlgorithmException x)
      {
         throw new SecurityException("Could not find digest algorithm " + algorithm);
      }
   }

   private Map passwords;

   /**
    * Creates a new PasswordAuthenticator that reads user/password pairs from the specified properties file.
    * The file format is described in the javadoc of this class.
    *
    * @see #obfuscatePassword
    */
   public PasswordAuthenticator(File passwordFile) throws IOException
   {
      this(new FileInputStream(passwordFile));
   }

   /**
    * Creates a new PasswordAuthenticator that reads user/password pairs from the specified InputStream.
    * The file format is described in the javadoc of this class.
    *
    * @see #obfuscatePassword
    */
   public PasswordAuthenticator(InputStream is) throws IOException
   {
      passwords = readPasswords(is);
   }

   private Map readPasswords(InputStream is) throws IOException
   {
      Properties properties = new Properties();
      try
      {
         properties.load(is);
      }
      finally
      {
         is.close();
      }
      return new HashMap(properties);
   }

   public Subject authenticate(Object credentials) throws SecurityException
   {
      if (!(credentials instanceof String[])) throw new SecurityException("Bad credentials");
      String[] creds = (String[])credentials;
      if (creds.length != 2) throw new SecurityException("Bad credentials");

      String user = creds[0];
      String password = creds[1];

      if (password == null) throw new SecurityException("Bad password");

      if (!passwords.containsKey(user)) throw new SecurityException("Unknown user " + user);

      String storedPassword = (String)passwords.get(user);
      if (!isPasswordCorrect(password, storedPassword)) throw new SecurityException("Bad password");

      Set principals = new HashSet();
      principals.add(new JMXPrincipal(user));
      return new Subject(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET);
   }

   private boolean isPasswordCorrect(String password, String storedPassword)
   {
      if (password.startsWith(LEFT_DELIMITER))
      {
         if (storedPassword.startsWith(LEFT_DELIMITER))
         {
            return password.equals(storedPassword);
         }
         else
         {
            String algorithm = getAlgorithm(password);
            String obfuscated = obfuscatePassword(storedPassword, algorithm);
            return password.equals(obfuscated);
         }
      }
      else
      {
         if (storedPassword.startsWith(LEFT_DELIMITER))
         {
            // Password was sent in clear, bad practice
            String algorithm = getAlgorithm(storedPassword);
            String obfuscated = obfuscatePassword(password, algorithm);
            return obfuscated.equals(storedPassword);
         }
         else
         {
            return password.equals(storedPassword);
         }
      }
   }

   private String getAlgorithm(String obfuscatedPassword)
   {
      try
      {
         return obfuscatedPassword.substring(LEFT_DELIMITER.length(), obfuscatedPassword.indexOf(RIGHT_DELIMITER));
      }
      catch (IndexOutOfBoundsException x)
      {
         throw new SecurityException("Bad password");
      }
   }
}
TOP

Related Classes of mx4j.tools.remote.PasswordAuthenticator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.