Consume SSL protected Web Services with soap4r
After hours of google this deserves a blog post. I did not find a clear example about this, so I am writing one.
I had the need to call a .NET Web Service over https with mutual authentication and basic authentication.
First of all I installed the soap4r gem, then the httpclient gem (because that one supports basic authentication).
Then I made a folder called “certs” with all the certificates and key files I had:
- ca.cer – the certificate of the certification authority that signed the server certificate
- server.cer – the certificate of the server (signed by the guys who own ca.cer)
- client.cer – the client certificate I need to send along the request to get the content
- client.key – the key file for the client certificate
That’s all the certs and key files I needed.
Now it was time to try to get the wsdl:
This worked.
Time to try soap4r:
To tell soap4r that you want basic authentication and where the certificate files are, you need to create a soap/property file with the following content:
This file is loaded at startup (you can find other options in soap/lib/soap/httpconfigloader.rb), and configures the ssl and basic auth stuff for soap4r.
I had the need to call a .NET Web Service over https with mutual authentication and basic authentication.
First of all I installed the soap4r gem, then the httpclient gem (because that one supports basic authentication).
Then I made a folder called “certs” with all the certificates and key files I had:
- ca.cer – the certificate of the certification authority that signed the server certificate
- server.cer – the certificate of the server (signed by the guys who own ca.cer)
- client.cer – the client certificate I need to send along the request to get the content
- client.key – the key file for the client certificate
That’s all the certs and key files I needed.
Now it was time to try to get the wsdl:
require 'http-access2'
url = 'https://secure.example.com/web_service/wsdl'
client = HTTPAccess2::Client.new()
client.ssl_config.set_client_cert_file('certs/client.cer', 'certs/client.key')
client.ssl_config.set_trust_ca('certs/ca.cer')
client.set_basic_auth(url, 'username', 'password')
puts client.get(url).content
This worked.
Time to try soap4r:
require 'rubygems' #if you installed httpclient with rubygems you need this
require 'soap/wsdlDriver'
#this validates the server certificate
#so you can be sure that the server you are
#sending data to is the server you have the
#certificate of in certs/server.cer
def validate_certificate(is_ok, ctx)
cert = ctx.current_cert
unless (cert.subject.to_s == cert.issuer.to_s) #check the server certificate only
is_ok &&= File.open('certs/server.cer').read == ctx.current_cert.to_pem
end
is_ok
end
wsdl = 'https://secure.example.com/web_service/wsdl'
driver = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver
#driver.wiredump_dev = STDOUT
driver.options['protocol.http.ssl_config.verify_callback'] = method(:validate_certificate)
results = driver.web_service_method(arg1, arg2)
p results
To tell soap4r that you want basic authentication and where the certificate files are, you need to create a soap/property file with the following content:
client.protocol.http.basic_auth.1.url = https://secure.example.com/web_service/wsdl client.protocol.http.basic_auth.1.userid = username client.protocol.http.basic_auth.1.password = password client.protocol.http.ssl_config.client_cert = certs/client.cer client.protocol.http.ssl_config.client_key = certs/client.key client.protocol.http.ssl_config.ca_file = certs/ca.cer client.protocol.http.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER client.protocol.http.ssl_config.ciphers = ALL client.protocol.http.ssl_config.verify_depth = 1
This file is loaded at startup (you can find other options in soap/lib/soap/httpconfigloader.rb), and configures the ssl and basic auth stuff for soap4r.
Wow…this post is exactly what was looking for! However, I’m just writing an example of calling a simple .NET webservice from Ruby using Basic Authentication.
Do I NEED to do this mutual verification as it appears you specify in the soap/property file with the line:
client.protocol.http.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
Basically, what is the most stripped down approach I could use that would simply encrypt the transmission of credentials using HTTP Basic Authentication? Thanks again for this work….any help you can provide would be great.
Regards,
Sam
Hi Sam. Basic Authentication does not encrypt the traffic: it’s only for authentication (and it’s very insecure as well if you don’t use it together with ssl – credentials are send in plaintext). Maybe that was what you sad, I’m not sure I understood you correctly.
You don’t need mutual authentication to encrypt the traffic. Server authentication would be enough. In that case you can leave out the part where you specify the client credentials in soap/property. This two lines
become obsolete.
I doing poor job of explaining myself. I did know when doing HTTP basic authentication, since credentials are passed in clear text, if you don’t encrypt the traffic, you’ve got yourself a very big security hole in your app.
I’m still fuzzy on the soap/property file. Where is that located?
Just throw it in the same dir you have the script in and you should be fine.