Mercurial > 510Connectbot
comparison app/src/main/java/ch/ethz/ssh2/transport/KexManager.java @ 438:d29cce60f393
migrate from Eclipse to Android Studio
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 03 Dec 2015 11:23:55 -0800 |
parents | src/ch/ethz/ssh2/transport/KexManager.java@597138203c15 |
children | 7953570e5210 |
comparison
equal
deleted
inserted
replaced
437:208b31032318 | 438:d29cce60f393 |
---|---|
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.io.InterruptedIOException; | |
9 import java.security.DigestException; | |
10 import java.security.KeyPair; | |
11 import java.security.PrivateKey; | |
12 import java.security.SecureRandom; | |
13 import java.security.interfaces.DSAPrivateKey; | |
14 import java.security.interfaces.RSAPrivateKey; | |
15 import java.security.interfaces.ECPrivateKey; | |
16 import java.util.Arrays; | |
17 import java.util.ArrayList; | |
18 | |
19 import ch.ethz.ssh2.ConnectionInfo; | |
20 import ch.ethz.ssh2.DHGexParameters; | |
21 import ch.ethz.ssh2.compression.CompressionFactory; | |
22 import ch.ethz.ssh2.compression.Compressor; | |
23 import ch.ethz.ssh2.crypto.CryptoWishList; | |
24 import ch.ethz.ssh2.crypto.KeyMaterial; | |
25 import ch.ethz.ssh2.crypto.cipher.BlockCipher; | |
26 import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory; | |
27 import ch.ethz.ssh2.crypto.digest.MAC; | |
28 import ch.ethz.ssh2.log.Logger; | |
29 import ch.ethz.ssh2.packets.PacketKexInit; | |
30 import ch.ethz.ssh2.packets.PacketNewKeys; | |
31 import java.security.interfaces.DSAPrivateKey; | |
32 import java.security.interfaces.RSAPrivateKey; | |
33 import java.security.interfaces.ECPrivateKey; | |
34 | |
35 /** | |
36 * @version $Id: KexManager.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $ | |
37 */ | |
38 public abstract class KexManager implements MessageHandler { | |
39 protected static final Logger log = Logger.getLogger(KexManager.class); | |
40 | |
41 private static final ArrayList<String> HOSTKEY_ALGS = new ArrayList<String>(); | |
42 static { | |
43 HOSTKEY_ALGS.add("ssh-rsa"); | |
44 HOSTKEY_ALGS.add("ssh-dss"); | |
45 HOSTKEY_ALGS.add("ecdsa-sha2-nistp256"); | |
46 HOSTKEY_ALGS.add("ecdsa-sha2-nistp384"); | |
47 HOSTKEY_ALGS.add("ecdsa-sha2-nistp521"); | |
48 } | |
49 | |
50 private static final ArrayList<String> KEX_ALGS = new ArrayList<String>(); | |
51 static { | |
52 KEX_ALGS.add("diffie-hellman-group-exchange-sha256"); | |
53 KEX_ALGS.add("diffie-hellman-group-exchange-sha1"); | |
54 KEX_ALGS.add("diffie-hellman-group14-sha1"); | |
55 KEX_ALGS.add("diffie-hellman-group1-sha1"); | |
56 KEX_ALGS.add("ecdh-sha2-nistp256"); | |
57 KEX_ALGS.add("ecdh-sha2-nistp384"); | |
58 KEX_ALGS.add("ecdh-sha2-nistp521"); | |
59 } | |
60 | |
61 KexState kxs; | |
62 int kexCount = 0; | |
63 KeyMaterial km; | |
64 byte[] sessionId; | |
65 ClientServerHello csh; | |
66 | |
67 final Object accessLock = new Object(); | |
68 ConnectionInfo lastConnInfo = null; | |
69 | |
70 boolean connectionClosed = false; | |
71 | |
72 boolean ignore_next_kex_packet = false; | |
73 | |
74 final TransportManager tm; | |
75 | |
76 CryptoWishList nextKEXcryptoWishList; | |
77 DHGexParameters nextKEXdhgexParameters; | |
78 KeyPair nextKEXdsakey; | |
79 KeyPair nextKEXrsakey; | |
80 KeyPair nextKEXeckey; | |
81 | |
82 final SecureRandom rnd; | |
83 | |
84 public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, SecureRandom rnd) { | |
85 this.tm = tm; | |
86 this.csh = csh; | |
87 this.nextKEXcryptoWishList = initialCwl; | |
88 this.nextKEXdhgexParameters = new DHGexParameters(); | |
89 this.rnd = rnd; | |
90 } | |
91 | |
92 public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException { | |
93 synchronized (accessLock) { | |
94 while (true) { | |
95 if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount)) { | |
96 return lastConnInfo; | |
97 } | |
98 | |
99 if (connectionClosed) { | |
100 throw(IOException) new IOException("Key exchange was not finished, connection is closed.").initCause(tm.getReasonClosedCause()); | |
101 } | |
102 | |
103 try { | |
104 accessLock.wait(); | |
105 } | |
106 catch (InterruptedException e) { | |
107 throw new InterruptedIOException(e.getMessage()); | |
108 } | |
109 } | |
110 } | |
111 } | |
112 | |
113 private String getFirstMatch(String[] client, String[] server) throws NegotiateException { | |
114 if (client == null || server == null) { | |
115 throw new IllegalArgumentException(); | |
116 } | |
117 | |
118 for (String c : client) { | |
119 for (String s : server) { | |
120 if (c.equals(s)) { | |
121 return c; | |
122 } | |
123 } | |
124 } | |
125 | |
126 throw new NegotiateException(String.format("Negotiation failed for %s", Arrays.toString(server))); | |
127 } | |
128 | |
129 private boolean compareFirstOfNameList(String[] a, String[] b) { | |
130 if (a == null || b == null) { | |
131 throw new IllegalArgumentException(); | |
132 } | |
133 | |
134 if ((a.length == 0) && (b.length == 0)) { | |
135 return true; | |
136 } | |
137 | |
138 if ((a.length == 0) || (b.length == 0)) { | |
139 return false; | |
140 } | |
141 | |
142 return (a[0].equals(b[0])); | |
143 } | |
144 | |
145 private boolean isGuessOK(KexParameters cpar, KexParameters spar) { | |
146 if (cpar == null || spar == null) { | |
147 throw new IllegalArgumentException(); | |
148 } | |
149 | |
150 if (!compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms)) { | |
151 return false; | |
152 } | |
153 | |
154 if (!compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms)) { | |
155 return false; | |
156 } | |
157 | |
158 /* | |
159 * We do NOT check here if the other algorithms can be agreed on, this | |
160 * is just a check if kex_algorithms and server_host_key_algorithms were | |
161 * guessed right! | |
162 */ | |
163 return true; | |
164 } | |
165 | |
166 protected NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server) | |
167 throws NegotiateException { | |
168 NegotiatedParameters np = new NegotiatedParameters(); | |
169 np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms); | |
170 log.info("kex_algo=" + np.kex_algo); | |
171 np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms, | |
172 server.server_host_key_algorithms); | |
173 log.info("server_host_key_algo=" + np.server_host_key_algo); | |
174 np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server, | |
175 server.encryption_algorithms_client_to_server); | |
176 np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client, | |
177 server.encryption_algorithms_server_to_client); | |
178 log.info("enc_algo_client_to_server=" + np.enc_algo_client_to_server); | |
179 log.info("enc_algo_server_to_client=" + np.enc_algo_server_to_client); | |
180 np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server, | |
181 server.mac_algorithms_client_to_server); | |
182 np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client, | |
183 server.mac_algorithms_server_to_client); | |
184 log.info("mac_algo_client_to_server=" + np.mac_algo_client_to_server); | |
185 log.info("mac_algo_server_to_client=" + np.mac_algo_server_to_client); | |
186 np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server, | |
187 server.compression_algorithms_client_to_server); | |
188 np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client, | |
189 server.compression_algorithms_server_to_client); | |
190 log.info("comp_algo_client_to_server=" + np.comp_algo_client_to_server); | |
191 log.info("comp_algo_server_to_client=" + np.comp_algo_server_to_client); | |
192 np.lang_client_to_server = getFirstMatch(client.languages_client_to_server, | |
193 server.languages_client_to_server); | |
194 np.lang_server_to_client = getFirstMatch(client.languages_server_to_client, | |
195 server.languages_server_to_client); | |
196 | |
197 if (isGuessOK(client, server)) { | |
198 np.guessOK = true; | |
199 } | |
200 | |
201 return np; | |
202 } | |
203 | |
204 public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex, KeyPair dsa, KeyPair rsa, KeyPair ec) | |
205 throws IOException { | |
206 nextKEXcryptoWishList = cwl; | |
207 nextKEXdhgexParameters = dhgex; | |
208 nextKEXdsakey = dsa; | |
209 nextKEXrsakey = rsa; | |
210 nextKEXeckey = ec; | |
211 | |
212 if (kxs == null) { | |
213 kxs = new KexState(); | |
214 kxs.local_dsa_key = dsa; | |
215 kxs.local_rsa_key = rsa; | |
216 kxs.local_ec_key = ec; | |
217 kxs.dhgexParameters = nextKEXdhgexParameters; | |
218 kxs.localKEX = new PacketKexInit(nextKEXcryptoWishList, rnd); | |
219 tm.sendKexMessage(kxs.localKEX.getPayload()); | |
220 } | |
221 } | |
222 | |
223 private boolean establishKeyMaterial() throws IOException { | |
224 try { | |
225 int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server); | |
226 int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server); | |
227 int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server); | |
228 int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client); | |
229 int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client); | |
230 int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client); | |
231 km = KeyMaterial.create(kxs.hashAlgo, kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len, | |
232 enc_sc_key_len, enc_sc_block_len, mac_sc_key_len); | |
233 } | |
234 catch (IllegalArgumentException e) { | |
235 return false; | |
236 } | |
237 | |
238 return true; | |
239 } | |
240 | |
241 protected void finishKex(boolean clientMode) throws IOException { | |
242 if (sessionId == null) { | |
243 sessionId = kxs.H; | |
244 } | |
245 | |
246 establishKeyMaterial(); | |
247 /* Tell the other side that we start using the new material */ | |
248 PacketNewKeys ign = new PacketNewKeys(); | |
249 tm.sendKexMessage(ign.getPayload()); | |
250 BlockCipher cbc; | |
251 MAC mac; | |
252 Compressor comp; | |
253 | |
254 try { | |
255 cbc = BlockCipherFactory.createCipher(clientMode ? kxs.np.enc_algo_client_to_server | |
256 : kxs.np.enc_algo_server_to_client, true, clientMode ? km.enc_key_client_to_server | |
257 : km.enc_key_server_to_client, clientMode ? km.initial_iv_client_to_server | |
258 : km.initial_iv_server_to_client); | |
259 | |
260 try { | |
261 mac = new MAC(clientMode ? kxs.np.mac_algo_client_to_server : kxs.np.mac_algo_server_to_client, clientMode | |
262 ? km.integrity_key_client_to_server : km.integrity_key_server_to_client); | |
263 } | |
264 catch (DigestException e) { | |
265 throw new IOException(e); | |
266 } | |
267 | |
268 comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server); | |
269 } | |
270 catch (IllegalArgumentException f) { | |
271 throw new IOException(String.format("Fatal error initializing ciphers. %s", f.getMessage())); | |
272 } | |
273 | |
274 tm.changeSendCipher(cbc, mac); | |
275 tm.changeSendCompression(comp); | |
276 tm.kexFinished(); | |
277 } | |
278 | |
279 public static String[] getDefaultServerHostkeyAlgorithmList() { | |
280 return HOSTKEY_ALGS.toArray(new String[HOSTKEY_ALGS.size()]); | |
281 } | |
282 | |
283 public static void checkServerHostkeyAlgorithmsList(String[] algos) { | |
284 for (final String algo : algos) { | |
285 if (!HOSTKEY_ALGS.contains(algo)) | |
286 throw new IllegalArgumentException("Unknown server host key algorithm '" + algo + "'"); | |
287 } | |
288 } | |
289 | |
290 public static String[] getDefaultClientKexAlgorithmList() { | |
291 return KEX_ALGS.toArray(new String[KEX_ALGS.size()]); | |
292 } | |
293 | |
294 public static String[] getDefaultServerKexAlgorithmList() { | |
295 return KEX_ALGS.toArray(new String[KEX_ALGS.size()]); | |
296 } | |
297 | |
298 public static void checkKexAlgorithmList(String[] algos) { | |
299 for (final String algo : algos) { | |
300 if (!KEX_ALGS.contains(algo)) | |
301 throw new IllegalArgumentException("Unknown kex algorithm '" + algo + "'"); | |
302 } | |
303 } | |
304 } |