tlspretense 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -0
  4. data/README.rdoc +25 -16
  5. data/Rakefile +1 -4
  6. data/doc/general_setup.rdoc +22 -1
  7. data/doc/macosx_setup.rdoc +86 -0
  8. data/lib/packetthief.rb +8 -1
  9. data/lib/packetthief/handlers/abstract_ssl_handler.rb +1 -2
  10. data/lib/packetthief/handlers/ssl_server.rb +3 -4
  11. data/lib/packetthief/handlers/ssl_smart_proxy.rb +3 -3
  12. data/lib/packetthief/handlers/ssl_transparent_proxy.rb +1 -1
  13. data/lib/packetthief/handlers/transparent_proxy.rb +1 -2
  14. data/lib/packetthief/logging.rb +32 -24
  15. data/lib/tlspretense/app.rb +5 -2
  16. data/lib/tlspretense/cert_maker.rb +5 -0
  17. data/lib/tlspretense/cert_maker/certificate_factory.rb +11 -1
  18. data/lib/tlspretense/cert_maker/certificate_suite_generator.rb +21 -3
  19. data/lib/tlspretense/cert_maker/subject_alt_name_factory.rb +115 -0
  20. data/lib/tlspretense/ext_compat/openssl_pkey_read.rb +38 -0
  21. data/lib/tlspretense/skel/config.yml +194 -17
  22. data/lib/tlspretense/test_harness/input_handler.rb +6 -2
  23. data/lib/tlspretense/test_harness/runner.rb +9 -3
  24. data/lib/tlspretense/test_harness/ssl_test_case.rb +2 -9
  25. data/lib/tlspretense/test_harness/ssl_test_report.rb +3 -0
  26. data/lib/tlspretense/test_harness/test_listener.rb +2 -2
  27. data/lib/tlspretense/test_harness/test_manager.rb +1 -2
  28. data/lib/tlspretense/version.rb +1 -1
  29. data/packetthief_examples/ssl_client_simple.rb +7 -0
  30. data/spec/packetthief/impl/ipfw_spec.rb +4 -2
  31. data/spec/packetthief/impl/netfilter_spec.rb +4 -2
  32. data/spec/packetthief/impl/pf_divert_spec.rb +4 -2
  33. data/spec/packetthief/logging_spec.rb +24 -26
  34. data/spec/spec_helper.rb +7 -8
  35. data/spec/tlspretense/cert_maker/subject_alt_name_factory_spec.rb +75 -0
  36. data/spec/tlspretense/ext_compat/openssl_pkey_read_spec.rb +126 -0
  37. data/spec/tlspretense/test_harness/runner_spec.rb +3 -5
  38. data/tlspretense.gemspec +2 -2
  39. metadata +41 -48
  40. data/Gemfile.lock +0 -41
@@ -8,12 +8,16 @@ module TestHarness
8
8
  @actions = {}
9
9
 
10
10
  # Set the term to accept keystrokes immediately.
11
- @stdin.enable_raw_chars
11
+ if @stdin.tty?
12
+ @stdin.enable_raw_chars
13
+ end
12
14
  end
13
15
 
14
16
  def unbind
15
17
  # Clean up by resotring the old termios
16
- @stdin.disable_raw_chars
18
+ if @stdin.tty?
19
+ @stdin.disable_raw_chars
20
+ end
17
21
  end
18
22
 
19
23
  # Receives one character at a time.
@@ -20,6 +20,7 @@ module TestHarness
20
20
  @logger.formatter = proc do |severity, datetime, progname, msg|
21
21
  "#{datetime}:#{severity}: #{msg}\n"
22
22
  end
23
+ PacketThief.logger = @logger
23
24
  @app_context = AppContext.new(@config, cert_manager, @logger)
24
25
 
25
26
  @report = SSLTestReport.new
@@ -46,18 +47,24 @@ module TestHarness
46
47
  stop_packetthief
47
48
 
48
49
  @report.print_results(@stdout)
50
+
51
+ if @report.fail?
52
+ @stdout.puts 'FAIL'
53
+ else
54
+ @stdout.puts 'PASS'
55
+ end
56
+ !@report.fail?
49
57
  else
