Security.java
上传用户:sxlinghang
上传日期:2022-07-20
资源大小:1405k
文件大小:10k
源码类别:

数据库编程

开发平台:

Java

  1. /*
  2.    Copyright (C) 2002 MySQL AB
  3.       This program is free software; you can redistribute it and/or modify
  4.       it under the terms of the GNU General Public License as published by
  5.       the Free Software Foundation; either version 2 of the License, or
  6.       (at your option) any later version.
  7.       This program is distributed in the hope that it will be useful,
  8.       but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.       GNU General Public License for more details.
  11.       You should have received a copy of the GNU General Public License
  12.       along with this program; if not, write to the Free Software
  13.       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  14.  */
  15. package com.mysql.jdbc;
  16. import java.security.MessageDigest;
  17. import java.security.NoSuchAlgorithmException;
  18. /**
  19.  * Methods for doing secure authentication with MySQL-4.1
  20.  * and newer.
  21.  *
  22.  * @author Mark Matthews
  23.  *
  24.  * @version $Id: Security.java,v 1.1.4.4 2003/09/11 19:34:01 mmatthew Exp $
  25.  */
  26. class Security {
  27.     private static final int SHA1_HASH_SIZE = 20;
  28.     private static final char PVERSION41_CHAR = '*';
  29.     /**
  30.      * Prevent construction.
  31.      */
  32.     private Security() {
  33.         super();
  34.     }
  35.     /*
  36.       Convert password in salted form to binary string password and hash-salt
  37.       For old password this involes one more hashing
  38.       SYNOPSIS
  39.             get_hash_and_password()
  40.             salt         IN  Salt to convert from
  41.             pversion     IN  Password version to use
  42.             hash         OUT Store zero ended hash here
  43.             bin_password OUT Store binary password here (no zero at the end)
  44.       RETURN
  45.             0 for pre 4.1 passwords
  46.        !0 password version char for newer passwords
  47.     */
  48.     /**
  49.      * DOCUMENT ME!
  50.      *
  51.      * @param salt DOCUMENT ME!
  52.      * @param usingNewPasswords DOCUMENT ME!
  53.      *
  54.      * @return DOCUMENT ME!
  55.      *
  56.      * @throws NoSuchAlgorithmException if the message digest 'SHA-1' is not
  57.      * available.
  58.      */
  59.     static byte[] getBinaryPassword(int[] salt, boolean usingNewPasswords)
  60.         throws NoSuchAlgorithmException {
  61.         int val = 0;
  62.         byte[] binaryPassword = new byte[SHA1_HASH_SIZE]; /* Binary password loop pointer */
  63.         if (usingNewPasswords) /* New password version assumed */ {
  64.             int pos = 0;
  65.             for (int i = 0; i < 4; i++) /* Iterate over these elements*/ {
  66.                 val = salt[i];
  67.                 for (int t = 3; t >= 0; t--) {
  68.                     binaryPassword[pos++] = (byte) (val & 255);
  69.                     val >>= 8; /* Scroll 8 bits to get next part*/
  70.                 }
  71.             }
  72.             return binaryPassword;
  73.         } else {
  74.             int offset = 0;
  75.             for (int i = 0; i < 2; i++) /* Iterate over these elements*/ {
  76.                 val = salt[i];
  77.                 for (int t = 3; t >= 0; t--) {
  78.                     binaryPassword[t + offset] = (byte) (val % 256);
  79.                     val >>= 8; /* Scroll 8 bits to get next part*/
  80.                 }
  81.                 offset += 4;
  82.             }
  83.             MessageDigest md = MessageDigest.getInstance("SHA-1");
  84.             md.update(binaryPassword, 0, 8);
  85.             return md.digest();
  86.         }
  87.     }
  88.     /**
  89.      * Creates key from old password to decode scramble
  90.      * Used in 4.1 authentication with passwords stored
  91.      * pre-4.1 hashing.
  92.      *
  93.      * @param passwd the password to create the key from
  94.      *
  95.      * @return 20 byte generated key
  96.      *
  97.      * @throws NoSuchAlgorithmException if the message digest 'SHA-1'
  98.      *          is not available.
  99.      */
  100.     static byte[] createKeyFromOldPassword(String passwd)
  101.         throws NoSuchAlgorithmException {
  102.         /* At first hash password to the string stored in password */
  103.         passwd = makeScrambledPassword(passwd);
  104.         /* Now convert it to the salt form */
  105.         int[] salt = getSaltFromPassword(passwd);
  106.         /* Finally get hash and bin password from salt */
  107.         return getBinaryPassword(salt, false);
  108.     }
  109.     /**
  110.      * Creates password to be stored in user database
  111.      * from raw string.
  112.      *
  113.      * Handles Pre-MySQL 4.1 passwords.
  114.      *
  115.      * @param password plaintext password
  116.      *
  117.      * @return scrambled password
  118.      *
  119.      * @throws NoSuchAlgorithmException if the message digest 'SHA-1' is not
  120.      *          available.
  121.      */
  122.     static String makeScrambledPassword(String password)
  123.         throws NoSuchAlgorithmException {
  124.         long[] passwordHash = Util.newHash(password);
  125.         StringBuffer scramble = new StringBuffer();
  126.         scramble.append(longToHex(passwordHash[0]));
  127.         scramble.append(longToHex(passwordHash[1]));
  128.         return scramble.toString();
  129.     }
  130.     /**
  131.      * Encrypt/Decrypt function used for password encryption in authentication
  132.      *
  133.      * Simple XOR is used here but it is OK as we crypt random strings
  134.      *
  135.      * @param from     IN     Data for encryption
  136.      * @param to       OUT    Encrypt data to the buffer (may be the same)
  137.      * @param password IN     Password used for encryption (same length)
  138.      * @param length   IN     Length of data to encrypt
  139.      */
  140.     static void passwordCrypt(byte[] from, byte[] to, byte[] password,
  141.         int length) {
  142.         int pos = 0;
  143.         while ((pos < from.length) && (pos < length)) {
  144.             to[pos] = (byte) (from[pos] ^ password[pos]);
  145.             pos++;
  146.         }
  147.     }
  148.     /**
  149.      * Stage one password hashing, used in MySQL 4.1 password handling
  150.      *
  151.      * @param password plaintext password
  152.      *
  153.      * @return stage one hash of password
  154.      *
  155.      * @throws NoSuchAlgorithmException if the message digest 'SHA-1' is not
  156.      *          available.
  157.      */
  158.     static byte[] passwordHashStage1(String password)
  159.         throws NoSuchAlgorithmException {
  160.         MessageDigest md = MessageDigest.getInstance("SHA-1");
  161.         StringBuffer cleansedPassword = new StringBuffer();
  162.         int passwordLength = password.length();
  163.         for (int i = 0; i < passwordLength; i++) {
  164.             char c = password.charAt(i);
  165.             if ((c == ' ') || (c == 't')) {
  166.                 continue; /* skip space in password */
  167.             }
  168.             cleansedPassword.append(c);
  169.         }
  170.         return md.digest(cleansedPassword.toString().getBytes());
  171.     }
  172.     /**
  173.      * Stage two password hashing used in MySQL 4.1
  174.      * password handling
  175.      *
  176.      * @param hash from passwordHashStage1
  177.      * @param salt salt used for stage two hashing
  178.      *
  179.      * @return result of stage two password hash
  180.      *
  181.      * @throws NoSuchAlgorithmException if the message digest 'SHA-1' is not
  182.      * available.
  183.      */
  184.     static byte[] passwordHashStage2(byte[] hashedPassword, byte[] salt)
  185.         throws NoSuchAlgorithmException {
  186.         MessageDigest md = MessageDigest.getInstance("SHA-1");
  187.         // hash 4 bytes of salt
  188.         md.update(salt, 0, 4);
  189.         md.update(hashedPassword, 0, SHA1_HASH_SIZE);
  190.         return md.digest();
  191.     }
  192.     private static int[] getSaltFromPassword(String password) {
  193.         int[] result = new int[6];
  194.         if ((password == null) || (password.length() == 0)) {
  195.             return result;
  196.         }
  197.         if (password.charAt(0) == PVERSION41_CHAR) {
  198.             // new password
  199.             String saltInHex = password.substring(1, 5);
  200.             int val = 0;
  201.             for (int i = 0; i < 4; i++) {
  202.                 val = (val << 4) + charVal(saltInHex.charAt(i));
  203.             }
  204.             return result;
  205.         } else {
  206.             int resultPos = 0;
  207.             int pos = 0;
  208.             int length = password.length();
  209.             while (pos < length) {
  210.                 int val = 0;
  211.                 for (int i = 0; i < 8; i++) {
  212.                     val = (val << 4) + charVal(password.charAt(pos++));
  213.                 }
  214.                 result[resultPos++] = val;
  215.             }
  216.             return result;
  217.         }
  218.     }
  219.     /**
  220.      * Returns hex value for given char
  221.      */
  222.     private static int charVal(char c) {
  223.         return (int) (((c >= '0') && (c <= '9')) ? (c - '0')
  224.                                                  : (((c >= 'A') && (c <= 'Z'))
  225.         ? (c - 'A' + 10) : (c - 'a' + 10)));
  226.     }
  227.     private static String longToHex(long val) {
  228.         String longHex = Long.toHexString(val);
  229.         int length = longHex.length();
  230.         if (length < 8) {
  231.             int padding = 8 - length;
  232.             StringBuffer buf = new StringBuffer();
  233.             for (int i = 0; i < padding; i++) {
  234.                 buf.append("0");
  235.             }
  236.             buf.append(longHex);
  237.             return buf.toString();
  238.         } else {
  239.             return longHex.substring(0, 8);
  240.         }
  241.     }
  242. // SERVER:  public_seed=create_random_string()
  243. //  send(public_seed)
  244. //
  245. // CLIENT:  recv(public_seed)
  246. //  hash_stage1=sha1("password")
  247. //  hash_stage2=sha1(hash_stage1)
  248. //  reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
  249. //
  250. //  // this three steps are done in scramble()
  251. //
  252. //  send(reply)
  253. //
  254. //
  255. // SERVER:  recv(reply)
  256. //  hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
  257. //  candidate_hash2=sha1(hash_stage1)
  258. //  check(candidate_hash2==hash_stage2)
  259. static byte[] scramble411(String password, String seed) throws NoSuchAlgorithmException {
  260. MessageDigest md = MessageDigest.getInstance("SHA-1");
  261. byte[] passwordHashStage1 = md.digest(password.getBytes());
  262. md.reset();
  263. byte[] passwordHashStage2 = md.digest(passwordHashStage1);
  264. md.reset();
  265. byte[] seedAsBytes = seed.getBytes(); // for debugging
  266. md.update(seedAsBytes);
  267. md.update(passwordHashStage2);
  268. byte[] toBeXord = md.digest();
  269. int numToXor = toBeXord.length;
  270. for (int i = 0; i < numToXor; i++) {
  271. toBeXord[i] = (byte)(toBeXord[i] ^ passwordHashStage1[i]);
  272. }
  273. return toBeXord;
  274. }
  275. }