Security.java
上传用户:tanyanyong
上传日期:2013-06-23
资源大小:1355k
文件大小:10k
- /*
- Copyright (C) 2002 MySQL AB
- 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; either version 2 of the License, or
- (at your option) any later version.
- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- package com.mysql.jdbc;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- /**
- * Methods for doing secure authentication with MySQL-4.1
- * and newer.
- *
- * @author Mark Matthews
- *
- * @version $Id: Security.java,v 1.1.4.4 2003/09/11 19:34:01 mmatthew Exp $
- */
- class Security {
- private static final int SHA1_HASH_SIZE = 20;
- private static final char PVERSION41_CHAR = '*';
- /**
- * Prevent construction.
- */
- private Security() {
- super();
- }
- /*
- 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
- */
- /**
- * DOCUMENT ME!
- *
- * @param salt DOCUMENT ME!
- * @param usingNewPasswords DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- *
- * @throws NoSuchAlgorithmException if the message digest 'SHA-1' is not
- * available.
- */
- 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;
- } else {
- 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");
- md.update(binaryPassword, 0, 8);
- return md.digest();
- }
- }
- /**
- * 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.
- */
- 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);
- }
- /**
- * 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.
- */
- 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
- */
- 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.
- */
- static byte[] passwordHashStage1(String password)
- throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-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.
- */
- static byte[] passwordHashStage2(byte[] hashedPassword, byte[] salt)
- throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- // hash 4 bytes of salt
- md.update(salt, 0, 4);
- md.update(hashedPassword, 0, SHA1_HASH_SIZE);
- 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;
- } else {
- 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;
- }
- }
- /**
- * Returns hex value for given char
- */
- private static int charVal(char c) {
- return (int) (((c >= '0') && (c <= '9')) ? (c - '0')
- : (((c >= 'A') && (c <= 'Z'))
- ? (c - 'A' + 10) : (c - 'a' + 10)));
- }
- 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");
- }
- buf.append(longHex);
- return buf.toString();
- } else {
- return longHex.substring(0, 8);
- }
- }
- // 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) throws NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
-
- byte[] passwordHashStage1 = md.digest(password.getBytes());
- md.reset();
- byte[] passwordHashStage2 = md.digest(passwordHashStage1);
- md.reset();
- byte[] seedAsBytes = seed.getBytes(); // 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;
-
- }
- }