Logo Search packages:      
Sourcecode: mysql-connector-java version File versions  Download package

Security.java

/*
 Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
 

  The MySQL Connector/J is licensed under the terms of the GPLv2
  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
  There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
  this software, see the FLOSS License Exception
  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.

  This program is free software; you can redistribute it and/or modify it under the terms
  of the GNU General Public License as published by the Free Software Foundation; version 2
  of the License.

  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along with this
  program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
  Floor, Boston, MA 02110-1301  USA



 */
package com.mysql.jdbc;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Methods for doing secure authentication with MySQL-4.1 and newer.
 * 
 * @author Mark Matthews
 * 
 * @version $Id$
 */
00039 class Security {
      private static final char PVERSION41_CHAR = '*';

      private static final int SHA1_HASH_SIZE = 20;

      /**
       * Returns hex value for given char
       */
00047       private static int charVal(char c) {
            return ((c >= '0') && (c <= '9')) ? (c - '0')
                        : (((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 10) : (c - 'a' + 10));
      }

      /*
       * Convert password in salted form to binary string password and hash-salt
       * For old password this involes one more hashing
       * 
       * SYNOPSIS get_hash_and_password() salt IN Salt to convert from pversion IN
       * Password version to use hash OUT Store zero ended hash here bin_password
       * OUT Store binary password here (no zero at the end)
       * 
       * RETURN 0 for pre 4.1 passwords !0 password version char for newer
       * passwords
       */

      /**
       * Creates key from old password to decode scramble Used in 4.1
       * authentication with passwords stored pre-4.1 hashing.
       * 
       * @param passwd
       *            the password to create the key from
       * 
       * @return 20 byte generated key
       * 
       * @throws NoSuchAlgorithmException
       *             if the message digest 'SHA-1' is not available.
       */
00076       static byte[] createKeyFromOldPassword(String passwd)
                  throws NoSuchAlgorithmException {
            /* At first hash password to the string stored in password */
            passwd = makeScrambledPassword(passwd);

            /* Now convert it to the salt form */
            int[] salt = getSaltFromPassword(passwd);

            /* Finally get hash and bin password from salt */
            return getBinaryPassword(salt, false);
      }

      /**
       * DOCUMENT ME!
       * 
       * @param salt
       *            DOCUMENT ME!
       * @param usingNewPasswords
       *            DOCUMENT ME!
       * 
       * @return DOCUMENT ME!
       * 
       * @throws NoSuchAlgorithmException
       *             if the message digest 'SHA-1' is not available.
       */
00101       static byte[] getBinaryPassword(int[] salt, boolean usingNewPasswords)
                  throws NoSuchAlgorithmException {
            int val = 0;

            byte[] binaryPassword = new byte[SHA1_HASH_SIZE]; /*
                                                                                           * Binary password
                                                                                           * loop pointer
                                                                                           */

            if (usingNewPasswords) /* New password version assumed */{
                  int pos = 0;

                  for (int i = 0; i < 4; i++) /* Iterate over these elements */{
                        val = salt[i];

                        for (int t = 3; t >= 0; t--) {
                              binaryPassword[pos++] = (byte) (val & 255);
                              val >>= 8; /* Scroll 8 bits to get next part */
                        }
                  }

                  return binaryPassword;
            }

            int offset = 0;

            for (int i = 0; i < 2; i++) /* Iterate over these elements */{
                  val = salt[i];

                  for (int t = 3; t >= 0; t--) {
                        binaryPassword[t + offset] = (byte) (val % 256);
                        val >>= 8; /* Scroll 8 bits to get next part */
                  }

                  offset += 4;
            }

            MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$

            md.update(binaryPassword, 0, 8);

            return md.digest();
      }

      private static int[] getSaltFromPassword(String password) {
            int[] result = new int[6];

            if ((password == null) || (password.length() == 0)) {
                  return result;
            }

            if (password.charAt(0) == PVERSION41_CHAR) {
                  // new password
                  String saltInHex = password.substring(1, 5);

                  int val = 0;

                  for (int i = 0; i < 4; i++) {
                        val = (val << 4) + charVal(saltInHex.charAt(i));
                  }

                  return result;
            }

            int resultPos = 0;
            int pos = 0;
            int length = password.length();

            while (pos < length) {
                  int val = 0;

                  for (int i = 0; i < 8; i++) {
                        val = (val << 4) + charVal(password.charAt(pos++));
                  }

                  result[resultPos++] = val;
            }

            return result;
      }

      private static String longToHex(long val) {
            String longHex = Long.toHexString(val);

            int length = longHex.length();

            if (length < 8) {
                  int padding = 8 - length;
                  StringBuffer buf = new StringBuffer();

                  for (int i = 0; i < padding; i++) {
                        buf.append("0"); //$NON-NLS-1$
                  }

                  buf.append(longHex);

                  return buf.toString();
            }

            return longHex.substring(0, 8);
      }

      /**
       * Creates password to be stored in user database from raw string.
       * 
       * Handles Pre-MySQL 4.1 passwords.
       * 
       * @param password
       *            plaintext password
       * 
       * @return scrambled password
       * 
       * @throws NoSuchAlgorithmException
       *             if the message digest 'SHA-1' is not available.
       */
00216       static String makeScrambledPassword(String password)
                  throws NoSuchAlgorithmException {
            long[] passwordHash = Util.newHash(password);
            StringBuffer scramble = new StringBuffer();

            scramble.append(longToHex(passwordHash[0]));
            scramble.append(longToHex(passwordHash[1]));

            return scramble.toString();
      }

      /**
       * Encrypt/Decrypt function used for password encryption in authentication
       * 
       * Simple XOR is used here but it is OK as we crypt random strings
       * 
       * @param from
       *            IN Data for encryption
       * @param to
       *            OUT Encrypt data to the buffer (may be the same)
       * @param password
       *            IN Password used for encryption (same length)
       * @param length
       *            IN Length of data to encrypt
       */
00241       static void passwordCrypt(byte[] from, byte[] to, byte[] password,
                  int length) {
            int pos = 0;

            while ((pos < from.length) && (pos < length)) {
                  to[pos] = (byte) (from[pos] ^ password[pos]);
                  pos++;
            }
      }

      /**
       * Stage one password hashing, used in MySQL 4.1 password handling
       * 
       * @param password
       *            plaintext password
       * 
       * @return stage one hash of password
       * 
       * @throws NoSuchAlgorithmException
       *             if the message digest 'SHA-1' is not available.
       */
00262       static byte[] passwordHashStage1(String password)
                  throws NoSuchAlgorithmException {
            MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
            StringBuffer cleansedPassword = new StringBuffer();

            int passwordLength = password.length();

            for (int i = 0; i < passwordLength; i++) {
                  char c = password.charAt(i);

                  if ((c == ' ') || (c == '\t')) {
                        continue; /* skip space in password */
                  }

                  cleansedPassword.append(c);
            }

            return md.digest(cleansedPassword.toString().getBytes());
      }

      /**
       * Stage two password hashing used in MySQL 4.1 password handling
       * 
       * @param hash
       *            from passwordHashStage1
       * @param salt
       *            salt used for stage two hashing
       * 
       * @return result of stage two password hash
       * 
       * @throws NoSuchAlgorithmException
       *             if the message digest 'SHA-1' is not available.
       */
00295       static byte[] passwordHashStage2(byte[] hashedPassword, byte[] salt)
                  throws NoSuchAlgorithmException {
            MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$

            // hash 4 bytes of salt
            md.update(salt, 0, 4);

            md.update(hashedPassword, 0, SHA1_HASH_SIZE);

            return md.digest();
      }

      // SERVER: public_seed=create_random_string()
      // send(public_seed)
      //
      // CLIENT: recv(public_seed)
      // hash_stage1=sha1("password")
      // hash_stage2=sha1(hash_stage1)
      // reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
      //
      // // this three steps are done in scramble()
      //
      // send(reply)
      //
      //
      // SERVER: recv(reply)
      // hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
      // candidate_hash2=sha1(hash_stage1)
      // check(candidate_hash2==hash_stage2)
      static byte[] scramble411(String password, String seed, Connection conn)
                  throws NoSuchAlgorithmException, UnsupportedEncodingException {
            MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
            String passwordEncoding = conn.getPasswordCharacterEncoding();
            
            byte[] passwordHashStage1 = md
                        .digest((passwordEncoding == null || passwordEncoding.length() == 0) ? password
                                    .getBytes()
                                    : password.getBytes(passwordEncoding));
            md.reset();

            byte[] passwordHashStage2 = md.digest(passwordHashStage1);
            md.reset();

            byte[] seedAsBytes = seed.getBytes("ASCII"); // for debugging
            md.update(seedAsBytes);
            md.update(passwordHashStage2);

            byte[] toBeXord = md.digest();

            int numToXor = toBeXord.length;

            for (int i = 0; i < numToXor; i++) {
                  toBeXord[i] = (byte) (toBeXord[i] ^ passwordHashStage1[i]);
            }

            return toBeXord;
      }

      /**
       * Prevent construction.
       */
00356       private Security() {
            super();
      }
}

Generated by  Doxygen 1.6.0   Back to index