2011-10-25 9 views
6

Estoy trabajando en la creación de un servidor SSL habilitado en Ruby, junto con un cliente Ruby correspondiente para usar con el servidor. Para probar, creé mi propio certificado de CA raíz con los siguientes comandos.¿Por qué funciona esta prueba de cliente/servidor habilitada para Ruby SSL?

$:~/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 

entonces genera un certificado SSL para mi servidor:

$:~/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 

Después de eso, he creado un servidor habilitado para SSL simplista para utilizar el certificado SSL que acaba de crear.

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 

Cuando se inicia, que parece funcionar bien ...

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

Entonces creé un cliente capaz no SSL sólo para asegurarse de que se le negó la conectividad.

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 

Cuando ejecuto esto a través de lo siguiente:

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

En consecuencia, me sale el siguiente desde el servidor:

$:~/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>' 

Esto fue todo lo esperado. Luego, modifiqué el código del cliente para usar un contexto 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 

estaba totalmente convencida que falle también durante el proceso de apretón de manos, pero no fue así ... me dieron el siguiente resultado:

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

¿Por qué este trabajo? Asumí que fallaría porque no creía que el cliente tuviera ninguna idea sobre la CA raíz que creé y firmé con el certificado SSL del servidor, y por lo tanto no podría verificar el certificado del servidor. ¿Qué me estoy perdiendo? Cuando creé y firmé el certificado del servidor y estaba "comprometido", ¿esto de alguna manera lo puso a disposición de la biblioteca OpenSSL? Esperaba tener que decir de alguna manera el contexto SSL en el cliente dónde buscar la CA raíz que creé para realizar pruebas ...

Como prueba de seguimiento, copié el código de mi cliente en una máquina diferente que definitivamente no sabe nada acerca de la CA raíz que creé para esta prueba, cambió la dirección IP a la que se conecta el cliente y volvió a ejecutar la prueba. Esta prueba produjo los mismos resultados: el cliente pudo comunicarse con el servidor cuando asumí que no podría hacerlo. ¿Algunas ideas?

Respuesta

5

Dependiendo de la versión de Ruby que esté utilizando, el modo de verificación predeterminado para el objeto SSLContext puede no estar imponiendo la verificación del certificado. Puede forzar el modo de verificación con:

context = OpenSSL::SSL::SSLContext.new 
context.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT

Esto debe provocar que el intento de conexión del cliente falle, como se esperaba.

+0

Ah sí, eso funcionó. Gracias Ian! Ahora, ¿qué debo hacer para informar al contexto SSL en el cliente sobre dónde buscar mi CA raíz personalizada para que se pueda verificar el certificado del servidor? – Bryan

+0

Por extraño que parezca, de acuerdo con http://rubydoc.info/stdlib/openssl/1.9.2/OpenSSL/SSL/SSLContext#DEFAULT_PARAMS-constant VERIFY_PEER debe ser un parámetro predeterminado, pero aparentemente los valores predeterminados no se aplican correctamente. –

+0

@Bryan Te recomendamos utilizar las configuraciones 'ca_file' o' ca_path' en el objeto 'SSLContext'. 'ca_file' es probablemente el más fácil ya que solo lo apunta al archivo .pem, o lo que sea. 'ca_path' requiere un directorio que ha sido correctamente hash por openssl. No he hecho eso por un tiempo, pero veré si puedo encontrar un enlace sobre él. –

Cuestiones relacionadas