/** * Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved. * * For product documentation visit https://www.sshtools.com/ * * This file is part of J2SSH Maverick. * * J2SSH Maverick is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * J2SSH Maverick is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with J2SSH Maverick. If not, see <http://www.gnu.org/licenses/>. */ package com.sshtools.ssh2; import java.io.IOException; import com.sshtools.ssh.PublicKeyAuthentication; import com.sshtools.ssh.SshException; import com.sshtools.ssh.components.SshRsaPublicKey; import com.sshtools.util.ByteArrayWriter; /** * SSH2 public key authentication providing additional SSH2 public key * authentication features. This implementation extends basic public key * authentication to provide the ability to pre-check whether a public key is * acceptable to the server. Use exactly the same as <a * href="../ssh/PublicKeyAuthentication.html>PublicKeyAuthentication</a> except * that a flag can be set to turn off the actual authenticating process, instead * the authentication process will return PUBLIC_KEY_ACCEPTABLE as the * authentication result if the server would accept the key. * * @author Lee David Painter * */ public class Ssh2PublicKeyAuthentication extends PublicKeyAuthentication implements AuthenticationClient { final static int SSH_MSG_USERAUTH_PK_OK = 60; SignatureGenerator generator; public Ssh2PublicKeyAuthentication() { } /* * (non-Javadoc) * * @see * com.sshtools.ssh2.AuthenticationClient#authenticate(com.sshtools.ssh2 * .AuthenticationProtocol, java.lang.String) */ public void authenticate(AuthenticationProtocol authentication, String servicename) throws SshException, AuthenticationResult { ByteArrayWriter baw = new ByteArrayWriter(); try { if (getPublicKey() == null) { throw new SshException("Public key not set!", SshException.BAD_API_USAGE); } if ((getPrivateKey() == null && generator == null) && isAuthenticating()) { throw new SshException( "Private key or signature generator not set!", SshException.BAD_API_USAGE); } if (getUsername() == null) { throw new SshException("Username not set!", SshException.BAD_API_USAGE); } // Generate the data to sign baw.writeBinaryString(authentication.getSessionIdentifier()); baw.write(AuthenticationProtocol.SSH_MSG_USERAUTH_REQUEST); baw.writeString(getUsername()); baw.writeString(servicename); baw.writeString("publickey"); baw.writeBoolean(isAuthenticating()); byte[] encoded; /** * Try an SSH1 key over SSH2, not sure whether this actually works * in practice but it stops the authentication from falling over * with EOFException and allows a normal failure. */ try { if (getPublicKey() instanceof SshRsaPublicKey && ((SshRsaPublicKey) getPublicKey()).getVersion() == 1) { SshRsaPublicKey pk = (SshRsaPublicKey) getPublicKey(); baw.writeString("ssh-rsa"); ByteArrayWriter baw2 = new ByteArrayWriter(); try { baw2.writeString("ssh-rsa"); baw2.writeBigInteger(pk.getPublicExponent()); baw2.writeBigInteger(pk.getModulus()); baw.writeBinaryString(encoded = baw2.toByteArray()); } finally { baw2.close(); } } else { baw.writeString(getPublicKey().getAlgorithm()); baw.writeBinaryString(encoded = getPublicKey().getEncoded()); } } catch (Throwable t) { throw new SshException("Unsupported public key type " + getPublicKey().getAlgorithm(), SshException.BAD_API_USAGE); // SSHException } ByteArrayWriter baw2 = new ByteArrayWriter(); try { // Generate the authentication request baw2.writeBoolean(isAuthenticating()); baw2.writeString(getPublicKey().getAlgorithm()); baw2.writeBinaryString(encoded); if (isAuthenticating()) { byte[] signature; if (generator != null) { signature = generator.sign(getPublicKey(), baw.toByteArray()); } else { signature = getPrivateKey().sign(baw.toByteArray()); } // Format the signature correctly ByteArrayWriter sig = new ByteArrayWriter(); try { sig.writeString(getPublicKey().getAlgorithm()); sig.writeBinaryString(signature); baw2.writeBinaryString(sig.toByteArray()); } finally { sig.close(); } } authentication.sendRequest(getUsername(), servicename, "publickey", baw2.toByteArray()); // We need to read the response since we may have password // change. byte[] response = authentication.readMessage(); if (response[0] == SSH_MSG_USERAUTH_PK_OK) { throw new AuthenticationResult(PUBLIC_KEY_ACCEPTABLE); } authentication.transport.disconnect( TransportProtocol.PROTOCOL_ERROR, "Unexpected message " + response[0] + " received"); throw new SshException("Unexpected message " + response[0] + " received", SshException.PROTOCOL_VIOLATION); } finally { baw2.close(); } } catch (IOException ex) { throw new SshException(ex, SshException.INTERNAL_ERROR); } finally { try { baw.close(); } catch (IOException e) { } } } /** * Set the signature generator for this authentication attempt. This will * overide any previous configured private key. * * @param generator */ public void setSignatureGenerator(SignatureGenerator generator) { this.generator = generator; } }