@Override public boolean verify(PublicKey key) throws IOException { return getKeyString(key).equals(getKeyString(this.key)) && marker != Marker.REVOKED; }
public static Marker fromString(String str) { for (Marker m: values()) { if (m.sMarker.equals(str)) { return m; } } return null; } }
public String getLine() { final StringBuilder line = new StringBuilder(); if (marker != null) line.append(marker.getMarkerString()).append(" "); line.append(getHostPart()); line.append(" ").append(type.toString()); line.append(" ").append(getKeyString(key)); return line.toString(); }
public KnownHostEntry parseEntry(String line) throws IOException { if (isComment(line)) { return new CommentEntry(line); final Marker marker = Marker.fromString(split[i]); if (marker != null) { i++; return new BadHostEntry(line); } catch (IOException ioe) { log.warn("Error decoding Base64 key bytes", ioe); return new BadHostEntry(line); } else if (isBits(sType)) { type = KeyType.RSA; } catch (Exception ex) { log.error("Error reading entry `{}`, could not create key", line, ex); return new BadHostEntry(line); return new BadHostEntry(line); return new HostEntry(marker, hostnames, type, key);
@Override public boolean verify(final String hostname, final int port, final PublicKey key) { final KeyType type = KeyType.fromKey(key); if (type == KeyType.UNKNOWN) { return false; } final String adjustedHostname = (port != 22) ? "[" + hostname + "]:" + port : hostname; boolean foundApplicableHostEntry = false; for (KnownHostEntry e : entries) { try { if (e.appliesTo(type, adjustedHostname)) { foundApplicableHostEntry = true; if (e.verify(key)) { return true; } } } catch (IOException ioe) { log.error("Error with {}: {}", e, ioe); return false; } } if (foundApplicableHostEntry) { return hostKeyChangedAction(adjustedHostname, key); } return hostKeyUnverifiableAction(adjustedHostname, key); }
@Override protected boolean hostKeyUnverifiableAction(String hostname, PublicKey key) { final KeyType type = KeyType.fromKey(key); console.printf("The authenticity of host '%s' can't be established.\n" + "%s key fingerprint is %s.\n", hostname, type, SecurityUtils.getFingerprint(key)); String response = console.readLine("Are you sure you want to continue connecting (yes/no)? "); while (!(response.equalsIgnoreCase(YES) || response.equalsIgnoreCase(NO))) { response = console.readLine("Please explicitly enter yes/no: "); } if (response.equalsIgnoreCase(YES)) { try { entries().add(new HostEntry(null, hostname, KeyType.fromKey(key), key)); write(); console.printf("Warning: Permanently added '%s' (%s) to the list of known hosts.\n", hostname, type); } catch (IOException e) { throw new RuntimeException(e); } return true; } return false; }
/** * Adds a {@link OpenSSHKnownHosts} object created from the specified location as a host key verifier. * * @param location location for {@code known_hosts} file * * @throws IOException if there is an error loading from any of these locations */ public void loadKnownHosts(File location) throws IOException { addHostKeyVerifier(new OpenSSHKnownHosts(location, loggerFactory)); }
public OpenSSHKnownHosts(File khFile, LoggerFactory loggerFactory) throws IOException { this.khFile = khFile; log = loggerFactory.getLogger(getClass()); if (khFile.exists()) { final EntryFactory entryFactory = new EntryFactory(); final BufferedReader br = new BufferedReader(new FileReader(khFile)); try { // Read in the file, storing each line as an entry String line; while ((line = br.readLine()) != null) { try { KnownHostEntry entry = entryFactory.parseEntry(line); if (entry != null) { entries.add(entry); } } catch (SSHException ignore) { log.debug("Bad line ({}): {} ", ignore.toString(), line); } catch (SSHRuntimeException ignore) { log.debug("Failed to process line ({}): {} ", ignore.toString(), line); } } } finally { IOUtils.closeQuietly(br); } } }
public String getLine() { final StringBuilder line = new StringBuilder(); if (marker != null) line.append(marker.getMarkerString()).append(" "); line.append(getHostPart()); line.append(" ").append(type.toString()); line.append(" ").append(getKeyString()); return line.toString(); }
/** * Attempts loading the user's {@code known_hosts} file from the default locations, i.e. {@code ~/.ssh/known_hosts} * and {@code ~/.ssh/known_hosts2} on most platforms. Adds the resulting {@link OpenSSHKnownHosts} object as a host * key verifier. * <p/> * For finer control over which file is used, see {@link #loadKnownHosts(File)}. * * @throws IOException if there is an error loading from <em>both</em> locations */ public void loadKnownHosts() throws IOException { boolean loaded = false; final File sshDir = OpenSSHKnownHosts.detectSSHDir(); if (sshDir != null) { for (File loc : Arrays.asList(new File(sshDir, "known_hosts"), new File(sshDir, "known_hosts2"))) { try { loadKnownHosts(loc); loaded = true; } catch (IOException e) { // Ignore for now } } } if (!loaded) throw new IOException("Could not load known_hosts"); }
@Override protected boolean hostKeyChangedAction(String hostname, PublicKey key) { final KeyType type = KeyType.fromKey(key); final String fp = SecurityUtils.getFingerprint(key); final String path = getFile().getAbsolutePath(); console.printf( "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\n" + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" + "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n" + "Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n" + "It is also possible that the host key has just been changed.\n" + "The fingerprint for the %s key sent by the remote host is\n" + "%s.\n" + "Please contact your system administrator or" + "add correct host key in %s to get rid of this message.\n", type, fp, path); return false; }
/** * Tries to validate host key with all the host key verifiers known to this instance ( {@link #hostVerifiers}) * * @param key the host key to verify * * @throws TransportException */ private synchronized void verifyHost(PublicKey key) throws TransportException { for (HostKeyVerifier hkv : hostVerifiers) { log.debug("Trying to verify host key with {}", hkv); if (hkv.verify(transport.getRemoteHost(), transport.getRemotePort(), key)) return; } log.error("Disconnecting because none of the configured Host key verifiers ({}) could verify '{}' host key with fingerprint {} for {}:{}", hostVerifiers, KeyType.fromKey(key), SecurityUtils.getFingerprint(key), transport.getRemoteHost(), transport.getRemotePort()); throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE, "Could not verify `" + KeyType.fromKey(key) + "` host key with fingerprint `" + SecurityUtils.getFingerprint(key) + "` for `" + transport.getRemoteHost() + "` on port " + transport.getRemotePort()); }
public void write() throws IOException { final BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(khFile)); try { for (KnownHostEntry entry : entries) bos.write((entry.getLine() + LS).getBytes(IOUtils.UTF8)); } finally { bos.close(); } }
/** * Add a {@link HostKeyVerifier} that will verify any host that's able to claim a host key with the given {@code * fingerprint}. * * The fingerprint can be specified in either an MD5 colon-delimited format (16 hexadecimal octets, delimited by a colon), * or in a Base64 encoded format for SHA-1 or SHA-256 fingerprints. * Valid examples are: * * <ul><li>"SHA1:2Fo8c/96zv32xc8GZWbOGYOlRak="</li> * <li>"SHA256:oQGbQTujGeNIgh0ONthcEpA/BHxtt3rcYY+NxXTxQjs="</li> * <li>"MD5:d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32"</li> * <li>"d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32"</li></ul> * * @param fingerprint expected fingerprint in colon-delimited format (16 octets in hex delimited by a colon) * * @see SecurityUtils#getFingerprint */ public void addHostKeyVerifier(final String fingerprint) { addHostKeyVerifier(FingerprintVerifier.getInstance(fingerprint)); }
return new FingerprintVerifier("SHA-1", fingerprint.substring(5)); return new FingerprintVerifier("SHA-256", fingerprint.substring(7));
private void gotKexInit(SSHPacket buf) throws TransportException { buf.rpos(buf.rpos() - 1); final Proposal serverProposal = new Proposal(buf); negotiatedAlgs = clientProposal.negotiate(serverProposal); log.debug("Negotiated algorithms: {}", negotiatedAlgs); for(AlgorithmsVerifier v: algorithmVerifiers) { log.debug("Trying to verify algorithms with {}", v); if(!v.verify(negotiatedAlgs)) { throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, "Failed to verify negotiated algorithms `" + negotiatedAlgs + "`"); } } kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(), negotiatedAlgs.getKeyExchangeAlgorithm()); try { kex.init(transport, transport.getServerID(), transport.getClientID(), serverProposal.getPacket().getCompactData(), clientProposal.getPacket().getCompactData()); } catch (GeneralSecurityException e) { throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, e); } }
@Override public boolean verify(final String hostname, final int port, final PublicKey key) throws ConnectionCanceledException, ChecksumException { if(null == database) { return super.verify(hostname, port, key); } return database.verify(hostname, port, key); }
public String getLine() { final StringBuilder line = new StringBuilder(); if (marker != null) line.append(marker.getMarkerString()).append(" "); line.append(getHostPart()); line.append(" ").append(type.toString()); line.append(" ").append(getKeyString(key)); return line.toString(); }
/** * Adds a {@link OpenSSHKnownHosts} object created from the specified location as a host key verifier. * * @param location location for {@code known_hosts} file * * @throws IOException if there is an error loading from any of these locations */ public void loadKnownHosts(File location) throws IOException { addHostKeyVerifier(new OpenSSHKnownHosts(location, loggerFactory)); }
/** * Append a single entry */ public void write(KnownHostEntry entry) throws IOException { final BufferedWriter writer = new BufferedWriter(new FileWriter(khFile, true)); try { writer.write(entry.getLine()); writer.newLine(); writer.flush(); } finally { IOUtils.closeQuietly(writer); } }