httpi-adapter-openssl_gost 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +121 -0
- data/Rakefile +2 -0
- data/httpi-adapter-openssl_gost.gemspec +26 -0
- data/lib/httpi/adapter/openssl_gost.rb +78 -0
- data/lib/httpi/adapter/openssl_gost/version.rb +9 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7246e71d3ac930e54148951c0f832c4e285fb70b
|
4
|
+
data.tar.gz: 0e1b4be04689555e7eeba4664beb366877713fe7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7fd06b785759998e6ec5893f7c2d5292e452f3c9416241c66cbed363861db8da28ab61acd0ff57f122c691f01c844ffc7a713921a9ce8acacefc79f119175349
|
7
|
+
data.tar.gz: e1c2581189f1bb79158f416c565b2b74ea4a678484c30c0092fcdcc665aec3e86d80a50a2c986e0e05cc317a62c896a6562ac22fdf30c77b946f46d891075e63
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Andrey Novikov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
OpenSSL GOST adapter for [HTTPI]
|
2
|
+
================================
|
3
|
+
|
4
|
+
This gem allows to perform HTTP requests over secure connections, that requires russian GOST cryptoalgorithms to be used.
|
5
|
+
It allows to use client certificate and private key for authentication.
|
6
|
+
|
7
|
+
**WARNING!** This gem is kind of «last chance software». It uses `openssl s_client` command to perform requests.
|
8
|
+
As it's command for debug and testing purposes only, it's slow and unreliable. Use at your own risk only if nothing else is working for you (as it for me).
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
### OpenSSL installation and configuration
|
13
|
+
|
14
|
+
You need to install OpenSSL 1.0.0 or newer (OpenSSL 1.0.1 or newer is better) with GOST engine installed (it's bundled starting from 1.0.0).
|
15
|
+
|
16
|
+
Usually in modern Lunux distributions it's installed already (at least in Ubuntu 12.04).
|
17
|
+
Mac OS X users should install it through Homebrew or MacPorts: `brew install openssl`
|
18
|
+
|
19
|
+
In `/etc/ssl/openssl.cnf` (`/usr/local/etc/openssl/openssl.cnf` for Mac OS X) add next line to _the very beginning of file_:
|
20
|
+
|
21
|
+
openssl_conf = openssl_def
|
22
|
+
|
23
|
+
Add next lines to _the very end of file_:
|
24
|
+
|
25
|
+
[openssl_def]
|
26
|
+
engines = engine_section
|
27
|
+
[engine_section]
|
28
|
+
gost = gost_section
|
29
|
+
[gost_section]
|
30
|
+
default_algorithms = ALL
|
31
|
+
dynamic_path = /usr/lib/x86_64-linux-gnu/openssl-1.0.0/engines/libgost.so
|
32
|
+
engine_id = gost
|
33
|
+
CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
|
34
|
+
|
35
|
+
`dynamic_path` isn't required in Mac OS X. Linux users can get it's value executing `locate libgost.so`.
|
36
|
+
|
37
|
+
After that, `openssl ciphers | tr ":" "\n" | grep GOST` should return following lines:
|
38
|
+
|
39
|
+
GOST2001-GOST89-GOST89
|
40
|
+
GOST94-GOST89-GOST89
|
41
|
+
|
42
|
+
### Gem installation
|
43
|
+
|
44
|
+
Add this line to your application's Gemfile:
|
45
|
+
|
46
|
+
gem 'httpi-adapter-openssl_gost'
|
47
|
+
|
48
|
+
And then execute:
|
49
|
+
|
50
|
+
$ bundle
|
51
|
+
|
52
|
+
Or install it yourself as:
|
53
|
+
|
54
|
+
$ gem install httpi-adapter-openssl_gost
|
55
|
+
|
56
|
+
## Usage
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
require 'httpi/adapter/openssl_gost'
|
60
|
+
request = HTTPI::Request.new
|
61
|
+
request.url = 'https://example.com/'
|
62
|
+
HTTPI.get(request, :openssl_gost)
|
63
|
+
```
|
64
|
+
|
65
|
+
Or you can specify it as default adapter for all requests:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require 'httpi/adapter/openssl_gost'
|
69
|
+
HTTPI.adapter :openssl_gost
|
70
|
+
```
|
71
|
+
|
72
|
+
If you need to specify client credentials (certificate and private key),
|
73
|
+
pass file paths to `cert_file=` and `cert_key_file=` methods of the `request.auth.ssl`:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
request.auth.ssl.cert_file = '/full/path/to/client.crt'
|
77
|
+
request.auth.ssl.cert_key_file = '/full/path/to/client.pem'
|
78
|
+
```
|
79
|
+
|
80
|
+
### Usage with savon
|
81
|
+
|
82
|
+
You need to use custom branches of [savon] and [wasabi] until next pull requests are not merged in:
|
83
|
+
1. [savonrb/wasabi#44](https://github.com/savonrb/wasabi/issues/44)
|
84
|
+
2. [savonrb/savon#566](https://github.com/savonrb/savon/pull/566):
|
85
|
+
|
86
|
+
Place in your Gemfile:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
gem 'wasabi', github: 'Envek/wasabi', branch: 'specify_adapter'
|
90
|
+
gem 'savon', github: 'Envek/savon', branch: 'specify_adapter'
|
91
|
+
```
|
92
|
+
|
93
|
+
Specify `:adapter` in savon client global options:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
require 'httpi/adapter/openssl_gost'
|
97
|
+
soap_client = Savon.client(
|
98
|
+
wsdl: 'https://service-requiring-gost.ru/service?wsdl',
|
99
|
+
ssl_cert_file: '/full/path/to/client.crt',
|
100
|
+
ssl_cert_key_file: '/full/path/to/client.pem',
|
101
|
+
adapter: :openssl_gost,
|
102
|
+
)
|
103
|
+
```
|
104
|
+
|
105
|
+
And use it as usual:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
soap_client.call(:method, message: {foo: 1})
|
109
|
+
```
|
110
|
+
|
111
|
+
## Contributing
|
112
|
+
|
113
|
+
1. Fork it ( https://github.com/Envek/httpi-adapter-openssl_gost/fork )
|
114
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
115
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
116
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
117
|
+
5. Create a new Pull Request
|
118
|
+
|
119
|
+
[HTTPI]: https://github.com/savonrb/httpi
|
120
|
+
[savon]: https://github.com/savonrb/savon
|
121
|
+
[wasabi]: https://github.com/savonrb/wasabi
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'httpi/adapter/openssl_gost/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'httpi-adapter-openssl_gost'
|
8
|
+
spec.version = HTTPI::Adapter::OpensslGost::VERSION
|
9
|
+
spec.authors = ['Andrey Novikov']
|
10
|
+
spec.email = ['envek@envek.name']
|
11
|
+
spec.summary = 'HTTPI adapter for accessing HTTPS servers with GOST algorithms and certificates'
|
12
|
+
spec.description = 'It uses OpenSSL `s_client` command to securely connect with server that requires usage of GOST algorithms and client certificates.'
|
13
|
+
spec.homepage = 'https://github.com/Envek/httpi-adapter-openssl_gost'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.requirements << 'OpenSSL 1.0.0 and newer with GOST engine installed, enabled, and configured.'
|
22
|
+
spec.add_dependency 'httpi', '~> 2.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10'
|
26
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'httpi'
|
2
|
+
require 'open3'
|
3
|
+
require 'httpi/adapter/openssl_gost/version'
|
4
|
+
|
5
|
+
module HTTPI
|
6
|
+
module Adapter
|
7
|
+
class OpensslGost < Base
|
8
|
+
|
9
|
+
register :openssl_gost
|
10
|
+
|
11
|
+
def initialize(request)
|
12
|
+
@request = request
|
13
|
+
@pubkey_path = request.auth.ssl.cert_file
|
14
|
+
@privkey_path = request.auth.ssl.cert_key_file
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :client
|
18
|
+
attr_accessor :pubkey_path
|
19
|
+
attr_accessor :privkey_path
|
20
|
+
|
21
|
+
def request(method)
|
22
|
+
uri = @request.url
|
23
|
+
cmd = "openssl s_client -engine gost -connect '#{uri.host}:#{uri.port}' -quiet"
|
24
|
+
cmd += " -cert '#{pubkey_path}'" if pubkey_path
|
25
|
+
cmd += " -key '#{privkey_path}'" if privkey_path
|
26
|
+
|
27
|
+
# Prepare request
|
28
|
+
req = "#{method.upcase} #{uri.request_uri} HTTP/1.1\r\n"
|
29
|
+
headers = @request.headers.map{|k,v| "#{k}: #{v}\r\n" }.join
|
30
|
+
# Set up Content-Length header if body present (HTTPI doesn't it for us)
|
31
|
+
if @request.body and !@request.headers['Content-Length']
|
32
|
+
headers += "Content-Length: #{@request.body.bytesize}\r\n"
|
33
|
+
end
|
34
|
+
# Add hostname header and explicitly close connection (we need command to exit immediately)
|
35
|
+
headers += "Host: #{uri.host}\r\nConnection: close\r\n\r\n"
|
36
|
+
req += headers
|
37
|
+
req += "#{@request.body}\r\n\r\n" if @request.body
|
38
|
+
|
39
|
+
# Send request, get answer
|
40
|
+
HTTPI.logger.debug "Connecting to server with command: #{cmd}"
|
41
|
+
HTTPI.logger.debug "Sending request:\r\n#{req}"
|
42
|
+
retries = 0
|
43
|
+
begin
|
44
|
+
raw_response, openssl_stderr, status = Open3.capture3(cmd, stdin_data: req, binmode: true)
|
45
|
+
rescue Errno::EPIPE # Sometimes fails with no reason
|
46
|
+
retry if retries+=1 < 3
|
47
|
+
end
|
48
|
+
|
49
|
+
# Check whether command finished correctly and prepare response
|
50
|
+
if status.success?
|
51
|
+
HTTPI.logger.debug "Received response:\r\n#{raw_response}"
|
52
|
+
status_string, headers_and_body = raw_response.split("\r\n", 2)
|
53
|
+
response_headers, response_body = headers_and_body.split("\r\n\r\n", 2)
|
54
|
+
response_code = status_string.scan(/\d{3}/).first
|
55
|
+
response_headers = Hash[response_headers.split("\r\n").map{|h| h.split(':', 2).map(&:strip) }]
|
56
|
+
HTTPI::Response.new(response_code, response_headers, response_body)
|
57
|
+
else
|
58
|
+
HTTPI.logger.fatal "While connecting to server #{uri.host} with command: #{cmd}"
|
59
|
+
HTTPI.logger.fatal "Command returned:\r\n#{status.inspect}"
|
60
|
+
HTTPI.logger.fatal "STDERR is:\n#{openssl_stderr}"
|
61
|
+
# OpenSSL's s_client always return 1 on fail, try to catch most common errors
|
62
|
+
case openssl_stderr
|
63
|
+
when /connect:errno=60/ then raise HTTPI::TimeoutError
|
64
|
+
when /connect:errno=61/ then raise (HTTPI::Error.new).extend(HTTPI::ConnectionError) # Connection refused
|
65
|
+
when /connect:errno=2/ then raise (HTTPI::Error.new).extend(HTTPI::ConnectionError) # No DNS name found
|
66
|
+
when /ssl handshake failure/ then raise HTTPI::SSLError, 'Seems like you trying to connect to HTTP, not HTTPS'
|
67
|
+
when /missing dsa signing cert/ then raise HTTPI::SSLError, 'Probably your OpenSSL lacks GOST configuration'
|
68
|
+
when /unable to load certificate/ then raise HTTPI::SSLError, 'Can not load client certificate, check file path and access rights'
|
69
|
+
when /unable to load .*? private key/ then raise HTTPI::SSLError, 'Can not load client certificate private key, check file path and access rights'
|
70
|
+
else raise HTTPI::Error
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: httpi-adapter-openssl_gost
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrey Novikov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httpi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10'
|
55
|
+
description: It uses OpenSSL `s_client` command to securely connect with server that
|
56
|
+
requires usage of GOST algorithms and client certificates.
|
57
|
+
email:
|
58
|
+
- envek@envek.name
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- httpi-adapter-openssl_gost.gemspec
|
69
|
+
- lib/httpi/adapter/openssl_gost.rb
|
70
|
+
- lib/httpi/adapter/openssl_gost/version.rb
|
71
|
+
homepage: https://github.com/Envek/httpi-adapter-openssl_gost
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements:
|
90
|
+
- OpenSSL 1.0.0 and newer with GOST engine installed, enabled, and configured.
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.2.2
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: HTTPI adapter for accessing HTTPS servers with GOST algorithms and certificates
|
96
|
+
test_files: []
|