⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 knownhosts.java

📁 thinking in java4 src
💻 JAVA
📖 第 1 页 / 共 2 页
字号:

package ch.ethz.ssh2;

import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;

import ch.ethz.ssh2.crypto.Base64;
import ch.ethz.ssh2.crypto.digest.Digest;
import ch.ethz.ssh2.crypto.digest.HMAC;
import ch.ethz.ssh2.crypto.digest.MD5;
import ch.ethz.ssh2.crypto.digest.SHA1;
import ch.ethz.ssh2.signature.DSAPublicKey;
import ch.ethz.ssh2.signature.DSASHA1Verify;
import ch.ethz.ssh2.signature.RSAPublicKey;
import ch.ethz.ssh2.signature.RSASHA1Verify;

/**
 * The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
 * based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
 * <p>
 * It offers basically an in-memory database for known_hosts entries, as well as some
 * helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
 * It is also possible to add more keys later (e.g., one can parse different
 * <code>known_hosts<code> files).
 * <p>
 * It is a thread safe implementation, therefore, you need only to instantiate one
 * <code>KnownHosts</code> for your whole application.
 * 
 * @author Christian Plattner, plattner@inf.ethz.ch
 * @version $Id: KnownHosts.java,v 1.4 2006/02/14 19:43:16 cplattne Exp $
 */

public class KnownHosts
{
	public static final int HOSTKEY_IS_OK = 0;
	public static final int HOSTKEY_IS_NEW = 1;
	public static final int HOSTKEY_HAS_CHANGED = 2;

	private class KnownHostsEntry
	{
		String[] patterns;
		Object key;

		KnownHostsEntry(String[] patterns, Object key)
		{
			this.patterns = patterns;
			this.key = key;
		}
	}

	private LinkedList publicKeys = new LinkedList();

	public KnownHosts()
	{
	}

	public KnownHosts(char[] knownHostsData) throws IOException
	{
		initialize(knownHostsData);
	}

	public KnownHosts(File knownHosts) throws IOException
	{
		initialize(knownHosts);
	}

	/**
	 * Adds a single public key entry to the database. Note: this will NOT add the public key
	 * to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> for that purpose.
	 * This method is designed to be used in a {@link ServerHostKeyVerifier}.
	 * 
	 * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
	 *        OpenSSH sshd man page for a description of the pattern matching algorithm.
	 * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
	 * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
	 * @throws IOException
	 */
	public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
	{
		if (hostnames == null)
			throw new IllegalArgumentException("hostnames may not be null");

		if ("ssh-rsa".equals(serverHostKeyAlgorithm))
		{
			RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);

			synchronized (publicKeys)
			{
				publicKeys.add(new KnownHostsEntry(hostnames, rpk));
			}
		}
		else if ("ssh-dss".equals(serverHostKeyAlgorithm))
		{
			DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);

