Powershell SSL Socket Client - ruby

I am having issues using powershell to connect to an ssl socket with a self singed cert. I have a ruby client that connects fine, and I have also tested ncat with the --ssl switch and confirmed it connects just fine. $sslStream.AuthenticateAsClient is where the code fails and I get the following error from powershell "Exception calling "AuthenticateAsClient" with "1" argument(s): "A call to SSPI failed, see inner exception.". Any help is much appreciated.
Powershell Client
$socket = New-Object Net.Sockets.TcpClient('172.26.4.38', 8080)
$stream = $socket.GetStream()
$sslStream = New-Object System.Net.Security.SslStream($stream,$false,({$True} -as [Net.Security.RemoteCertificateValidationCallback]))
$sslStream.AuthenticateAsClient('172.26.4.38')
$writer = new-object System.IO.StreamWriter($sslStream)
$writer.WriteLine('hello world')
$writer.flush()
$socket.close()
Ruby Server
#!/usr/bin/env ruby
require 'openssl'
require 'socket'
tcp_server = TCPServer.new('172.26.4.38', 8080)
ctx = OpenSSL::SSL::SSLContext.new
ctx.ssl_version = :SSLv23
ctx.key = OpenSSL::PKey::RSA.new 2048
ctx.cert = OpenSSL::X509::Certificate.new
ctx.cert.subject = OpenSSL::X509::Name.new [['CN', '172.26.4.38']]
ctx.cert.issuer = ctx.cert.subject
ctx.cert.public_key = ctx.key
ctx.cert.not_before = Time.now
ctx.cert.not_after = Time.now + 60 * 60 * 24
ctx.cert.sign ctx.key, OpenSSL::Digest::SHA1.new
server = OpenSSL::SSL::SSLServer.new tcp_server, ctx
socket = server.accept
puts 'client connected'
puts socket.gets

This was an issue with the ruby code creating a self-sign cert. Using openssl and openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt the following powershell code worked correctly.
Client
$socket = New-Object Net.Sockets.TcpClient('172.26.4.38', 8080)
$stream = $socket.GetStream()
$sslStream = New-Object System.Net.Security.SslStream($stream,$false,({$True} -as [Net.Security.RemoteCertificateValidationCallback]))
$sslStream.AuthenticateAsClient('172.26.4.38')
$writer = new-object System. IO.StreamWriter($sslStream)
$writer.WriteLine('Hello World')
$writer.flush()
$socket.close()
Server
#!/usr/bin/env ruby
require 'openssl'
require 'socket'
tcp_server = TCPServer.new('172.26.4.38', 8080)
ctx = OpenSSL::SSL::SSLContext.new
ctx.cert = OpenSSL::X509::Certificate.new(File.open('server.crt'))
ctx.key = OpenSSL::PKey::RSA.new(File.open('server.key'))
server = OpenSSL::SSL::SSLServer.new tcp_server, ctx
socket = server.accept
puts 'client connected'
puts socket.gets

You can get powershell to ignore any cert issue by forcing the callback to just return true no matter what.
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
beaware that this will bypass any ssl validation in the powershell session.

Related

Ruby SSLServer run https server

I have a custom HTTP server with ruby/socket (just for experimental purposes, no actual use case) and want to try to use SSL/https for the connections. I have tried several solutions with OpenSSL/SSLServer but none of them work.
$HOST = "0.0.0.0"
$PORT = 7777
tcp_server = TCPServer.new($HOST, $PORT)
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = OpenSSL::PKey::RSA.new 2048
ctx.cert = OpenSSL::X509::Certificate.new
ctx.cert.subject = OpenSSL::X509::Name.new [["CN", "localhost"]]
ctx.cert.issuer = ctx.cert.subject
ctx.cert.public_key = ctx.key
ctx.cert.not_before = Time.now
ctx.cert.not_after = Time.now + 60 * 60 * 24
ctx.cert.sign ctx.key, OpenSSL::Digest::SHA1.new
server = OpenSSL::SSL::SSLServer.new tcp_server, ctx
puts "Server listening on #{$HOST}:#{$PORT.to_s}"
loop do
serverThread = Thread.start(server.accept) do |client|
client.print "HTTP/1.1 200\r\n"
client.print "Content-type: text/html\r\n"
client.print "\r\n"
client.print "<h1>Hello World</h1>"
end
end
That code just causes chrome to say connection not private - NET::ERR_CERT_INVALID

Ruby SSL Client

