summaryrefslogtreecommitdiff
path: root/lib/leap_cli/ssh_key.rb
blob: 4b3c98508ab5c2c3055305b55371b91d0a5c6c49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#
# A wrapper around OpenSSL::PKey::RSA instances to provide a better api for dealing with SSH keys.
#
#

require 'net/ssh'
require 'forwardable'

module LeapCli
  class SshKey
    extend Forwardable

    attr_accessor :filename
    attr_accessor :comment

    ##
    ## CLASS METHODS
    ##

    def self.load(arg1, arg2=nil)
      key = nil
      if arg1.is_a? OpenSSL::PKey::RSA
        key = SshKey.new arg1
      elsif arg1.is_a? String
        if arg1 =~ /^ssh-/
          type, data = arg1.split(' ')
          key = SshKey.new load_from_data(data, type)
        elsif File.exists? arg1
          key = SshKey.new load_from_file(arg1)
          key.filename = arg1
        else
          key = SshKey.new load_from_data(arg1, arg2)
        end
      end
      return key
    end

    def self.load_from_file(filename)
      public_key = nil
      private_key = nil
      begin
        public_key = Net::SSH::KeyFactory.load_public_key(filename)
      rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
        begin
          private_key = Net::SSH::KeyFactory.load_private_key(filename)
        rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
        end
      end
      public_key || private_key
    end

    def self.load_from_data(data, type='ssh-rsa')
      public_key = nil
      private_key = nil
      begin
        public_key = Net::SSH::KeyFactory.load_data_public_key("#{type} #{data}")
      rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
        begin
          private_key = Net::SSH::KeyFactory.load_data_private_key("#{type} #{data}")
        rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
        end
      end
      public_key || private_key
    end

    ##
    ## INSTANCE METHODS
    ##

    public

    def initialize(rsa_key)
      @key = rsa_key
    end

    def_delegator :@key, :fingerprint, :fingerprint
    def_delegator :@key, :public?, :public?
    def_delegator :@key, :private?, :private?
    def_delegator :@key, :ssh_type, :type
    def_delegator :@key, :public_encrypt, :public_encrypt
    def_delegator :@key, :public_decrypt, :public_decrypt
    def_delegator :@key, :private_encrypt, :private_encrypt
    def_delegator :@key, :private_decrypt, :private_decrypt
    def_delegator :@key, :params, :params

    def public_key
      SshKey.new(@key.public_key)
    end

    def private_key
      SshKey.new(@key.private_key)
    end

    #
    # not sure if this will always work, but is seems to for now.
    #
    def bits
      Net::SSH::Buffer.from(:key, @key).to_s.split("\001\000").last.size * 8
    end

    def summary
      "%s %s %s (%s)" % [self.type, self.bits, self.fingerprint, self.filename || self.comment || '']
    end

    def to_s
      self.type + " " + self.key
    end

    def key
      [Net::SSH::Buffer.from(:key, @key).to_s].pack("m*").gsub(/\s/, "")
    end

    def ==(other_key)
      return false if other_key.nil?
      return false if self.class != other_key.class
      return self.to_text == other_key.to_text
    end

    def in_known_hosts?(*identifiers)
      identifiers.each do |identifier|
        Net::SSH::KnownHosts.search_for(identifier).each do |key|
          return true if self == key
        end
      end
      return false
    end

  end
end