Почему работает этот тест сервера / клиента с поддержкой SSL в Ruby?

Я работаю над созданием сервера с поддержкой SSL в Ruby вместе с соответствующим клиентом Ruby для использования с сервером. Для тестирования я создал свой собственный сертификат корневого центра сертификации с помощью следующих команд.

$:~/devel/ssl-test/ssl/CA$ openssl genrsa -out TestCA.key 2048
Generating RSA private key, 2048 bit long modulus
............+++
...........................+++
e is 65537 (0x10001)

$:~/devel/ssl-test/ssl/CA$ openssl req -new -key TestCA.key -out TestCA.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

$:~/devel/ssl-test/ssl/CA$ openssl x509 -req -days 365 -in TestCA.csr -out TestCA.crt -signkey TestCA.key 
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
Getting Private key

Затем я сгенерировал сертификат SSL для своего сервера:

$:~/devel/ssl-test/ssl/keys$ openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
.+++
............................................+++
e is 65537 (0x10001)

$:~/devel/ssl-test/ssl/keys$ cd ../csrs/
$:~/devel/ssl-test/ssl/csrs$ openssl req -new -key ../keys/server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:my.secure.test
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$:~/devel/ssl-test/ssl/csrs$ cd ../certs/
$:~/devel/ssl-test/ssl/certs$ openssl ca -in ../csrs/server.csr -cert ../CA/TestCA.crt -keyfile ../CA/TestCA.key -out server.crt
Using configuration from /usr/lib/ssl/openssl.cnf
I am unable to access the ./demoCA/newcerts directory
./demoCA/newcerts: No such file or directory
$:~/devel/ssl-test/ssl/certs$ mkdir -p demoCA/newcerts
$:~/devel/ssl-test/ssl/certs$ touch demoCA/index.txt
$:~/devel/ssl-test/ssl/certs$ echo "01" > demoCA/serial
$:~/devel/ssl-test/ssl/certs$ openssl ca -in ../csrs/server.csr -cert ../CA/TestCA.crt -keyfile ../CA/TestCA.key -out server.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Oct 25 16:25:05 2011 GMT
            Not After : Oct 24 16:25:05 2012 GMT
        Subject:
            countryName               = AU
            stateOrProvinceName       = Some-State
            organizationName          = Internet Widgits Pty Ltd
            commonName                = my.secure.test
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                48:50:B5:04:11:02:F1:40:97:58:BF:5F:8B:27:50:10:C0:3F:EE:D9
            X509v3 Authority Key Identifier: 
                DirName:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
                serial:81:44:16:06:5C:EB:5E:71

Certificate is to be certified until Oct 24 16:25:05 2012 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

После этого я создал упрощенный сервер с поддержкой SSL, чтобы использовать только что созданный сертификат SSL.

require 'socket'
require 'openssl'
require 'thread'

server  = TCPServer.new(1337)
context = OpenSSL::SSL::SSLContext.new

context.cert = OpenSSL::X509::Certificate.new(File.open('ssl/certs/server.crt'))
context.key  = OpenSSL::PKey::RSA.new(File.open('ssl/keys/server.key'))

secure = OpenSSL::SSL::SSLServer.new(server, context)

puts 'Listening securely on port 1337...'

loop do
  Thread.new(secure.accept) do |conn|
    begin
      while request = conn.gets
        $stdout.puts '=> ' + request
        response = "You said: #{request}"
        $stdout.puts '<= ' + response
        conn.puts response
      end
    rescue
      $stderr.puts $!
    end
  end
end

При запуске, кажется, все работает нормально ...

$:~/devel/ssl-test$ ruby server.rb 
Listening securely on port 1337...

Затем я создал клиента без поддержки SSL, чтобы убедиться, что ему было отказано в подключении.

require 'socket'
require 'thread'

client = TCPSocket.new('127.0.0.1', 1337)

Thread.new do
  begin
    while response = client.gets
      $stdout.puts response
    end
  rescue
    $stderr.puts "Error from client: #{$!}"
  end
end

while request = $stdin.gets
  request = request.chomp
  client.puts request
end

Когда я запускаю это с помощью следующего:

$:~/devel/ssl-test$ ruby client.rb
hello
Error from client: Connection reset by peer

Соответственно, я получаю от сервера следующее:

$:~/devel/ssl-test$ ruby server.rb
Listening securely on port 1337...
/usr/local/rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/openssl/ssl-internal.rb:164:in `accept': SSL_accept returned=1 errno=0 state=SSLv2/v3 read client hello A: unknown protocol (OpenSSL::SSL::SSLError)
    from /usr/local/rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/openssl/ssl-internal.rb:164:in `accept'
    from server.rb:16:in `block in <main>'
    from server.rb:15:in `loop'
    from server.rb:15:in `<main>'

Все это было ожидаемо. Затем я изменил код клиента, чтобы использовать контекст SSL.

require 'socket'
require 'openssl'
require 'thread'

client  = TCPSocket.new('127.0.0.1', 1337)
context = OpenSSL::SSL::SSLContext.new

secure = OpenSSL::SSL::SSLSocket.new(client, context)
secure.sync_close = true
secure.connect

Thread.new do
  begin
    while response = secure.gets
      $stdout.puts response
    end
  rescue
    $stderr.puts "Error from client: #{$!}"
  end
end

while request = $stdin.gets
  request = request.chomp
  secure.puts request
end

Я полностью ожидал, что это тоже не сработает во время процесса рукопожатия, но этого не произошло ... Я получил следующий результат:

$:~/devel/ssl-test$ ruby client.rb
hello
You Said: hello

Почему это сработало? Я предполагал, что это не удастся, потому что я не думал, что клиент будет иметь представление о корневом ЦС, который я создал и подписал SSL-сертификат сервера, и, следовательно, не сможет проверить сертификат сервера. Что мне не хватает? Когда я создал и подписал сертификат сервера, и он был «зафиксирован», это каким-то образом сделало его доступным для библиотеки OpenSSL? Я ожидал, что мне придется каким-то образом указать контексту SSL в клиенте, где искать корневой ЦС, который я создал для целей тестирования ...

В качестве последующего теста я скопировал свой клиентский код на другую машину, которая определенно ничего не знает о корневом ЦС, созданном мной для этого теста, изменил IP-адрес, к которому подключается клиент, и снова запустил тест. Этот тест дал те же результаты - клиент смог связаться с сервером, хотя я предполагал, что он не сможет. Есть идеи?

6
задан Bryan 25 October 2011 в 18:13
поделиться