I've got a SSL Echo Server working fine when I tested with
gnutls-cli --starttls --port 9002 --insecure localhost
My SSL Echo server is as below:
#!/usr/bin/ruby
require 'socket';
require 'openssl';
certfile = 'privkey.pem';
host = "127.0.0.1"
port = 9002;
server = TCPServer.new( host, port );
# Establish an SSL context
sslContext = OpenSSL::SSL::SSLContext.new
sslContext.cert = OpenSSL::X509::Certificate.new(File.open( "myssl.cert.cert" ))
sslContext.key = OpenSSL::PKey::RSA.new(File.open( "myssl.cert.key" ))
# Create SSL server
sslServer = OpenSSL::SSL::SSLServer.new( server, sslContext );
# Don't expect an immidate SSL handshake upon connection.
sslServer.start_immediately = false;
sslSocket = sslServer.accept;
sslSocket.puts( "Toast.." );
# Server loop
while line = sslSocket.gets
line.chomp!;
if "STARTTLS" == line
# Starting TLS
sslSocket.accept;
end
sslSocket.puts( "Got '#{line}'" );
end
sslSocket.close;
My Client is however, not working (which I borrow somewhere in StackOverflow) as below:
#!/usr/bin/env ruby
require "socket"
require "thread"
require "openssl"
host = "127.0.0.1"
port = 9002
socket = TCPSocket.new(host, port)
expectedCert = OpenSSL::X509::Certificate.new(File.open("myssl.cert.cert"))
ssl = OpenSSL::SSL::SSLSocket.new(socket)
ssl.sync_close = true
ssl.connect
if ssl.peer_cert.to_s != expectedCert.to_s
stderrr.puts "Unexpected certificate"
exit(1)
end
Thread.new {
begin
while lineIn = ssl.gets
lineIn = lineIn.chomp
$stdout.puts lineIn
end
rescue
$stderr.puts "Error in input loop: " + $!
end
}
while (lineOut = $stdin.gets)
lineOut = lineOut.chomp
ssl.puts lineOut
end
Error I've got.
./sslclient.rb:13:in `connect': SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol (OpenSSL::SSL::SSLError)
from ./sslclient.rb:13:in `<main>'
Why is there a error in ssl.connect? Did I miss anything?
On the server side you start with plain text, send a welcome message and expect a starttls command - only then you upgrade to TLS. But on the client side you immediatly try to upgrade to TLS after the TCP connect, e.g. without reading the server hello and without sending the starttls command.

Ruby XMLRPC pass keypair

