diff options
Diffstat (limited to 'jni/ruby/test/open-uri')
| -rw-r--r-- | jni/ruby/test/open-uri/test_open-uri.rb | 832 | ||||
| -rw-r--r-- | jni/ruby/test/open-uri/test_ssl.rb | 390 | 
2 files changed, 1222 insertions, 0 deletions
| diff --git a/jni/ruby/test/open-uri/test_open-uri.rb b/jni/ruby/test/open-uri/test_open-uri.rb new file mode 100644 index 0000000..3545f5d --- /dev/null +++ b/jni/ruby/test/open-uri/test_open-uri.rb @@ -0,0 +1,832 @@ +require 'test/unit' +require 'open-uri' +require 'webrick' +require 'webrick/httpproxy' +begin +  require 'zlib' +rescue LoadError +end + +class TestOpenURI < Test::Unit::TestCase + +  NullLog = Object.new +  def NullLog.<<(arg) +  end + +  def with_http(log_tester=lambda {|log| assert_equal([], log) }) +    log = [] +    logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN) +    Dir.mktmpdir {|dr| +      srv = WEBrick::HTTPServer.new({ +        :DocumentRoot => dr, +        :ServerType => Thread, +        :Logger => logger, +        :AccessLog => [[NullLog, ""]], +        :BindAddress => '127.0.0.1', +        :Port => 0}) +      _, port, _, host = srv.listeners[0].addr +      server_thread = srv.start +      server_thread2 = Thread.new { +        server_thread.join +        if log_tester +          log_tester.call(log) +        end +      } +      client_thread = Thread.new { +        begin +          yield srv, dr, "http://#{host}:#{port}", server_thread, log +        ensure +          srv.shutdown +        end +      } +      assert_join_threads([client_thread, server_thread2]) +    } +  end + +  def with_env(h) +    begin +      old = {} +      h.each_key {|k| old[k] = ENV[k] } +      h.each {|k, v| ENV[k] = v } +      yield +    ensure +      h.each_key {|k| ENV[k] = old[k] } +    end +  end + +  def setup +    @proxies = %w[http_proxy HTTP_PROXY ftp_proxy FTP_PROXY no_proxy] +    @old_proxies = @proxies.map {|k| ENV[k] } +    @proxies.each {|k| ENV[k] = nil } +  end + +  def teardown +    @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } +  end + +  def test_200 +    with_http {|srv, dr, url| +      srv.mount_proc("/foo200", lambda { |req, res| res.body = "foo200" } ) +      open("#{url}/foo200") {|f| +        assert_equal("200", f.status[0]) +        assert_equal("foo200", f.read) +      } +    } +  end + +  def test_200big +    with_http {|srv, dr, url| +      content = "foo200big"*10240 +      srv.mount_proc("/foo200big", lambda { |req, res| res.body = content } ) +      open("#{url}/foo200big") {|f| +        assert_equal("200", f.status[0]) +        assert_equal(content, f.read) +      } +    } +  end + +  def test_404 +    log_tester = lambda {|server_log| +      assert_equal(1, server_log.length) +      assert_match(%r{ERROR `/not-exist' not found}, server_log[0]) +    } +    with_http(log_tester) {|srv, dr, url, server_thread, server_log| +      exc = assert_raise(OpenURI::HTTPError) { open("#{url}/not-exist") {} } +      assert_equal("404", exc.io.status[0]) +    } +  end + +  def test_open_uri +    with_http {|srv, dr, url| +      srv.mount_proc("/foo_ou", lambda { |req, res| res.body = "foo_ou" } ) +      u = URI("#{url}/foo_ou") +      open(u) {|f| +        assert_equal("200", f.status[0]) +        assert_equal("foo_ou", f.read) +      } +    } +  end + +  def test_open_too_many_arg +    assert_raise(ArgumentError) { open("http://192.0.2.1/tma", "r", 0666, :extra) {} } +  end + +  def test_read_timeout +    TCPServer.open("127.0.0.1", 0) {|serv| +      port = serv.addr[1] +      th = Thread.new { +        sock = serv.accept +        begin +          req = sock.gets("\r\n\r\n") +          assert_match(%r{\AGET /foo/bar }, req) +          sock.print "HTTP/1.0 200 OK\r\n" +          sock.print "Content-Length: 4\r\n\r\n" +          sleep 1 +          sock.print "ab\r\n" +        ensure +          sock.close +        end +      } +      begin +        assert_raise(Net::ReadTimeout) { URI("http://127.0.0.1:#{port}/foo/bar").read(:read_timeout=>0.1) } +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +  def test_open_timeout +    assert_raises(Net::OpenTimeout) do +      URI("http://example.com/").read(open_timeout: 0.000001) +    end if false # avoid external resources in tests + +    with_http {|srv, dr, url| +      url += '/' +      srv.mount_proc('/', lambda { |_, res| res.body = 'hi' }) +      begin +        URI(url).read(open_timeout: 0.000001) +      rescue Net::OpenTimeout +        # not guaranteed to fire, since the kernel negotiates the +        # TCP connection even if the server thread is sleeping +      end +      assert_equal 'hi', URI(url).read(open_timeout: 60), 'should not timeout' +    } +  end + +  def test_invalid_option +    assert_raise(ArgumentError) { open("http://127.0.0.1/", :invalid_option=>true) {} } +  end + +  def test_mode +    with_http {|srv, dr, url| +      srv.mount_proc("/mode", lambda { |req, res| res.body = "mode" } ) +      open("#{url}/mode", "r") {|f| +        assert_equal("200", f.status[0]) +        assert_equal("mode", f.read) +      } +      open("#{url}/mode", "r", 0600) {|f| +        assert_equal("200", f.status[0]) +        assert_equal("mode", f.read) +      } +      assert_raise(ArgumentError) { open("#{url}/mode", "a") {} } +      open("#{url}/mode", "r:us-ascii") {|f| +        assert_equal(Encoding::US_ASCII, f.read.encoding) +      } +      open("#{url}/mode", "r:utf-8") {|f| +        assert_equal(Encoding::UTF_8, f.read.encoding) +      } +      assert_raise(ArgumentError) { open("#{url}/mode", "r:invalid-encoding") {} } +    } +  end + +  def test_without_block +    with_http {|srv, dr, url| +      srv.mount_proc("/without_block", lambda { |req, res| res.body = "without_block" } ) +      begin +        f = open("#{url}/without_block") +        assert_equal("200", f.status[0]) +        assert_equal("without_block", f.read) +      ensure +        f.close if f && !f.closed? +      end +    } +  end + +  def test_close_in_block_small +    with_http {|srv, dr, url| +      srv.mount_proc("/close200", lambda { |req, res| res.body = "close200" } ) +      assert_nothing_raised { +        open("#{url}/close200") {|f| +          f.close +        } +      } +    } +  end + +  def test_close_in_block_big +    with_http {|srv, dr, url| +      content = "close200big"*10240 +      srv.mount_proc("/close200big", lambda { |req, res| res.body = content } ) +      assert_nothing_raised { +        open("#{url}/close200big") {|f| +          f.close +        } +      } +    } +  end + +  def test_header +    myheader1 = 'barrrr' +    myheader2 = nil +    with_http {|srv, dr, url| +      srv.mount_proc("/h/") {|req, res| myheader2 = req['myheader']; res.body = "foo" } +      open("#{url}/h/", 'MyHeader'=>myheader1) {|f| +        assert_equal("foo", f.read) +        assert_equal(myheader1, myheader2) +      } +    } +  end + +  def test_multi_proxy_opt +    assert_raise(ArgumentError) { +      open("http://127.0.0.1/", :proxy_http_basic_authentication=>true, :proxy=>true) {} +    } +  end + +  def test_non_http_proxy +    assert_raise(RuntimeError) { +      open("http://127.0.0.1/", :proxy=>URI("ftp://127.0.0.1/")) {} +    } +  end + +  def test_proxy +    with_http {|srv, dr, url| +      proxy_log = StringIO.new('') +      proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) +      proxy_auth_log = '' +      proxy = WEBrick::HTTPProxyServer.new({ +        :ServerType => Thread, +        :Logger => proxy_logger, +        :AccessLog => [[NullLog, ""]], +        :ProxyAuthProc => lambda {|req, res| +          proxy_auth_log << req.request_line +        }, +        :BindAddress => '127.0.0.1', +        :Port => 0}) +      _, proxy_port, _, proxy_host = proxy.listeners[0].addr +      proxy_url = "http://#{proxy_host}:#{proxy_port}/" +      begin +        proxy_thread = proxy.start +        srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) +        open("#{url}/proxy", :proxy=>proxy_url) {|f| +          assert_equal("200", f.status[0]) +          assert_equal("proxy", f.read) +        } +        assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear +        open("#{url}/proxy", :proxy=>URI(proxy_url)) {|f| +          assert_equal("200", f.status[0]) +          assert_equal("proxy", f.read) +        } +        assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear +        open("#{url}/proxy", :proxy=>nil) {|f| +          assert_equal("200", f.status[0]) +          assert_equal("proxy", f.read) +        } +        assert_equal("", proxy_auth_log); proxy_auth_log.clear +        assert_raise(ArgumentError) { +          open("#{url}/proxy", :proxy=>:invalid) {} +        } +        assert_equal("", proxy_auth_log); proxy_auth_log.clear +        with_env("http_proxy"=>proxy_url) { +          # should not use proxy for 127.0.0.0/8. +          open("#{url}/proxy") {|f| +            assert_equal("200", f.status[0]) +            assert_equal("proxy", f.read) +          } +        } +        assert_equal("", proxy_auth_log); proxy_auth_log.clear +      ensure +        proxy.shutdown +        proxy_thread.join +      end +      assert_equal("", proxy_log.string) +    } +  end + +  def test_proxy_http_basic_authentication_failure +    with_http {|srv, dr, url| +      proxy_log = StringIO.new('') +      proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) +      proxy_auth_log = '' +      proxy = WEBrick::HTTPProxyServer.new({ +        :ServerType => Thread, +        :Logger => proxy_logger, +        :AccessLog => [[NullLog, ""]], +        :ProxyAuthProc => lambda {|req, res| +          proxy_auth_log << req.request_line +          if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" +            raise WEBrick::HTTPStatus::ProxyAuthenticationRequired +          end +        }, +        :BindAddress => '127.0.0.1', +        :Port => 0}) +      _, proxy_port, _, proxy_host = proxy.listeners[0].addr +      proxy_url = "http://#{proxy_host}:#{proxy_port}/" +      begin +        th = proxy.start +        srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) +        exc = assert_raise(OpenURI::HTTPError) { open("#{url}/proxy", :proxy=>proxy_url) {} } +        assert_equal("407", exc.io.status[0]) +        assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear +      ensure +        proxy.shutdown +        th.join +      end +      assert_match(/ERROR WEBrick::HTTPStatus::ProxyAuthenticationRequired/, proxy_log.string) +    } +  end + +  def test_proxy_http_basic_authentication_success +    with_http {|srv, dr, url| +      proxy_log = StringIO.new('') +      proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) +      proxy_auth_log = '' +      proxy = WEBrick::HTTPProxyServer.new({ +        :ServerType => Thread, +        :Logger => proxy_logger, +        :AccessLog => [[NullLog, ""]], +        :ProxyAuthProc => lambda {|req, res| +          proxy_auth_log << req.request_line +          if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" +            raise WEBrick::HTTPStatus::ProxyAuthenticationRequired +          end +        }, +        :BindAddress => '127.0.0.1', +        :Port => 0}) +      _, proxy_port, _, proxy_host = proxy.listeners[0].addr +      proxy_url = "http://#{proxy_host}:#{proxy_port}/" +      begin +        th = proxy.start +        srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) +        open("#{url}/proxy", +            :proxy_http_basic_authentication=>[proxy_url, "user", "pass"]) {|f| +          assert_equal("200", f.status[0]) +          assert_equal("proxy", f.read) +        } +        assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear +        assert_raise(ArgumentError) { +          open("#{url}/proxy", +              :proxy_http_basic_authentication=>[true, "user", "pass"]) {} +        } +        assert_equal("", proxy_auth_log); proxy_auth_log.clear +      ensure +        proxy.shutdown +        th.join +      end +      assert_equal("", proxy_log.string) +    } +  end + +  def test_redirect +    with_http {|srv, dr, url| +      srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } +      srv.mount_proc("/r2/") {|req, res| res.body = "r2" } +      srv.mount_proc("/to-file/") {|req, res| res.status = 301; res["location"] = "file:///foo" } +      open("#{url}/r1/") {|f| +        assert_equal("#{url}/r2", f.base_uri.to_s) +        assert_equal("r2", f.read) +      } +      assert_raise(OpenURI::HTTPRedirect) { open("#{url}/r1/", :redirect=>false) {} } +      assert_raise(RuntimeError) { open("#{url}/to-file/") {} } +    } +  end + +  def test_redirect_loop +    with_http {|srv, dr, url| +      srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } +      srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r1"; res.body = "r2" } +      assert_raise(RuntimeError) { open("#{url}/r1/") {} } +    } +  end + +  def test_redirect_relative +    TCPServer.open("127.0.0.1", 0) {|serv| +      port = serv.addr[1] +      th = Thread.new { +        sock = serv.accept +        begin +          req = sock.gets("\r\n\r\n") +          assert_match(%r{\AGET /foo/bar }, req) +          sock.print "HTTP/1.0 302 Found\r\n" +          sock.print "Location: ../baz\r\n\r\n" +        ensure +          sock.close +        end +        sock = serv.accept +        begin +          req = sock.gets("\r\n\r\n") +          assert_match(%r{\AGET /baz }, req) +          sock.print "HTTP/1.0 200 OK\r\n" +          sock.print "Content-Length: 4\r\n\r\n" +          sock.print "ab\r\n" +        ensure +          sock.close +        end +      } +      begin +        content = URI("http://127.0.0.1:#{port}/foo/bar").read +        assert_equal("ab\r\n", content) +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +  def test_redirect_invalid +    TCPServer.open("127.0.0.1", 0) {|serv| +      port = serv.addr[1] +      th = Thread.new { +        sock = serv.accept +        begin +          req = sock.gets("\r\n\r\n") +          assert_match(%r{\AGET /foo/bar }, req) +          sock.print "HTTP/1.0 302 Found\r\n" +          sock.print "Location: ::\r\n\r\n" +        ensure +          sock.close +        end +      } +      begin +        assert_raise(OpenURI::HTTPError) { +          URI("http://127.0.0.1:#{port}/foo/bar").read +        } +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +  def setup_redirect_auth(srv, url) +    srv.mount_proc("/r1/") {|req, res| +      res.status = 301 +      res["location"] = "#{url}/r2" +    } +    srv.mount_proc("/r2/") {|req, res| +      if req["Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" +        raise WEBrick::HTTPStatus::Unauthorized +      end +      res.body = "r2" +    } +  end + +  def test_redirect_auth_success +    with_http {|srv, dr, url| +      setup_redirect_auth(srv, url) +      open("#{url}/r2/", :http_basic_authentication=>['user', 'pass']) {|f| +        assert_equal("r2", f.read) +      } +    } +  end + +  def test_redirect_auth_failure_r2 +    log_tester = lambda {|server_log| +      assert_equal(1, server_log.length) +      assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, server_log[0]) +    } +    with_http(log_tester) {|srv, dr, url, server_thread, server_log| +      setup_redirect_auth(srv, url) +      exc = assert_raise(OpenURI::HTTPError) { open("#{url}/r2/") {} } +      assert_equal("401", exc.io.status[0]) +    } +  end + +  def test_redirect_auth_failure_r1 +    log_tester = lambda {|server_log| +      assert_equal(1, server_log.length) +      assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, server_log[0]) +    } +    with_http(log_tester) {|srv, dr, url, server_thread, server_log| +      setup_redirect_auth(srv, url) +      exc = assert_raise(OpenURI::HTTPError) { open("#{url}/r1/", :http_basic_authentication=>['user', 'pass']) {} } +      assert_equal("401", exc.io.status[0]) +    } +  end + +  def test_userinfo +    assert_raise(ArgumentError) { open("http://user:pass@127.0.0.1/") {} } +  end + +  def test_progress +    with_http {|srv, dr, url| +      content = "a" * 100000 +      srv.mount_proc("/data/") {|req, res| res.body = content } +      length = [] +      progress = [] +      open("#{url}/data/", +           :content_length_proc => lambda {|n| length << n }, +           :progress_proc => lambda {|n| progress << n } +          ) {|f| +        assert_equal(1, length.length) +        assert_equal(content.length, length[0]) +        assert(progress.length>1,"maybe test is wrong") +        assert(progress.sort == progress,"monotone increasing expected but was\n#{progress.inspect}") +        assert_equal(content.length, progress[-1]) +        assert_equal(content, f.read) +      } +    } +  end + +  def test_progress_chunked +    with_http {|srv, dr, url| +      content = "a" * 100000 +      srv.mount_proc("/data/") {|req, res| res.body = content; res.chunked = true } +      length = [] +      progress = [] +      open("#{url}/data/", +           :content_length_proc => lambda {|n| length << n }, +           :progress_proc => lambda {|n| progress << n } +          ) {|f| +        assert_equal(1, length.length) +        assert_equal(nil, length[0]) +        assert(progress.length>1,"maybe test is worng") +        assert(progress.sort == progress,"monotone increasing expected but was\n#{progress.inspect}") +        assert_equal(content.length, progress[-1]) +        assert_equal(content, f.read) +      } +    } +  end + +  def test_uri_read +    with_http {|srv, dr, url| +      srv.mount_proc("/uriread", lambda { |req, res| res.body = "uriread" } ) +      data = URI("#{url}/uriread").read +      assert_equal("200", data.status[0]) +      assert_equal("uriread", data) +    } +  end + +  def test_encoding +    with_http {|srv, dr, url| +      content_u8 = "\u3042" +      content_ej = "\xa2\xa4".force_encoding("euc-jp") +      srv.mount_proc("/u8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset=utf-8' } +      srv.mount_proc("/ej/") {|req, res| res.body = content_ej; res['content-type'] = 'TEXT/PLAIN; charset=EUC-JP' } +      srv.mount_proc("/nc/") {|req, res| res.body = "aa"; res['content-type'] = 'Text/Plain' } +      open("#{url}/u8/") {|f| +        assert_equal(content_u8, f.read) +        assert_equal("text/plain", f.content_type) +        assert_equal("utf-8", f.charset) +      } +      open("#{url}/ej/") {|f| +        assert_equal(content_ej, f.read) +        assert_equal("text/plain", f.content_type) +        assert_equal("euc-jp", f.charset) +      } +      open("#{url}/nc/") {|f| +        assert_equal("aa", f.read) +        assert_equal("text/plain", f.content_type) +        assert_equal("iso-8859-1", f.charset) +        assert_equal("unknown", f.charset { "unknown" }) +      } +    } +  end + +  def test_quoted_attvalue +    with_http {|srv, dr, url| +      content_u8 = "\u3042" +      srv.mount_proc("/qu8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset="utf\-8"' } +      open("#{url}/qu8/") {|f| +        assert_equal(content_u8, f.read) +        assert_equal("text/plain", f.content_type) +        assert_equal("utf-8", f.charset) +      } +    } +  end + +  def test_last_modified +    with_http {|srv, dr, url| +      srv.mount_proc("/data/") {|req, res| res.body = "foo"; res['last-modified'] = 'Fri, 07 Aug 2009 06:05:04 GMT' } +      open("#{url}/data/") {|f| +        assert_equal("foo", f.read) +        assert_equal(Time.utc(2009,8,7,6,5,4), f.last_modified) +      } +    } +  end + +  def test_content_encoding +    with_http {|srv, dr, url| +      content = "abc" * 10000 +      Zlib::GzipWriter.wrap(StringIO.new(content_gz="".force_encoding("ascii-8bit"))) {|z| z.write content } +      srv.mount_proc("/data/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip' } +      srv.mount_proc("/data2/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip'; res.chunked = true } +      srv.mount_proc("/noce/") {|req, res| res.body = content_gz } +      open("#{url}/data/") {|f| +        assert_equal [], f.content_encoding +        assert_equal(content, f.read) +      } +      open("#{url}/data2/") {|f| +        assert_equal [], f.content_encoding +        assert_equal(content, f.read) +      } +      open("#{url}/noce/") {|f| +        assert_equal [], f.content_encoding +        assert_equal(content_gz, f.read.force_encoding("ascii-8bit")) +      } +    } +  end if defined?(Zlib::GzipWriter) + +  def test_multiple_cookies +    with_http {|srv, dr, url| +      srv.mount_proc("/mcookie/") {|req, res| +        res.cookies << "name1=value1; blabla" +        res.cookies << "name2=value2; blabla" +        res.body = "foo" +      } +      open("#{url}/mcookie/") {|f| +        assert_equal("foo", f.read) +        assert_equal(["name1=value1; blabla", "name2=value2; blabla"], +                     f.metas['set-cookie'].sort) +      } +    } +  end + +  # 192.0.2.0/24 is TEST-NET.  [RFC3330] + +  def test_ftp_invalid_request +    assert_raise(ArgumentError) { URI("ftp://127.0.0.1/").read } +    assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db").read } +    assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab").read } +    assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db/f").read } +    assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab/f").read } +    assert_nothing_raised(URI::InvalidComponentError) { URI("ftp://127.0.0.1/d/f;type=x") } +  end + +  def test_ftp +    TCPServer.open("127.0.0.1", 0) {|serv| +      _, port, _, host = serv.addr +      th = Thread.new { +        s = serv.accept +        begin +          s.print "220 Test FTP Server\r\n" +          assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" +          assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" +          assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" +          assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" +          assert_equal("PASV\r\n", s.gets) +          TCPServer.open("127.0.0.1", 0) {|data_serv| +            _, data_serv_port, _, _ = data_serv.addr +            hi = data_serv_port >> 8 +            lo = data_serv_port & 0xff +            s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" +            assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" +            data_sock = data_serv.accept +            begin +              data_sock << "content" +            ensure +              data_sock.close +            end +            s.print "226 transfer complete\r\n" +            assert_nil(s.gets) +          } +        ensure +          s.close if s +        end +      } +      begin +        content = URI("ftp://#{host}:#{port}/foo/bar").read +        assert_equal("content", content) +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +  def test_ftp_active +    TCPServer.open("127.0.0.1", 0) {|serv| +      _, port, _, host = serv.addr +      th = Thread.new { +        s = serv.accept +        begin +          content = "content" +          s.print "220 Test FTP Server\r\n" +          assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" +          assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" +          assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" +          assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" +          assert(m = /\APORT 127,0,0,1,(\d+),(\d+)\r\n\z/.match(s.gets)) +          active_port = m[1].to_i << 8 | m[2].to_i +          TCPSocket.open("127.0.0.1", active_port) {|data_sock| +            s.print "200 data connection opened\r\n" +            assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" +            begin +              data_sock << content +            ensure +              data_sock.close +            end +            s.print "226 transfer complete\r\n" +            assert_nil(s.gets) +          } +        ensure +          s.close if s +        end +      } +      begin +        content = URI("ftp://#{host}:#{port}/foo/bar").read(:ftp_active_mode=>true) +        assert_equal("content", content) +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +  def test_ftp_ascii +    TCPServer.open("127.0.0.1", 0) {|serv| +      _, port, _, host = serv.addr +      th = Thread.new { +        s = serv.accept +        begin +          content = "content" +          s.print "220 Test FTP Server\r\n" +          assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" +          assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" +          assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" +          assert_equal("CWD /foo\r\n", s.gets); s.print "250 CWD successful\r\n" +          assert_equal("TYPE A\r\n", s.gets); s.print "200 type set to A\r\n" +          assert_equal("SIZE bar\r\n", s.gets); s.print "213 #{content.bytesize}\r\n" +          assert_equal("PASV\r\n", s.gets) +          TCPServer.open("127.0.0.1", 0) {|data_serv| +            _, data_serv_port, _, _ = data_serv.addr +            hi = data_serv_port >> 8 +            lo = data_serv_port & 0xff +            s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" +            assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" +            data_sock = data_serv.accept +            begin +              data_sock << content +            ensure +              data_sock.close +            end +            s.print "226 transfer complete\r\n" +            assert_nil(s.gets) +          } +        ensure +          s.close if s +        end +      } +      begin +        length = [] +        progress = [] +        content = URI("ftp://#{host}:#{port}/%2Ffoo/b%61r;type=a").read( +         :content_length_proc => lambda {|n| length << n }, +         :progress_proc => lambda {|n| progress << n }) +        assert_equal("content", content) +        assert_equal([7], length) +        assert_equal(7, progress.inject(&:+)) +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +  def test_ftp_over_http_proxy +    TCPServer.open("127.0.0.1", 0) {|proxy_serv| +      proxy_port = proxy_serv.addr[1] +      th = Thread.new { +        proxy_sock = proxy_serv.accept +        begin +          req = proxy_sock.gets("\r\n\r\n") +          assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) +          proxy_sock.print "HTTP/1.0 200 OK\r\n" +          proxy_sock.print "Content-Length: 4\r\n\r\n" +          proxy_sock.print "ab\r\n" +        ensure +          proxy_sock.close +        end +      } +      begin +        with_env('ftp_proxy'=>"http://127.0.0.1:#{proxy_port}") { +          content = URI("ftp://192.0.2.1/foo/bar").read +          assert_equal("ab\r\n", content) +        } +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +  def test_ftp_over_http_proxy_auth +    TCPServer.open("127.0.0.1", 0) {|proxy_serv| +      proxy_port = proxy_serv.addr[1] +      th = Thread.new { +        proxy_sock = proxy_serv.accept +        begin +          req = proxy_sock.gets("\r\n\r\n") +          assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) +          assert_match(%r{Proxy-Authorization: Basic #{['proxy-user:proxy-password'].pack('m').chomp}\r\n}, req) +          proxy_sock.print "HTTP/1.0 200 OK\r\n" +          proxy_sock.print "Content-Length: 4\r\n\r\n" +          proxy_sock.print "ab\r\n" +        ensure +          proxy_sock.close +        end +      } +      begin +        content = URI("ftp://192.0.2.1/foo/bar").read( +          :proxy_http_basic_authentication => ["http://127.0.0.1:#{proxy_port}", "proxy-user", "proxy-password"]) +        assert_equal("ab\r\n", content) +      ensure +        Thread.kill(th) +        th.join +      end +    } +  end + +end + diff --git a/jni/ruby/test/open-uri/test_ssl.rb b/jni/ruby/test/open-uri/test_ssl.rb new file mode 100644 index 0000000..fa40f6d --- /dev/null +++ b/jni/ruby/test/open-uri/test_ssl.rb @@ -0,0 +1,390 @@ +require 'test/unit' +require 'open-uri' +require 'stringio' +require 'webrick' +begin +  require 'openssl' +  require 'webrick/https' +  require_relative '../openssl/utils' +rescue LoadError +end +require 'webrick/httpproxy' + +class TestOpenURISSL < Test::Unit::TestCase +end + +class TestOpenURISSL +  NullLog = Object.new +  def NullLog.<<(arg) +  end + +  def with_https(log_tester=lambda {|log| assert_equal([], log) }) +    log = [] +    logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN) +    Dir.mktmpdir {|dr| +      srv = WEBrick::HTTPServer.new({ +        :DocumentRoot => dr, +        :ServerType => Thread, +        :Logger => logger, +        :AccessLog => [[NullLog, ""]], +        :SSLEnable => true, +        :SSLCertificate => OpenSSL::X509::Certificate.new(SERVER_CERT), +        :SSLPrivateKey => OpenSSL::PKey::RSA.new(SERVER_KEY), +        :SSLTmpDhCallback => proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }, +        :BindAddress => '127.0.0.1', +        :Port => 0}) +      _, port, _, host = srv.listeners[0].addr +      threads = [] +      server_thread = srv.start +      threads << Thread.new { +        server_thread.join +        if log_tester +          log_tester.call(log) +        end +      } +      threads << Thread.new { +        begin +          yield srv, dr, "https://#{host}:#{port}", server_thread, log, threads +        ensure +          srv.shutdown +        end +      } +      assert_join_threads(threads) +    } +  end + +  def setup +    @proxies = %w[http_proxy HTTP_PROXY https_proxy HTTPS_PROXY ftp_proxy FTP_PROXY no_proxy] +    @old_proxies = @proxies.map {|k| ENV[k] } +    @proxies.each {|k| ENV[k] = nil } +  end + +  def teardown +    @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } +  end + +  def setup_validation(srv, dr) +    cacert_filename = "#{dr}/cacert.pem" +    open(cacert_filename, "w") {|f| f << CA_CERT } +    srv.mount_proc("/data", lambda { |req, res| res.body = "ddd" } ) +    cacert_filename +  end + +  def test_validation_success +    with_https {|srv, dr, url| +      cacert_filename = setup_validation(srv, dr) +      open("#{url}/data", :ssl_ca_cert => cacert_filename) {|f| +        assert_equal("200", f.status[0]) +        assert_equal("ddd", f.read) +      } +    } +  end + +  def test_validation_noverify +    with_https {|srv, dr, url| +      setup_validation(srv, dr) +      open("#{url}/data", :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE) {|f| +        assert_equal("200", f.status[0]) +        assert_equal("ddd", f.read) +      } +    } +  end + +  def test_validation_failure +    unless /mswin|mingw/ =~ RUBY_PLATFORM +      # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by +      # WEBrick +      log_tester = lambda {|server_log| +        assert_equal(1, server_log.length) +        assert_match(/ERROR OpenSSL::SSL::SSLError:/, server_log[0]) +      } +    end +    with_https(log_tester) {|srv, dr, url, server_thread, server_log| +      setup_validation(srv, dr) +      assert_raise(OpenSSL::SSL::SSLError) { open("#{url}/data") {} } +    } +  end + +  def with_https_proxy(proxy_log_tester=lambda {|proxy_log, proxy_access_log| assert_equal([], proxy_log) }) +    proxy_log = [] +    proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN) +    with_https {|srv, dr, url, server_thread, server_log, threads| +      srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) +      cacert_filename = "#{dr}/cacert.pem" +      open(cacert_filename, "w") {|f| f << CA_CERT } +      cacert_directory = "#{dr}/certs" +      Dir.mkdir cacert_directory +      hashed_name = "%08x.0" % OpenSSL::X509::Certificate.new(CA_CERT).subject.hash +      open("#{cacert_directory}/#{hashed_name}", "w") {|f| f << CA_CERT } +      proxy = WEBrick::HTTPProxyServer.new({ +        :ServerType => Thread, +        :Logger => proxy_logger, +        :AccessLog => [[proxy_access_log=[], WEBrick::AccessLog::COMMON_LOG_FORMAT]], +        :BindAddress => '127.0.0.1', +        :Port => 0}) +      _, proxy_port, _, proxy_host = proxy.listeners[0].addr +      proxy_thread = proxy.start +      threads << Thread.new { +        proxy_thread.join +        if proxy_log_tester +          proxy_log_tester.call(proxy_log, proxy_access_log) +        end +      } +      begin +        yield srv, dr, url, cacert_filename, cacert_directory, proxy_host, proxy_port +      ensure +        proxy.shutdown +      end +    } +  end + +  def test_proxy_cacert_file +    url = nil +    proxy_log_tester = lambda {|proxy_log, proxy_access_log| +      assert_equal(1, proxy_access_log.length) +      assert_match(%r[CONNECT #{url.sub(%r{\Ahttps://}, '')} ], proxy_access_log[0]) +      assert_equal([], proxy_log) +    } +    with_https_proxy(proxy_log_tester) {|srv, dr, url_, cacert_filename, cacert_directory, proxy_host, proxy_port| +      url = url_ +      open("#{url}/proxy", :proxy=>"http://#{proxy_host}:#{proxy_port}/", :ssl_ca_cert => cacert_filename) {|f| +        assert_equal("200", f.status[0]) +        assert_equal("proxy", f.read) +      } +    } +  end + +  def test_proxy_cacert_dir +    url = nil +    proxy_log_tester = lambda {|proxy_log, proxy_access_log| +      assert_equal(1, proxy_access_log.length) +      assert_match(%r[CONNECT #{url.sub(%r{\Ahttps://}, '')} ], proxy_access_log[0]) +      assert_equal([], proxy_log) +    } +    with_https_proxy(proxy_log_tester) {|srv, dr, url_, cacert_filename, cacert_directory, proxy_host, proxy_port| +      url = url_ +      open("#{url}/proxy", :proxy=>"http://#{proxy_host}:#{proxy_port}/", :ssl_ca_cert => cacert_directory) {|f| +        assert_equal("200", f.status[0]) +        assert_equal("proxy", f.read) +      } +    } +  end + +end if defined?(OpenSSL::TestUtils) + +if defined?(OpenSSL::TestUtils) +# mkdir demoCA demoCA/private demoCA/newcerts +# touch demoCA/index.txt +# echo 00 > demoCA/serial +# openssl req -new -keyout demoCA/private/cakey.pem -out demoCA/careq.pem +# openssl ca -out demoCA/cacert.pem -startdate 090101000000Z -enddate 491231235959Z -batch -keyfile demoCA/private/cakey.pem -selfsign -infiles demoCA/careq.pem + +# cp /etc/ssl/openssl.cnf openssl-server.cnf # Debian +# vi openssl-server.cnf # enable "nsCertType = server" +# mkdir server +# openssl genrsa -des3 -out server/server.key 1024 +# openssl rsa -in server/server.key -out server/servernopass.key +# openssl req -new -days 365 -key server/servernopass.key -out server/csr.pem +# openssl ca -config openssl-server.cnf -startdate 090101000000Z -enddate 491231235959Z -in server/csr.pem -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out server/cert.pem + +# demoCA/cacert.pem => TestOpenURISSL::CA_CERT +# server/cert.pem => TestOpenURISSL::SERVER_CERT +# server/servernopass.key => TestOpenURISSL::SERVER_KEY + +TestOpenURISSL::CA_CERT = <<'End' +Certificate: +    Data: +        Version: 3 (0x2) +        Serial Number: 0 (0x0) +        Signature Algorithm: sha1WithRSAEncryption +        Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA +        Validity +            Not Before: Jan  1 00:00:00 2009 GMT +            Not After : Dec 31 23:59:59 2049 GMT +        Subject: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA +        Subject Public Key Info: +            Public Key Algorithm: rsaEncryption +            RSA Public Key: (1024 bit) +                Modulus (1024 bit): +                    00:9f:58:19:39:bc:ea:0c:b8:c3:5d:12:a7:d8:20: +                    6c:53:ac:91:34:c8:b4:db:3f:56:f6:75:b6:6c:23: +                    80:23:6a:5f:b3:f6:9a:3e:00:b4:16:19:1c:9c:2c: +                    8d:e8:53:d5:0b:f1:52:3f:7b:60:93:86:ae:89:ab: +                    20:82:9a:b6:72:14:3c:4d:a9:0b:6c:34:79:9e:d3: +                    14:82:6d:c9:3b:90:d9:5e:68:6f:8c:b5:d8:09:f4: +                    6f:3b:22:9f:5e:81:9c:37:df:cf:90:36:65:57:dc: +                    ad:31:ca:8b:48:92:a7:3c:1e:42:e9:1c:4e:1e:cb: +                    36:c1:44:4e:ab:9a:b2:73:6d +                Exponent: 65537 (0x10001) +        X509v3 extensions: +            X509v3 Basic Constraints: +                CA:FALSE +            Netscape Comment: +                OpenSSL Generated Certificate +            X509v3 Subject Key Identifier: +                24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F +            X509v3 Authority Key Identifier: +                keyid:24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F + +    Signature Algorithm: sha1WithRSAEncryption +        13:eb:db:ca:cd:90:f2:09:9e:d9:72:70:5e:42:5d:11:84:ce: +        00:1d:c4:2f:41:d2:3e:16:e5:d4:97:1f:43:a9:a7:9c:fa:60: +        c4:35:96:f2:f6:0d:13:6d:0f:36:dd:59:03:08:ee:2e:a6:df: +        9e:d8:6d:ca:72:8f:02:c2:2b:53:7b:12:7f:55:81:6c:9e:7d: +        e7:40:7e:f8:f5:75:0d:4b:a0:8d:ee:a4:d9:e8:5f:06:c9:86: +        66:71:70:6c:41:81:6a:dd:a4:4f:a3:c1:ac:70:d4:78:1b:23: +        30:2f:a5:ef:98:ee:d4:62:80:fd:bf:d4:7a:9b:8e:2d:18:e5: +        00:46 +-----BEGIN CERTIFICATE----- +MIICfzCCAeigAwIBAgIBADANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJKUDEO +MAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYDVQQDEwxSdWJ5 +IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBHMQswCQYD +VQQGEwJKUDEOMAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYD +VQQDEwxSdWJ5IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ9Y +GTm86gy4w10Sp9ggbFOskTTItNs/VvZ1tmwjgCNqX7P2mj4AtBYZHJwsjehT1Qvx +Uj97YJOGromrIIKatnIUPE2pC2w0eZ7TFIJtyTuQ2V5ob4y12An0bzsin16BnDff +z5A2ZVfcrTHKi0iSpzweQukcTh7LNsFETquasnNtAgMBAAGjezB5MAkGA1UdEwQC +MAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRl +MB0GA1UdDgQWBBQkbwOj7gZRdbK6/Do4Wb/th83ofzAfBgNVHSMEGDAWgBQkbwOj +7gZRdbK6/Do4Wb/th83ofzANBgkqhkiG9w0BAQUFAAOBgQAT69vKzZDyCZ7ZcnBe +Ql0RhM4AHcQvQdI+FuXUlx9Dqaec+mDENZby9g0TbQ823VkDCO4upt+e2G3Kco8C +witTexJ/VYFsnn3nQH749XUNS6CN7qTZ6F8GyYZmcXBsQYFq3aRPo8GscNR4GyMw +L6XvmO7UYoD9v9R6m44tGOUARg== +-----END CERTIFICATE----- +End + +TestOpenURISSL::SERVER_CERT = <<'End' +Certificate: +    Data: +        Version: 3 (0x2) +        Serial Number: 1 (0x1) +        Signature Algorithm: sha1WithRSAEncryption +        Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA +        Validity +            Not Before: Jan  1 00:00:00 2009 GMT +            Not After : Dec 31 23:59:59 2049 GMT +        Subject: C=JP, ST=Tokyo, O=RubyTest, CN=127.0.0.1 +        Subject Public Key Info: +            Public Key Algorithm: rsaEncryption +            RSA Public Key: (1024 bit) +                Modulus (1024 bit): +                    00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97: +                    e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a: +                    62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44: +                    69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a: +                    82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e: +                    a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10: +                    e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1: +                    1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c: +                    f0:86:61:ce:f1:ff:42:c7:23 +                Exponent: 65537 (0x10001) +        X509v3 extensions: +            X509v3 Basic Constraints: +                CA:FALSE +            Netscape Cert Type: +                SSL Server +            Netscape Comment: +                OpenSSL Generated Certificate +            X509v3 Subject Key Identifier: +                7F:17:5A:58:88:96:E1:1F:44:EA:FF:AD:C6:2E:90:E2:95:32:DD:F0 +            X509v3 Authority Key Identifier: +                keyid:24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F + +    Signature Algorithm: sha1WithRSAEncryption +        9a:34:99:ea:76:a2:ed:f0:f7:a7:75:3b:81:fb:75:57:93:c1: +        27:b6:1e:7a:38:67:95:be:58:42:9a:0a:dd:2b:23:fb:85:42: +        80:34:bf:b9:0e:9c:5e:5a:dc:2d:25:8c:68:02:a2:c7:7f:c0: +        eb:f3:e0:61:e2:05:e5:7e:c1:e0:33:1c:76:65:23:2c:25:08: +        f6:5a:11:b9:d4:f7:e3:80:bb:b0:ce:76:1a:56:22:af:e2:4a: +        e1:7e:a4:60:f3:fd:9c:53:46:51:57:32:6b:05:53:80:5c:a5: +        61:93:87:ae:06:a8:a2:ba:4d:a1:b7:1b:0f:8f:82:0a:e8:b3: +        ea:63 +-----BEGIN CERTIFICATE----- +MIICkTCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJKUDEO +MAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYDVQQDEwxSdWJ5 +IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBEMQswCQYD +VQQGEwJKUDEOMAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRIwEAYD +VQQDEwkxMjcuMC4wLjEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALu9dGlT +WFAkefLr24uX5Gmk3UgMQDViQrM1jJYqYnaYtSrg+Hgztv/4Vb9EaSHXtQ69it0x +G4jVtF56guC6mWwEdun/5vj1Bo5+pNvb60NEEqfKyiuqX4MQ4p41Vejor77IfbvC +1KrBHFcLwAw6HW4jqQMmfOqM8IZhzvH/QscjAgMBAAGjgY8wgYwwCQYDVR0TBAIw +ADARBglghkgBhvhCAQEEBAMCBkAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu +ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBR/F1pYiJbhH0Tq/63GLpDilTLd +8DAfBgNVHSMEGDAWgBQkbwOj7gZRdbK6/Do4Wb/th83ofzANBgkqhkiG9w0BAQUF +AAOBgQCaNJnqdqLt8PendTuB+3VXk8Enth56OGeVvlhCmgrdKyP7hUKANL+5Dpxe +WtwtJYxoAqLHf8Dr8+Bh4gXlfsHgMxx2ZSMsJQj2WhG51PfjgLuwznYaViKv4krh +fqRg8/2cU0ZRVzJrBVOAXKVhk4euBqiiuk2htxsPj4IK6LPqYw== +-----END CERTIFICATE----- +End + +TestOpenURISSL::SERVER_KEY = <<'End' +Private-Key: (1024 bit) +modulus: +    00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97: +    e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a: +    62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44: +    69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a: +    82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e: +    a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10: +    e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1: +    1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c: +    f0:86:61:ce:f1:ff:42:c7:23 +publicExponent: 65537 (0x10001) +privateExponent: +    00:af:3a:ec:17:0a:f5:d9:07:d2:d3:4c:15:c5:3b: +    66:b4:bc:6e:d5:ba:a9:8b:aa:45:3b:63:f5:ee:8b: +    6d:0f:e9:04:e0:1a:cf:8f:d2:25:32:d1:a5:a7:3a: +    c1:2e:17:5a:25:82:00:c4:e7:fb:1d:42:ea:71:6c: +    c4:0f:e1:db:23:ff:1e:d6:c8:d6:60:ca:2d:06:fc: +    54:3c:03:d4:09:96:bb:38:7a:22:a1:61:2c:f7:d0: +    d0:90:6c:9f:61:ba:61:30:5a:aa:64:ad:43:3a:53: +    38:e8:ba:cc:8c:51:3e:68:3e:3a:6a:0f:5d:5d:e0: +    d6:df:f2:54:93:d3:14:22:a1 +prime1: +    00:e8:ec:11:fe:e6:2b:23:21:29:d5:40:a6:11:ec: +    4c:ae:4d:08:2a:71:18:ac:d1:3e:40:2f:12:41:59: +    12:09:e2:f7:c2:d7:6b:0a:96:0a:06:e3:90:6a:4e: +    b2:eb:25:b7:09:68:e9:13:ab:d0:5a:29:7a:e4:72: +    1a:ee:46:a0:8b +prime2: +    00:ce:57:5e:31:e9:c9:a8:5b:1f:55:af:67:e2:49: +    2a:af:90:b6:02:c0:32:2f:ca:ae:1e:de:47:81:73: +    a8:f8:37:53:70:93:24:62:77:d4:b8:80:30:9f:65: +    26:20:46:ae:5a:65:6e:6d:af:68:4c:8d:e8:3c:f3: +    d1:d1:d9:6e:c9 +exponent1: +    03:f1:02:b8:f2:82:26:5d:08:4d:30:83:de:e7:c5: +    c0:69:53:4b:0c:90:e3:53:c3:1e:e8:ed:01:28:15: +    b3:0f:21:2c:2d:e3:04:d1:d7:27:98:b0:37:ec:4f: +    00:c5:a9:9c:42:27:37:8a:ff:c2:96:d3:1a:8c:87: +    c2:22:75:d3 +exponent2: +    6f:17:32:ab:84:c7:01:51:2d:e9:9f:ea:3a:36:52: +    38:fb:9c:42:96:df:6e:43:9c:c3:19:c1:3d:bc:db: +    77:e7:b1:90:a6:67:ac:6b:ff:a6:e5:bd:47:d3:d9: +    56:ff:36:d7:8c:4c:8b:d9:28:3a:2f:1c:9d:d4:57: +    5e:b7:c5:a1 +coefficient: +    45:50:47:66:56:e9:21:d9:40:0e:af:3f:f2:05:77: +    ab:e7:08:40:97:88:2a:51:b3:7e:86:b0:b2:03:2e: +    6d:36:3f:46:42:97:7d:5a:a2:93:6c:05:c2:8b:8b: +    2d:af:d5:7d:75:e9:70:f0:2d:21:e3:b9:cf:4d:9a: +    c4:97:e2:79 +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC7vXRpU1hQJHny69uLl+RppN1IDEA1YkKzNYyWKmJ2mLUq4Ph4 +M7b/+FW/RGkh17UOvYrdMRuI1bReeoLguplsBHbp/+b49QaOfqTb2+tDRBKnysor +ql+DEOKeNVXo6K++yH27wtSqwRxXC8AMOh1uI6kDJnzqjPCGYc7x/0LHIwIDAQAB +AoGBAK867BcK9dkH0tNMFcU7ZrS8btW6qYuqRTtj9e6LbQ/pBOAaz4/SJTLRpac6 +wS4XWiWCAMTn+x1C6nFsxA/h2yP/HtbI1mDKLQb8VDwD1AmWuzh6IqFhLPfQ0JBs +n2G6YTBaqmStQzpTOOi6zIxRPmg+OmoPXV3g1t/yVJPTFCKhAkEA6OwR/uYrIyEp +1UCmEexMrk0IKnEYrNE+QC8SQVkSCeL3wtdrCpYKBuOQak6y6yW3CWjpE6vQWil6 +5HIa7kagiwJBAM5XXjHpyahbH1WvZ+JJKq+QtgLAMi/Krh7eR4FzqPg3U3CTJGJ3 +1LiAMJ9lJiBGrlplbm2vaEyN6Dzz0dHZbskCQAPxArjygiZdCE0wg97nxcBpU0sM +kONTwx7o7QEoFbMPISwt4wTR1yeYsDfsTwDFqZxCJzeK/8KW0xqMh8IiddMCQG8X +MquExwFRLemf6jo2Ujj7nEKW325DnMMZwT2823fnsZCmZ6xr/6blvUfT2Vb/NteM +TIvZKDovHJ3UV163xaECQEVQR2ZW6SHZQA6vP/IFd6vnCECXiCpRs36GsLIDLm02 +P0ZCl31aopNsBcKLiy2v1X116XDwLSHjuc9NmsSX4nk= +-----END RSA PRIVATE KEY----- +End + +end | 
