KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > ch > ethz > ssh2 > KnownHosts


1
2 package ch.ethz.ssh2;
3
4 import java.io.BufferedReader JavaDoc;
5 import java.io.CharArrayReader JavaDoc;
6 import java.io.CharArrayWriter JavaDoc;
7 import java.io.File JavaDoc;
8 import java.io.FileReader JavaDoc;
9 import java.io.IOException JavaDoc;
10 import java.io.RandomAccessFile JavaDoc;
11 import java.net.InetAddress JavaDoc;
12 import java.net.UnknownHostException JavaDoc;
13 import java.security.SecureRandom JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.LinkedList JavaDoc;
16 import java.util.Vector JavaDoc;
17
18 import ch.ethz.ssh2.crypto.Base64;
19 import ch.ethz.ssh2.crypto.digest.Digest;
20 import ch.ethz.ssh2.crypto.digest.HMAC;
21 import ch.ethz.ssh2.crypto.digest.MD5;
22 import ch.ethz.ssh2.crypto.digest.SHA1;
23 import ch.ethz.ssh2.signature.DSAPublicKey;
24 import ch.ethz.ssh2.signature.DSASHA1Verify;
25 import ch.ethz.ssh2.signature.RSAPublicKey;
26 import ch.ethz.ssh2.signature.RSASHA1Verify;
27
28 /**
29  * The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
30  * based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
31  * <p>
32  * It offers basically an in-memory database for known_hosts entries, as well as some
33  * helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
34  * It is also possible to add more keys later (e.g., one can parse different
35  * <code>known_hosts<code> files).
36  * <p>
37  * It is a thread safe implementation, therefore, you need only to instantiate one
38  * <code>KnownHosts</code> for your whole application.
39  *
40  * @author Christian Plattner, plattner@inf.ethz.ch
41  * @version $Id: KnownHosts.java,v 1.5 2006/07/30 21:59:29 cplattne Exp $
42  */