I have to interact with an XML-RPC server that uses key authentication. Before accepting the request, the server verifies the key pair (ssl private key and ssl cert). I understand this is terrible, but it's what I have to work with.
My question is this: is there an easy way to pass the key and certificate in this request? I am using the xmlrpc/Client in Ruby, but the documentation does not seem to indicate this can be done. My current code is as follows:
Error:
/usr/lib/ruby/1.8/net/http.rb:586:in connect': sslv3 alert handshake failure >(OpenSSL::SSL::SSLError)
from /usr/lib/ruby/1.8/net/http.rb:586:inconnect'
from /usr/lib/ruby/1.8/net/http.rb:553:in do_start'
from /usr/lib/ruby/1.8/net/http.rb:542:instart'
from /usr/lib/ruby/1.8/net/http.rb:1035:in request'
from /usr/lib/ruby/1.8/net/http.rb:992:inpost2'
from /usr/lib/ruby/1.8/xmlrpc/client.rb:535:in do_rpc'
from /usr/lib/ruby/1.8/xmlrpc/client.rb:420:incall2'
from /usr/lib/ruby/1.8/xmlrpc/client.rb:410:in `call'
Class File:
class RpcRequest
require "xmlrpc/client"
# Automatically create the connection
def initialize(connection_params)
#connection = XMLRPC::Client.new(connection_params['host'], connection_params['path'], connection_params['port'], connection_params['proxy_host'], connection_params['proxy_port'], connection_params['user'], connection_params['password'], connection_params['use_ssl'], connection_params['timeout'])
end
# Make the xml rpc call
def make_call(method, request_params)
#response = #connection.call(method, request_params)
end
end
Driver file:
require 'RpcRequest.rb'
require 'config.rb'
require 'openssl'
# Define parameters for the connection
connect_params = Hash.new
connect_params['host'] = $host
connect_params['path'] = $path
connect_params['port'] = $port
connect_params['proxy_host'] = $proxy_host
connect_params['proxy_port'] = $proxy_port
connect_params['user'] = $user
connect_params['password'] = $password
connect_params['use_ssl'] = $use_ssl
connect_params['timeout'] = $timeout
# Define parameters for the request
request_params = Hash.new
request_params['fname'] = 'Robert'
request_params['lname'] = 'Jones'
request = RpcRequest.new(connect_params)
puts request.inspect
request.make_call('test.hello', request_params)
You can interact with the .http property of the client instance.

Connect to a FTPS server with mismatched server certificate using Net::FTPTLS

I am trying to connect via Net::FTPTLS to a Microsoft-based file server (IIS) which is configured to use FTP on port 22 and requires SSL.
I connect via:
require 'net/ftptls'
ftp = Net::FTPTLS.new()
ftp.connect('host.com', port_number)
ftp.login('Username', 'Password')
ftp.puttextfile('somefile.txt', 'where/to/save/somefile.txt')
ftp.close
Problem is, I get the following error:
hostname does not match the server certificate
It seems that I have to disable the openssl peer verification: OpenSSL::SSL::VERIFY_PEER should become OpenSSL::SSL::VERIFY_NONE.
Any ideas on how to monkey-patch the Net::FTPTLS class? Has anyone done this successfully?
Instead using Net::FTPTLS, use Ruby 2.4+ with the following code:
require 'net/ftp'
ftp = Net::FTP.new(nil, ssl: {:verify_mode => OpenSSL::SSL::VERIFY_NONE})
ftp.connect('host.com', port_number)
ftp.login('Username', 'Password')
ftp.puttextfile('somefile.txt', 'where/to/save/somefile.txt')
ftp.close
What I did, rather than monkeypatching ruby itself, was bring a copy of this into /lib of my project.
module Net
class FTPTLS < FTP
def connect(host, port=FTP_PORT)
#hostname = host
super
end
def login(user = "anonymous", params = {:password => nil, :acct => nil, :ignore_cert => false})
store = OpenSSL::X509::Store.new
store.set_default_paths
ctx = OpenSSL::SSL::SSLContext.new('SSLv23')
ctx.cert_store = store
ctx.verify_mode = params[:ignore_cert] ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
ctx.key = nil
ctx.cert = nil
voidcmd("AUTH TLS")
#sock = OpenSSL::SSL::SSLSocket.new(#sock, ctx)
#sock.connect
#sock.post_connection_check(#hostname) unless params[:ignore_cert]
super(user, params[:password], params[:acct])
voidcmd("PBSZ 0")
end
end
end
I also cleaned up the param passing a bit. You would use this like so:
require 'ftptls' # Use my local version, not net/ftptls
#ftp_connection = Net::FTPTLS.new()
#ftp_connection.passive = true
#ftp_connection.connect(host, 21)
#ftp_connection.login('user', :password => 'pass', :ignore_cert => true)
HTH
This works fine for me. #ROR
ftp = Net::FTP.new("ftps.host.com", ftp_options)
open("where/is/your/file/somefile.txt") do |file_data|
ftp.putbinaryfile(file_data, 'where/to/save/somefile.txt')
end
ftp.puttextfile('somefile.txt', 'where/to/save/somefile.txt')
def ftp_options
{
port: FTP_PORT,
username: 'ftp_user',
password: 'password',
passive: true,
ssl: { verify_mode: 0 }
}
end
Remember that you have to provide ftps.hostname.com.

How to establish a SSL enabled TCP/IP Connection in Ruby

I need to establish a TCP connection with my server which has a SSL enabled port, that I need to access.
I need to send a XML file and get the response from the server.
Before the SSL was enabled, I was able to get the data from the server using the below mentioned code.
require 'socket'
myXML = 'test_xml'
host = 'myhost.com'
port = 12482
socket = TCPSocket.open(host,port) # Connect to server
socket.send(myXML, 0)
response = socket.recvfrom(port)
puts response
socket.close
Now I have a 'certi.pfx' with which I need to establish a connection, Send my_xml data and get the response. How can this be done.
I would also like to know if I have the 'pem' and 'key' file, how can I establish a connection, Send my_xml data and get the response.
Please help.
require 'socket'
require 'openssl'
myXML = 'my_sample_data'
host = 'my_host.com'
port = my_port
socket = TCPSocket.open(host,port)
ssl_context = OpenSSL::SSL::SSLContext.new()
ssl_context.cert = OpenSSL::X509::Certificate.new(File.open("certificate.crt"))
ssl_context.key = OpenSSL::PKey::RSA.new(File.open("certificate.key"))
ssl_context.ssl_version = :SSLv23
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
ssl_socket.sync_close = true
ssl_socket.connect
ssl_socket.puts(myXML)
while line = ssl_socket.gets
p line
end
ssl_socket.close
Like this:
sock = TCPSocket.new('hostname', 443)
ctx = OpenSSL::SSL::SSLContext.new
ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER)
#socket = OpenSSL::SSL::SSLSocket.new(sock, ctx).tap do |socket|
socket.sync_close = true
socket.connect
end

Resources