50
58
  raise "Unknown action: #{opts[:action]}"
51
59
  end
52
60
  end
53
61
 
54
62
  def run_tests(testlist)
55
- test_manager = TestManager.new(@app_context, testlist, @report, @logger)
63
+ test_manager = TestManager.new(@app_context, testlist, @report)
56
64
  EM.run do
57
65
  # @listener handles the initial server socket, not the accepted connections.
58
66
  # h in the code block is for each accepted connection.
59
67
  @listener = TestListener.start('', @config.listener_port, test_manager)
60
- @listener.logger = @logger
61
68
  @keyboard = EM.open_keyboard InputHandler do |h|
62
69
  h.on(' ') { test_manager.test_completed(test_manager.current_test, :skipped) }
63
70
  h.on('q') { test_manager.stop_testing }
@@ -72,7 +79,6 @@ module TestHarness
72
79
  # preconfigured destination, and external for when PT does not manage the
73
80
  # firewall rules but still needs to know how to discover the destination.
74
81
  def init_packetthief
75
- PacketThief.logger = @logger
76
82
  if @config.packetthief.has_key? 'implementation'
77
83
  impl = @config.packetthief['implementation']
78
84
  case impl
@@ -29,18 +29,11 @@ module TestHarness
29
29
  @description = @raw['name']
30
30
  @certchainalias = @raw['certchain']
31
31
  @expected_result = @raw['expected_result']
32
- end
33
-
34
- def certchain
32
+ # ensure that the certificate exists
35
33
  @certchain ||= @appctx.cert_manager.get_chain(@certchainalias)
36
- end
37
-
38
- def keychain
39
34
  @keychain ||= @appctx.cert_manager.get_keychain(@certchainalias)
40
- end
41
-
42
- def hosttotest
43
35
  @hosttotest ||= @appctx.config.hosttotest
36
+ nil
44
37
  end
45
38
 
46
39
  end
@@ -21,6 +21,9 @@ module TestHarness
21
21
 
22
22
  end
23
23
 
24
+ def fail?
25
+ @results.any? {|r| !r.passed?}
26
+ end
24
27
  end
25
28
  end
26
29
  end
@@ -25,7 +25,7 @@ module TestHarness
25
25
  # present when the client attempts to connect to hostname.
26
26
  # * _keytotest_ [OpenSSL::PKey::PKey] The key corresponding to the leaf
27
27
  # node in _chaintotest_.
28
- def initialize(tcpsocket, test_manager, logger=nil)
28
+ def initialize(tcpsocket, test_manager)
29
29
  @test_manager = test_manager
30
30
 
31
31
  if @test_manager.paused?
@@ -40,7 +40,7 @@ module TestHarness
40
40
  @extrachain = chain
41
41
  end
42
42
  # Use the goodca for hosts we don't care to test against.
43
- super(tcpsocket, @test_manager.goodcacert, @test_manager.goodcakey, logger)
43
+ super(tcpsocket, @test_manager.goodcacert, @test_manager.goodcakey)
44
44
 
45
45
  @test_status = :running
46
46
  @testing_host = false
@@ -12,11 +12,10 @@ module TestHarness
12
12
  attr_accessor :goodcacert
13
13
  attr_accessor :goodcakey
14
14
 
15
- def initialize(context, testlist, report, logger=nil)
15
+ def initialize(context, testlist, report)
16
16
  @appctx = context
17
17
  @testlist = testlist
18
18
  @report = report
19
- @logger = logger
20
19
  @remaining_tests = @testlist.dup
21
20
 
22
21
  @goodcacert = @appctx.cert_manager.get_cert("goodca")
@@ -1,3 +1,3 @@
1
1
  module TLSPretense
2
- VERSION = '0.6.2'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -11,6 +11,13 @@ require 'rubygems'
11
11
  require 'eventmachine'
12
12
  require 'packetthief' # needs root
13
13
 
14
+ Signal.trap('ABRT') do
15
+ puts 'catching abort'
16
+ end
17
+
18
+ log = Logger.new(STDOUT)
19
+ log.level = Logger::DEBUG
20
+ PacketThief.logger = log
14
21
 
