Mercurial > 510Connectbot
diff src/ch/ethz/ssh2/KnownHosts.java @ 308:42b15aaa7ac7 ganymed
merge
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Wed, 30 Jul 2014 14:21:50 -0700 |
parents | 071eccdff8ea |
children | b40bc65fa09a |
line wrap: on
line diff
--- a/src/ch/ethz/ssh2/KnownHosts.java Wed Jul 30 13:38:04 2014 -0700 +++ b/src/ch/ethz/ssh2/KnownHosts.java Wed Jul 30 14:21:50 2014 -0700 @@ -96,28 +96,28 @@ * @throws IOException */ public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException { - if(hostnames == null) { + if (hostnames == null) { throw new IllegalArgumentException("hostnames may not be null"); } - if("ssh-rsa".equals(serverHostKeyAlgorithm)) { + if ("ssh-rsa".equals(serverHostKeyAlgorithm)) { RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey); - synchronized(publicKeys) { + synchronized (publicKeys) { publicKeys.add(new KnownHostsEntry(hostnames, rpk)); } } - else if("ssh-dss".equals(serverHostKeyAlgorithm)) { + else if ("ssh-dss".equals(serverHostKeyAlgorithm)) { DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey); - synchronized(publicKeys) { + synchronized (publicKeys) { publicKeys.add(new KnownHostsEntry(hostnames, dpk)); } } else if (serverHostKeyAlgorithm.startsWith("ecdsa-sha2-")) { ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey); - synchronized(publicKeys) { + synchronized (publicKeys) { publicKeys.add(new KnownHostsEntry(hostnames, epk)); } } @@ -155,61 +155,54 @@ */ public static String createHashedHostname(String hostname) throws IOException { SHA1 sha1 = new SHA1(); - byte[] salt = new byte[sha1.getDigestLength()]; + new SecureRandom().nextBytes(salt); + byte[] hash; - new SecureRandom().nextBytes(salt); - - byte[] hash; try { hash = hmacSha1Hash(salt, hostname); } - catch(IOException e) { + catch (IOException e) { throw new IOException(e); } String base64_salt = new String(Base64.encode(salt)); String base64_hash = new String(Base64.encode(hash)); - return String.format("|1|%s|%s", base64_salt, base64_hash); } private static byte[] hmacSha1Hash(byte[] salt, String hostname) throws IOException { SHA1 sha1 = new SHA1(); - if(salt.length != sha1.getDigestLength()) { + if (salt.length != sha1.getDigestLength()) { throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")"); } + try { HMAC hmac = new HMAC(sha1, salt, salt.length); - hmac.update(StringEncoder.GetBytes(hostname)); - byte[] dig = new byte[hmac.getDigestLength()]; - hmac.digest(dig); - return dig; } - catch(DigestException e) { + catch (DigestException e) { throw new IOException(e); } } private boolean checkHashed(String entry, String hostname) { - if(entry.startsWith("|1|") == false) { + if (entry.startsWith("|1|") == false) { return false; } int delim_idx = entry.indexOf('|', 3); - if(delim_idx == -1) { + if (delim_idx == -1) { return false; } String salt_base64 = entry.substring(3, delim_idx); String hash_base64 = entry.substring(delim_idx + 1); - byte[] salt; byte[] hash; @@ -217,26 +210,27 @@ salt = Base64.decode(salt_base64.toCharArray()); hash = Base64.decode(hash_base64.toCharArray()); } - catch(IOException e) { + catch (IOException e) { return false; } SHA1 sha1 = new SHA1(); - if(salt.length != sha1.getDigestLength()) { + if (salt.length != sha1.getDigestLength()) { return false; } byte[] dig = new byte[0]; + try { dig = hmacSha1Hash(salt, hostname); } - catch(IOException e) { + catch (IOException e) { return false; } - for(int i = 0; i < dig.length; i++) { - if(dig[i] != hash[i]) { + for (int i = 0; i < dig.length; i++) { + if (dig[i] != hash[i]) { return false; } } @@ -247,30 +241,31 @@ private int checkKey(String remoteHostname, PublicKey remoteKey) { int result = HOSTKEY_IS_NEW; - synchronized(publicKeys) { - for(KnownHostsEntry ke : publicKeys) { - if(hostnameMatches(ke.patterns, remoteHostname) == false) { + synchronized (publicKeys) { + for (KnownHostsEntry ke : publicKeys) { + if (hostnameMatches(ke.patterns, remoteHostname) == false) { continue; } boolean res = matchKeys(ke.key, remoteKey); - if(res == true) { + if (res == true) { return HOSTKEY_IS_OK; } result = HOSTKEY_HAS_CHANGED; } } + return result; } private List<Object> getAllKeys(String hostname) { List<Object> keys = new ArrayList<Object>(); - synchronized(publicKeys) { - for(KnownHostsEntry ke : publicKeys) { - if(hostnameMatches(ke.patterns, hostname) == false) { + synchronized (publicKeys) { + for (KnownHostsEntry ke : publicKeys) { + if (hostnameMatches(ke.patterns, hostname) == false) { continue; } @@ -296,7 +291,7 @@ public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname) { String[] algos = recommendHostkeyAlgorithms(hostname); - if(algos != null) { + if (algos != null) { return algos; } @@ -305,14 +300,14 @@ try { ipAdresses = InetAddress.getAllByName(hostname); } - catch(UnknownHostException e) { + catch (UnknownHostException e) { return null; } - for(int i = 0; i < ipAdresses.length; i++) { + for (int i = 0; i < ipAdresses.length; i++) { algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress()); - if(algos != null) { + if (algos != null) { return algos; } } @@ -323,21 +318,20 @@ private boolean hostnameMatches(String[] hostpatterns, String hostname) { boolean isMatch = false; boolean negate; - hostname = hostname.toLowerCase(); - for(int k = 0; k < hostpatterns.length; k++) { - if(hostpatterns[k] == null) { + for (int k = 0; k < hostpatterns.length; k++) { + if (hostpatterns[k] == null) { continue; } String pattern; - /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed + /* 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) == '!')) { + if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!')) { pattern = hostpatterns[k].substring(1); negate = true; } @@ -346,37 +340,40 @@ negate = false; } - /* Optimize, no need to check this entry */ + /* Optimize, no need to check this entry */ - if((isMatch) && (negate == false)) { + if ((isMatch) && (negate == false)) { continue; } - /* Now compare */ + /* Now compare */ - if(pattern.charAt(0) == '|') { - if(checkHashed(pattern, hostname)) { - if(negate) { + 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) { + 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) { + else if (pattern.compareTo(hostname) == 0) { + if (negate) { return false; } + isMatch = true; } } @@ -388,33 +385,32 @@ private void initialize(char[] knownHostsData) throws IOException { BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData)); - while(true) { + while (true) { String line = br.readLine(); - if(line == null) { + if (line == null) { break; } line = line.trim(); - if(line.startsWith("#")) { + if (line.startsWith("#")) { continue; } String[] arr = line.split(" "); - if(arr.length >= 3) { + if (arr.length >= 3) { if ((arr[1].compareTo("ssh-rsa") == 0) || - (arr[1].compareTo("ssh-dss") == 0) || - (arr[1].startsWith("ecdsa-sha2-"))) { + (arr[1].compareTo("ssh-dss") == 0) || + (arr[1].startsWith("ecdsa-sha2-"))) { String[] hostnames = arr[0].split(","); - byte[] msg = Base64.decode(arr[2].toCharArray()); try { addHostkey(hostnames, arr[1], msg); } - catch(IOException e) { + catch (IOException e) { continue; } } @@ -424,23 +420,21 @@ private void initialize(File knownHosts) throws IOException { char[] buff = new char[512]; - CharArrayWriter cw = new CharArrayWriter(); - knownHosts.createNewFile(); - FileReader fr = new FileReader(knownHosts); - while(true) { + while (true) { int len = fr.read(buff); - if(len < 0) { + + if (len < 0) { break; } + cw.write(buff, 0, len); } fr.close(); - initialize(cw.toCharArray()); } @@ -449,50 +443,52 @@ } private boolean pseudoRegex(char[] pattern, int i, char[] match, int j) { - /* This matching logic is equivalent to the one present in OpenSSH 4.1 */ - - while(true) { - /* Are we at the end of the pattern? */ - - if(pattern.length == i) { + /* This matching logic is equivalent to the one present in OpenSSH 4.1 */ + while (true) { + /* Are we at the end of the pattern? */ + if (pattern.length == i) { return (match.length == j); } - if(pattern[i] == '*') { + if (pattern[i] == '*') { i++; - if(pattern.length == i) { + if (pattern.length == i) { return true; } - if((pattern[i] != '*') && (pattern[i] != '?')) { - while(true) { - if((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1)) { + if ((pattern[i] != '*') && (pattern[i] != '?')) { + while (true) { + if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1)) { return true; } + j++; - if(match.length == j) { + + if (match.length == j) { return false; } } } - while(true) { - if(pseudoRegex(pattern, i, match, j)) { + while (true) { + if (pseudoRegex(pattern, i, match, j)) { return true; } + j++; - if(match.length == j) { + + if (match.length == j) { return false; } } } - if(match.length == j) { + if (match.length == j) { return false; } - if((pattern[i] != '?') && (pattern[i] != match[j])) { + if ((pattern[i] != '?') && (pattern[i] != match[j])) { return false; } @@ -503,16 +499,15 @@ private String[] recommendHostkeyAlgorithms(String hostname) { String preferredAlgo = null; - List<Object> keys = getAllKeys(hostname); - for(Object key : keys) { + for (Object key : keys) { String thisAlgo; - if(key instanceof RSAPublicKey) { + if (key instanceof RSAPublicKey) { thisAlgo = "ssh-rsa"; } - else if(key instanceof DSAPublicKey) { + else if (key instanceof DSAPublicKey) { thisAlgo = "ssh-dss"; } else if (key instanceof ECPublicKey) { @@ -524,10 +519,9 @@ continue; } - if(preferredAlgo != null) { - /* If we find different key types, then return null */ - - if(preferredAlgo.compareTo(thisAlgo) != 0) { + if (preferredAlgo != null) { + /* If we find different key types, then return null */ + if (preferredAlgo.compareTo(thisAlgo) != 0) { return null; } } @@ -536,29 +530,29 @@ } } - /* If we did not find anything that we know of, return null */ + /* If we did not find anything that we know of, return null */ - if(preferredAlgo == null) { + if (preferredAlgo == null) { return null; } - /* Now put the preferred algo to the start of the array. - * You may ask yourself why we do it that way - basically, we could just - * return only the preferred algorithm: since we have a saved key of that - * type (sent earlier from the remote host), then that should work out. - * However, imagine that the server is (for whatever reasons) not offering - * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and - * now "ssh-dss" is being used). If we then do not let the server send us - * a fresh key of the new type, then we shoot ourself into the foot: - * the connection cannot be established and hence the user cannot decide - * if he/she wants to accept the new key. - */ + /* Now put the preferred algo to the start of the array. + * You may ask yourself why we do it that way - basically, we could just + * return only the preferred algorithm: since we have a saved key of that + * type (sent earlier from the remote host), then that should work out. + * However, imagine that the server is (for whatever reasons) not offering + * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and + * now "ssh-dss" is being used). If we then do not let the server send us + * a fresh key of the new type, then we shoot ourself into the foot: + * the connection cannot be established and hence the user cannot decide + * if he/she wants to accept the new key. + */ - if(preferredAlgo.equals("ssh-rsa")) { - return new String[]{"ssh-rsa", "ssh-dss", "ecdsa-sha2-nistp256"}; + if (preferredAlgo.equals("ssh-rsa")) { + return new String[] {"ssh-rsa", "ssh-dss", "ecdsa-sha2-nistp256"}; } - return new String[]{"ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256"}; + return new String[] {"ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256"}; } /** @@ -580,10 +574,10 @@ public int verifyHostkey(String hostname, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException { PublicKey remoteKey; - if("ssh-rsa".equals(serverHostKeyAlgorithm)) { + if ("ssh-rsa".equals(serverHostKeyAlgorithm)) { remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey); } - else if("ssh-dss".equals(serverHostKeyAlgorithm)) { + else if ("ssh-dss".equals(serverHostKeyAlgorithm)) { remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey); } else if (serverHostKeyAlgorithm.startsWith("ecdsa-sha2-")) { @@ -595,7 +589,7 @@ int result = checkKey(hostname, remoteKey); - if(result == HOSTKEY_IS_OK) { + if (result == HOSTKEY_IS_OK) { return result; } @@ -604,18 +598,18 @@ try { ipAdresses = InetAddress.getAllByName(hostname); } - catch(UnknownHostException e) { + catch (UnknownHostException e) { return result; } - for(int i = 0; i < ipAdresses.length; i++) { + for (int i = 0; i < ipAdresses.length; i++) { int newresult = checkKey(ipAdresses[i].getHostAddress(), remoteKey); - if(newresult == HOSTKEY_IS_OK) { + if (newresult == HOSTKEY_IS_OK) { return newresult; } - if(newresult == HOSTKEY_HAS_CHANGED) { + if (newresult == HOSTKEY_HAS_CHANGED) { result = HOSTKEY_HAS_CHANGED; } } @@ -636,20 +630,21 @@ */ public static void addHostkeyToFile(File knownHosts, String[] hostnames, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException { - if((hostnames == null) || (hostnames.length == 0)) { + if ((hostnames == null) || (hostnames.length == 0)) { throw new IllegalArgumentException("Need at least one hostname specification"); } - if((serverHostKeyAlgorithm == null) || (serverHostKey == null)) { + if ((serverHostKeyAlgorithm == null) || (serverHostKey == null)) { throw new IllegalArgumentException(); } CharArrayWriter writer = new CharArrayWriter(); - for(int i = 0; i < hostnames.length; i++) { - if(i != 0) { + for (int i = 0; i < hostnames.length; i++) { + if (i != 0) { writer.write(','); } + writer.write(hostnames[i]); } @@ -658,17 +653,15 @@ writer.write(' '); writer.write(Base64.encode(serverHostKey)); writer.write("\n"); - char[] entry = writer.toCharArray(); - RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw"); - long len = raf.length(); - if(len > 0) { + if (len > 0) { raf.seek(len - 1); int last = raf.read(); - if(last != '\n') { + + if (last != '\n') { raf.write('\n'); } } @@ -688,19 +681,19 @@ static private byte[] rawFingerPrint(String type, String keyType, byte[] hostkey) throws IOException { Digest dig; - if("md5".equals(type)) { + if ("md5".equals(type)) { dig = new MD5(); } - else if("sha1".equals(type)) { + else if ("sha1".equals(type)) { dig = new SHA1(); } else { throw new IllegalArgumentException("Unknown hash type " + type); } - if("ssh-rsa".equals(keyType)) { + if ("ssh-rsa".equals(keyType)) { } - else if("ssh-dss".equals(keyType)) { + else if ("ssh-dss".equals(keyType)) { } else if (keyType.startsWith("ecdsa-sha2-")) { } @@ -708,18 +701,20 @@ throw new IllegalArgumentException("Unknown key type " + keyType); } - if(hostkey == null) { + if (hostkey == null) { throw new IllegalArgumentException("hostkey is null"); } dig.update(hostkey); byte[] res = new byte[dig.getDigestLength()]; + try { dig.digest(res); } - catch(DigestException e) { + catch (DigestException e) { throw new IOException(e); } + return res; } @@ -731,13 +726,13 @@ */ static private String rawToHexFingerprint(byte[] fingerprint) { final char[] alpha = "0123456789abcdef".toCharArray(); - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < fingerprint.length; i++) { - if(i != 0) { + for (int i = 0; i < fingerprint.length; i++) { + if (i != 0) { sb.append(':'); } + int b = fingerprint[i] & 0xff; sb.append(alpha[b >> 4]); sb.append(alpha[b & 15]); @@ -755,22 +750,18 @@ static private String rawToBubblebabbleFingerprint(byte[] raw) { final char[] v = "aeiouy".toCharArray(); final char[] c = "bcdfghklmnprstvzx".toCharArray(); - StringBuilder sb = new StringBuilder(); - int seed = 1; - int rounds = (raw.length / 2) + 1; - sb.append('x'); - for(int i = 0; i < rounds; i++) { - if(((i + 1) < rounds) || ((raw.length) % 2 != 0)) { + for (int i = 0; i < rounds; i++) { + if (((i + 1) < rounds) || ((raw.length) % 2 != 0)) { sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]); sb.append(c[(raw[2 * i] >> 2) & 15]); sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]); - if((i + 1) < rounds) { + if ((i + 1) < rounds) { sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]); sb.append('-'); sb.append(c[(((raw[(2 * i) + 1]))) & 15]); @@ -786,7 +777,6 @@ } sb.append('x'); - return sb.toString(); }