class Net::SSH::Authentication::Agent
Written by Artūras Šlajus <arturas.slajus@gmail.com>
This class implements an agent for JRuby + Pageant.
def self.connect(logger=nil)
Instantiates a new agent object, connects to a running SSH agent,
def self.connect(logger=nil) agent = new(logger) agent.connect! agent end
def self.connect(logger=nil)
Instantiates a new agent object, connects to a running SSH agent,
def self.connect(logger=nil) agent = new(logger) agent.connect! agent.negotiate! agent end
def agent_failed(type)
Returns +true+ if the parameter indicates a "failure" response from
def agent_failed(type) type == SSH_AGENT_FAILURE || type == SSH2_AGENT_FAILURE || type == SSH_COM_AGENT2_FAILURE end
def agent_socket_factory
def agent_socket_factory if Net::SSH::Authentication::PLATFORM == :win32 Pageant::socket_factory else UNIXSocket end end
def close
Simulate agent close. This agent reference is no longer able to
def close @agent_proxy = nil end
def close
Closes this socket. This agent reference is no longer able to
def close @socket.close end
def connect!
socket reports that it is an SSH2-compatible agent, this will fail
given by the attribute writers. If the agent on the other end of the
Connect to the agent process using the socket factory and socket name
def connect! debug { "connecting to Pageant ssh-agent (via java connector)" } @agent_proxy = JRubyPageant.create unless @agent_proxy.is_running raise AgentNotAvailable, "Pageant is not running!" end debug { "connection to Pageant ssh-agent (via java connector) succeeded" } rescue AgentProxyException => e error { "could not connect to Pageant ssh-agent (via java connector)" } raise AgentNotAvailable, e.message, e.backtrace end
def connect!
socket reports that it is an SSH2-compatible agent, this will fail
given by the attribute writers. If the agent on the other end of the
Connect to the agent process using the socket factory and socket name
def connect! begin debug { "connecting to ssh-agent" } @socket = agent_socket_factory.open(ENV['SSH_AUTH_SOCK']) rescue error { "could not connect to ssh-agent" } raise AgentNotAvailable, $!.message end end
def identities
Each key returned is augmented with a +comment+ property which is set
Return an array of all identities (public keys) known to the agent.
def identities debug { "getting identities from Pageant" } @agent_proxy.get_identities.map do |identity| blob = identity.get_blob key = Buffer.new(String.from_java_bytes(blob)).read_key key.extend(Key) key.java_blob = blob key.comment = String.from_java_bytes(identity.get_comment) key end rescue AgentProxyException => e raise AgentError, "Cannot get identities: #{e.message}", e.backtrace end
def identities
Each key returned is augmented with a +comment+ property which is set
Return an array of all identities (public keys) known to the agent.
def identities type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES) raise AgentError, "could not get identity count" if agent_failed(type) raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER identities = [] body.read_long.times do key = Buffer.new(body.read_string).read_key key.extend(Comment) key.comment = body.read_string identities.push key end return identities end
def initialize(logger=nil)
Creates a new Agent object, using the optional logger instance to
def initialize(logger=nil) self.logger = logger end
def initialize(logger=nil)
Creates a new Agent object, using the optional logger instance to
def initialize(logger=nil) self.logger = logger end
def negotiate!
Attempts to negotiate the SSH agent protocol version. Raises an error
def negotiate! # determine what type of agent we're communicating with type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION) if type == SSH2_AGENT_VERSION_RESPONSE raise NotImplementedError, "SSH2 agents are not yet supported" elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2 raise AgentError, "unknown response from agent: #{type}, #{body.to_s.inspect}" end end
def read_packet
tuple consisting of the packet type, and the packet's body (which
Read the next packet from the agent. This will return a two-part
def read_packet buffer = Net::SSH::Buffer.new(@socket.read(4)) buffer.append(@socket.read(buffer.read_long)) type = buffer.read_byte debug { "received agent packet #{type} len #{buffer.length-4}" } return type, buffer end
def send_and_wait(type, *args)
Send the given packet and return the subsequent reply from the agent.
def send_and_wait(type, *args) send_packet(type, *args) read_packet end
def send_packet(type, *args)
def send_packet(type, *args) buffer = Buffer.from(*args) data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*") debug { "sending agent request #{type} len #{buffer.length}" } @socket.send data, 0 end
def sign(key, data)
Using the agent and the given public key, sign the given data. The
def sign(key, data) signed = @agent_proxy.sign(key.java_blob, data.to_java_bytes) String.from_java_bytes(signed) rescue AgentProxyException => e raise AgentError, "agent could not sign data with requested identity: #{e.message}", e.backtrace end
def sign(key, data)
Using the agent and the given public key, sign the given data. The
def sign(key, data) type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0) if agent_failed(type) raise AgentError, "agent could not sign data with requested identity" elsif type != SSH2_AGENT_SIGN_RESPONSE raise AgentError, "bad authentication response #{type}" end return reply.read_string end