Source Code Cross Referenced for KnownHosts.java in  » Net » Ganymed-SSH-2 » ch » ethz » ssh2 » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » Ganymed SSH 2 » ch.ethz.ssh2 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package ch.ethz.ssh2;
002:
003:        import java.io.BufferedReader;
004:        import java.io.CharArrayReader;
005:        import java.io.CharArrayWriter;
006:        import java.io.File;
007:        import java.io.FileReader;
008:        import java.io.IOException;
009:        import java.io.RandomAccessFile;
010:        import java.net.InetAddress;
011:        import java.net.UnknownHostException;
012:        import java.security.SecureRandom;
013:        import java.util.Iterator;
014:        import java.util.LinkedList;
015:        import java.util.Vector;
016:
017:        import ch.ethz.ssh2.crypto.Base64;
018:        import ch.ethz.ssh2.crypto.digest.Digest;
019:        import ch.ethz.ssh2.crypto.digest.HMAC;
020:        import ch.ethz.ssh2.crypto.digest.MD5;
021:        import ch.ethz.ssh2.crypto.digest.SHA1;
022:        import ch.ethz.ssh2.signature.DSAPublicKey;
023:        import ch.ethz.ssh2.signature.DSASHA1Verify;
024:        import ch.ethz.ssh2.signature.RSAPublicKey;
025:        import ch.ethz.ssh2.signature.RSASHA1Verify;
026:
027:        /**
028:         * The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
029:         * based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
030:         * <p>
031:         * It offers basically an in-memory database for known_hosts entries, as well as some
032:         * helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
033:         * It is also possible to add more keys later (e.g., one can parse different
034:         * <code>known_hosts<code> files).
035:         * <p>
036:         * It is a thread safe implementation, therefore, you need only to instantiate one
037:         * <code>KnownHosts</code> for your whole application.
038:         * 
039:         * @author Christian Plattner, plattner@inf.ethz.ch
040:         * @version $Id: KnownHosts.java,v 1.5 2006/07/30 21:59:29 cplattne Exp $
041:         */
042:
043:        public class KnownHosts {
044:            public static final int HOSTKEY_IS_OK = 0;
045:            public static final int HOSTKEY_IS_NEW = 1;
046:            public static final int HOSTKEY_HAS_CHANGED = 2;
047:
048:            private class KnownHostsEntry {
049:                String[] patterns;
050:                Object key;
051:
052:                KnownHostsEntry(String[] patterns, Object key) {
053:                    this .patterns = patterns;
054:                    this .key = key;
055:                }
056:            }
057:
058:            private LinkedList publicKeys = new LinkedList();
059:
060:            public KnownHosts() {
061:            }
062:
063:            public KnownHosts(char[] knownHostsData) throws IOException {
064:                initialize(knownHostsData);
065:            }
066:
067:            public KnownHosts(File knownHosts) throws IOException {
068:                initialize(knownHosts);
069:            }
070:
071:            /**
072:             * Adds a single public key entry to the database. Note: this will NOT add the public key
073:             * to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> for that purpose.
074:             * This method is designed to be used in a {@link ServerHostKeyVerifier}.
075:             * 
076:             * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
077:             *        OpenSSH sshd man page for a description of the pattern matching algorithm.
078:             * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
079:             * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
080:             * @throws IOException
081:             */
082:            public void addHostkey(String hostnames[],
083:                    String serverHostKeyAlgorithm, byte[] serverHostKey)
084:                    throws IOException {
085:                if (hostnames == null)
086:                    throw new IllegalArgumentException(
087:                            "hostnames may not be null");
088:
089:                if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
090:                    RSAPublicKey rpk = RSASHA1Verify
091:                            .decodeSSHRSAPublicKey(serverHostKey);
092:
093:                    synchronized (publicKeys) {
094:                        publicKeys.add(new KnownHostsEntry(hostnames, rpk));
095:                    }
096:                } else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
097:                    DSAPublicKey dpk = DSASHA1Verify
098:                            .decodeSSHDSAPublicKey(serverHostKey);
099:
100:                    synchronized (publicKeys) {
101:                        publicKeys.add(new KnownHostsEntry(hostnames, dpk));
102:                    }
103:                } else
104:                    throw new IOException("Unknwon host key type ("
105:                            + serverHostKeyAlgorithm + ")");
106:            }
107:
108:            /**
109:             * Parses the given known_hosts data and adds entries to the database.
110:             * 
111:             * @param knownHostsData
112:             * @throws IOException
113:             */
114:            public void addHostkeys(char[] knownHostsData) throws IOException {
115:                initialize(knownHostsData);
116:            }
117:
118:            /**
119:             * Parses the given known_hosts file and adds entries to the database.
120:             * 
121:             * @param knownHosts
122:             * @throws IOException
123:             */
124:            public void addHostkeys(File knownHosts) throws IOException {
125:                initialize(knownHosts);
126:            }
127:
128:            /**
129:             * Generate the hashed representation of the given hostname. Useful for adding entries
130:             * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
131:             *  
132:             * @param hostname
133:             * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
134:             */
135:            public static final String createHashedHostname(String hostname) {
136:                SHA1 sha1 = new SHA1();
137:
138:                byte[] salt = new byte[sha1.getDigestLength()];
139:
140:                new SecureRandom().nextBytes(salt);
141:
142:                byte[] hash = hmacSha1Hash(salt, hostname);
143:
144:                String base64_salt = new String(Base64.encode(salt));
145:                String base64_hash = new String(Base64.encode(hash));
146:
147:                return new String("|1|" + base64_salt + "|" + base64_hash);
148:            }
149:
150:            private static final byte[] hmacSha1Hash(byte[] salt,
151:                    String hostname) {
152:                SHA1 sha1 = new SHA1();
153:
154:                if (salt.length != sha1.getDigestLength())
155:                    throw new IllegalArgumentException(
156:                            "Salt has wrong length (" + salt.length + ")");
157:
158:                HMAC hmac = new HMAC(sha1, salt, salt.length);
159:
160:                hmac.update(hostname.getBytes());
161:
162:                byte[] dig = new byte[hmac.getDigestLength()];
163:
164:                hmac.digest(dig);
165:
166:                return dig;
167:            }
168:
169:            private final boolean checkHashed(String entry, String hostname) {
170:                if (entry.startsWith("|1|") == false)
171:                    return false;
172:
173:                int delim_idx = entry.indexOf('|', 3);
174:
175:                if (delim_idx == -1)
176:                    return false;
177:
178:                String salt_base64 = entry.substring(3, delim_idx);
179:                String hash_base64 = entry.substring(delim_idx + 1);
180:
181:                byte[] salt = null;
182:                byte[] hash = null;
183:
184:                try {
185:                    salt = Base64.decode(salt_base64.toCharArray());
186:                    hash = Base64.decode(hash_base64.toCharArray());
187:                } catch (IOException e) {
188:                    return false;
189:                }
190:
191:                SHA1 sha1 = new SHA1();
192:
193:                if (salt.length != sha1.getDigestLength())
194:                    return false;
195:
196:                byte[] dig = hmacSha1Hash(salt, hostname);
197:
198:                for (int i = 0; i < dig.length; i++)
199:                    if (dig[i] != hash[i])
200:                        return false;
201:
202:                return true;
203:            }
204:
205:            private int checkKey(String remoteHostname, Object remoteKey) {
206:                int result = HOSTKEY_IS_NEW;
207:
208:                synchronized (publicKeys) {
209:                    Iterator i = publicKeys.iterator();
210:
211:                    while (i.hasNext()) {
212:                        KnownHostsEntry ke = (KnownHostsEntry) i.next();
213:
214:                        if (hostnameMatches(ke.patterns, remoteHostname) == false)
215:                            continue;
216:
217:                        boolean res = matchKeys(ke.key, remoteKey);
218:
219:                        if (res == true)
220:                            return HOSTKEY_IS_OK;
221:
222:                        result = HOSTKEY_HAS_CHANGED;
223:                    }
224:                }
225:                return result;
226:            }
227:
228:            private Vector getAllKeys(String hostname) {
229:                Vector keys = new Vector();
230:
231:                synchronized (publicKeys) {
232:                    Iterator i = publicKeys.iterator();
233:
234:                    while (i.hasNext()) {
235:                        KnownHostsEntry ke = (KnownHostsEntry) i.next();
236:
237:                        if (hostnameMatches(ke.patterns, hostname) == false)
238:                            continue;
239:
240:                        keys.addElement(ke.key);
241:                    }
242:                }
243:
244:                return keys;
245:            }
246:
247:            /**
248:             * Try to find the preferred order of hostkey algorithms for the given hostname.
249:             * Based on the type of hostkey that is present in the internal database
250:             * (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
251:             * an ordered list of hostkey algorithms is returned which can be passed
252:             * to <code>Connection.setServerHostKeyAlgorithms</code>. 
253:             * 
254:             * @param hostname
255:             * @return <code>null</code> if no key for the given hostname is present or
256:             * there are keys of multiple types present for the given hostname. Otherwise,
257:             * an array with hostkey algorithms is returned (i.e., an array of length 2).
258:             */
259:            public String[] getPreferredServerHostkeyAlgorithmOrder(
260:                    String hostname) {
261:                String[] algos = recommendHostkeyAlgorithms(hostname);
262:
263:                if (algos != null)
264:                    return algos;
265:
266:                InetAddress[] ipAdresses = null;
267:
268:                try {
269:                    ipAdresses = InetAddress.getAllByName(hostname);
270:                } catch (UnknownHostException e) {
271:                    return null;
272:                }
273:
274:                for (int i = 0; i < ipAdresses.length; i++) {
275:                    algos = recommendHostkeyAlgorithms(ipAdresses[i]
276:                            .getHostAddress());
277:
278:                    if (algos != null)
279:                        return algos;
280:                }
281:
282:                return null;
283:            }
284:
285:            private final boolean hostnameMatches(String[] hostpatterns,
286:                    String hostname) {
287:                boolean isMatch = false;
288:                boolean negate = false;
289:
290:                hostname = hostname.toLowerCase();
291:
292:                for (int k = 0; k < hostpatterns.length; k++) {
293:                    if (hostpatterns[k] == null)
294:                        continue;
295:
296:                    String pattern = null;
297:
298:                    /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
299:                     * entries in lines with multiple entries).
300:                     */
301:
302:                    if ((hostpatterns[k].length() > 0)
303:                            && (hostpatterns[k].charAt(0) == '!')) {
304:                        pattern = hostpatterns[k].substring(1);
305:                        negate = true;
306:                    } else {
307:                        pattern = hostpatterns[k];
308:                        negate = false;
309:                    }
310:
311:                    /* Optimize, no need to check this entry */
312:
313:                    if ((isMatch) && (negate == false))
314:                        continue;
315:
316:                    /* Now compare */
317:
318:                    if (pattern.charAt(0) == '|') {
319:                        if (checkHashed(pattern, hostname)) {
320:                            if (negate)
321:                                return false;
322:                            isMatch = true;
323:                        }
324:                    } else {
325:                        pattern = pattern.toLowerCase();
326:
327:                        if ((pattern.indexOf('?') != -1)
328:                                || (pattern.indexOf('*') != -1)) {
329:                            if (pseudoRegex(pattern.toCharArray(), 0, hostname
330:                                    .toCharArray(), 0)) {
331:                                if (negate)
332:                                    return false;
333:                                isMatch = true;
334:                            }
335:                        } else if (pattern.compareTo(hostname) == 0) {
336:                            if (negate)
337:                                return false;
338:                            isMatch = true;
339:                        }
340:                    }
341:                }
342:
343:                return isMatch;
344:            }
345:
346:            private void initialize(char[] knownHostsData) throws IOException {
347:                BufferedReader br = new BufferedReader(new CharArrayReader(
348:                        knownHostsData));
349:
350:                while (true) {
351:                    String line = br.readLine();
352:
353:                    if (line == null)
354:                        break;
355:
356:                    line = line.trim();
357:
358:                    if (line.startsWith("#"))
359:                        continue;
360:
361:                    String[] arr = line.split(" ");
362:
363:                    if (arr.length >= 3) {
364:                        if ((arr[1].compareTo("ssh-rsa") == 0)
365:                                || (arr[1].compareTo("ssh-dss") == 0)) {
366:                            String[] hostnames = arr[0].split(",");
367:
368:                            byte[] msg = Base64.decode(arr[2].toCharArray());
369:
370:                            addHostkey(hostnames, arr[1], msg);
371:                        }
372:                    }
373:                }
374:            }
375:
376:            private void initialize(File knownHosts) throws IOException {
377:                char[] buff = new char[512];
378:
379:                CharArrayWriter cw = new CharArrayWriter();
380:
381:                knownHosts.createNewFile();
382:
383:                FileReader fr = new FileReader(knownHosts);
384:
385:                while (true) {
386:                    int len = fr.read(buff);
387:                    if (len < 0)
388:                        break;
389:                    cw.write(buff, 0, len);
390:                }
391:
392:                fr.close();
393:
394:                initialize(cw.toCharArray());
395:            }
396:
397:            private final boolean matchKeys(Object key1, Object key2) {
398:                if ((key1 instanceof  RSAPublicKey)
399:                        && (key2 instanceof  RSAPublicKey)) {
400:                    RSAPublicKey savedRSAKey = (RSAPublicKey) key1;
401:                    RSAPublicKey remoteRSAKey = (RSAPublicKey) key2;
402:
403:                    if (savedRSAKey.getE().equals(remoteRSAKey.getE()) == false)
404:                        return false;
405:
406:                    if (savedRSAKey.getN().equals(remoteRSAKey.getN()) == false)
407:                        return false;
408:
409:                    return true;
410:                }
411:
412:                if ((key1 instanceof  DSAPublicKey)
413:                        && (key2 instanceof  DSAPublicKey)) {
414:                    DSAPublicKey savedDSAKey = (DSAPublicKey) key1;
415:                    DSAPublicKey remoteDSAKey = (DSAPublicKey) key2;
416:
417:                    if (savedDSAKey.getG().equals(remoteDSAKey.getG()) == false)
418:                        return false;
419:
420:                    if (savedDSAKey.getP().equals(remoteDSAKey.getP()) == false)
421:                        return false;
422:
423:                    if (savedDSAKey.getQ().equals(remoteDSAKey.getQ()) == false)
424:                        return false;
425:
426:                    if (savedDSAKey.getY().equals(remoteDSAKey.getY()) == false)
427:                        return false;
428:
429:                    return true;
430:                }
431:
432:                return false;
433:            }
434:
435:            private final boolean pseudoRegex(char[] pattern, int i,
436:                    char[] match, int j) {
437:                /* This matching logic is equivalent to the one present in OpenSSH 4.1 */
438:
439:                while (true) {
440:                    /* Are we at the end of the pattern? */
441:
442:                    if (pattern.length == i)
443:                        return (match.length == j);
444:
445:                    if (pattern[i] == '*') {
446:                        i++;
447:
448:                        if (pattern.length == i)
449:                            return true;
450:
451:                        if ((pattern[i] != '*') && (pattern[i] != '?')) {
452:                            while (true) {
453:                                if ((pattern[i] == match[j])
454:                                        && pseudoRegex(pattern, i + 1, match,
455:                                                j + 1))
456:                                    return true;
457:                                j++;
458:                                if (match.length == j)
459:                                    return false;
460:                            }
461:                        }
462:
463:                        while (true) {
464:                            if (pseudoRegex(pattern, i, match, j))
465:                                return true;
466:                            j++;
467:                            if (match.length == j)
468:                                return false;
469:                        }
470:                    }
471:
472:                    if (match.length == j)
473:                        return false;
474:
475:                    if ((pattern[i] != '?') && (pattern[i] != match[j]))
476:                        return false;
477:
478:                    i++;
479:                    j++;
480:                }
481:            }
482:
483:            private String[] recommendHostkeyAlgorithms(String hostname) {
484:                String preferredAlgo = null;
485:
486:                Vector keys = getAllKeys(hostname);
487:
488:                for (int i = 0; i < keys.size(); i++) {
489:                    String this Algo = null;
490:
491:                    if (keys.elementAt(i) instanceof  RSAPublicKey)
492:                        this Algo = "ssh-rsa";
493:                    else if (keys.elementAt(i) instanceof  DSAPublicKey)
494:                        this Algo = "ssh-dss";
495:                    else
496:                        continue;
497:
498:                    if (preferredAlgo != null) {
499:                        /* If we find different key types, then return null */
500:
501:                        if (preferredAlgo.compareTo(this Algo) != 0)
502:                            return null;
503:
504:                        /* OK, we found the same algo again, optimize */
505:
506:                        continue;
507:                    }
508:                }
509:
510:                /* If we did not find anything that we know of, return null */
511:
512:                if (preferredAlgo == null)
513:                    return null;
514:
515:                /* Now put the preferred algo to the start of the array.
516:                 * You may ask yourself why we do it that way - basically, we could just
517:                 * return only the preferred algorithm: since we have a saved key of that
518:                 * type (sent earlier from the remote host), then that should work out.
519:                 * However, imagine that the server is (for whatever reasons) not offering
520:                 * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and
521:                 * now "ssh-dss" is being used). If we then do not let the server send us
522:                 * a fresh key of the new type, then we shoot ourself into the foot:
523:                 * the connection cannot be established and hence the user cannot decide
524:                 * if he/she wants to accept the new key.
525:                 */
526:
527:                if (preferredAlgo.equals("ssh-rsa"))
528:                    return new String[] { "ssh-rsa", "ssh-dss" };
529:
530:                return new String[] { "ssh-dss", "ssh-rsa" };
531:            }
532:
533:            /**
534:             * Checks the internal hostkey database for the given hostkey.
535:             * If no matching key can be found, then the hostname is resolved to an IP address
536:             * and the search is repeated using that IP address.
537:             * 
538:             * @param hostname the server's hostname, will be matched with all hostname patterns
539:             * @param serverHostKeyAlgorithm type of hostkey, either <code>ssh-rsa</code> or <code>ssh-dss</code>
540:             * @param serverHostKey the key blob
541:             * @return <ul>
542:             *         <li><code>HOSTKEY_IS_OK</code>: the given hostkey matches an entry for the given hostname</li>
543:             *         <li><code>HOSTKEY_IS_NEW</code>: no entries found for this hostname and this type of hostkey</li>
544:             *         <li><code>HOSTKEY_HAS_CHANGED</code>: hostname is known, but with another key of the same type
545:             *         (man-in-the-middle attack?)</li>
546:             *         </ul>
547:             * @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type.
548:             */
549:            public int verifyHostkey(String hostname,
550:                    String serverHostKeyAlgorithm, byte[] serverHostKey)
551:                    throws IOException {
552:                Object remoteKey = null;
553:
554:                if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
555:                    remoteKey = RSASHA1Verify
556:                            .decodeSSHRSAPublicKey(serverHostKey);
557:                } else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
558:                    remoteKey = DSASHA1Verify
559:                            .decodeSSHDSAPublicKey(serverHostKey);
560:                } else
561:                    throw new IllegalArgumentException("Unknown hostkey type "
562:                            + serverHostKeyAlgorithm);
563:
564:                int result = checkKey(hostname, remoteKey);
565:
566:                if (result == HOSTKEY_IS_OK)
567:                    return result;
568:
569:                InetAddress[] ipAdresses = null;
570:
571:                try {
572:                    ipAdresses = InetAddress.getAllByName(hostname);
573:                } catch (UnknownHostException e) {
574:                    return result;
575:                }
576:
577:                for (int i = 0; i < ipAdresses.length; i++) {
578:                    int newresult = checkKey(ipAdresses[i].getHostAddress(),
579:                            remoteKey);
580:
581:                    if (newresult == HOSTKEY_IS_OK)
582:                        return newresult;
583:
584:                    if (newresult == HOSTKEY_HAS_CHANGED)
585:                        result = HOSTKEY_HAS_CHANGED;
586:                }
587:
588:                return result;
589:            }
590:
591:            /**
592:             * Adds a single public key entry to the a known_hosts file.
593:             * This method is designed to be used in a {@link ServerHostKeyVerifier}.
594:             * 
595:             * @param knownHosts the file where the publickey entry will be appended.
596:             * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
597:             *        OpenSSH sshd man page for a description of the pattern matching algorithm.
598:             * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
599:             * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
600:             * @throws IOException
601:             */
602:            public final static void addHostkeyToFile(File knownHosts,
603:                    String[] hostnames, String serverHostKeyAlgorithm,
604:                    byte[] serverHostKey) throws IOException {
605:                if ((hostnames == null) || (hostnames.length == 0))
606:                    throw new IllegalArgumentException(
607:                            "Need at least one hostname specification");
608:
609:                if ((serverHostKeyAlgorithm == null) || (serverHostKey == null))
610:                    throw new IllegalArgumentException();
611:
612:                CharArrayWriter writer = new CharArrayWriter();
613:
614:                for (int i = 0; i < hostnames.length; i++) {
615:                    if (i != 0)
616:                        writer.write(',');
617:                    writer.write(hostnames[i]);
618:                }
619:
620:                writer.write(' ');
621:                writer.write(serverHostKeyAlgorithm);
622:                writer.write(' ');
623:                writer.write(Base64.encode(serverHostKey));
624:                writer.write("\n");
625:
626:                char[] entry = writer.toCharArray();
627:
628:                RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw");
629:
630:                long len = raf.length();
631:
632:                if (len > 0) {
633:                    raf.seek(len - 1);
634:                    int last = raf.read();
635:                    if (last != '\n')
636:                        raf.write('\n');
637:                }
638:
639:                raf.write(new String(entry).getBytes());
640:                raf.close();
641:            }
642:
643:            /**
644:             * Generates a "raw" fingerprint of a hostkey.
645:             * 
646:             * @param type either "md5" or "sha1"
647:             * @param keyType either "ssh-rsa" or "ssh-dss"
648:             * @param hostkey the hostkey
649:             * @return the raw fingerprint
650:             */
651:            static final private byte[] rawFingerPrint(String type,
652:                    String keyType, byte[] hostkey) {
653:                Digest dig = null;
654:
655:                if ("md5".equals(type)) {
656:                    dig = new MD5();
657:                } else if ("sha1".equals(type)) {
658:                    dig = new SHA1();
659:                } else
660:                    throw new IllegalArgumentException("Unknown hash type "
661:                            + type);
662:
663:                if ("ssh-rsa".equals(keyType)) {
664:                } else if ("ssh-dss".equals(keyType)) {
665:                } else
666:                    throw new IllegalArgumentException("Unknown key type "
667:                            + keyType);
668:
669:                if (hostkey == null)
670:                    throw new IllegalArgumentException("hostkey is null");
671:
672:                dig.update(hostkey);
673:                byte[] res = new byte[dig.getDigestLength()];
674:                dig.digest(res);
675:                return res;
676:            }
677:
678:            /**
679:             * Convert a raw fingerprint to hex representation (XX:YY:ZZ...).
680:             * @param fingerprint raw fingerprint
681:             * @return the hex representation
682:             */
683:            static final private String rawToHexFingerprint(byte[] fingerprint) {
684:                final char[] alpha = "0123456789abcdef".toCharArray();
685:
686:                StringBuffer sb = new StringBuffer();
687:
688:                for (int i = 0; i < fingerprint.length; i++) {
689:                    if (i != 0)
690:                        sb.append(':');
691:                    int b = fingerprint[i] & 0xff;
692:                    sb.append(alpha[b >> 4]);
693:                    sb.append(alpha[b & 15]);
694:                }
695:
696:                return sb.toString();
697:            }
698:
699:            /**
700:             * Convert a raw fingerprint to bubblebabble representation.
701:             * @param raw raw fingerprint
702:             * @return the bubblebabble representation
703:             */
704:            static final private String rawToBubblebabbleFingerprint(byte[] raw) {
705:                final char[] v = "aeiouy".toCharArray();
706:                final char[] c = "bcdfghklmnprstvzx".toCharArray();
707:
708:                StringBuffer sb = new StringBuffer();
709:
710:                int seed = 1;
711:
712:                int rounds = (raw.length / 2) + 1;
713:
714:                sb.append('x');
715:
716:                for (int i = 0; i < rounds; i++) {
717:                    if (((i + 1) < rounds) || ((raw.length) % 2 != 0)) {
718:                        sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]);
719:                        sb.append(c[(raw[2 * i] >> 2) & 15]);
720:                        sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]);
721:
722:                        if ((i + 1) < rounds) {
723:                            sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]);
724:                            sb.append('-');
725:                            sb.append(c[(((raw[(2 * i) + 1]))) & 15]);
726:                            // As long as seed >= 0, seed will be >= 0 afterwards
727:                            seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36;
728:                        }
729:                    } else {
730:                        sb.append(v[seed % 6]); // seed >= 0, therefore index positive
731:                        sb.append('x');
732:                        sb.append(v[seed / 6]);
733:                    }
734:                }
735:
736:                sb.append('x');
737:
738:                return sb.toString();
739:            }
740:
741:            /**
742:             * Convert a ssh2 key-blob into a human readable hex fingerprint.
743:             * Generated fingerprints are identical to those generated by OpenSSH.
744:             * <p>
745:             * Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47.
746:
747:             * @param keytype either "ssh-rsa" or "ssh-dss"
748:             * @param publickey key blob
749:             * @return Hex fingerprint
750:             */
751:            public final static String createHexFingerprint(String keytype,
752:                    byte[] publickey) {
753:                byte[] raw = rawFingerPrint("md5", keytype, publickey);
754:                return rawToHexFingerprint(raw);
755:            }
756:
757:            /**
758:             * Convert a ssh2 key-blob into a human readable bubblebabble fingerprint.
759:             * The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints
760:             * that are easier to remember for humans.
761:             * <p>
762:             * Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux.
763:             * 
764:             * @param keytype either "ssh-rsa" or "ssh-dss"
765:             * @param publickey key data
766:             * @return Bubblebabble fingerprint
767:             */
768:            public final static String createBubblebabbleFingerprint(
769:                    String keytype, byte[] publickey) {
770:                byte[] raw = rawFingerPrint("sha1", keytype, publickey);
771:                return rawToBubblebabbleFingerprint(raw);
772:            }
773:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.