Skip to content
Browse files

Merge pull request #54 from arax/client-dev

Added PKCS12 support for jRuby, closes #53
  • Loading branch information...
2 parents c823fec + 8a2868e commit b21e760ac60f55b000b1fb599a67ddda7e41cf17 @ffeldhaus committed
View
36 lib/occi/api/client/client_http.rb
@@ -1,8 +1,9 @@
require 'rubygems'
require 'httparty'
-require 'openssl'
+
require 'occi/api/client/http/net_http_fix'
require 'occi/api/client/http/httparty_fix'
+require 'occi/api/client/http/authn_utils'
module Occi
module Api
@@ -647,29 +648,19 @@ def change_auth(auth_options)
raise ArgumentError, "Missing required option 'user_cert' for x509 auth!" unless @auth_options[:user_cert]
raise ArgumentError, "The file specified in 'user_cert' does not exist!" unless File.exists? @auth_options[:user_cert]
- # handle PKCS#12 credentials
+ # handle PKCS#12 credentials before passing them
+ # to httparty
if /\A(.)+\.p12\z/ =~ @auth_options[:user_cert]
- # decode certificate and its private key
- if (defined? RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
- raise ArgumentError, "Reading credentials from PKCS#12 files is not supported in jRuby!"
- else
- pkcs12 = OpenSSL::PKCS12.new(File.open(@auth_options[:user_cert], 'rb'), @auth_options[:user_cert_password])
- end
-
- # store them in a single variable in PEM format
- pem_from_pkcs12 = ""
- pem_from_pkcs12 << pkcs12.certificate.to_pem << pkcs12.key.to_pem
-
- # private key has already been decrypted
- self.class.pem pem_from_pkcs12, ''
+ self.class.pem AuthnUtils.extract_pem_from_pkcs12(@auth_options[:user_cert], @auth_options[:user_cert_password]), ''
else
# httparty will handle ordinary PEM formatted credentials
+ # TODO: Issue #49, check PEM credentials in jRuby
self.class.pem File.open(@auth_options[:user_cert], 'rb').read, @auth_options[:user_cert_password]
end
self.class.ssl_ca_path @auth_options[:ca_path] unless @auth_options[:ca_path].nil?
self.class.ssl_ca_file @auth_options[:ca_file] unless @auth_options[:ca_file].nil?
- self.class.ssl_extra_chain_cert certs_to_file_ary(@auth_options[:proxy_ca]) unless @auth_options[:proxy_ca].nil?
+ self.class.ssl_extra_chain_cert AuthnUtils.certs_to_file_ary(@auth_options[:proxy_ca]) unless @auth_options[:proxy_ca].nil?
when "keystone"
# set up OpenStack Keystone token based auth
raise ArgumentError, "Missing required option 'token' for OpenStack Keystone auth!" unless @auth_options[:token]
@@ -681,19 +672,6 @@ def change_auth(auth_options)
end
end
- # Reads X.509 certificates from a file to an array.
- #
- # @example
- # certs_to_file_ary "~/.globus/usercert.pem"
- # # => [#<String>, #<String>, ...]
- #
- # @param [String] Path to a PEM file containing certificates
- # @return [Array<String>] An array of read certificates
- def certs_to_file_ary(ca_file)
- # TODO: read and separate multiple certificates
- [] << File.read(ca_file)
- end
-
# Performs GET request and parses the responses to collections.
#
# @example
View
82 lib/occi/api/client/http/authn_utils.rb
@@ -0,0 +1,82 @@
+require 'openssl'
+
+if defined? JRUBY_VERSION
+ require 'java'
+end
+
+module Occi
+ module Api
+ module Client
+
+ class AuthnUtils
+ # Reads credentials from a PKCS#12 compliant file. Returns
+ # X.509 certificate and decrypted private key in PEM
+ # formatted string.
+ #
+ # @example
+ # extract_pem_from_pkcs12 "~/.globus/usercert.p12", "123456"
+ # # => #<String>
+ #
+ # @param [String] Path to a PKCS#12 file with credentials
+ # @param [String] Password needed to unlock the PKCS#12 file
+ # @return [String] Decrypted credentials in a PEM formatted string
+ def self.extract_pem_from_pkcs12(path_to_p12_file, p12_password)
+ # decode certificate and its private key
+ pem_from_pkcs12 = ""
+ if defined? JRUBY_VERSION
+ # Java-based Ruby, read PKCS12 manually
+ # using KeyStore
+ keystore = Java::JavaSecurity::KeyStore.getInstance("PKCS12")
+ p12_input_stream = Java::JavaIo::FileInputStream.new(path_to_p12_file)
+ pass_char_array = Java::JavaLang::String.new(p12_password).to_char_array
+
+ # load and unlock PKCS#12 store
+ keystore.load p12_input_stream, pass_char_array
+
+ # read the first certificate and PK
+ cert = keystore.getCertificate("1")
+ pk = keystore.getKey("1", pass_char_array)
+
+ pem_from_pkcs12 << "-----BEGIN CERTIFICATE-----\n"
+ pem_from_pkcs12 << Java::JavaxXmlBind::DatatypeConverter.printBase64Binary(cert.getEncoded())
+ pem_from_pkcs12 << "\n-----END CERTIFICATE-----"
+
+ pem_from_pkcs12 << "\n"
+
+ pem_from_pkcs12 << "-----BEGIN PRIVATE KEY-----\n"
+ pem_from_pkcs12 << Java::JavaxXmlBind::DatatypeConverter.printBase64Binary(pk.getEncoded())
+ pem_from_pkcs12 << "\n-----END PRIVATE KEY-----"
+ else
+ # C-based Ruby, use OpenSSL::PKCS12
+ pkcs12 = OpenSSL::PKCS12.new(
+ File.open(
+ path_to_p12_file,
+ 'rb'
+ ),
+ p12_password
+ )
+
+ # store cert and private key in a single PEM formatted string
+ pem_from_pkcs12 << pkcs12.certificate.to_pem << pkcs12.key.to_pem
+ end
+
+ pem_from_pkcs12
+ end
+
+ # Reads X.509 certificates from a file to an array.
+ #
+ # @example
+ # certs_to_file_ary "~/.globus/usercert.pem"
+ # # => [#<String>, #<String>, ...]
+ #
+ # @param [String] Path to a PEM file containing certificates
+ # @return [Array<String>] An array of read certificates
+ def self.certs_to_file_ary(ca_file)
+ # TODO: read and separate multiple certificates
+ [] << File.open(ca_file).read
+ end
+ end
+
+ end
+ end
+end
View
55 spec/occi/api/client/http/authn_utils_spec.rb
@@ -0,0 +1,55 @@
+require 'rspec'
+require 'occi/api/client/http/authn_utils'
+
+module Occi
+ module Api
+ module Client
+
+ describe AuthnUtils do
+
+ it "can handle PKCS#12 user credentials" do
+ path = File.expand_path("..", __FILE__)
+
+ pem_cert_pk = AuthnUtils.extract_pem_from_pkcs12(
+ path + "/rocci-cred.p12",
+ "passworD123"
+ )
+
+ pem_cert_ok = File.open(path + "/rocci-cred-cert.pem", "rb").read
+
+ if defined? JRUBY_VERSION
+ # PK is in PKCS#8 when running jRuby
+ pem_pk_ok = File.open(path + "/rocci-cred-key-jruby.pem", "rb").read
+ else
+ # PK is raw RSA key when running cRuby
+ pem_pk_ok = File.open(path + "/rocci-cred-key.pem", "rb").read
+ end
+
+ pem_cert_pk_ok = ""
+ pem_cert_pk_ok << pem_cert_ok << pem_pk_ok
+
+
+ # remove line wrapping
+ pem_cert_pk.delete! "\n"
+ pem_cert_pk_ok.delete! "\n"
+
+ # remove trailing new lines
+ pem_cert_pk.chomp!
+ pem_cert_pk_ok.chomp!
+
+ pem_cert_pk.should eq pem_cert_pk_ok
+ end
+
+ it "can read CA certificates from a file" do
+ path = File.expand_path("..", __FILE__)
+
+ ca_certs = AuthnUtils.certs_to_file_ary(path + "/rocci-cred-cert.pem")
+
+ ca_certs.should =~ [File.open(path + "/rocci-cred-cert.pem", "rb").read]
+ end
+
+ end
+
+ end
+ end
+end
View
0 spec/occi/api/client/http/httparty_fix_spec.rb
No changes.
View
0 spec/occi/api/client/http/net_http_fix_spec.rb
No changes.
View
3 spec/occi/api/client/http/rocci-cred-cert.pem
@@ -0,0 +1,3 @@
+-----BEGIN CERTIFICATE-----
+MIIEFzCCAv+gAwIBAgIDEAABMA0GCSqGSIb3DQEBBQUAMIGWMQswCQYDVQQGEwJDWjEXMBUGA1UECAwOQ3plY2ggUmVwdWJsaWMxDTALBgNVBAcMBEJybm8xFTATBgNVBAoMDEZha2UgQ0EgaW5jLjERMA8GA1UECwwIck9DQ0kgQ0ExFTATBgNVBAMMDHJPQ0NJIHRlc3RlcjEeMBwGCSqGSIb3DQEJARYPc2VleWFAbG9jYWxob3N0MB4XDTEzMDIwODAxNTkzNloXDTIzMDIwNjAxNTkzNlowgYcxCzAJBgNVBAYTAkNaMRcwFQYDVQQIDA5DemVjaCBSZXB1YmxpYzEVMBMGA1UECgwMRmFrZSBDQSBpbmMuMREwDwYDVQQLDAhyT0NDSSBDQTEVMBMGA1UEAwwMck9DQ0kgdGVzdGVyMR4wHAYJKoZIhvcNAQkBFg9zZWV5YUBsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcjeRWvUe+p+bOTgl0JtUWpHCDrSoV5DQD59Rinol+1+471JDF6wloBrmo7nCyLtOpDUDUcRZIp9UKCZZLqx0FhmUySFCjU7wBtU1+IOhbEVH97+pff/2x9920C9uyV/06Vr7Ch8G8fYOZCplrmmWs5wj88mUSQBtSYMOZNQxkX18tj3oKgvLhAlRYoIAYJgkhFUsXegMUc7NRa9Kpt2ag2C7eKs9VMZuI9XF+e6xPK6lUPFV8lWzfKtrhiUkmEMbzb0A3CQLZNw8IPXYjNm4daoeZSZkLY9P6KlIXJnBZX1DfJ1WdpS19GUVlU7EmRqwccOeT7FkS3MAR8nK35nOVAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTcmlZ0FPwCBm+9huhalzJjpeqnHTAfBgNVHSMEGDAWgBSpNuMNySOjsarUO20BwqS+56i5gjANBgkqhkiG9w0BAQUFAAOCAQEAtd3IEWcifc9jz594PMz44AiilgHWFAizjB58PWAXXpbRylhAxFBbhXxmSLmxBEsJNmf+l5S5EV5LjFVbuFzF05CzWZpkvS6Md+zgp6y0K6ZRfZpB9FESiPYPVrh+AgGQGaJCnY9WQPG9ySMBTt6FfqggiuK3I2d6b6CyNjt0GFEzFzaP40Y7zIjUT+gEbJJrp/OFmbYYTdRv1dXMT8TzImhew7C2LcOYN4+L5s0+Yl2m4LDNDHsCbvGhixj2hxTdLm+ervsc7gsb3s9j9LXL7wZlO4cqmd9xZ/hYl+zYSgMAQaZc9QAteL/o7M4a2INxgsAu5vWZKaV5geqRSu3tdg==
+-----END CERTIFICATE-----
View
3 spec/occi/api/client/http/rocci-cred-key-jruby.pem
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcjeRWvUe+p+bOTgl0JtUWpHCDrSoV5DQD59Rinol+1+471JDF6wloBrmo7nCyLtOpDUDUcRZIp9UKCZZLqx0FhmUySFCjU7wBtU1+IOhbEVH97+pff/2x9920C9uyV/06Vr7Ch8G8fYOZCplrmmWs5wj88mUSQBtSYMOZNQxkX18tj3oKgvLhAlRYoIAYJgkhFUsXegMUc7NRa9Kpt2ag2C7eKs9VMZuI9XF+e6xPK6lUPFV8lWzfKtrhiUkmEMbzb0A3CQLZNw8IPXYjNm4daoeZSZkLY9P6KlIXJnBZX1DfJ1WdpS19GUVlU7EmRqwccOeT7FkS3MAR8nK35nOVAgMBAAECggEAS3pTlkuYberP1fJm0dUoP2NvDeEI8gzysBN6X3WZ4ErNQLRyBwl0YqFKkEFpV9Empkfe09UPpzWodpLpWGPqvqVLpHWTAxH42SzOpEdmvSOkR57/Okd27pzAxoj4WoQ6KOjex3vmFTdu/cTe1DA2c4FtVgMuC5zcvMhYp+m7n+1Q5Y/MFWgatMh5Oo20q34CzLNkJ1w0sZfKd8+yhFZc6PJRqp4obJTWALL5EJNxLxw8vzXfnn8y69VZWIqMHHE6QqgZGj6T9aXhDKpjgao6n0zXQu7GrGSz+KxefqEAEV8YyZMeCkFEBMmOS/huWotlzhj8ALtq7vm7JMgIkTDz0QKBgQDu7ezX3UC3JskQOoXKPuwUAdkoL1C7O6k/A8meHzrSvPTnPzdmO7Zj8nZOG58O5cFAh6jT+2k0xa/TrvWUgdD7Xiaw7pWk/MBAEqdReYaAHl6Q440fkvWS4Qn/VP0ZAXwHmqO3zdHRlHE8HsKzqPIjOaU2stNwvgqS2QZs5MOybwKBgQDsT+GfJlESE/MRZ04VZTn6HQSy4NocRPJ+t7ddFYxoETBGDtAg4lvSNhpl9aTY625iGASFVec3A8pSPIgOO4q7yX0FyEtkNHhgKnbGVZzLyBTiHv+eMDVFGauWV7HTUjxiasalSLAtUKVC+AzsSdrDT2HhEt7KK5W/erkw3ZLsOwKBgBJqHfXpJIK5J4frqsISpWlJVy5Er0ku6PVlWlWofIUfcRSdh6yGb6E2X63XbSPdy04o5ncoCky567o54s2GT8//yqWaTchAfqSxi1m5LweSz6EFGKxVTD38wCOArl0OAzwmy/VvlHs9oJiMml70Z20VlUko8SjgTLpSpa8g1oBbAoGBAOP33+m/diAhfLDCXvKG99pQWjMSdXcmEGWsZ8j1u8C9LDu5mJDQrdHp/ad0xPW6kX8yOlJC384T6v9Qg9RvHlQweVq1sf1WHPZwoKqO9rckKvlE/EWnrgweSoi7ysHZKIWtCjtzFvpwPS2QzC66xL77+qm9oY2ZZFoXU0ZPyqf3AoGATw1FWCPsdyC8iA4D9UlWvK5JskLxlNr0OZGgjo1rc+n+oopgn/GxxyMHhwaUZpftA1ab1G58xGnlcjCkhr2ZK/7XNhb7SfLKxTjB2BAehaq2YyzwCpsKUI7oLi0wTt7mD51qh3B1fMSTkfBZg/prwq6nIaDBwXL1xsd/BPfMCYw=
+-----END PRIVATE KEY-----
View
3 spec/occi/api/client/http/rocci-cred-key.pem
@@ -0,0 +1,3 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA3I3kVr1Hvqfmzk4JdCbVFqRwg60qFeQ0A+fUYp6JftfuO9SQxesJaAa5qO5wsi7TqQ1A1HEWSKfVCgmWS6sdBYZlMkhQo1O8AbVNfiDoWxFR/e/qX3/9sffdtAvbslf9Ola+wofBvH2DmQqZa5plrOcI/PJlEkAbUmDDmTUMZF9fLY96CoLy4QJUWKCAGCYJIRVLF3oDFHOzUWvSqbdmoNgu3irPVTGbiPVxfnusTyupVDxVfJVs3yra4YlJJhDG829ANwkC2TcPCD12IzZuHWqHmUmZC2PT+ipSFyZwWV9Q3ydVnaUtfRlFZVOxJkasHHDnk+xZEtzAEfJyt+ZzlQIDAQABAoIBAEt6U5ZLmG3qz9XyZtHVKD9jbw3hCPIM8rATel91meBKzUC0cgcJdGKhSpBBaVfRJqZH3tPVD6c1qHaS6Vhj6r6lS6R1kwMR+NkszqRHZr0jpEee/zpHdu6cwMaI+FqEOijo3sd75hU3bv3E3tQwNnOBbVYDLguc3LzIWKfpu5/tUOWPzBVoGrTIeTqNtKt+AsyzZCdcNLGXynfPsoRWXOjyUaqeKGyU1gCy+RCTcS8cPL81355/MuvVWViKjBxxOkKoGRo+k/Wl4QyqY4GqOp9M10Luxqxks/isXn6hABFfGMmTHgpBRATJjkv4blqLZc4Y/AC7au75uyTICJEw89ECgYEA7u3s191AtybJEDqFyj7sFAHZKC9QuzupPwPJnh860rz05z83Zju2Y/J2ThufDuXBQIeo0/tpNMWv0671lIHQ+14msO6VpPzAQBKnUXmGgB5ekOONH5L1kuEJ/1T9GQF8B5qjt83R0ZRxPB7Cs6jyIzmlNrLTcL4KktkGbOTDsm8CgYEA7E/hnyZREhPzEWdOFWU5+h0EsuDaHETyfre3XRWMaBEwRg7QIOJb0jYaZfWk2OtuYhgEhVXnNwPKUjyIDjuKu8l9BchLZDR4YCp2xlWcy8gU4h7/njA1RRmrllex01I8YmrGpUiwLVClQvgM7Enaw09h4RLeyiuVv3q5MN2S7DsCgYASah316SSCuSeH66rCEqVpSVcuRK9JLuj1ZVpVqHyFH3EUnYeshm+hNl+t120j3ctOKOZ3KApMueu6OeLNhk/P/8qlmk3IQH6ksYtZuS8Hks+hBRisVUw9/MAjgK5dDgM8Jsv1b5R7PaCYjJpe9GdtFZVJKPEo4Ey6UqWvINaAWwKBgQDj99/pv3YgIXywwl7yhvfaUFozEnV3JhBlrGfI9bvAvSw7uZiQ0K3R6f2ndMT1upF/MjpSQt/OE+r/UIPUbx5UMHlatbH9Vhz2cKCqjva3JCr5RPxFp64MHkqIu8rB2SiFrQo7cxb6cD0tkMwuusS++/qpvaGNmWRaF1NGT8qn9wKBgE8NRVgj7HcgvIgOA/VJVryuSbJC8ZTa9DmRoI6Na3Pp/qKKYJ/xsccjB4cGlGaX7QNWm9RufMRp5XIwpIa9mSv+1zYW+0nyysU4wdgQHoWqtmMs8AqbClCO6C4tME7e5g+daodwdXzEk5HwWYP6a8KupyGgwcFy9cbHfwT3zAmM
+-----END RSA PRIVATE KEY-----
View
BIN spec/occi/api/client/http/rocci-cred.p12
Binary file not shown.
View
26 spec/spec_helper.rb
@@ -1,16 +1,42 @@
+# make sure the local files will be loaded first;
+# this should prevent installed versions of this
+# gem to be included in the testing process
+$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+$:.unshift(File.dirname(__FILE__))
+
+# enable coverage reports
if ENV['COVERAGE']
require 'simplecov'
SimpleCov.start
end
+# enable VCR integration;
+# this allows us to record and re-play network
+# communication between client and server
+# using so called cassettes (YAML)
require 'vcr'
+# enable VCR for HTTP/HTTPS connections
+# using RSPEC metadata integration;
+# this will automatically generate a named
+# cassette for each unit test
VCR.configure do |c|
c.hook_into :webmock
c.cassette_library_dir = 'spec/cassettes'
c.configure_rspec_metadata!
end
+# simplify the usage of VCR; this will allow us to use
+#
+# it "does something", :vcr do
+# ...
+# end
+#
+# instead of
+#
+# it "does something else", :vcr => true do
+# ...
+# end
RSpec.configure do |c|
# in RSpec 3 this will no longer be necessary.
c.treat_symbols_as_metadata_keys_with_true_values = true

0 comments on commit b21e760

Please sign in to comment.
Something went wrong with that request. Please try again.