httpi-adapter-openssl_gost 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|