Home | History | Annotate | Download | only in transport
      1 /*
      2  * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
      3  * Please refer to the LICENSE.txt for licensing details.
      4  */
      5 package ch.ethz.ssh2.transport;
      6 
      7 import java.io.IOException;
      8 import java.security.SecureRandom;
      9 
     10 import ch.ethz.ssh2.ConnectionInfo;
     11 import ch.ethz.ssh2.DHGexParameters;
     12 import ch.ethz.ssh2.ServerHostKeyVerifier;
     13 import ch.ethz.ssh2.crypto.CryptoWishList;
     14 import ch.ethz.ssh2.crypto.KeyMaterial;
     15 import ch.ethz.ssh2.crypto.cipher.BlockCipher;
     16 import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
     17 import ch.ethz.ssh2.crypto.dh.DhExchange;
     18 import ch.ethz.ssh2.crypto.dh.DhGroupExchange;
     19 import ch.ethz.ssh2.crypto.digest.MAC;
     20 import ch.ethz.ssh2.log.Logger;
     21 import ch.ethz.ssh2.packets.PacketKexDHInit;
     22 import ch.ethz.ssh2.packets.PacketKexDHReply;
     23 import ch.ethz.ssh2.packets.PacketKexDhGexGroup;
     24 import ch.ethz.ssh2.packets.PacketKexDhGexInit;
     25 import ch.ethz.ssh2.packets.PacketKexDhGexReply;
     26 import ch.ethz.ssh2.packets.PacketKexDhGexRequest;
     27 import ch.ethz.ssh2.packets.PacketKexDhGexRequestOld;
     28 import ch.ethz.ssh2.packets.PacketKexInit;
     29 import ch.ethz.ssh2.packets.PacketNewKeys;
     30 import ch.ethz.ssh2.packets.Packets;
     31 import ch.ethz.ssh2.signature.DSAPublicKey;
     32 import ch.ethz.ssh2.signature.DSASHA1Verify;
     33 import ch.ethz.ssh2.signature.DSASignature;
     34 import ch.ethz.ssh2.signature.RSAPublicKey;
     35 import ch.ethz.ssh2.signature.RSASHA1Verify;
     36 import ch.ethz.ssh2.signature.RSASignature;
     37 
     38 /**
     39  * KexManager.
     40  *
     41  * @author Christian Plattner
     42  * @version $Id: KexManager.java 45 2011-07-01 15:09:41Z dkocher (at) sudo.ch $
     43  */
     44 public class KexManager
     45 {
     46 	private static final Logger log = Logger.getLogger(KexManager.class);
     47 
     48 	KexState kxs;
     49 	int kexCount = 0;
     50 	KeyMaterial km;
     51 	byte[] sessionId;
     52 	ClientServerHello csh;
     53 
     54 	final Object accessLock = new Object();
     55 	ConnectionInfo lastConnInfo = null;
     56 
     57 	boolean connectionClosed = false;
     58 
     59 	boolean ignore_next_kex_packet = false;
     60 
     61 	final TransportManager tm;
     62 
     63 	CryptoWishList nextKEXcryptoWishList;
     64 	DHGexParameters nextKEXdhgexParameters;
     65 
     66 	ServerHostKeyVerifier verifier;
     67 	final String hostname;
     68 	final int port;
     69 	final SecureRandom rnd;
     70 
     71 	public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
     72 			ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
     73 	{
     74 		this.tm = tm;
     75 		this.csh = csh;
     76 		this.nextKEXcryptoWishList = initialCwl;
     77 		this.nextKEXdhgexParameters = new DHGexParameters();
     78 		this.hostname = hostname;
     79 		this.port = port;
     80 		this.verifier = keyVerifier;
     81 		this.rnd = rnd;
     82 	}
     83 
     84 	public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
     85 	{
     86 		boolean wasInterrupted = false;
     87 
     88 		try
     89 		{
     90 			synchronized (accessLock)
     91 			{
     92 				while (true)
     93 				{
     94 					if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
     95 						return lastConnInfo;
     96 
     97 					if (connectionClosed)
     98 						throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
     99 								.initCause(tm.getReasonClosedCause());
    100 
    101 					try
    102 					{
    103 						accessLock.wait();
    104 					}
    105 					catch (InterruptedException e)
    106 					{
    107 						wasInterrupted = true;
    108 					}
    109 				}
    110 			}
    111 		}
    112 		finally
    113 		{
    114 			if (wasInterrupted)
    115 				Thread.currentThread().interrupt();
    116 		}
    117 	}
    118 
    119 	private String getFirstMatch(String[] client, String[] server) throws NegotiateException
    120 	{
    121 		if (client == null || server == null)
    122 			throw new IllegalArgumentException();
    123 
    124 		if (client.length == 0)
    125 			return null;
    126 
    127 		for (int i = 0; i < client.length; i++)
    128 		{
    129 			for (int j = 0; j < server.length; j++)
    130 			{
    131 				if (client[i].equals(server[j]))
    132 					return client[i];
    133 			}
    134 		}
    135 		throw new NegotiateException();
    136 	}
    137 
    138 	private boolean compareFirstOfNameList(String[] a, String[] b)
    139 	{
    140 		if (a == null || b == null)
    141 			throw new IllegalArgumentException();
    142 
    143 		if ((a.length == 0) && (b.length == 0))
    144 			return true;
    145 
    146 		if ((a.length == 0) || (b.length == 0))
    147 			return false;
    148 
    149 		return (a[0].equals(b[0]));
    150 	}
    151 
    152 	private boolean isGuessOK(KexParameters cpar, KexParameters spar)
    153 	{
    154 		if (cpar == null || spar == null)
    155 			throw new IllegalArgumentException();
    156 
    157 		if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
    158 		{
    159 			return false;
    160 		}
    161 
    162 		if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
    163 		{
    164 			return false;
    165 		}
    166 
    167 		/*
    168 		 * We do NOT check here if the other algorithms can be agreed on, this
    169 		 * is just a check if kex_algorithms and server_host_key_algorithms were
    170 		 * guessed right!
    171 		 */
    172 
    173 		return true;
    174 	}
    175 
    176 	private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
    177 	{
    178 		NegotiatedParameters np = new NegotiatedParameters();
    179 
    180 		try
    181 		{
    182 			np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
    183 
    184 			log.info("kex_algo=" + np.kex_algo);
    185 
    186 			np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
    187 					server.server_host_key_algorithms);
    188 
    189 			log.info("server_host_key_algo=" + np.server_host_key_algo);
    190 
    191 			np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
    192 					server.encryption_algorithms_client_to_server);
    193 			np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
    194 					server.encryption_algorithms_server_to_client);
    195 
    196 			log.info("enc_algo_client_to_server=" + np.enc_algo_client_to_server);
    197 			log.info("enc_algo_server_to_client=" + np.enc_algo_server_to_client);
    198 
    199 			np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
    200 					server.mac_algorithms_client_to_server);
    201 			np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
    202 					server.mac_algorithms_server_to_client);
    203 
    204 			log.info("mac_algo_client_to_server=" + np.mac_algo_client_to_server);
    205 			log.info("mac_algo_server_to_client=" + np.mac_algo_server_to_client);
    206 
    207 			np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
    208 					server.compression_algorithms_client_to_server);
    209 			np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
    210 					server.compression_algorithms_server_to_client);
    211 
    212 			log.info("comp_algo_client_to_server=" + np.comp_algo_client_to_server);
    213 			log.info("comp_algo_server_to_client=" + np.comp_algo_server_to_client);
    214 
    215 		}
    216 		catch (NegotiateException e)
    217 		{
    218 			return null;
    219 		}
    220 
    221 		try
    222 		{
    223 			np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
    224 					server.languages_client_to_server);
    225 		}
    226 		catch (NegotiateException e1)
    227 		{
    228 			np.lang_client_to_server = null;
    229 		}
    230 
    231 		try
    232 		{
    233 			np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
    234 					server.languages_server_to_client);
    235 		}
    236 		catch (NegotiateException e2)
    237 		{
    238 			np.lang_server_to_client = null;
    239 		}
    240 
    241 		if (isGuessOK(client, server))
    242 			np.guessOK = true;
    243 
    244 		return np;
    245 	}
    246 
    247 	public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
    248 	{
    249 		nextKEXcryptoWishList = cwl;
    250 		nextKEXdhgexParameters = dhgex;
    251 
    252 		if (kxs == null)
    253 		{
    254 			kxs = new KexState();
    255 
    256 			kxs.dhgexParameters = nextKEXdhgexParameters;
    257 			PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
    258 			kxs.localKEX = kp;
    259 			tm.sendKexMessage(kp.getPayload());
    260 		}
    261 	}
    262 
    263 	private boolean establishKeyMaterial()
    264 	{
    265 		try
    266 		{
    267 			int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
    268 			int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
    269 			int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
    270 
    271 			int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
    272 			int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
    273 			int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
    274 
    275 			km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
    276 					enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
    277 		}
    278 		catch (IllegalArgumentException e)
    279 		{
    280 			return false;
    281 		}
    282 		return true;
    283 	}
    284 
    285 	private void finishKex() throws IOException
    286 	{
    287 		if (sessionId == null)
    288 			sessionId = kxs.H;
    289 
    290 		establishKeyMaterial();
    291 
    292 		/* Tell the other side that we start using the new material */
    293 
    294 		PacketNewKeys ign = new PacketNewKeys();
    295 		tm.sendKexMessage(ign.getPayload());
    296 
    297 		BlockCipher cbc;
    298 		MAC mac;
    299 
    300 		try
    301 		{
    302 			cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
    303 					km.initial_iv_client_to_server);
    304 
    305 			mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
    306 
    307 		}
    308 		catch (IllegalArgumentException e1)
    309 		{
    310 			throw new IOException("Fatal error during MAC startup!");
    311 		}
    312 
    313 		tm.changeSendCipher(cbc, mac);
    314 		tm.kexFinished();
    315 	}
    316 
    317 	public static String[] getDefaultServerHostkeyAlgorithmList()
    318 	{
    319 		return new String[] { "ssh-rsa", "ssh-dss" };
    320 	}
    321 
    322 	public static void checkServerHostkeyAlgorithmsList(String[] algos)
    323 	{
    324 		for (int i = 0; i < algos.length; i++)
    325 		{
    326 			if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
    327 				throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
    328 		}
    329 	}
    330 
    331 	public static String[] getDefaultKexAlgorithmList()
    332 	{
    333 		return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
    334 				"diffie-hellman-group1-sha1" };
    335 	}
    336 
    337 	public static void checkKexAlgorithmList(String[] algos)
    338 	{
    339 		for (int i = 0; i < algos.length; i++)
    340 		{
    341 			if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
    342 				continue;
    343 
    344 			if ("diffie-hellman-group14-sha1".equals(algos[i]))
    345 				continue;
    346 
    347 			if ("diffie-hellman-group1-sha1".equals(algos[i]))
    348 				continue;
    349 
    350 			throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
    351 		}
    352 	}
    353 
    354 	private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
    355 	{
    356 		if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
    357 		{
    358 			RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
    359 			RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
    360 
    361 			log.debug("Verifying ssh-rsa signature");
    362 
    363 			return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
    364 		}
    365 
    366 		if (kxs.np.server_host_key_algo.equals("ssh-dss"))
    367 		{
    368 			DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
    369 			DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
    370 
    371 			log.debug("Verifying ssh-dss signature");
    372 
    373 			return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
    374 		}
    375 
    376 		throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
    377 	}
    378 
    379 	public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
    380 	{
    381 		PacketKexInit kip;
    382 
    383 		if (msg == null)
    384 		{
    385 			synchronized (accessLock)
    386 			{
    387 				connectionClosed = true;
    388 				accessLock.notifyAll();
    389 				return;
    390 			}
    391 		}
    392 
    393 		if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
    394 			throw new IOException("Unexpected KEX message (type " + msg[0] + ")");
    395 
    396 		if (ignore_next_kex_packet)
    397 		{
    398 			ignore_next_kex_packet = false;
    399 			return;
    400 		}
    401 
    402 		if (msg[0] == Packets.SSH_MSG_KEXINIT)
    403 		{
    404 			if ((kxs != null) && (kxs.state != 0))
    405 				throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");
    406 
    407 			if (kxs == null)
    408 			{
    409 				/*
    410 				 * Ah, OK, peer wants to do KEX. Let's be nice and play
    411 				 * together.
    412 				 */
    413 				kxs = new KexState();
    414 				kxs.dhgexParameters = nextKEXdhgexParameters;
    415 				kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
    416 				kxs.localKEX = kip;
    417 				tm.sendKexMessage(kip.getPayload());
    418 			}
    419 
    420 			kip = new PacketKexInit(msg, 0, msglen);
    421 			kxs.remoteKEX = kip;
    422 
    423 			kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
    424 
    425 			if (kxs.np == null)
    426 				throw new IOException("Cannot negotiate, proposals do not match.");
    427 
    428 			if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
    429 			{
    430 				/*
    431 				 * Guess was wrong, we need to ignore the next kex packet.
    432 				 */
    433 
    434 				ignore_next_kex_packet = true;
    435 			}
    436 
    437 			if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
    438 			{
    439 				if (kxs.dhgexParameters.getMin_group_len() == 0)
    440 				{
    441 					PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
    442 					tm.sendKexMessage(dhgexreq.getPayload());
    443 
    444 				}
    445 				else
    446 				{
    447 					PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
    448 					tm.sendKexMessage(dhgexreq.getPayload());
    449 				}
    450 				kxs.state = 1;
    451 				return;
    452 			}
    453 
    454 			if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
    455 					|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
    456 			{
    457 				kxs.dhx = new DhExchange();
    458 
    459 				if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
    460 					kxs.dhx.init(1, rnd);
    461 				else
    462 					kxs.dhx.init(14, rnd);
    463 
    464 				PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
    465 				tm.sendKexMessage(kp.getPayload());
    466 				kxs.state = 1;
    467 				return;
    468 			}
    469 
    470 			throw new IllegalStateException("Unkown KEX method!");
    471 		}
    472 
    473 		if (msg[0] == Packets.SSH_MSG_NEWKEYS)
    474 		{
    475 			if (km == null)
    476 				throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
    477 
    478 			BlockCipher cbc;
    479 			MAC mac;
    480 
    481 			try
    482 			{
    483 				cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
    484 						km.enc_key_server_to_client, km.initial_iv_server_to_client);
    485 
    486 				mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
    487 
    488 			}
    489 			catch (IllegalArgumentException e1)
    490 			{
    491 				throw new IOException("Fatal error during MAC startup!");
    492 			}
    493 
    494 			tm.changeRecvCipher(cbc, mac);
    495 
    496 			ConnectionInfo sci = new ConnectionInfo();
    497 
    498 			kexCount++;
    499 
    500 			sci.keyExchangeAlgorithm = kxs.np.kex_algo;
    501 			sci.keyExchangeCounter = kexCount;
    502 			sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
    503 			sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
    504 			sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
    505 			sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
    506 			sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
    507 			sci.serverHostKey = kxs.hostkey;
    508 
    509 			synchronized (accessLock)
    510 			{
    511 				lastConnInfo = sci;
    512 				accessLock.notifyAll();
    513 			}
    514 
    515 			kxs = null;
    516 			return;
    517 		}
    518 
    519 		if ((kxs == null) || (kxs.state == 0))
    520 			throw new IOException("Unexpected Kex submessage!");
    521 
    522 		if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
    523 		{
    524 			if (kxs.state == 1)
    525 			{
    526 				PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
    527 				kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
    528 				kxs.dhgx.init(rnd);
    529 				PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
    530 				tm.sendKexMessage(dhgexinit.getPayload());
    531 				kxs.state = 2;
    532 				return;
    533 			}
    534 
    535 			if (kxs.state == 2)
    536 			{
    537 				PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);
    538 
    539 				kxs.hostkey = dhgexrpl.getHostKey();
    540 
    541 				if (verifier != null)
    542 				{
    543 					boolean vres = false;
    544 
    545 					try
    546 					{
    547 						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
    548 					}
    549 					catch (Exception e)
    550 					{
    551 						throw (IOException) new IOException(
    552 								"The server hostkey was not accepted by the verifier callback.").initCause(e);
    553 					}
    554 
    555 					if (vres == false)
    556 						throw new IOException("The server hostkey was not accepted by the verifier callback");
    557 				}
    558 
    559 				kxs.dhgx.setF(dhgexrpl.getF());
    560 
    561 				try
    562 				{
    563 					kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
    564 							kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
    565 							kxs.dhgexParameters);
    566 				}
    567 				catch (IllegalArgumentException e)
    568 				{
    569 					throw (IOException) new IOException("KEX error.").initCause(e);
    570 				}
    571 
    572 				boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);
    573 
    574 				if (res == false)
    575 					throw new IOException("Hostkey signature sent by remote is wrong!");
    576 
    577 				kxs.K = kxs.dhgx.getK();
    578 
    579 				finishKex();
    580 				kxs.state = -1;
    581 				return;
    582 			}
    583 
    584 			throw new IllegalStateException("Illegal State in KEX Exchange!");
    585 		}
    586 
    587 		if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
    588 				|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
    589 		{
    590 			if (kxs.state == 1)
    591 			{
    592 
    593 				PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);
    594 
    595 				kxs.hostkey = dhr.getHostKey();
    596 
    597 				if (verifier != null)
    598 				{
    599 					boolean vres = false;
    600 
    601 					try
    602 					{
    603 						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
    604 					}
    605 					catch (Exception e)
    606 					{
    607 						throw (IOException) new IOException(
    608 								"The server hostkey was not accepted by the verifier callback.").initCause(e);
    609 					}
    610 
    611 					if (vres == false)
    612 						throw new IOException("The server hostkey was not accepted by the verifier callback");
    613 				}
    614 
    615 				kxs.dhx.setF(dhr.getF());
    616 
    617 				try
    618 				{
    619 					kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
    620 							kxs.remoteKEX.getPayload(), dhr.getHostKey());
    621 				}
    622 				catch (IllegalArgumentException e)
    623 				{
    624 					throw (IOException) new IOException("KEX error.").initCause(e);
    625 				}
    626 
    627 				boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);
    628 
    629 				if (res == false)
    630 					throw new IOException("Hostkey signature sent by remote is wrong!");
    631 
    632 				kxs.K = kxs.dhx.getK();
    633 
    634 				finishKex();
    635 				kxs.state = -1;
    636 				return;
    637 			}
    638 		}
    639 
    640 		throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
    641 	}
    642 }
    643