43
44 public class KnownHosts
45 {
46     public static final int HOSTKEY_IS_OK = 0;
47     public static final int HOSTKEY_IS_NEW = 1;
48     public static final int HOSTKEY_HAS_CHANGED = 2;
49
50     private class KnownHostsEntry
51     {
52         String JavaDoc[] patterns;
53         Object JavaDoc key;
54
55         KnownHostsEntry(String JavaDoc[] patterns, Object JavaDoc key)
56         {
57             this.patterns = patterns;
58             this.key = key;
59         }
60     }
61
62     private LinkedList JavaDoc publicKeys = new LinkedList JavaDoc();
63
64     public KnownHosts()
65     {
66     }
67
68     public KnownHosts(char[] knownHostsData) throws IOException JavaDoc
69     {
70         initialize(knownHostsData);
71     }
72
73     public KnownHosts(File JavaDoc knownHosts) throws IOException JavaDoc
74     {
75         initialize(knownHosts);
76     }
77
78     /**
79      * Adds a single public key entry to the database. Note: this will NOT add the public key
80      * to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> for that purpose.
81      * This method is designed to be used in a {@link ServerHostKeyVerifier}.
82      *
83      * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
84      * OpenSSH sshd man page for a description of the pattern matching algorithm.
85      * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
86      * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
87      * @throws IOException
88      */

89     public void addHostkey(String JavaDoc hostnames[], String JavaDoc serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException JavaDoc
90     {
91         if (hostnames == null)
92             throw new IllegalArgumentException JavaDoc("hostnames may not be null");
93
94         if ("ssh-rsa".equals(serverHostKeyAlgorithm))
95         {
96             RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
97
98             synchronized (publicKeys)
99             {
100                 publicKeys.add(new KnownHostsEntry(hostnames, rpk));
101             }
102         }
103         else if ("ssh-dss".equals(serverHostKeyAlgorithm))
104         {
105             DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
106
107             synchronized (publicKeys)
108             {
109                 publicKeys.add(new KnownHostsEntry(hostnames, dpk));
110             }
111         }
112         else
113             throw new IOException JavaDoc("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
114     }
115
116     /**
117      * Parses the given known_hosts data and adds entries to the database.
118      *
119      * @param knownHostsData
120      * @throws IOException
121      */

122     public void addHostkeys(char[] knownHostsData) throws IOException JavaDoc
123     {
124         initialize(knownHostsData);
125     }
126
127     /**
128      * Parses the given known_hosts file and adds entries to the database.
129      *
130      * @param knownHosts
131      * @throws IOException
132      */

133     public void addHostkeys(File JavaDoc knownHosts) throws IOException JavaDoc
134     {
135         initialize(knownHosts);
136     }
137
138     /**
139      * Generate the hashed representation of the given hostname. Useful for adding entries
140      * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
141      *
142      * @param hostname
143      * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
144      */

145     public static final String JavaDoc createHashedHostname(String JavaDoc hostname)
146     {
147         SHA1 sha1 = new SHA1();
148
149         byte[] salt = new byte[sha1.getDigestLength()];
150
151         new SecureRandom JavaDoc().nextBytes(salt);
152
153         byte[] hash = hmacSha1Hash(salt, hostname);
154
155         String JavaDoc base64_salt = new String JavaDoc(Base64.encode(salt));
156         String JavaDoc base64_hash = new String JavaDoc(Base64.encode(hash));
157
158         return new String JavaDoc("|1|" + base64_salt + "|" + base64_hash);
159     }
160
161     private static final byte[] hmacSha1Hash(byte[] salt, String JavaDoc hostname)
162     {
163         SHA1 sha1 = new SHA1();
164
165         if (salt.length != sha1.getDigestLength())
166             throw new IllegalArgumentException JavaDoc("Salt has wrong length (" + salt.length + ")");
167
168         HMAC hmac = new HMAC(sha1, salt, salt.length);
169
170         hmac.update(hostname.getBytes());
171
172         byte[] dig = new byte[hmac.getDigestLength()];
173
174         hmac.digest(dig);
175
176         return dig;
177     }
178
179     private final boolean checkHashed(String JavaDoc entry, String JavaDoc hostname)
180     {
181         if (entry.startsWith("|1|") == false)
182             return false;
183
184         int delim_idx = entry.indexOf('|', 3);
185
186         if (delim_idx == -1)
187             return false;
188
189         String JavaDoc salt_base64 = entry.substring(3, delim_idx);
190         String JavaDoc hash_base64 = entry.substring(delim_idx + 1);
191
192         byte[] salt = null;
193         byte[] hash = null;
194
195         try
196         {
197             salt = Base64.decode(salt_base64.toCharArray());
198             hash = Base64.decode(hash_base64.toCharArray());
199         }
200         catch (IOException JavaDoc e)
201         {
202             return false;
203         }
204
205         SHA1 sha1 = new SHA1();
206
207         if (salt.length != sha1.getDigestLength())
208             return false;
209
210         byte[] dig = hmacSha1Hash(salt, hostname);
211
212         for (int i = 0; i < dig.length; i++)
213             if (dig[i] != hash[i])
214                 return false;
215
216         return true;
217     }
218
219     private int checkKey(String JavaDoc remoteHostname, Object JavaDoc remoteKey)
220     {
221         int result = HOSTKEY_IS_NEW;
222
223         synchronized (publicKeys)
224         {
225             Iterator JavaDoc i = publicKeys.iterator();
226             
227             while (i.hasNext())
228             {
229                 KnownHostsEntry ke = (KnownHostsEntry) i.next();
230
231                 if (hostnameMatches(ke.patterns, remoteHostname) == false)
232                     continue;
233
234                 boolean res = matchKeys(ke.key, remoteKey);
235
236                 if (res == true)
237                     return HOSTKEY_IS_OK;
238
239                 result = HOSTKEY_HAS_CHANGED;
240             }
241         }
242         return result;
243     }
244
245     private Vector JavaDoc getAllKeys(String JavaDoc hostname)
246     {
247         Vector JavaDoc keys = new Vector JavaDoc();
248
249         synchronized (publicKeys)
250         {
251             Iterator JavaDoc i = publicKeys.iterator();
252
253             while (i.hasNext())
254             {
255                 KnownHostsEntry ke = (KnownHostsEntry) i.next();
256
257                 if (hostnameMatches(ke.patterns, hostname) == false)
258                     continue;
259
260                 keys.addElement(ke.key);
261             }
262         }
263
264         return keys;
265     }
266
267     /**
268      * Try to find the preferred order of hostkey algorithms for the given hostname.
269      * Based on the type of hostkey that is present in the internal database
270      * (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
271      * an ordered list of hostkey algorithms is returned which can be passed
272      * to <code>Connection.setServerHostKeyAlgorithms</code>.
273      *
274      * @param hostname
275      * @return <code>null</code> if no key for the given hostname is present or
276      * there are keys of multiple types present for the given hostname. Otherwise,
277      * an array with hostkey algorithms is returned (i.e., an array of length 2).
278      */

279     public String JavaDoc[] getPreferredServerHostkeyAlgorithmOrder(String JavaDoc hostname)
280     {
281         String JavaDoc[] algos = recommendHostkeyAlgorithms(hostname);
282
283         if (algos != null)
284             return algos;
285
286         InetAddress JavaDoc[] ipAdresses = null;
287
288         try
289         {
290             ipAdresses = InetAddress.getAllByName(hostname);
291         }
292         catch (UnknownHostException JavaDoc e)
293         {
294             return null;
295         }
296
297         for (int i = 0; i < ipAdresses.length; i++)
298         {
299             algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress());
300
301             if (algos != null)
302                 return algos;
303         }
304
305         return null;
306     }
307
308     private final boolean hostnameMatches(String JavaDoc[] hostpatterns, String JavaDoc hostname)
309     {
310         boolean isMatch = false;
311         boolean negate = false;
312
313         hostname = hostname.toLowerCase();
314
315         for (int k = 0; k < hostpatterns.length; k++)
316         {
317             if (hostpatterns[k] == null)
318                 continue;
319
320             String JavaDoc pattern = null;
321
322             /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
323              * entries in lines with multiple entries).
324              */

325
326             if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!'))
327             {
328                 pattern = hostpatterns[k].substring(1);
329                 negate = true;
330             }
331             else
332             {
333                 pattern = hostpatterns[k];
334                 negate = false;
335             }
336
337             /* Optimize, no need to check this entry */
338
339             if ((isMatch) && (negate == false))
340                 continue;
341
342             /* Now compare */
343
344             if (pattern.charAt(0) == '|')
345             {
346                 if (checkHashed(pattern, hostname))
347                 {
348                     if (negate)
349                         return false;
350                     isMatch = true;
351                 }
352             }
353             else
354             {
355                 pattern = pattern.toLowerCase();
356
357                 if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1))
358                 {
359                     if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0))
360                     {
361                         if (negate)
362                             return false;
363                         isMatch = true;
364                     }
365                 }
366                 else if (pattern.compareTo(hostname) == 0)
367                 {
368                     if (negate)
369                         return false;
370                     isMatch = true;
371                 }
372             }
373         }
374
375         return isMatch;
376     }
377
378     private void initialize(char[] knownHostsData) throws IOException JavaDoc
379     {
380         BufferedReader JavaDoc br = new BufferedReader JavaDoc(new CharArrayReader JavaDoc(knownHostsData));
381
382         while (true)
383         {
384             String JavaDoc line = br.readLine();
385
386             if (line == null)
387                 break;
388
389             line = line.trim();
390
391             if (line.startsWith("#"))
392                 continue;
393
394             String JavaDoc[] arr = line.split(" ");
395
396             if (arr.length >= 3)
397             {
398                 if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0))
399                 {
400                     String JavaDoc[] hostnames = arr[0].split(",");
401
402                     byte[] msg = Base64.decode(arr[2].toCharArray());
403
404                     addHostkey(hostnames, arr[1], msg);
405                 }
406             }
407         }
408     }
409
410     private void initialize(File JavaDoc knownHosts) throws IOException JavaDoc
411     {
412         char[] buff = new char[512];
413
414         CharArrayWriter JavaDoc cw = new CharArrayWriter JavaDoc();
415
416         knownHosts.createNewFile();
417
418         FileReader JavaDoc fr = new FileReader JavaDoc(knownHosts);
419
420         while (true)
421         {
422             int len = fr.read(buff);
423             if (len < 0)
424                 break;
425             cw.write(buff, 0, len);
426         }
427
428         fr.close();
429
430         initialize(cw.toCharArray());
431     }
432
433     private final boolean matchKeys(Object JavaDoc key1, Object JavaDoc key2)
434     {
435         if ((key1 instanceof RSAPublicKey) && (key2 instanceof RSAPublicKey))
436         {
437             RSAPublicKey savedRSAKey = (RSAPublicKey) key1;
438             RSAPublicKey remoteRSAKey = (RSAPublicKey) key2;
439
440             if (savedRSAKey.getE().equals(remoteRSAKey.getE()) == false)
441                 return false;
442
443             if (savedRSAKey.getN().equals(remoteRSAKey.getN()) == false)
444                 return false;
445
446             return true;
447         }
448
449         if ((key1 instanceof DSAPublicKey) && (key2 instanceof DSAPublicKey))
450         {
451             DSAPublicKey savedDSAKey = (DSAPublicKey) key1;
452             DSAPublicKey remoteDSAKey = (DSAPublicKey) key2;
453
454             if (savedDSAKey.getG().equals(remoteDSAKey.getG()) == false)
455                 return false;
456
457             if (savedDSAKey.getP().equals(remoteDSAKey.getP()) == false)
458                 return false;
459
460             if (savedDSAKey.getQ().equals(remoteDSAKey.getQ()) == false)
461                 return false;
462
463             if (savedDSAKey.getY().equals(remoteDSAKey.getY()) == false)
464                 return false;
465
466             return true;
467         }
468
469         return false;
470     }
471
472     private final boolean pseudoRegex(char[] pattern, int i, char[] match, int j)
473     {
474         /* This matching logic is equivalent to the one present in OpenSSH 4.1 */
475
476         while (true)
477         {
478             /* Are we at the end of the pattern? */
479
480             if (pattern.length == i)
481                 return (match.length == j);
482
483             if (pattern[i] == '*')
484             {
485                 i++;
486
487                 if (pattern.length == i)
488                     return true;
489
490                 if ((pattern[i] != '*') && (pattern[i] != '?'))
491                 {
492                     while (true)
493                     {
494                         if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1))
495                             return true;
496                         j++;
497                         if (match.length == j)
498                             return false;
499                     }
500                 }
501
502                 while (true)
503                 {
504                     if (pseudoRegex(pattern, i, match, j))
505                         return true;
506                     j++;
507                     if (match.length == j)
508                         return false;
509                 }
510             }
511
512             if (match.length == j)
513                 return false;
514
515             if ((pattern[i] != '?') && (pattern[i] != match[j]))
516                 return false;
517
518             i++;
519             j++;
520         }
521     }
522
523     private String JavaDoc[] recommendHostkeyAlgorithms(String JavaDoc hostname)
524     {
525         String JavaDoc preferredAlgo = null;
526
527         Vector JavaDoc keys = getAllKeys(hostname);
528
529         for (int i = 0; i < keys.size(); i++)
530         {
531             String JavaDoc thisAlgo = null;
532
533             if (keys.elementAt(i) instanceof RSAPublicKey)
534                 thisAlgo = "ssh-rsa";
535             else if (keys.elementAt(i) instanceof DSAPublicKey)
536                 thisAlgo = "ssh-dss";
537             else
538                 continue;
539
540             if (preferredAlgo != null)
541             {
542                 /* If we find different key types, then return null */
543
544                 if (preferredAlgo.compareTo(thisAlgo) != 0)
545                     return null;
546
547                 /* OK, we found the same algo again, optimize */
548
549                 continue;
550             }
551         }
552
553         /* If we did not find anything that we know of, return null */
554
555         if (preferredAlgo == null)
556             return null;
557
558         /* Now put the preferred algo to the start of the array.
559          * You may ask yourself why we do it that way - basically, we could just
560          * return only the preferred algorithm: since we have a saved key of that
561          * type (sent earlier from the remote host), then that should work out.
562          * However, imagine that the server is (for whatever reasons) not offering
563          * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and
564          * now "ssh-dss" is being used). If we then do not let the server send us
565          * a fresh key of the new type, then we shoot ourself into the foot:
566          * the connection cannot be established and hence the user cannot decide
567          * if he/she wants to accept the new key.
568          */

569
570         if (preferredAlgo.equals("ssh-rsa"))
571             return new String JavaDoc[] { "ssh-rsa", "ssh-dss" };
572
573         return new String JavaDoc[] { "ssh-dss", "ssh-rsa" };
574     }
575
576     /**
577      * Checks the internal hostkey database for the given hostkey.
578      * If no matching key can be found, then the hostname is resolved to an IP address
579      * and the search is repeated using that IP address.
580      *
581      * @param hostname the server's hostname, will be matched with all hostname patterns
582      * @param serverHostKeyAlgorithm type of hostkey, either <code>ssh-rsa</code> or <code>ssh-dss</code>
583      * @param serverHostKey the key blob
584      * @return <ul>
585      * <li><code>HOSTKEY_IS_OK</code>: the given hostkey matches an entry for the given hostname</li>
586      * <li><code>HOSTKEY_IS_NEW</code>: no entries found for this hostname and this type of hostkey</li>
587      * <li><code>HOSTKEY_HAS_CHANGED</code>: hostname is known, but with another key of the same type
588      * (man-in-the-middle attack?)</li>
589      * </ul>
590      * @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type.
591      */

592     public int verifyHostkey(String JavaDoc hostname, String JavaDoc serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException JavaDoc
593     {
594         Object JavaDoc remoteKey = null;
595
596         if ("ssh-rsa".equals(serverHostKeyAlgorithm))
597         {
598             remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
599         }
600         else if ("ssh-dss".equals(serverHostKeyAlgorithm))
601         {
602             remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
603         }
604         else
605             throw new IllegalArgumentException JavaDoc("Unknown hostkey type " + serverHostKeyAlgorithm);
606
607         int result = checkKey(hostname, remoteKey);
608
609         if (result == HOSTKEY_IS_OK)
610             return result;
611
612         InetAddress JavaDoc[] ipAdresses = null;
613
614         try
615         {
616             ipAdresses = InetAddress.getAllByName(hostname);
617         }
618         catch (UnknownHostException JavaDoc e)
619         {
620             return result;
621         }
622
623         for (int i = 0; i < ipAdresses.length; i++)
624         {
625             int newresult = checkKey(ipAdresses[i].getHostAddress(), remoteKey);
626
627             if (newresult == HOSTKEY_IS_OK)
628                 return newresult;
629
630             if (newresult == HOSTKEY_HAS_CHANGED)
631                 result = HOSTKEY_HAS_CHANGED;
632         }
633
634         return result;
635     }
636
637     /**
638      * Adds a single public key entry to the a known_hosts file.
639      * This method is designed to be used in a {@link ServerHostKeyVerifier}.
640      *
641      * @param knownHosts the file where the publickey entry will be appended.
642      * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
643      * OpenSSH sshd man page for a description of the pattern matching algorithm.
644      * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
645      * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
646      * @throws IOException
647      */

648     public final static void addHostkeyToFile(File JavaDoc knownHosts, String JavaDoc[] hostnames, String JavaDoc serverHostKeyAlgorithm,
649             byte[] serverHostKey) throws IOException JavaDoc
650     {
651         if ((hostnames == null) || (hostnames.length == 0))
652             throw new IllegalArgumentException JavaDoc("Need at least one hostname specification");
653
654         if ((serverHostKeyAlgorithm == null) || (serverHostKey == null))
655             throw new IllegalArgumentException JavaDoc();
656
657         CharArrayWriter JavaDoc writer = new CharArrayWriter JavaDoc();
658         
659         for (int i = 0; i < hostnames.length; i++)
660         {
661             if (i != 0)
662                 writer.write(',');
663             writer.write(hostnames[i]);
664         }
665
666         writer.write(' ');
667         writer.write(serverHostKeyAlgorithm);
668         writer.write(' ');
669         writer.write(Base64.encode(serverHostKey));
670         writer.write("\n");
671
672         char[] entry = writer.toCharArray();
673         
674         RandomAccessFile JavaDoc raf = new RandomAccessFile JavaDoc(knownHosts, "rw");
675
676         long len = raf.length();
677         
678         if (len > 0)
679         {
680             raf.seek(len - 1);
681             int last = raf.read();
682             if (last != '\n')
683                 raf.write('\n');
684         }
685         
686         raf.write(new String JavaDoc(entry).getBytes());
687         raf.close();
688     }
689
690     /**
691      * Generates a "raw" fingerprint of a hostkey.
692      *
693      * @param type either "md5" or "sha1"
694      * @param keyType either "ssh-rsa" or "ssh-dss"
695      * @param hostkey the hostkey
696      * @return the raw fingerprint
697      */

698     static final private byte[] rawFingerPrint(String JavaDoc type, String JavaDoc keyType, byte[] hostkey)
699     {
700         Digest dig = null;
701
702         if ("md5".equals(type))
703         {
704             dig = new MD5();
705         }
706         else if ("sha1".equals(type))
707         {
708             dig = new SHA1();
709         }
710         else
711             throw new IllegalArgumentException JavaDoc("Unknown hash type " + type);
712
713         if ("ssh-rsa".equals(keyType))
714         {
715         }
716         else if ("ssh-dss".equals(keyType))
717         {
718         }
719         else
720             throw new IllegalArgumentException JavaDoc("Unknown key type " + keyType);
721
722         if (hostkey == null)
723             throw new IllegalArgumentException JavaDoc("hostkey is null");
724
725         dig.update(hostkey);
726         byte[] res = new byte[dig.getDigestLength()];
727         dig.digest(res);
728         return res;
729     }
730
731     /**
732      * Convert a raw fingerprint to hex representation (XX:YY:ZZ...).
733      * @param fingerprint raw fingerprint
734      * @return the hex representation
735      */

736     static final private String JavaDoc rawToHexFingerprint(byte[] fingerprint)
737     {
738         final char[] alpha = "0123456789abcdef".toCharArray();
739
740         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
741
742         for (int i = 0; i < fingerprint.length; i++)
743         {
744             if (i != 0)
745                 sb.append(':');
746             int b = fingerprint[i] & 0xff;
747             sb.append(alpha[b >> 4]);
748             sb.append(alpha[b & 15]);
749         }
750
751         return sb.toString();
752     }
753
754     /**
755      * Convert a raw fingerprint to bubblebabble representation.
756      * @param raw raw fingerprint
757      * @return the bubblebabble representation
758      */

759     static final private String JavaDoc rawToBubblebabbleFingerprint(byte[] raw)
760     {
761         final char[] v = "aeiouy".toCharArray();
762         final char[] c = "bcdfghklmnprstvzx".toCharArray();
763
764         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
765
766         int seed = 1;
767
768         int rounds = (raw.length / 2) + 1;
769
770         sb.append('x');
771
772         for (int i = 0; i < rounds; i++)
773         {
774             if (((i + 1) < rounds) || ((raw.length) % 2 != 0))
775             {
776                 sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]);
777                 sb.append(c[(raw[2 * i] >> 2) & 15]);
778                 sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]);
779
780                 if ((i + 1) < rounds)
781                 {
782                     sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]);
783                     sb.append('-');
784                     sb.append(c[(((raw[(2 * i) + 1]))) & 15]);
785                     // As long as seed >= 0, seed will be >= 0 afterwards
786
seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36;
787                 }
788             }
789             else
790             {
791                 sb.append(v[seed % 6]); // seed >= 0, therefore index positive
792
sb.append('x');
793                 sb.append(v[seed / 6]);
794             }
795         }
796
797         sb.append('x');
798
799         return sb.toString();
800     }
801
802     /**
803      * Convert a ssh2 key-blob into a human readable hex fingerprint.
804      * Generated fingerprints are identical to those generated by OpenSSH.
805      * <p>
806      * Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47.
807
808      * @param keytype either "ssh-rsa" or "ssh-dss"
809      * @param publickey key blob
810      * @return Hex fingerprint
811      */

812     public final static String JavaDoc createHexFingerprint(String JavaDoc keytype, byte[] publickey)
813     {
814         byte[] raw = rawFingerPrint("md5", keytype, publickey);
815         return rawToHexFingerprint(raw);
816     }
817
818     /**
819      * Convert a ssh2 key-blob into a human readable bubblebabble fingerprint.
820      * The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints
821      * that are easier to remember for humans.
822      * <p>
823      * Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux.
824      *
825      * @param keytype either "ssh-rsa" or "ssh-dss"
826      * @param publickey key data
827      * @return Bubblebabble fingerprint
828      */

829     public final static String JavaDoc createBubblebabbleFingerprint(String JavaDoc keytype, byte[] publickey)
830     {
831         byte[] raw = rawFingerPrint("sha1", keytype, publickey);
832         return rawToBubblebabbleFingerprint(raw);
833     }
834 }
835
Popular Tags