Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. require 'socket'
  2. require 'openssl'
  3. require 'httparty'
  4. module CryptCheck
  5. module Tls
  6. class Server
  7. TCP_TIMEOUT = 10
  8. SSL_TIMEOUT = 2*TCP_TIMEOUT
  9. EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
  10. SUPPORTED_METHODS = ::OpenSSL::SSL::SSLContext::METHODS
  11. class TLSException < ::StandardError
  12. end
  13. class TLSNotAvailableException < TLSException
  14. def to_s
  15. 'TLS seems not supported on this server'
  16. end
  17. end
  18. class MethodNotAvailable < TLSException
  19. end
  20. class CipherNotAvailable < TLSException
  21. end
  22. class Timeout < ::StandardError
  23. end
  24. class TLSTimeout < Timeout
  25. end
  26. class ConnectionError < ::StandardError
  27. end
  28. attr_reader :family, :ip, :port, :hostname, :prefered_ciphers, :cert, :cert_valid, :cert_trusted, :dh
  29. def initialize(hostname, family, ip, port)
  30. @hostname, @family, @ip, @port = hostname, family, ip, port
  31. @dh = []
  32. Logger.info { name.colorize :blue }
  33. extract_cert
  34. Logger.info { '' }
  35. Logger.info { "Key : #{Tls.key_to_s self.key}" }
  36. fetch_prefered_ciphers
  37. check_supported_cipher
  38. uniq_dh
  39. end
  40. def key
  41. @cert.public_key
  42. end
  43. def cipher_size
  44. supported_ciphers.collect { |c| c.size }.min
  45. end
  46. def supported_protocols
  47. @supported_ciphers.keys
  48. end
  49. def supported_ciphers
  50. @supported_ciphers.values.flatten 1
  51. end
  52. def supported_ciphers_by_protocol(protocol)
  53. @supported_ciphers[protocol]
  54. end
  55. EXISTING_METHODS.each do |method|
  56. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  57. def #{method.to_s.downcase}?
  58. !prefered_ciphers[:#{method}].nil?
  59. end
  60. RUBY_EVAL
  61. end
  62. {
  63. md2: %w(md2WithRSAEncryption),
  64. md5: %w(md5WithRSAEncryption md5WithRSA),
  65. sha1: %w(sha1WithRSAEncryption sha1WithRSA dsaWithSHA1 dsaWithSHA1_2 ecdsa_with_SHA1)
  66. }.each do |name, signature|
  67. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  68. def #{name}_sig?
  69. #{signature}.include? @cert.signature_algorithm
  70. end
  71. RUBY_EVAL
  72. end
  73. Cipher::TYPES.each do |type, _|
  74. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  75. def #{type}?
  76. supported_ciphers.any? { |c| c.#{type}? }
  77. end
  78. RUBY_EVAL
  79. end
  80. def key_size
  81. @cert.public_key.rsa_equivalent_size
  82. end
  83. def ssl?
  84. sslv2? or sslv3?
  85. end
  86. def tls?
  87. tlsv1? or tlsv1_1? or tlsv1_2?
  88. end
  89. def tls_only?
  90. tls? and !ssl?
  91. end
  92. def pfs?
  93. supported_ciphers.any? { |c| c.pfs? }
  94. end
  95. def pfs_only?
  96. supported_ciphers.all? { |c| c.pfs? }
  97. end
  98. private
  99. def name
  100. name = "#@ip:#@port"
  101. name += " [#@hostname]" if @hostname
  102. name
  103. end
  104. def connect(&block)
  105. socket = ::Socket.new @family, sock_type
  106. sockaddr = ::Socket.sockaddr_in @port, @ip
  107. Logger.trace { "Connecting to #{@ip}:#{@port}" }
  108. begin
  109. status = socket.connect_nonblock sockaddr
  110. Logger.trace { "Connecting to #{@ip}:#{@port} status : #{status}" }
  111. raise ConnectionError, status unless status == 0
  112. Logger.trace { "Connected to #{@ip}:#{@port}" }
  113. block_given? ? block.call(socket) : nil
  114. rescue ::IO::WaitReadable
  115. Logger.trace { "Waiting for read to #{@ip}:#{@port}" }
  116. raise Timeout, "Timeout when connect to #{@ip}:#{@port} (max #{TCP_TIMEOUT.humanize})" unless IO.select [socket], nil, nil, TCP_TIMEOUT
  117. retry
  118. rescue ::IO::WaitWritable
  119. Logger.trace { "Waiting for write to #{@ip}:#{@port}" }
  120. raise Timeout, "Timeout when connect to #{@ip}:#{@port} (max #{TCP_TIMEOUT.humanize})" unless IO.select nil, [socket], nil, TCP_TIMEOUT
  121. retry
  122. ensure
  123. socket.close
  124. end
  125. end
  126. def ssl_connect(socket, context, method, &block)
  127. ssl_socket = ::OpenSSL::SSL::SSLSocket.new socket, context
  128. ssl_socket.hostname = @hostname if @hostname and method != :SSLv2
  129. Logger.trace { "SSL connecting to #{name}" }
  130. begin
  131. ssl_socket.connect_nonblock
  132. Logger.trace { "SSL connected to #{name}" }
  133. return block_given? ? block.call(ssl_socket) : nil
  134. rescue ::IO::WaitReadable
  135. Logger.trace { "Waiting for SSL read to #{name}" }
  136. raise TLSTimeout, "Timeout when TLS connect to #{@ip}:#{@port} (max #{SSL_TIMEOUT.humanize})" unless IO.select [ssl_socket], nil, nil, SSL_TIMEOUT
  137. retry
  138. rescue ::IO::WaitWritable
  139. Logger.trace { "Waiting for SSL write to #{name}" }
  140. raise TLSTimeout, "Timeout when TLS connect to #{@ip}:#{@port} (max #{SSL_TIMEOUT.humanize})" unless IO.select nil, [ssl_socket], nil, SSL_TIMEOUT
  141. retry
  142. rescue ::OpenSSL::SSL::SSLError => e
  143. case e
  144. when /state=SSLv2 read server hello A$/,
  145. /state=SSLv3 read server hello A: wrong version number$/
  146. raise MethodNotAvailable, e
  147. when /state=error: no ciphers available$/,
  148. /state=SSLv3 read server hello A: sslv3 alert handshake failure$/
  149. raise CipherNotAvailable, e
  150. end
  151. rescue SystemCallError => e
  152. case e
  153. when /^Connection reset by peer$/
  154. raise MethodNotAvailable, e
  155. end
  156. ensure
  157. ssl_socket.close
  158. end
  159. end
  160. def ssl_client(method, ciphers = nil, &block)
  161. ssl_context = ::OpenSSL::SSL::SSLContext.new method
  162. ssl_context.ciphers = ciphers if ciphers
  163. Logger.trace { "Try #{method} connection with #{ciphers}" }
  164. connect do |socket|
  165. ssl_connect socket, ssl_context, method do |ssl_socket|
  166. return block_given? ? block.call(ssl_socket) : nil
  167. end
  168. end
  169. Logger.debug { "No SSL available on #{name}" }
  170. raise CipherNotAvailable
  171. end
  172. def extract_cert
  173. EXISTING_METHODS.each do |method|
  174. next unless SUPPORTED_METHODS.include? method
  175. begin
  176. @cert, @chain = ssl_client(method) { |s| [s.peer_cert, s.peer_cert_chain] }
  177. Logger.debug { "Certificate #{@cert.subject}" }
  178. break
  179. rescue TLSException
  180. end
  181. end
  182. raise TLSNotAvailableException unless @cert
  183. @cert_valid = ::OpenSSL::SSL.verify_certificate_identity @cert, (@hostname || @ip)
  184. @cert_trusted = verify_trust @chain, @cert
  185. end
  186. def prefered_cipher(method)
  187. cipher = ssl_client(method, 'ALL:COMPLEMENTOFALL') { |s| Cipher.new method, s.cipher, s.tmp_key }
  188. Logger.info { "Prefered cipher for #{Tls.colorize method} : #{cipher.colorize}" }
  189. cipher
  190. rescue TLSException => e
  191. Logger.debug { "Method #{Tls.colorize method} not supported : #{e}" }
  192. nil
  193. end
  194. def fetch_prefered_ciphers
  195. @prefered_ciphers = {}
  196. EXISTING_METHODS.each do |method|
  197. next unless SUPPORTED_METHODS.include? method
  198. @prefered_ciphers[method] = prefered_cipher method
  199. end
  200. raise TLSNotAvailableException unless @prefered_ciphers.any? { |_, c| !c.nil? }
  201. end
  202. def available_ciphers(method)
  203. context = ::OpenSSL::SSL::SSLContext.new method
  204. context.ciphers = 'ALL:COMPLEMENTOFALL'
  205. context.ciphers
  206. end
  207. def supported_cipher?(method, cipher)
  208. dh = ssl_client method, [cipher] { |s| s.tmp_key }
  209. @dh << dh if dh
  210. cipher = Cipher.new method, cipher, dh
  211. dh = dh ? " (#{'DH'.colorize :green} : #{Tls.key_to_s dh})" : ''
  212. Logger.info { "#{Tls.colorize method} / #{cipher.colorize} : Supported#{dh}" }
  213. cipher
  214. rescue TLSException => e
  215. cipher = Cipher.new method, cipher
  216. Logger.debug { "#{Tls.colorize method} / #{cipher.colorize} : Not supported (#{e})" }
  217. nil
  218. end
  219. def check_supported_cipher
  220. Logger.info { '' }
  221. @supported_ciphers = {}
  222. EXISTING_METHODS.each do |method|
  223. next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method]
  224. supported_ciphers = available_ciphers(method).collect { |c| supported_cipher? method, c }.reject { |c| c.nil? }
  225. Logger.info { '' } unless supported_ciphers.empty?
  226. @supported_ciphers[method] = supported_ciphers
  227. end
  228. end
  229. def verify_trust(chain, cert)
  230. store = ::OpenSSL::X509::Store.new
  231. store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
  232. store.set_default_paths
  233. %w(/etc/ssl/certs).each do |directory|
  234. ::Dir.glob(::File.join directory, '*.pem').each do |file|
  235. cert = ::OpenSSL::X509::Certificate.new ::File.read file
  236. begin
  237. store.add_cert cert
  238. rescue ::OpenSSL::X509::StoreError
  239. end
  240. end
  241. end
  242. chain.each do |cert|
  243. begin
  244. store.add_cert cert
  245. rescue ::OpenSSL::X509::StoreError
  246. end
  247. end
  248. trusted = store.verify cert
  249. p store.error_string unless trusted
  250. trusted
  251. end
  252. def uniq_dh
  253. dh, find = [], []
  254. @dh.each do |k|
  255. f = [k.type, k.size]
  256. unless find.include? f
  257. dh << k
  258. find << f
  259. end
  260. end
  261. @dh = dh
  262. end
  263. end
  264. class TcpServer < Server
  265. private
  266. def sock_type
  267. ::Socket::SOCK_STREAM
  268. end
  269. end
  270. class UdpServer < Server
  271. private
  272. def sock_type
  273. ::Socket::SOCK_DGRAM
  274. end
  275. end
  276. end
  277. end