tlspretense 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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