http_signature 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +0 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +22 -0
- data/README.md +168 -0
- data/Rakefile +8 -0
- data/examples/faraday_middleware.rb +31 -0
- data/examples/rack_middleware.rb +72 -0
- data/http_signature.gemspec +23 -0
- data/lib/http_signature.rb +162 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ce69899c93af8245c7a11f207ae55d487ecc360df04c231d5ae1089afe897a6b
|
4
|
+
data.tar.gz: c26e3a5d0a06e97b03354cab9f3e20dc0fee3d06aca01ddd03fb55a7ab9c022e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0b96af0ec445b8b0f298d74e0743035ff2818d85c827892a8366cdf171338b9bf24cd5183b3317de58bcff01e191b60935e0ec4bc84fef7dcf05bc4d6d622666
|
7
|
+
data.tar.gz: 5b709409aa43df9606c5fadfc202721664db25005b7935e5aeb62df7755f22b8fd536f13c5972fb321a361d11e6c9e02ef77e96d553bf7766b9417b40958f6c6
|
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
http-signature (0.0.2)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
minitest (5.10.3)
|
10
|
+
rake (12.2.1)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
ruby
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
bundler
|
17
|
+
http-signature!
|
18
|
+
minitest
|
19
|
+
rake
|
20
|
+
|
21
|
+
BUNDLED WITH
|
22
|
+
1.16.0
|
data/README.md
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# HTTP Signature
|
2
|
+
[![CircleCI](https://circleci.com/gh/bolmaster2/http-signature.svg?style=svg)](https://circleci.com/gh/bolmaster2/http-signature)
|
3
|
+
|
4
|
+
Create and validate HTTP request signature according to this draft: https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
5
|
+
|
6
|
+
Aims to only implement the creation and validation of the signature without any external dependencies.
|
7
|
+
The idea is to implement adapters to popular http libraries to make it easy to use.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
### Basic
|
12
|
+
The most basic usage without any extra headers. The default algorithm is `hmac-sha256`.
|
13
|
+
```ruby
|
14
|
+
HTTPSignature.create(
|
15
|
+
url: 'https://example.com/foo',
|
16
|
+
key_id: 'Test',
|
17
|
+
key: 'secret 🙈'
|
18
|
+
)
|
19
|
+
# 'keyId="Test",algorithm="hmac-sha256",headers="(request-target)",signature="OQ/dHqRW9vFmrW/RCHg7O2Fqx+3uqxJw81p6k9Rcyo4="'
|
20
|
+
```
|
21
|
+
|
22
|
+
### With headers, query parameters and a body
|
23
|
+
Uses both query parameters (in query string) and a `json` body as a `POST` request.
|
24
|
+
Also shows how to set `rsa-sha256` as algorithm. The `digest` is as you see basically
|
25
|
+
a `sha-256` digest of the request body.
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
params = {
|
29
|
+
param: 'value',
|
30
|
+
pet: 'dog'
|
31
|
+
}
|
32
|
+
|
33
|
+
body = '{"hello": "world"}'
|
34
|
+
|
35
|
+
headers = {
|
36
|
+
'date': 'Thu, 05 Jan 2014 21:31:40 GMT',
|
37
|
+
'content-type': 'application/json',
|
38
|
+
'digest': HTTPSignature.create_digest(body),
|
39
|
+
'content-length': body.length
|
40
|
+
}
|
41
|
+
|
42
|
+
HTTPSignature.create(
|
43
|
+
url: 'https://example.com/foo',
|
44
|
+
method: :post,
|
45
|
+
query_string_params: params,
|
46
|
+
headers: headers,
|
47
|
+
key_id: 'Test',
|
48
|
+
algorithm: 'rsa-sha256',
|
49
|
+
key: File.read('key.pem'),
|
50
|
+
body: body
|
51
|
+
)
|
52
|
+
```
|
53
|
+
|
54
|
+
### With digest header auto-added
|
55
|
+
When digest header is omitted it's auto added as last header generated from the `body`:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
body = '{"foo": "bar"}'
|
59
|
+
|
60
|
+
HTTPSignature.create(
|
61
|
+
url: 'https://example.com/foo',
|
62
|
+
key_id: 'Test',
|
63
|
+
key: 'secret 🙈',
|
64
|
+
body: body
|
65
|
+
)
|
66
|
+
# 'keyId="Test",algorithm="hmac-sha256",headers="(request-target) digest",signature="3Jm5jnCSKX3fYLd58RqRdafZKeuSbUEPhn7grCGx4vg="'
|
67
|
+
```
|
68
|
+
|
69
|
+
### Validate asymmetric signature
|
70
|
+
With an asymmetric algorithm you can't just recreate the same header and see if they
|
71
|
+
check out, because you need the private key to do that and because the one validating
|
72
|
+
the signature should only have access to the public key, you need to validate it with that.
|
73
|
+
|
74
|
+
Imagine the incoming HTTP request looks like this:
|
75
|
+
```
|
76
|
+
POST /foo HTTP/1.1
|
77
|
+
Host: example.com
|
78
|
+
Date: Thu, 05 Jan 2014 21:31:40 GMT
|
79
|
+
Content-Type: application/json
|
80
|
+
Content-Length: 18
|
81
|
+
Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
|
82
|
+
Signature: keyId="Test-1",algorithm="rsa-sha256",headers="(request-target) host date content-type content-length digest",signature="YGPVM1tGHD7CHgTmroy9apLtVazdESzMl4vj1koYHNCMmTEDor4Om5TDZDFaJdny5dF3gq+PQQuPwyknNEvACmSjwVXzljPFxaY/JMZTqAdD0yHTP2Rx0Y/J4GwgKARWTZUmccfVYsXp86PhIlCymzleZzYCzj6shyg9NB7Ht+k="
|
83
|
+
|
84
|
+
{"hello": "world"}
|
85
|
+
```
|
86
|
+
|
87
|
+
Let's assume we have this request ☝️ in a `request` object for the sake of the example:
|
88
|
+
```ruby
|
89
|
+
HTTPSignature.valid?(
|
90
|
+
url: request.url,
|
91
|
+
method: request.method,
|
92
|
+
headers: request.headers,
|
93
|
+
body: request.body,
|
94
|
+
key: OpenSSL::PKey::RSA.new('public_key.pem'),
|
95
|
+
algorithm: 'rsa-sha256'
|
96
|
+
)
|
97
|
+
```
|
98
|
+
|
99
|
+
## Setup
|
100
|
+
```
|
101
|
+
bundle install
|
102
|
+
```
|
103
|
+
|
104
|
+
## Test
|
105
|
+
The tests are written with `minitest` using specs. Run them all with `rake`:
|
106
|
+
```bash
|
107
|
+
rake test
|
108
|
+
```
|
109
|
+
Or a single with pattern matching:
|
110
|
+
```bash
|
111
|
+
rake test TEST=test/http_signature_test.rb TESTOPTS="--name=/appends\ the\ query_string_params/"
|
112
|
+
```
|
113
|
+
|
114
|
+
## Example usage
|
115
|
+
### Faraday middleware on outgoing requests
|
116
|
+
Example of using it on an outgoing request.
|
117
|
+
```ruby
|
118
|
+
# TODO: Move this into gem
|
119
|
+
class AddRequestSignature < Faraday::Middleware
|
120
|
+
def call(env)
|
121
|
+
if env[:body]
|
122
|
+
env[:request_headers].merge!('Digest' => HTTPSignature.create_digest(env[:body]))
|
123
|
+
end
|
124
|
+
|
125
|
+
# Choose which headers to sign
|
126
|
+
headers_filter = %w{ Host Date Digest }
|
127
|
+
headers_to_sign = env[:request_headers].select { |k, v| headers_filter.include?(k.to_s) }
|
128
|
+
|
129
|
+
signature = HTTPSignature.create(
|
130
|
+
url: env[:url],
|
131
|
+
method: env[:method],
|
132
|
+
headers: headers_to_sign,
|
133
|
+
key: ENV.fetch('REQUEST_SIGNATURE_KEY'),
|
134
|
+
key_id: ENV.fetch('REQUEST_SIGNATURE_KEY_ID'),
|
135
|
+
algorithm: 'hmac-sha256',
|
136
|
+
body: env[:body] ? env[:body] : ''
|
137
|
+
)
|
138
|
+
|
139
|
+
env[:request_headers].merge!('Signature' => signature)
|
140
|
+
|
141
|
+
@app.call(env)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Tell faraday to use the middleware. Read more about it here: https://github.com/lostisland/faraday#advanced-middleware-usage
|
146
|
+
Faraday.new('http://example.com') do |faraday|
|
147
|
+
faraday.use(AddRequestSignature)
|
148
|
+
faraday.adapter(Faraday.default_adapter)
|
149
|
+
end
|
150
|
+
|
151
|
+
response = conn.get('/')
|
152
|
+
```
|
153
|
+
|
154
|
+
### Rack middleware
|
155
|
+
I've written a quite sloppy but totally usable rack middleware that validates every incoming request.
|
156
|
+
[See it here](examples/rack_middleware.rb). Soon I'll add it to the gem.
|
157
|
+
|
158
|
+
## License
|
159
|
+
This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
|
160
|
+
|
161
|
+
## Todo
|
162
|
+
- Structure and add middlewares into gem
|
163
|
+
- Add more example of use with different http libraries
|
164
|
+
- Implement algorithms:
|
165
|
+
- ecdsa-sha256
|
166
|
+
- When creating the signing string, follow the spec exactly:
|
167
|
+
https://tools.ietf.org/html/draft-cavage-http-signatures-08#section-2.3,
|
168
|
+
e.g, concatenate multiple instances of the same headers and remove surrounding whitespaces
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'http_signature'
|
4
|
+
|
5
|
+
class AddRequestSignature < Faraday::Middleware
|
6
|
+
def call(env)
|
7
|
+
if env[:body]
|
8
|
+
env[:request_headers].merge!('Digest' => HTTPSignature.create_digest(env[:body]))
|
9
|
+
end
|
10
|
+
|
11
|
+
# Choose which headers to sign
|
12
|
+
filtered_headers = %w{ Host Date Digest }
|
13
|
+
headers_to_sign = env[:request_headers].select { |k, v| filtered_headers.include?(k.to_s) }
|
14
|
+
|
15
|
+
headers.select { |header| headers_to_sign.includes(header) }.to_h
|
16
|
+
|
17
|
+
signature = HTTPSignature.create(
|
18
|
+
url: env[:url],
|
19
|
+
method: env[:method],
|
20
|
+
headers: headers,
|
21
|
+
key: ENV.fetch('REQUEST_SIGNATURE_KEY'),
|
22
|
+
key_id: ENV.fetch('REQUEST_SIGNATURE_KEY_ID'),
|
23
|
+
algorithm: 'hmac-sha256',
|
24
|
+
body: env[:body] ? env[:body] : ''
|
25
|
+
)
|
26
|
+
|
27
|
+
env[:request_headers].merge!('Signature' => signature)
|
28
|
+
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'http_signature'
|
4
|
+
|
5
|
+
# Rack middleware using http-signature gem to validate signature on every incoming request
|
6
|
+
class ValidateRequestSignature
|
7
|
+
KEY = ENV.fetch('REQUEST_SIGNATURE_KEY')
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
request = Rack::Request.new(env)
|
15
|
+
return [401, {}, ['No signature header']] unless request.get_header("HTTP_SIGNATURE")
|
16
|
+
|
17
|
+
request_body = request.body.gets
|
18
|
+
request_headers = parse_request_headers(request)
|
19
|
+
begin
|
20
|
+
parsed_signature = parse_signature(request_headers)
|
21
|
+
rescue
|
22
|
+
return [401, {}, ['Invalid signature :(']]
|
23
|
+
end
|
24
|
+
headers_to_sign = request_headers.select { |k, v| parsed_signature['headers'].include?(k) }
|
25
|
+
|
26
|
+
params = {
|
27
|
+
url: request.path,
|
28
|
+
method: request.request_method,
|
29
|
+
headers: headers_to_sign,
|
30
|
+
key: KEY,
|
31
|
+
key_id: parsed_signature['keyId'],
|
32
|
+
algorithm: parsed_signature['algorithm'],
|
33
|
+
body: request_body ? request_body : '',
|
34
|
+
query_string_params: Rack::Utils.parse_nested_query(request.query_string)
|
35
|
+
}
|
36
|
+
|
37
|
+
valid_signature =
|
38
|
+
if parsed_signature['algorithm'].include?('rsa')
|
39
|
+
HTTPSignature.valid?(**params)
|
40
|
+
else
|
41
|
+
HTTPSignature.create(**params) == request_headers['signature']
|
42
|
+
end
|
43
|
+
|
44
|
+
if valid_signature
|
45
|
+
@app.call(env)
|
46
|
+
else
|
47
|
+
[401, {}, ['Invalid signature :(']]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def parse_request_headers(request)
|
54
|
+
request_headers = {}
|
55
|
+
|
56
|
+
request.each_header do |header|
|
57
|
+
if header[0].include?('HTTP_') && header[0] != 'HTTP_VERSION'
|
58
|
+
request_headers[header[0].gsub('HTTP_', '').gsub("_", "-").downcase] = header[1]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
request_headers
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_signature(request_headers)
|
66
|
+
Rack::Utils.parse_nested_query(
|
67
|
+
request_headers['signature'].gsub(',', '&')
|
68
|
+
).map do |k, v|
|
69
|
+
[k, v.tr('"', '')]
|
70
|
+
end.to_h
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'http_signature'
|
6
|
+
spec.version = '0.0.2'
|
7
|
+
spec.authors = ['Joel Larsson']
|
8
|
+
spec.email = ['bolmaster2@gmail.com']
|
9
|
+
|
10
|
+
spec.summary = 'Create and validate HTTP request signature'
|
11
|
+
spec.description = 'Create and validate HTTP request signature according to this draft: https://tools.ietf.org/html/draft-cavage-http-signatures-08'
|
12
|
+
spec.homepage = 'https://github.com/bolmaster2/http-signature'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = 'exe'
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler'
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'minitest'
|
23
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'base64'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module HTTPSignature
|
9
|
+
# Create signature based on the data sent in
|
10
|
+
#
|
11
|
+
# @param url [String] Full request url, can include query string as well
|
12
|
+
# @param query_string_params [Hash] Query string parameters, appends params to
|
13
|
+
# url if query string is already found in it
|
14
|
+
# @param body [String] Request body as a string, i.e., the "raw" request body
|
15
|
+
# @param headers [Hash] Request headers to include in the signature
|
16
|
+
# @param key [String] Key/secret that is used by the corresponding `algorithm`
|
17
|
+
# @param key_id [String] Key id
|
18
|
+
# @param method [Symbol] Request method, default is `:get`
|
19
|
+
# @param algorithm [String] Algorithm to use when signing, check `supported_algorithms` for
|
20
|
+
# @return [String] The signature header value to use in "Signature" header
|
21
|
+
def self.create(url:, query_string_params: {}, body: '', headers: {}, key:,
|
22
|
+
key_id: SecureRandom.hex(8),
|
23
|
+
method: :get,
|
24
|
+
algorithm: 'hmac-sha256'
|
25
|
+
)
|
26
|
+
|
27
|
+
raise 'Unsupported algorithm :(' unless supported_algorithms.include?(algorithm)
|
28
|
+
|
29
|
+
uri = URI(url)
|
30
|
+
path = uri.path
|
31
|
+
headers = add_digest(headers, body)
|
32
|
+
headers = convert_headers(headers)
|
33
|
+
query = create_query_string(uri, query_string_params)
|
34
|
+
|
35
|
+
string_to_sign = create_signing_string(method: method, path: path,
|
36
|
+
query: query, headers: headers)
|
37
|
+
|
38
|
+
signature = sign(string_to_sign, key: key, algorithm: algorithm)
|
39
|
+
create_signature_header(key_id: key_id, headers: headers, signature: signature,
|
40
|
+
algorithm: algorithm)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.sign(string, key:, algorithm:)
|
44
|
+
case algorithm
|
45
|
+
when 'hmac-sha256'
|
46
|
+
OpenSSL::HMAC.digest('SHA256', key, string)
|
47
|
+
when 'hmac-sha512'
|
48
|
+
OpenSSL::HMAC.digest('SHA512', key, string)
|
49
|
+
when 'rsa-sha256'
|
50
|
+
k = OpenSSL::PKey::RSA.new(key)
|
51
|
+
k.sign(OpenSSL::Digest::SHA256.new, string)
|
52
|
+
when 'rsa-sha512'
|
53
|
+
k = OpenSSL::PKey::RSA.new(key)
|
54
|
+
k.sign(OpenSSL::Digest::SHA512.new, string)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.create_signature_header(key_id:, headers: [], signature:, algorithm:)
|
59
|
+
headers = headers.map { |h| h.split(':').first }
|
60
|
+
header_fields = ['(request-target)'].concat(headers).join(' ')
|
61
|
+
|
62
|
+
[
|
63
|
+
"keyId=\"#{key_id}\"",
|
64
|
+
"algorithm=\"#{algorithm}\"",
|
65
|
+
"headers=\"#{header_fields}\"",
|
66
|
+
"signature=\"#{Base64.strict_encode64(signature)}\""
|
67
|
+
].join(',')
|
68
|
+
end
|
69
|
+
|
70
|
+
# TODO: Support them all: rsa-sha1, rsa-sha512, dsa-sha1, hmac-sha1
|
71
|
+
def self.supported_algorithms
|
72
|
+
['hmac-sha256', 'hmac-sha512', 'rsa-sha256', 'rsa-sha512']
|
73
|
+
end
|
74
|
+
|
75
|
+
# Create the digest header based on the request body
|
76
|
+
# @param body [String] Raw request body string
|
77
|
+
# @return [String] SHA256 and base64 digested string with prefix: 'SHA-256='
|
78
|
+
def self.create_digest(body)
|
79
|
+
'SHA-256=' + Digest::SHA256.base64digest(body)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Creates the string to sign
|
83
|
+
# See details here: https://tools.ietf.org/html/draft-cavage-http-signatures-08#section-2.3
|
84
|
+
# TODO: Concatenate multiple instances of the same headers
|
85
|
+
# Also remove leading and trailing whitespace
|
86
|
+
# @return [String]
|
87
|
+
def self.create_signing_string(method:, path:, query:, headers:)
|
88
|
+
[
|
89
|
+
"(request-target): #{method.upcase} #{path}#{query}",
|
90
|
+
].concat(headers).join("\n")
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if signature is valid. Using the exact same parameters as .create
|
94
|
+
# minus `key_id`
|
95
|
+
#
|
96
|
+
# @param url [String] Full request url, can include query string as well
|
97
|
+
# @param query_string_params [Hash] Query string parameters, appends params to
|
98
|
+
# url if query string is already found in it
|
99
|
+
# @param body [String] Request body as a string, i.e., the "raw" request body
|
100
|
+
# @param headers [Hash] Request headers to include in the signature
|
101
|
+
# @param key [String] Key/secret that is used by the corresponding `algorithm`
|
102
|
+
# @param method [Symbol] Request method, default is `:get`
|
103
|
+
# @param algorithm [String] Algorithm to use when signing, check `supported_algorithms` for
|
104
|
+
# @return [Boolean] Valid or not, Crypto is kinda binary in this case :)
|
105
|
+
def self.valid?(url:, query_string_params: {}, body: '', headers: {}, key:, method:, algorithm:)
|
106
|
+
raise 'Key needs to be public' unless key.public?
|
107
|
+
|
108
|
+
# TODO: A lot of the code here is exactly as `.create`, i.e., this could be DRYed :point_down:
|
109
|
+
uri = URI(url)
|
110
|
+
path = uri.path
|
111
|
+
signature = headers.delete(:signature)
|
112
|
+
headers = convert_headers(headers)
|
113
|
+
query = create_query_string(uri, query_string_params)
|
114
|
+
|
115
|
+
string_to_sign = create_signing_string(
|
116
|
+
method: method, path: path, query: query, headers: headers
|
117
|
+
)
|
118
|
+
|
119
|
+
key.verify(
|
120
|
+
get_digest(algorithm), get_signature_from_header(signature), string_to_sign
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Maps algoritgm string to digest object
|
125
|
+
# @param algorithm [String]
|
126
|
+
# @return [OpenSSL::Digest] Instance of `OpenSSL::Digest::SHA256` or OpenSSL::Digest::SHA512
|
127
|
+
def self.get_digest(algorithm)
|
128
|
+
{
|
129
|
+
'rsa-sha256' => OpenSSL::Digest::SHA256.new,
|
130
|
+
'rsa-sha512' => OpenSSL::Digest::SHA512.new
|
131
|
+
}[algorithm]
|
132
|
+
end
|
133
|
+
|
134
|
+
# Extract the actual signature from the whole "Signature" header
|
135
|
+
# @param header [String]
|
136
|
+
# @return [String]
|
137
|
+
def self.get_signature_from_header(header)
|
138
|
+
Base64.strict_decode64(header.match(/signature\=\"(.*)\"/)[1])
|
139
|
+
end
|
140
|
+
|
141
|
+
# When query string params is also set on the url, append the params defined
|
142
|
+
# in `query_string_params` and make a joint query string
|
143
|
+
def self.create_query_string(uri, query_string_params)
|
144
|
+
if uri.query || !query_string_params.empty?
|
145
|
+
delimiter = uri.query.nil? ? '' : '&'
|
146
|
+
'?' + (query_string_params.empty? ? '' : [uri.query.to_s, delimiter, URI.encode_www_form(query_string_params)].join)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
# Convert a header hash into an array with header strings
|
150
|
+
# { header: 'value'} -> ['header: value']
|
151
|
+
def self.convert_headers(headers)
|
152
|
+
headers.map do |key, value|
|
153
|
+
[key.to_s.downcase.strip, value.strip].join(': ')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.add_digest(headers, body)
|
158
|
+
headers[:digest] = create_digest(body) unless body.empty?
|
159
|
+
|
160
|
+
headers
|
161
|
+
end
|
162
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: http_signature
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joel Larsson
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: 'Create and validate HTTP request signature according to this draft:
|
56
|
+
https://tools.ietf.org/html/draft-cavage-http-signatures-08'
|
57
|
+
email:
|
58
|
+
- bolmaster2@gmail.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".circleci/config.yml"
|
64
|
+
- Gemfile
|
65
|
+
- Gemfile.lock
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- examples/faraday_middleware.rb
|
69
|
+
- examples/rack_middleware.rb
|
70
|
+
- http_signature.gemspec
|
71
|
+
- lib/http_signature.rb
|
72
|
+
homepage: https://github.com/bolmaster2/http-signature
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.7.3
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Create and validate HTTP request signature
|
96
|
+
test_files: []
|