15
22
  EM.run do
16
23
 
@@ -77,7 +77,8 @@ module Impl
77
77
  context "when passed an object that implements Socket's #getsockname" do
78
78
  it "returns the destination socket's details" do
79
79
  @socket = double("socket")
80
- @socket.stub(:getsockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
80
+ # @socket.stub(:getsockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
81
+ @socket.stub(:getsockname).and_return(Socket.pack_sockaddr_in(16178, "10.101.96.97"))
81
82
 
82
83
  Ipfw.original_dest(@socket).should == [16178, "10.101.96.97"]
83
84
  end
@@ -86,7 +87,8 @@ module Impl
86
87
  context "when passed an object that implements EM::Connection's #getsockname" do
87
88
  it "returns the destination connection's details" do
88
89
  @socket = double("EM::Connection")
89
- @socket.stub(:get_sockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
90
+ # @socket.stub(:get_sockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
91
+ @socket.stub(:get_sockname).and_return(Socket.pack_sockaddr_in(16178, "10.101.96.97"))
90
92
 
91
93
  Ipfw.original_dest(@socket).should == [16178, "10.101.96.97"]
92
94
  end
@@ -44,7 +44,8 @@ module Impl
44
44
  context "when passed an object that implements Socket's #getsockopt" do
45
45
  it "returns the destination socket's details" do
46
46
  @socket = double("socket")
47
- @socket.should_receive(:getsockopt).with(Socket::IPPROTO_IP, Netfilter::SO_ORIGINAL_DST).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
47
+ # @socket.should_receive(:getsockopt).with(Socket::IPPROTO_IP, Netfilter::SO_ORIGINAL_DST).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
48
+ @socket.should_receive(:getsockopt).with(Socket::IPPROTO_IP, Netfilter::SO_ORIGINAL_DST).and_return(Socket.pack_sockaddr_in(16178, "10.101.96.97"))
48
49
 
49
50
  Netfilter.original_dest(@socket).should == [16178, "10.101.96.97"]
50
51
  end
@@ -53,7 +54,8 @@ module Impl
53
54
  context "when passed an object that implements EM::Connection's #get_sock_opt" do
54
55
  it "returns the destination connection's details" do
55
56
  @socket = double("EM::Connection")
56
- @socket.should_receive(:get_sock_opt).with(Socket::IPPROTO_IP, Netfilter::SO_ORIGINAL_DST).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
57
+ # @socket.should_receive(:get_sock_opt).with(Socket::IPPROTO_IP, Netfilter::SO_ORIGINAL_DST).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
58
+ @socket.should_receive(:get_sock_opt).with(Socket::IPPROTO_IP, Netfilter::SO_ORIGINAL_DST).and_return(Socket.pack_sockaddr_in(16178, "10.101.96.97"))
57
59
 
58
60
  Netfilter.original_dest(@socket).should == [16178, "10.101.96.97"]
59
61
  end
@@ -61,7 +61,8 @@ module Impl
61
61
  context "when passed an object that implements Socket's #getsockname" do
62
62
  it "returns the destination socket's details" do
63
63
  @socket = double("socket")
64
- @socket.stub(:getsockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
64
+ # @socket.stub(:getsockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
65
+ @socket.stub(:getsockname).and_return(Socket.pack_sockaddr_in(16178, "10.101.96.97"))
65
66
 
66
67
  subject.original_dest(@socket).should == [16178, "10.101.96.97"]
67
68
  end
@@ -70,7 +71,8 @@ module Impl
70
71
  context "when passed an object that implements EM::Connection's #getsockname" do
71
72
  it "returns the destination connection's details" do
72
73
  @socket = double("EM::Connection")
73
- @socket.stub(:get_sockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
74
+ # @socket.stub(:get_sockname).and_return("\020\002?2\ne`a\000\000\000\000\000\000\000\000")
75
+ @socket.stub(:get_sockname).and_return(Socket.pack_sockaddr_in(16178, "10.101.96.97"))
74
76
 
75
77
  subject.original_dest(@socket).should == [16178, "10.101.96.97"]
76
78
  end
@@ -6,37 +6,39 @@ module PacketThief
6
6
  include Logging
7
7
  end
8
8
 
9
- subject { LoggingObj.new }
10
-
11
9
  let(:logger) { Logger.new(nil) }
10
+ before(:each) { Logging.logger = logger }
12
11
 
13
- it "does not expose its log methods" do
14
- expect { subject.logdebug("some message") }.to raise_error NoMethodError
15
- end
12
+ describe "an object that has included PacketThief::Logging" do
13
+ subject { LoggingObj.new }
16
14
 
17
- context "when logger is unset" do
18
- before(:each) { subject.logger = nil }
15
+ it "does not expose its log methods" do
16
+ expect { subject.logdebug("some message") }.to raise_error NoMethodError
17
+ end
19
18
 
20
- it { expect { subject.send(:logdebug, "some message")}.to_not raise_error }
21
- end
19
+ context "when Logging.logger is unset" do
20
+ before(:each) { Logging.logger = nil }
22
21
 
23
- context "when logger is set" do
24
- before(:each) { subject.logger = logger }
22
+ it { expect { subject.send(:logdebug, "some message")}.to_not raise_error }
23
+ end
25
24
 
26
- it "sends a message with the classname to the logger" do
27
- logger.should_receive(:log).with(Logger::DEBUG, subject.class.to_s + ": hello world!")
25
+ context "when Logging.logger is set" do
28
26
 
29
- subject.send(:logdebug, 'hello world!')
30
- end
31
- it "prints an optional argument to the logger" do
32
- logger.should_receive(:log).with(Logger::DEBUG, subject.class.to_s + ": a message: data: 12345")
27
+ it "sends a message with the classname to the logger" do
28
+ logger.should_receive(:log).with(Logger::DEBUG, subject.class.to_s + ": hello world!")
33
29
 
34
- subject.send(:logdebug, 'a message', :data => 12345)
35
- end
36
- it "prints multiple optional arguments to the logger in sorted order" do
37
- logger.should_receive(:log).with(Logger::DEBUG, /#{subject.class.to_s}: a message: astring: ['"]inspect this['"], data: 12345/)
30
+ subject.send(:logdebug, 'hello world!')
31
+ end
32
+ it "prints an optional argument to the logger" do
33
+ logger.should_receive(:log).with(Logger::DEBUG, subject.class.to_s + ": a message: data: 12345")
34
+
35
+ subject.send(:logdebug, 'a message', :data => 12345)
36
+ end
37
+ it "prints multiple optional arguments to the logger in sorted order" do
38
+ logger.should_receive(:log).with(Logger::DEBUG, /#{subject.class.to_s}: a message: astring: ['"]inspect this['"], data: 12345/)
38
39
 
39
- subject.send(:logdebug, 'a message', :data => 12345, :astring => "inspect this")
40
+ subject.send(:logdebug, 'a message', :data => 12345, :astring => "inspect this")
41
+ end
40
42
  end
41
43
  end
42
44
 
@@ -49,8 +51,6 @@ module PacketThief
49
51
 
50
52
  subject { ClassToTest }
51
53
 
52
- before(:each) { subject.logger = logger }
53
-
54
54
  it "sets component to the class name" do
55
55
  logger.should_receive(:log).with(Logger::DEBUG, "#{subject.name}: a message")
56
56
 
@@ -66,8 +66,6 @@ module PacketThief
66
66
 
67
67
  subject { ModToTest }
68
68
 
69
- before(:each) { subject.logger = logger }
70
-
71
69
  it "sets component to the module name" do
72
70
  logger.should_receive(:log).with(Logger::DEBUG, "#{subject.name}: a message")
73
71
 
@@ -17,15 +17,14 @@ require 'tlspretense'
17
17
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
18
18
 
19
19
  RSpec.configure do |config|
20
- config.backtrace_clean_patterns = [
21
- /\/lib\d*\/ruby\//,
22
- /bin\//,
23
- # /gems/,
24
- /spec\/spec_helper\.rb/,
25
- /lib\/rspec\/(core|expectations|matchers|mocks)/
26
- ]
27
-
20
+ config.expect_with :rspec do |expectations|
21
+ expectations.syntax = [:should, :expect]
22
+ end
23
+ config.mock_with :rspec do |mocks|
24
+ mocks.syntax = :should
25
+ end
28
26
  end
27
+ RSpec::Expectations.configuration.on_potential_false_positives = :nothing
29
28
 
30
29
  def with_constants(constants, &block)
31
30
  saved_constants = {}
@@ -0,0 +1,75 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','..','spec_helper'))
2
+
3
+ module TLSPretense
4
+ module CertMaker
5
+ describe SubjectAltNameFactory do
6
+
7
+ describe "the created extension" do
8
+ subject { SubjectAltNameFactory.new.create_san_with_dns(arg) }
9
+
10
+ context "when created with www.isecpartners.com" do
11
+ let(:arg) { 'www.isecpartners.com' }
12
+ it "is a subjectAltName Extension" do
13
+ subject.oid.should == 'subjectAltName'
14
+ end
15
+ it "its der encoding contains www.isecpartners.com" do
16
+ subject.to_der.should match /www\.isecpartners\.com/
17
+ end
18
+ end
19
+
20
+ context 'when passed "www.isecpartners.com\0foo.com"' do
21
+ let(:arg) { "www.isecpartners.com\0foo.com" }
22
+ it 'its der encoding contains www.isecpartners.com\0foo.com' do
23
+ subject.to_der.should match /www\.isecpartners\.com\0foo\.com/
24
+ end
25
+ end
26
+ end
27
+
28
+ describe "the created extension" do
29
+ subject { SubjectAltNameFactory.new.create_san_ext(arg) }
30
+
31
+ context 'when the descriptor is "subjectAltName=DNS:www.isecpartners.com"' do
32
+ let(:arg) { "subjectAltName=DNS:www.isecpartners.com" }
33
+ it "the oid is 'subjectAltName'" do
34
+ subject.oid.should == 'subjectAltName'
35
+ end
36
+ it "the der encoding contains www.isecpartners.com" do
37
+ subject.to_der.should match /www\.isecpartners\.com/
38
+ end
39
+ end
40
+
41
+ context 'when the descriptor is "subjectAltName=DNS:www.isecpartners.com, DNS:foo.com"' do
42
+ let(:arg) { "subjectAltName=DNS:www.isecpartners.com, DNS:foo.com" }
43
+ it "the der encoding contains www.isecpartners.com" do
44
+ subject.to_der.should match /www\.isecpartners\.com/
45
+ end
46
+ it "the der encoding contains foo.com" do
47
+ subject.to_der.should match /foo\.com/
48
+ end
49
+ end
50
+
51
+ context 'when the descriptor is "subjectAltName=DNS:www.isecpartners.com\0foo.com, DNS:bar.com"' do
52
+ let(:arg) { "subjectAltName=DNS:www.isecpartners.com\0foo.com, DNS:bar.com" }
53
+ it 'the der encoding contains www.isecpartners.com\0foo.com' do
54
+ subject.to_der.should match /www\.isecpartners\.com\0foo\.com/
55
+ end
56
+ it "the der encoding contains bar.com" do
57
+ subject.to_der.should match /bar\.com/
58
+ end
59
+ end
60
+
61
+ context 'when the descriptor is "subjectAltName=DNS:www.isecpartners.com\0.foo.com, DNS:bar.com\0.foo.com"' do
62
+ let(:arg) { "subjectAltName=DNS:www.isecpartners.com\0.foo.com, DNS:bar.com\0.foo.com" }
63
+ it 'the der encoding contains www.isecpartners.com\0.foo.com' do
64
+ subject.to_der.should match /www\.isecpartners\.com\0\.foo\.com/
65
+ end
66
+ it "the der encoding contains bar.com\0.foo.com" do
67
+ subject.to_der.should match /bar\.com\0\.foo\.com/
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,126 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','..','spec_helper'))
2
+ require 'tlspretense/ext_compat/openssl_pkey_read'
3
+
4
+ module TLSPretense
5
+ module ExtCompat
6
+ describe OpenSSLPKeyRead do
7
+ subject { a = Object.new ; a.extend(OpenSSLPKeyRead) ; a }
8
+
9
+ describe "#read" do
10
+ context "when given a PEM-encoded RSA key" do
11
+ let(:pkey) do (<<-QUOTE).gsub(/^\s*/,'')
12
+ -----BEGIN RSA PRIVATE KEY-----
13
+ MIIEpAIBAAKCAQEAzRehpS5qO9LuW2dqm/Pc2YyPTOE3LMzoZSukguefwFuZqi8K
14
+ VWpn+4Tk5xACaZYnAKMQEHb3F9aC4REU3p3L6WiuEmarxvnfxAFahEIlsmJSSiE6
15
+ TaAxg3mXL3C5QiQxK/N9zjPGXW8QPlCCCs7CKQQ4RIQTIkoOn4OvSDbpRWrPZ1H4
16
+ ZBiCrAfqDQOSqzmHMTchUfhvezSWwDcFUQCfYGBdBkjzFEcrtEG6lFjx3aVHZfZm
17
+ uTKfki0fCEQCRsSd2l+/ZFJHIIphL0M2L/6VvCCDiaziHMwxVitnsQ5JURvje7QR
18
+ AXTKjArIgUl9h/IdfrIT9pbLKDNsDQMx2txfkQIDAQABAoIBAQC49fbx4Uotaa1N
19
+ AZdDzkn+aKVT0EjSPnnXw+Q5qmqIMBQFRycqoSvlyZQmTmnej2vdRzHVp3RwKyUd
20
+ lSodGnIrrhxOvAlvCSqkuhPH81/L4KAV+qF6IF6HE8ElJ6Pr4nf2C0IKFOdwnBkq
21
+ GbEtzgmMtCGKqRIYenF1qm0J03vM/Ugn3Xvq9qIUJjLEoX32z0m1x3+QQKmG4k2U
22
+ AZK2Ngs3J3dHNmCjAOzWhJ7yeIEiPPAznP6MV9JCvv3+dAZGMZmQUbNkrXuEy+LG
23
+ tQh7C5GMSj1wrB3q7o+1V2MGdZUu8uNkxaPlCnc3MnjbCHw3WydAJHW1lgurqlv5
24
+ cwDd2BgBAoGBAPh4IdffVxlIRx2CR7++9O1D2UED+zXJjMFHok3sPhrONb/qOzaQ
25
+ ectQFxzznObz4zUEnb9TLCVhkfMyFQ3L/CabLHPa21+ucMigObk8HdZ7nPtf7Xo0
26
+ v54vISFhH7rlpBi73fOf69+bzd7ECEPOvfI639ltfJRv8LgSW6GdoaR5AoGBANNO
27
+ 8DbvicZ7+Qq6U05QJLhl4HyLziCS+Rr0Arz5KnbKxjPYoiV3Ujpzec3DAazTCRho
28
+ OCRgzNMO2dEQCp7ZEYK+HK/J6UA2DYExYHcPyfYskrxwQVo5N20oiLa6jLisk5Jf
29
+ vsq6Lbuq5PgYcykXfcX3ZnB6XAt32nC9dzcu2F3ZAoGAbYa/HGKWCU4EEyzvpcVu
30
+ P/yNkwxHOzGKO1TxZboCslw980g0K9xJ4+Z9GcUFYAUYHbHYO5NVPXEiHfrwrvFB
31
+ SF9UnAlYdHf3vWhrqYyndnls/J4Pl7QS147c4tLmYsOBr2l48ECJgDs058KwBfvn
32
+ XRS4wiZyKRijGvD0tWw/6bkCgYEAv1crjZM6XtDDokM2TCOmHJOjwyOVc0mi6BUs
33
+ pZG6MfdLoob3zJVPkD4gfYGncqdmBQPaUpaU4kkAU58C/vPwN0OPFl7vJ4XKlMHx
34
+ Z96UMqYJ+Ths9RX6ao3Zvh0Ob+tVdaXdThVodBc7XqxFG2B6M1jjGdayom/VDWGD
35
+ IiT5J4ECgYBhEHnaEepaK+Z++yMdGD4oZCtrKkY+2FiR3yK4scldzkKp+FLLBSD0
36
+ nD0rF9hlhvpzlWa8HASrHHt2IB+rhKkljNpBuHSk1pK1q2KVYLCePOIunai9O/Mo
37
+ JWJnWLSeS/Fd3rXB9jnNhiudElJRXzkof0jKSm0xnDbxWvWztBUnkA==
38
+ -----END RSA PRIVATE KEY-----
39
+ QUOTE
40
+ end
41
+
42
+ it "returns an RSA key" do
43
+ subject.read(pkey).should be_a OpenSSL::PKey::RSA
44
+ end
45
+ end
46
+ context "when given a PEM-encoded DSA key" do
47
+ let(:pkey) do (<<-QUOTE).gsub(/^\s*/,'')
48
+ -----BEGIN DSA PRIVATE KEY-----
49
+ MIIDVgIBAAKCAQEA1nTwWYrSmUNZalLgB7hW5F/K7GLpc+8GyNJ+F7XVY6lbrKlY
50
+ 4xylX/kOUHvYgq/3C4Mn0lkME7lrvLKkh7rPybCNe1p40olQXB9JPfYc6QiiGLGL
51
+ WlbD1K4MNf1kftpBFB6x/Rs76NE6f8C3Wopp0TnbQhmlZskKo41mD+/MQl0j+d2w
52
+ fKsbLAo+kkW/znMqC2PLWNlOFE3bFrRzGQkU1Bu8HrBYrJT35bCtqbSJoyBkHPgI
53
+ SkYYdn/OuOD8swPO8xlgTxHBIZpwjqkyF3bzkroYOsCyAodu+xqHuTVzn+n/FWpf
54
+ vR3Lx5GRxBdEBqotpfxNBfF11B0fRWsAMUSQJQIhAO8V8dLdD/SYRsKUKUHD8eGC
55
+ ex6/ado7HluBizTjMI/NAoIBAC79cQQwFXb+ODfXTdG5QVYa9Q7fQ9oQZ1yDO1ab
56
+ eZu+awH2E5/PbwVqJEBVOWCF7Suq3CXGrINaInu4zsM6Seo/V61hBTZpWkdt+Xr4
57
+ ILlqQIw0UwaAwtE7Ynymk8Vx9MTFfekChi+Xu/4ReUDqRGKxzO89biZbd9XTOUVb
58
+ W4T4nNE/ONQJ4CKa10E+g7w9Af4quV88JDVfwaTKhC9N5It8N52Pze8cxDYKQPFs
59
+ 5LUCjtfACxK6YBplkeF0nWdRkIz97dhMQgvfF70YKPikX5JymLbxLiBqUZkWc3Jv
60
+ JzqOyvn08imGBZJmf6dEYhHDChKJ4OVALL4MpeStLQ01U5MCggEBALwAH7EnbbC/
61
+ G2GziHq0XvhfmDQKCYBcAO5Mmwl7qsyMeECQ639MKcNJmX9rmC4JFC/TB4TQA80d
62
+ YEwbFwlYJkUbJyM20oh7NMP0bsrn0Jzt8nYosRRfVZKman7tIE24QnVnZuEwOTQY
63
+ 0C4QLoq+m4jUumm6QsqclipuMzVUH1n0ngJYbx9nyvDU4nxd6j4MfmqHrQRUXIPH
64
+ 5namqyq/xELwpRy0i5kOh5Hs/KuyrMSesRzYcty8NwcYOcM1/whspsYAdX/psDjG
65
+ FNdknGUPM5ioDR2in6CA5z6iPDEAuFFQIUHt34Ujv+F4FNFXqnfiCa95BrjaCKNd
66
+ 3bRVzJDprc4CICImpd+oInjzy7PeCkbpUxwa4P3A4fHhRp0arIbId6jj
67
+ -----END DSA PRIVATE KEY-----
68
+ QUOTE
69
+ end
70
+
71
+ it "returns a DSA key" do
72
+ subject.read(pkey).should be_a OpenSSL::PKey::DSA
73
+ end
74
+ end
75
+ context "when given a PEM-encoded EC key" do
76
+
77
+ let(:pkey) do (<<-QUOTE).gsub(/^\s*/,'')
78
+ -----BEGIN EC PRIVATE KEY-----
79
+ MF8CAQEEGFpS6x8iOTNWBDNTO9nrqvyQjUvoaO7uaqAKBggqhkjOPQMBAaE0AzIA
80
+ BLOLCjnMg4a1++NZS5/XTeT2vv8JEANpctfJMjsoG12Uv7fwe8lldQwFFl0XQQZk
81
+ kg==
82
+ -----END EC PRIVATE KEY-----
83
+ QUOTE
84
+ end
85
+
86
+ it "returns an EC key" do
87
+ subject.read(pkey).should be_a OpenSSL::PKey::EC
88
+ end
89
+ end
90
+ context "when given a PEM-encoded ENCRYPTED key" do
91
+ let(:pkey) do (<<-QUOTE).gsub(/^\s*/,'')
92
+ -----BEGIN ENCRYPTED PRIVATE KEY-----
93
+ MIICzzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIBjDVSsSRDgwCAggA
94
+ MB0GCWCGSAFlAwQBAgQQp6si6kZjtBV0mh+IfcThUQSCAoB6djj/BwG1lHkYw9o1
95
+ AS4pDo+wF3VCDca26OCrMRK41lxsQ52Kdg1mDgkuLdBeAxx3uyuD4ZoytWhgv+st
96
+ oLPypCl85xA7yvYsw3UiR4HaS27KiLIsLu4ex2bkS+wOLOw4t9tjPHLvmZJgVneA
97
+ QFFdRH03woJfQaMHueseI6ok+p58Qs8xNbUetPJC47kwNtzD4tirrbGIsSPC0Mw9
98
+ THsP/UPMYYbfgBi9hOzoznzpDie7Siv/VM5UCFHJuA9JKisliX8Gc9elmZMc5iD8
99
+ QUo5XfquJCTGgdKpz3dLHsHK85kfXX+CpYzDLshIM8Qucf2Zx+fsW5h/1VrAR+H7
100
+ Oft7zeyP01fGxwi1jVXdLpi5emvd1/tVUF/wasXisvQRsicNxE2nmPVrdFYRCisp
101
+ 6hIAqgkMUMbz5LGwGSfCqVLLezsC20lk4oVhszG+Jyx64bx5pUVupkuTghm8wZgJ
102
+ ZnLKQIz+Bge+ZbNk3TzN1Z4O/Lpfs0wKbUaI+/glM10zl6s/tG0F+kNDRCp1xhWy
103
+ m1Dq6AE9H+Q8BBYrwR//p9vP6xvFXo3Ie5b3zx1am2WWmgQqlnI4C+W1ThUdf68+
104
+ eh1Qc/P8c5xwfS15kATbZT6lL6eBzkEFJYEN/5b1y2Bipsqn1Rjpcvu9VUOmFKyo
105
+ AsFK2e6QvvhCngRxgLT1O4kNlDACJx65e20qMFXMLWqdXGOzy7VXattEzSs4sV7W
106
+ 3c/u+bv0rqaIYn+JcZ9hqukH3i3eyKLHfmbYxERUgQIh6cZjds+46sODsi/7J3R4
107
+ ytZbBGZPeoS23Go6cfA2NEdN4hPH9k2CM0hol4W4ztWmPNoLmkhlFEhZHrdVoMR0
108
+ Tw07
109
+ -----END ENCRYPTED PRIVATE KEY-----
110
+ QUOTE
111
+ end
112
+ context "when given the right password" do
113
+ it "decrypts the key" do
114
+ subject.read(pkey, 'hello').should be_a OpenSSL::PKey::RSA
115
+ end
116
+ end
117
+ context "when given the wrong password" do
118
+ it "raises a generic error" do
119
+ expect { subject.read(pkey, 'hell') }.to raise_error
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end