			synchronized (publicKeys)
			{
				publicKeys.add(new KnownHostsEntry(hostnames, dpk));
			}
		}
		else
			throw new IOException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
	}

	/**
	 * Parses the given known_hosts data and adds entries to the database.
	 * 
	 * @param knownHostsData
	 * @throws IOException
	 */
	public void addHostkeys(char[] knownHostsData) throws IOException
	{
		initialize(knownHostsData);
	}

	/**
	 * Parses the given known_hosts file and adds entries to the database.
	 * 
	 * @param knownHosts
	 * @throws IOException
	 */
	public void addHostkeys(File knownHosts) throws IOException
	{
		initialize(knownHosts);
	}

	/**
	 * Generate the hashed representation of the given hostname. Useful for adding entries
	 * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
	 *  
	 * @param hostname
	 * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
	 */
	public static final String createHashedHostname(String hostname)
	{
		SHA1 sha1 = new SHA1();

		byte[] salt = new byte[sha1.getDigestLength()];

		new SecureRandom().nextBytes(salt);

		byte[] hash = hmacSha1Hash(salt, hostname);

		String base64_salt = new String(Base64.encode(salt));
		String base64_hash = new String(Base64.encode(hash));

		return new String("|1|" + base64_salt + "|" + base64_hash);
	}

	private static final byte[] hmacSha1Hash(byte[] salt, String hostname)
	{
		SHA1 sha1 = new SHA1();

		if (salt.length != sha1.getDigestLength())
			throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");

		HMAC hmac = new HMAC(sha1, salt, salt.length);

		hmac.update(hostname.getBytes());

		byte[] dig = new byte[hmac.getDigestLength()];

		hmac.digest(dig);

		return dig;
	}

	private final boolean checkHashed(String entry, String hostname)
	{
		if (entry.startsWith("|1|") == false)
			return false;

		int delim_idx = entry.indexOf('|', 3);

		if (delim_idx == -1)
			return false;

		String salt_base64 = entry.substring(3, delim_idx);
		String hash_base64 = entry.substring(delim_idx + 1);

		byte[] salt = null;
		byte[] hash = null;

		try
		{
			salt = Base64.decode(salt_base64.toCharArray());
			hash = Base64.decode(hash_base64.toCharArray());
		}
		catch (IOException e)
		{
			return false;
		}

		SHA1 sha1 = new SHA1();

		if (salt.length != sha1.getDigestLength())
			return false;

		byte[] dig = hmacSha1Hash(salt, hostname);

		for (int i = 0; i < dig.length; i++)
			if (dig[i] != hash[i])
				return false;

		return true;
	}

	private int checkKey(String remoteHostname, Object remoteKey)
	{
		int result = HOSTKEY_IS_NEW;

		synchronized (publicKeys)
		{
			Iterator i = publicKeys.iterator();
			
			while (i.hasNext())
			{
				KnownHostsEntry ke = (KnownHostsEntry) i.next();

				if (hostnameMatches(ke.patterns, remoteHostname) == false)
					continue;

				boolean res = matchKeys(ke.key, remoteKey);

				if (res == true)
					return HOSTKEY_IS_OK;

				result = HOSTKEY_HAS_CHANGED;
			}
		}
		return result;
	}

	private Vector getAllKeys(String hostname)
	{
		Vector keys = new Vector();

		synchronized (publicKeys)
		{
			Iterator i = publicKeys.iterator();

			while (i.hasNext())
			{
				KnownHostsEntry ke = (KnownHostsEntry) i.next();

				if (hostnameMatches(ke.patterns, hostname) == false)
					continue;

				keys.addElement(ke.key);
			}
		}

		return keys;
	}

	/**
	 * Try to find the preferred order of hostkey algorithms for the given hostname.
	 * Based on the type of hostkey that is present in the internal database
	 * (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
	 * an ordered list of hostkey algorithms is returned which can be passed
	 * to <code>Connection.setServerHostKeyAlgorithms</code>. 
	 * 
	 * @param hostname
	 * @return <code>null</code> if no key for the given hostname is present or
	 * there are keys of multiple types present for the given hostname. Otherwise,
	 * an array with hostkey algorithms is returned (i.e., an array of length 2).
	 */
	public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname)
	{
		String[] algos = recommendHostkeyAlgorithms(hostname);

		if (algos != null)
			return algos;

		InetAddress[] ipAdresses = null;

		try
		{
			ipAdresses = InetAddress.getAllByName(hostname);
		}
		catch (UnknownHostException e)
		{
			return null;
		}

		for (int i = 0; i < ipAdresses.length; i++)
		{
			algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress());

			if (algos != null)
				return algos;
		}

		return null;
	}

	private final boolean hostnameMatches(String[] hostpatterns, String hostname)
	{
		boolean isMatch = false;
		boolean negate = false;

		hostname = hostname.toLowerCase();

		for (int k = 0; k < hostpatterns.length; k++)
		{
			if (hostpatterns[k] == null)
				continue;

			String pattern = null;

			/* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
			 * entries in lines with multiple entries).
			 */

			if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!'))
			{
				pattern = hostpatterns[k].substring(1);
				negate = true;
			}
			else
			{
				pattern = hostpatterns[k];
				negate = false;
			}

			/* Optimize, no need to check this entry */

			if ((isMatch) && (negate == false))
				continue;

			/* Now compare */

			if (pattern.charAt(0) == '|')
			{
				if (checkHashed(pattern, hostname))
				{
					if (negate)
						return false;
					isMatch = true;
				}
			}
			else
			{
				pattern = pattern.toLowerCase();

				if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1))
				{
					if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0))
					{
						if (negate)
							return false;
						isMatch = true;
					}
				}
				else if (pattern.compareTo(hostname) == 0)
				{
					if (negate)
						return false;
					isMatch = true;
				}
			}
		}

		return isMatch;
	}

	private void initialize(char[] knownHostsData) throws IOException
	{
		BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));

		while (true)
		{
			String line = br.readLine();

			if (line == null)
				break;

			line = line.trim();

			if (line.startsWith("#"))
				continue;

			String[] arr = line.split(" ");

			if (arr.length >= 3)
			{
				if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0))
				{
					String[] hostnames = arr[0].split(",");

					byte[] msg = Base64.decode(arr[2].toCharArray());

					addHostkey(hostnames, arr[1], msg);
				}
			}
		}
	}

	private void initialize(File knownHosts) throws IOException
	{
		char[] buff = new char[512];

		CharArrayWriter cw = new CharArrayWriter();

		knownHosts.createNewFile();

		FileReader fr = new FileReader(knownHosts);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -