smart_id 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +55 -0
- data/LICENSE.txt +21 -0
- data/README.md +225 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/smart_id.rb +64 -0
- data/lib/smart_id/api/authentication/base.rb +55 -0
- data/lib/smart_id/api/authentication/confirmation_poller.rb +37 -0
- data/lib/smart_id/api/authentication/document.rb +20 -0
- data/lib/smart_id/api/authentication/identity_number.rb +29 -0
- data/lib/smart_id/api/confirmation_response.rb +55 -0
- data/lib/smart_id/api/request.rb +88 -0
- data/lib/smart_id/api/response.rb +15 -0
- data/lib/smart_id/authentication_certificate/certificate.rb +17 -0
- data/lib/smart_id/authentication_certificate/content.rb +45 -0
- data/lib/smart_id/exceptions.rb +23 -0
- data/lib/smart_id/utils/authentication_hash.rb +28 -0
- data/lib/smart_id/utils/certificate_validator.rb +55 -0
- data/lib/smart_id/utils/verification_code_calculator.rb +24 -0
- data/lib/smart_id/version.rb +3 -0
- data/smart_id.gemspec +35 -0
- data/trusted_certs/EID-SK_2016.pem.crt +63 -0
- data/trusted_certs/NQ-SK_2016.pem.crt +61 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f1a4792f8f44f13df58feb85e9641bf8c249b0939d6d727ca7234060717a4d86
|
4
|
+
data.tar.gz: f58e94d4586fb872662853aa5cf4d50a8e6a2b40b1e256c83cafe3f1f13acd5f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eab5dfbaa386542421940cfc1490121a012f047f95c38f967b4fb4365aa9e677c8a2ce31dbf430a681092834a96557a1e8760145fc3adaa30c73079a4e63069b
|
7
|
+
data.tar.gz: 81f9b99115c31819d1335592bd2a18d6f0ee4441b1b2ef479b1d9c303ecc83dd3f12c83a199a77e36837918e671bfc8b4e8cba090be5180e180ef12fbe20bf4f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
smart_id (0.1.0)
|
5
|
+
rest-client (~> 2.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
byebug (11.1.1)
|
11
|
+
diff-lcs (1.3)
|
12
|
+
domain_name (0.5.20190701)
|
13
|
+
unf (>= 0.0.5, < 1.0.0)
|
14
|
+
http-accept (1.7.0)
|
15
|
+
http-cookie (1.0.3)
|
16
|
+
domain_name (~> 0.5)
|
17
|
+
mime-types (3.3.1)
|
18
|
+
mime-types-data (~> 3.2015)
|
19
|
+
mime-types-data (3.2019.1009)
|
20
|
+
netrc (0.11.0)
|
21
|
+
rake (10.5.0)
|
22
|
+
rest-client (2.1.0)
|
23
|
+
http-accept (>= 1.7.0, < 2.0)
|
24
|
+
http-cookie (>= 1.0.2, < 2.0)
|
25
|
+
mime-types (>= 1.16, < 4.0)
|
26
|
+
netrc (~> 0.8)
|
27
|
+
rspec (3.9.0)
|
28
|
+
rspec-core (~> 3.9.0)
|
29
|
+
rspec-expectations (~> 3.9.0)
|
30
|
+
rspec-mocks (~> 3.9.0)
|
31
|
+
rspec-core (3.9.1)
|
32
|
+
rspec-support (~> 3.9.1)
|
33
|
+
rspec-expectations (3.9.0)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.9.0)
|
36
|
+
rspec-mocks (3.9.1)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.9.0)
|
39
|
+
rspec-support (3.9.2)
|
40
|
+
unf (0.1.4)
|
41
|
+
unf_ext
|
42
|
+
unf_ext (0.0.7.6)
|
43
|
+
|
44
|
+
PLATFORMS
|
45
|
+
ruby
|
46
|
+
|
47
|
+
DEPENDENCIES
|
48
|
+
bundler (~> 2.0)
|
49
|
+
byebug
|
50
|
+
rake (~> 10.0)
|
51
|
+
rspec (~> 3.0)
|
52
|
+
smart_id!
|
53
|
+
|
54
|
+
BUNDLED WITH
|
55
|
+
2.0.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 lekristapino
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
# SmartId
|
2
|
+
|
3
|
+
This gem provides a wrapper around [Smart ID API](https://github.com/SK-EID/smart-id-documentation). All the necessary checks, listed in point 3.5 are implemented. Currently this gem only supports authentication actions.
|
4
|
+
|
5
|
+
# TODO
|
6
|
+
- [x] Add authentication functionality
|
7
|
+
- [ ] Add Signing functionality (see if possible)
|
8
|
+
- [ ] More test coverage
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'smart_id', "~> 0.1"
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install smart_id
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
### Configuration
|
29
|
+
|
30
|
+
configuration can be done, by creating an initializer file and loading it before the application starts.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
SmartId.configure do |config|
|
34
|
+
config.relying_party_uuid = "MySmartIdUUID"
|
35
|
+
config.relying_party_name = "My Smart ID name"
|
36
|
+
config.environment = "demo" # possible options ar "demo" and "production", uses according smart-id parameters and keys
|
37
|
+
config.default_certificate_level = "ADVANCED" # Possible options are "ADVANCED" or "QUALIFIED". Defaults to "ADVANCED"
|
38
|
+
config.poller_timeout_secods = 10 # seconds to wait when fetching authentication confirmation
|
39
|
+
end
|
40
|
+
|
41
|
+
```
|
42
|
+
### Authentication types
|
43
|
+
|
44
|
+
Authentication can be done either with providing user's national identity number or an identity document number
|
45
|
+
|
46
|
+
For national identity number use
|
47
|
+
```ruby
|
48
|
+
SmartId::Api::Authentication::IdentityNumber
|
49
|
+
```
|
50
|
+
|
51
|
+
For document number use
|
52
|
+
```ruby
|
53
|
+
SmartId::Api::Authentication::Document
|
54
|
+
```
|
55
|
+
Smart ID authentication is done in 2 steps - **initializing the authentication** and then **getting confimation from Smart ID** service. Those two steps happen asynchronously, so some parameters should be persisted either in session storage or in database
|
56
|
+
|
57
|
+
* Back-end initializes authentification - user can see verification code on the app, and receives smart ID request to input PIN in they're mobile
|
58
|
+
|
59
|
+
* Back-end authenfication confirmation - check whether user has authenticated by correctly typing in they're PIN on the mobile device
|
60
|
+
|
61
|
+
## Authentication Request
|
62
|
+
|
63
|
+
To initialize authentication make a controller action.
|
64
|
+
|
65
|
+
#### National identity number
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class UserController < AplicationController
|
69
|
+
#...
|
70
|
+
def authenticate_smart_id
|
71
|
+
# authentication hash by default will generate random bytes, that will be hashed for signature check
|
72
|
+
# if you wish to provide your own randomization, you can pass a parameter to AuthenticationHash with the random string
|
73
|
+
# authentication_hash = SmartId::Utils::AuthenticationHash.new(SecureRandom.hex(64))
|
74
|
+
# each authentication should have a unique random string passed
|
75
|
+
authentication_hash = SmartId::Utils::AuthenticationHash.new
|
76
|
+
|
77
|
+
auth_response = SmartId::Api::Authentication::IdentityNumber.authenticate(
|
78
|
+
country: params[:country], # 2 character ISO 3166-1 alpha-2 format(for example EE, LT, LV, KZ)
|
79
|
+
identity_number: params[:identity_number],
|
80
|
+
authentication_hash: authentication_hash
|
81
|
+
)
|
82
|
+
|
83
|
+
session[:smart_id_session] = auth_response.session_id
|
84
|
+
session[:auth_hash] = authentication_hash.hash_data
|
85
|
+
|
86
|
+
# Screen/page after this call should show the user verification code, to see if it matches
|
87
|
+
# the one they see on their mobile device
|
88
|
+
render json: { verification_code: auth_response.verification_code }
|
89
|
+
|
90
|
+
end
|
91
|
+
#...
|
92
|
+
end
|
93
|
+
|
94
|
+
```
|
95
|
+
|
96
|
+
#### Document number
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class UserController < AplicationController
|
100
|
+
#...
|
101
|
+
def authenticate_smart_id
|
102
|
+
# authentication hash by default will generate random bytes, that will be hashed for signature check
|
103
|
+
# if you wish to provide your own randomization, you can pass a parameter to AuthenticationHash with the random string
|
104
|
+
# authentication_hash = SmartId::Utils::AuthenticationHash.new(SecureRandom.hex(64))
|
105
|
+
# each authentication should have a unique random string passeds
|
106
|
+
authentication_hash = SmartId::Utils::AuthenticationHash.new
|
107
|
+
|
108
|
+
auth_response = SmartId::Api::Authentication::Document.authenticate(
|
109
|
+
document_number: params[:document_number],
|
110
|
+
authentication_hash: authentication_hash
|
111
|
+
)
|
112
|
+
|
113
|
+
session[:smart_id_session] = auth_response.session_id
|
114
|
+
session[:auth_hash] = authentication_hash.hash_data
|
115
|
+
|
116
|
+
# Screen/page after this call should show the user verification code, to see if it matches
|
117
|
+
# the one they see on their mobile device
|
118
|
+
render json: { verification_code: auth_response.verification_code }
|
119
|
+
|
120
|
+
end
|
121
|
+
#...
|
122
|
+
end
|
123
|
+
|
124
|
+
```
|
125
|
+
|
126
|
+
## Authentication Confirmation
|
127
|
+
Create another controller action
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class UserController < AplicationController
|
131
|
+
#...
|
132
|
+
def confirm_smart_id
|
133
|
+
# use hash_data saved on authentication initialization as parameter
|
134
|
+
authentication_hash = SmartId::Utils::AuthenticationHash.new(session[:auth_hash])
|
135
|
+
|
136
|
+
confirmation_response = SmartId::Api::Authentication::ConfirmationPoller.confirm(
|
137
|
+
session_id: session[:smart_id_session],
|
138
|
+
authentication_hash: authentication_hash,
|
139
|
+
# if true, will continously make requests to smart-id and return only after verification is completed
|
140
|
+
# you can set this parameter to false, to handle polling yourself
|
141
|
+
poll: true # default - true
|
142
|
+
)
|
143
|
+
end
|
144
|
+
#...
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
|
149
|
+
## Response structure
|
150
|
+
|
151
|
+
confirmation response will have the following attributes
|
152
|
+
```ruby
|
153
|
+
authentication_hash = SmartId::Utils::AuthenticationHash.new(session[:auth_hash])
|
154
|
+
|
155
|
+
confirmation_response = SmartId::Api::Authentication::ConfirmationPoller.confirm(
|
156
|
+
session_id: session[:smart_id_session],
|
157
|
+
authentication_hash: authentication_hash,
|
158
|
+
# if true, will continously make requests to smart-id and return only after verification is completed
|
159
|
+
# you can set this parameter to false, to handle polling yourself
|
160
|
+
poll: true # default - true
|
161
|
+
)
|
162
|
+
|
163
|
+
confirmation_response.confirmation_running? # => true/false whether the user has finished authentication. Relevant, only if polling is not handled by the gem (with `poll` parameter set to false)
|
164
|
+
confirmation_response.end_result # => end result of the verification. possible values are "OK"/"USER_REFUSED"/"TIMEOUT"/"DOCUMENT_UNUSABLE", see details in https://github.com/SK-EID/smart-id-documentation#5-session-end-result-codes
|
165
|
+
confirmation_response.document_number #=> document number for user
|
166
|
+
confirmation_response.certificate_level #=> certificate level for user - values are "ADVANCED" or "QUALIFIED"
|
167
|
+
|
168
|
+
confirmation_response.certificate.content.given_name #=> given name for user
|
169
|
+
confirmation_response.certificate.content.surname #=> surname for user
|
170
|
+
confirmation_response.certificate.content.serial_number #=> string, that includes user's national identity number
|
171
|
+
|
172
|
+
```
|
173
|
+
|
174
|
+
## Customization options
|
175
|
+
You can provide extra parameters when initializing authentication (for both - identity number and document)
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
SmartId::Api::Authentication::Document.authenticate(
|
179
|
+
document_number: "", # REQUIRED - document number
|
180
|
+
authentication_hash: obj, # REQUIRED - authentification hash object of SmartId::Utils::AuthenticationHash
|
181
|
+
certificate_level: "", # OPTIONAL - Either "ADVANCED" or "QUALIFIED" - if none are provided, default certificate level is used
|
182
|
+
display_text: nil, # OPTIONAL - Text that user will see on their mobile device when asked for authentication
|
183
|
+
multiple_choice: false, # OPTIONAL - If true, user will be asked to choose the correct verification code from supplied options on their device
|
184
|
+
)
|
185
|
+
|
186
|
+
SmartId::Api::Authentication::IdentityNumber.authenticate(
|
187
|
+
country: "", # REQUIRED - 2 character ISO 3166-1 alpha-2 format(for example EE, LT, LV, KZ)
|
188
|
+
identity_number: "", # REQUIRED - natioanl identity number
|
189
|
+
authentication_hash: obj, # REQUIRED - authentification hash object of SmartId::Utils::AuthenticationHash
|
190
|
+
certificate_level: "", # OPTIONAL - Either "ADVANCED" or "QUALIFIED" - if none are provided, default certificate level is used
|
191
|
+
display_text: nil, # OPTIONAL - Text that user will see on their mobile device when asked for authentication
|
192
|
+
multiple_choice: false, # OPTIONAL - If true, user will be asked to choose the correct verification code from supplied options on their device
|
193
|
+
)
|
194
|
+
```
|
195
|
+
|
196
|
+
## Exceptions
|
197
|
+
All exceptions inherit from `SmartId::Exception`
|
198
|
+
| Exception class | Description |
|
199
|
+
| --------------- | :-----------: |
|
200
|
+
| `SmartId::InvalidParamsError` | either country or identity_number were not provided when trying to authenticate with identity number |
|
201
|
+
| `SmartId::ConnectionError` | authentication/confirmation request failed, when rescuing see `e.original_error` for more details |
|
202
|
+
| `SmartId::NoUserFoundError`| user with the supplied parameters (id number, document number, country) does not exist in smart ID system |
|
203
|
+
| `SmartId::SSLCertificateNotVerified` | SSL certificate for smart ID service was not verified. Check for newest version of this gem to always keep cerficates updated |
|
204
|
+
| `SmartId::InvalidResponseCertificate` | Certificate used in confirmation response is invalid|
|
205
|
+
| `SmartId::InvalidResponseSignature` | Signature used in confirmation response is invalid. |
|
206
|
+
| `SmartId::IncorrectAccountLevelError` | User's Smart ID account is below the required level by the authentication request ( "ADVANCED" < "QUALIFIED") |
|
207
|
+
| `SmartId::InvalidPermissionsError` | Relying Party has no permission to issue the request. This may happen when Relying Party has no permission to invoke operations on accounts with ADVANCED certificates. |
|
208
|
+
| `SmartId::OutdatedApiError` | API used by the gem is outdated, please see if you are running the newest version of the gem |
|
209
|
+
| `SmartId::SystemUnderMaintenanceError` | Smart ID System is under maintenance, try again later |
|
210
|
+
|
211
|
+
## Testing
|
212
|
+
Smart ID demo environment has provided some sample values to use when testing applications see [Smart ID WIKI page](https://github.com/SK-EID/smart-id-documentation/wiki/Environment-technical-parameters)
|
213
|
+
## Development
|
214
|
+
|
215
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
216
|
+
|
217
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
218
|
+
|
219
|
+
## Contributing
|
220
|
+
|
221
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/zippyvision/smart-id-ruby.
|
222
|
+
|
223
|
+
## License
|
224
|
+
|
225
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "smart_id"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/smart_id.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "smart_id/version"
|
2
|
+
require "smart_id/utils/authentication_hash"
|
3
|
+
require "smart_id/utils/certificate_validator"
|
4
|
+
require "smart_id/utils/verification_code_calculator"
|
5
|
+
require "smart_id/api/request"
|
6
|
+
require "smart_id/api/response"
|
7
|
+
require "smart_id/api/confirmation_response"
|
8
|
+
require "smart_id/api/authentication/identity_number"
|
9
|
+
require "smart_id/api/authentication/document"
|
10
|
+
require "smart_id/api/authentication/confirmation_poller"
|
11
|
+
require "smart_id/authentication_certificate/certificate"
|
12
|
+
require "smart_id/authentication_certificate/content"
|
13
|
+
|
14
|
+
module SmartId
|
15
|
+
@@environment = "DEMO" # possible options are demo and production
|
16
|
+
@@relying_party_uuid = nil
|
17
|
+
@@relying_party_name = nil
|
18
|
+
@@default_certificate_level = "ADVANCED" # possible values are "ADVANCED", "QUALIFIED"
|
19
|
+
@@poller_timeout_seconds = 10
|
20
|
+
|
21
|
+
def self.configure(&block)
|
22
|
+
yield(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.relying_party_uuid=(value)
|
26
|
+
@@relying_party_uuid = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.relying_party_uuid
|
30
|
+
@@relying_party_uuid
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.relying_party_name=(value)
|
34
|
+
@@relying_party_name = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.relying_party_name
|
38
|
+
@@relying_party_name
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.default_certificate_level=(value)
|
42
|
+
@@default_certificate_level = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.default_certificate_level
|
46
|
+
@@default_certificate_level
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.poller_timeout_seconds=(value)
|
50
|
+
@@poller_timeout_seconds = value
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.poller_timeout_seconds
|
54
|
+
@@poller_timeout_seconds
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.environment=(value)
|
58
|
+
@@environment = value.upcase
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.environment
|
62
|
+
@@environment
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "rest-client"
|
2
|
+
require "smart_id/exceptions"
|
3
|
+
require "smart_id/utils/authentication_hash"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module SmartId::Api
|
7
|
+
module Authentication
|
8
|
+
class Base
|
9
|
+
attr_reader :authentication_hash
|
10
|
+
|
11
|
+
def self.authenticate(**opts)
|
12
|
+
new(**opts).call
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(**opts)
|
16
|
+
@authentication_hash = opts[:authentication_hash]
|
17
|
+
@display_text = opts[:display_text]
|
18
|
+
@certificate_level = opts[:certificate_level]
|
19
|
+
@multiple_choice = opts[:multiple_choice]
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def call
|
24
|
+
response = SmartId::Api::Request.execute(method: :post, uri: api_uri, params: request_params)
|
25
|
+
SmartId::Api::Response.new(JSON.parse(response.body), authentication_hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def request_params
|
31
|
+
params = {
|
32
|
+
relyingPartyUUID: SmartId.relying_party_uuid,
|
33
|
+
relyingPartyName: SmartId.relying_party_name,
|
34
|
+
certificateLevel: @certificate_level || SmartId.default_certificate_level,
|
35
|
+
hash: authentication_hash.calculate_base64_digest,
|
36
|
+
hashType: "SHA256"
|
37
|
+
}
|
38
|
+
|
39
|
+
if @display_text
|
40
|
+
params.merge!(displayText: @display_text)
|
41
|
+
end
|
42
|
+
|
43
|
+
if @multiple_choice
|
44
|
+
params.merge!(requestProperties: { vcChoice: @multiple_choice })
|
45
|
+
end
|
46
|
+
|
47
|
+
params
|
48
|
+
end
|
49
|
+
|
50
|
+
def api_uri
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SmartId::Api
|
2
|
+
module Authentication
|
3
|
+
class ConfirmationPoller
|
4
|
+
BASE_URI = "session/"
|
5
|
+
|
6
|
+
def self.confirm(session_id:, authentication_hash:, poll: true)
|
7
|
+
new(session_id, authentication_hash, poll).call
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(session_id, authentication_hash, poll)
|
12
|
+
@session_id = session_id
|
13
|
+
@authentication_hash = authentication_hash
|
14
|
+
@poll = poll
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
params = { timeoutMs: SmartId.poller_timeout_seconds * 1000 }
|
19
|
+
uri = BASE_URI + @session_id
|
20
|
+
|
21
|
+
raw_response = SmartId::Api::Request.execute(method: :get, uri: uri, params: params)
|
22
|
+
|
23
|
+
response = SmartId::Api::ConfirmationResponse.new(
|
24
|
+
JSON.parse(raw_response.body),
|
25
|
+
@authentication_hash.hash_data
|
26
|
+
)
|
27
|
+
|
28
|
+
# repeat request if confirmation is still running
|
29
|
+
if response.confirmation_running? && @poll
|
30
|
+
call
|
31
|
+
else
|
32
|
+
response
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "smart_id/api/authentication/base"
|
2
|
+
|
3
|
+
module SmartId::Api
|
4
|
+
module Authentication
|
5
|
+
class Document < Base
|
6
|
+
BASE_URI = "authentication/document"
|
7
|
+
|
8
|
+
def initialize(**opts)
|
9
|
+
@document_number = opts[:document_number]
|
10
|
+
super(**opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def api_uri
|
16
|
+
"#{BASE_URI}/#{@document_number}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "smart_id/api/authentication/base"
|
2
|
+
require "smart_id/exceptions"
|
3
|
+
|
4
|
+
module SmartId::Api
|
5
|
+
module Authentication
|
6
|
+
class IdentityNumber < Base
|
7
|
+
BASE_URI = "authentication/pno"
|
8
|
+
|
9
|
+
# @param country: 2 character ISO 3166-1 alpha-2 format(for example EE, LT, LV, KZ)
|
10
|
+
# @param identity_number: national identity number of the individuals
|
11
|
+
def initialize(**opts)
|
12
|
+
@country = opts[:country].upcase
|
13
|
+
@identity_number = opts[:identity_number]
|
14
|
+
|
15
|
+
unless @country && @identity_number
|
16
|
+
raise InvalidParamsError
|
17
|
+
end
|
18
|
+
|
19
|
+
super(**opts)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def api_uri
|
25
|
+
"#{BASE_URI}/#{@country}/#{@identity_number}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SmartId::Api
|
2
|
+
class ConfirmationResponse
|
3
|
+
RUNNING_STATE = "RUNNING"
|
4
|
+
COMPLETED_STATE = "COMPLETE"
|
5
|
+
|
6
|
+
attr_reader :body
|
7
|
+
|
8
|
+
def initialize(response_body, hashed_data)
|
9
|
+
@body = response_body
|
10
|
+
validate!(hashed_data)
|
11
|
+
end
|
12
|
+
|
13
|
+
def confirmation_running?
|
14
|
+
state == RUNNING_STATE
|
15
|
+
end
|
16
|
+
|
17
|
+
def state
|
18
|
+
@body["state"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def end_result
|
22
|
+
@body.dig("result", "endResult")
|
23
|
+
end
|
24
|
+
|
25
|
+
def document_number
|
26
|
+
@body.dig("result", "documentNumber")
|
27
|
+
end
|
28
|
+
|
29
|
+
def certificate_level
|
30
|
+
@body.dig("cert", "certificateLevel")
|
31
|
+
end
|
32
|
+
|
33
|
+
def certificate
|
34
|
+
@certificate ||= SmartId::AuthenticationCertificate::Certificate.new(@body.dig("cert", "value"))
|
35
|
+
end
|
36
|
+
|
37
|
+
def signature_algorithm
|
38
|
+
@body.dig("signature", "algorithm")
|
39
|
+
end
|
40
|
+
|
41
|
+
def signature
|
42
|
+
@body.dig("signature", "value")
|
43
|
+
end
|
44
|
+
|
45
|
+
def ignored_properties
|
46
|
+
@body["ignoredProperties"]
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def validate!(hashed_data)
|
52
|
+
SmartId::Utils::CertificateValidator.validate!(hashed_data, signature, certificate)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'date'
|
3
|
+
module SmartId::Api
|
4
|
+
class Request
|
5
|
+
DEMO_BASE_URL = "https://sid.demo.sk.ee/smart-id-rp/v1/"
|
6
|
+
PRODUCTION_BASE_URL = "https://rp-api.smart-id.com/v1/"
|
7
|
+
|
8
|
+
DEMO_SSL_KEY = "QLZIaH7Qx9Rjq3gyznQuNsvwMQb7maC5L4SLu/z5qNU="
|
9
|
+
PROD_KEY_EXPIRY = Date.new(2020,11,5)
|
10
|
+
PRODUCTION_SSL_KEY = "l2uvq6ftLN4LZ+8Un+71J2vH1BT9wTbtrE5+Fj3Vc5g="
|
11
|
+
|
12
|
+
def initialize(method, uri, params)
|
13
|
+
@method = method
|
14
|
+
@url = self.class.const_get("#{SmartId.environment}_BASE_URL") + uri
|
15
|
+
@params = params
|
16
|
+
@logger = Logger.new(STDOUT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.execute(method:, uri:, params:)
|
20
|
+
begin
|
21
|
+
api_request = new(method, uri, params)
|
22
|
+
api_request.execute
|
23
|
+
rescue RestClient::RequestFailed => e
|
24
|
+
case e.http_code
|
25
|
+
when 471
|
26
|
+
raise SmartId::IncorrectAccountLevelError
|
27
|
+
when 403
|
28
|
+
raise SmartId::InvalidPermissionsError
|
29
|
+
when 404
|
30
|
+
raise SmartId::UserNotFoundError
|
31
|
+
when 480
|
32
|
+
raise SmartId::OutdatedApiError
|
33
|
+
when 580
|
34
|
+
raise SmartId::SystemUnderMaintenanceError
|
35
|
+
else
|
36
|
+
raise SmartId::ConnectionError.new(e)
|
37
|
+
end
|
38
|
+
rescue RestClient::SSLCertificateNotVerified
|
39
|
+
raise SmartId::SSLCertificateNotVerified
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def maybe_warn_of_ssl_key_expiry
|
44
|
+
if (PROD_KEY_EXPIRY - Date.today).to_i < 60
|
45
|
+
@logger.warn("[Smart-id-Ruby] SSL KEY for security checks will soon expire, please update to newer version of this gem")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute
|
50
|
+
maybe_warn_of_ssl_key_expiry
|
51
|
+
|
52
|
+
if @method.to_sym == :post
|
53
|
+
attrs = post_request_attrs
|
54
|
+
else
|
55
|
+
attrs = get_request_attrs
|
56
|
+
end
|
57
|
+
|
58
|
+
request = RestClient::Request.execute(**attrs)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def default_attrs
|
64
|
+
{
|
65
|
+
method: @method,
|
66
|
+
url: @url,
|
67
|
+
headers: { content_type: :json, accept: :json },
|
68
|
+
timeout: SmartId.poller_timeout_seconds + 1,
|
69
|
+
ssl_verify_callback: lambda do |_, cert_store|
|
70
|
+
provided_pub_key = cert_store.chain[0].public_key
|
71
|
+
saved_key = self.class.const_get("#{SmartId.environment}_SSL_KEY")
|
72
|
+
Digest::SHA256.digest(provided_pub_key.to_der) == Base64.decode64(saved_key)
|
73
|
+
end
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_request_attrs
|
78
|
+
default_attrs.merge(headers: {
|
79
|
+
**default_attrs[:headers],
|
80
|
+
params: @params
|
81
|
+
})
|
82
|
+
end
|
83
|
+
|
84
|
+
def post_request_attrs
|
85
|
+
default_attrs.merge(payload: JSON.generate(@params))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SmartId::Api
|
2
|
+
class Response
|
3
|
+
attr_reader :session_id
|
4
|
+
|
5
|
+
def initialize(response_body, authentication_hash)
|
6
|
+
@body = response_body
|
7
|
+
@session_id = response_body["sessionID"]
|
8
|
+
@authentication_hash = authentication_hash
|
9
|
+
end
|
10
|
+
|
11
|
+
def verification_code
|
12
|
+
@verification_code ||= SmartId::VerificationCodeCalculator.calculate(@authentication_hash.calculate_digest)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SmartId
|
2
|
+
module AuthenticationCertificate
|
3
|
+
class Certificate
|
4
|
+
def initialize(base64_cert)
|
5
|
+
@base64_cert = base64_cert
|
6
|
+
end
|
7
|
+
|
8
|
+
def content
|
9
|
+
@content ||= SmartId::AuthenticationCertificate::Content.new(cert.subject.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def cert
|
13
|
+
@cert ||= OpenSSL::X509::Certificate.new(Base64.decode64(@base64_cert))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SmartId
|
2
|
+
module AuthenticationCertificate
|
3
|
+
class Content
|
4
|
+
def initialize(raw_content)
|
5
|
+
@raw_content = raw_content
|
6
|
+
end
|
7
|
+
|
8
|
+
def given_name
|
9
|
+
structured_raw_content["GN"].gsub(",", " ")
|
10
|
+
end
|
11
|
+
|
12
|
+
def surname
|
13
|
+
structured_raw_content["SN"].gsub(",", " ")
|
14
|
+
end
|
15
|
+
|
16
|
+
def country
|
17
|
+
structured_raw_content["C"].gsub(",", " ")
|
18
|
+
end
|
19
|
+
|
20
|
+
def all_info
|
21
|
+
structured_raw_content["CN"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def organizational_unit
|
25
|
+
structured_raw_content["OU"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def serial_number
|
29
|
+
structured_raw_content["serialNumber"]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def structured_raw_content
|
35
|
+
return @structured_raw_content if @structured_raw_content
|
36
|
+
@structured_raw_content = @raw_content.split("/").each_with_object({}) do |c, result|
|
37
|
+
if c.include?("=")
|
38
|
+
key, val = c.split("=")
|
39
|
+
result[key] = val
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SmartId
|
2
|
+
class Exception < ::Exception; end
|
3
|
+
class InvalidParamsError < Exception; end
|
4
|
+
class SSLCertificateNotVerified < Exception; end
|
5
|
+
class InvalidResponseCertificate < Exception; end
|
6
|
+
class InvalidResponseSignature < Exception; end
|
7
|
+
class UserNotFoundError < Exception; end
|
8
|
+
class OutdatedApiError < Exception; end
|
9
|
+
class SystemUnderMaintenanceError < Exception; end
|
10
|
+
|
11
|
+
class ConnectionError < Exception;
|
12
|
+
attr_reader :original_error
|
13
|
+
def initialize(original_error)
|
14
|
+
@original_error = original_error
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class IncorrectAccountLevelError < Exception
|
19
|
+
def message
|
20
|
+
"User exists, but has lower level account than required by request"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module SmartId
|
5
|
+
module Utils
|
6
|
+
class AuthenticationHash
|
7
|
+
attr_reader :hash_data
|
8
|
+
|
9
|
+
def initialize(hash_data = nil)
|
10
|
+
@hash_data = hash_data || random_bytes
|
11
|
+
end
|
12
|
+
|
13
|
+
def calculate_digest
|
14
|
+
Digest::SHA256.digest(hash_data)
|
15
|
+
end
|
16
|
+
|
17
|
+
def calculate_base64_digest
|
18
|
+
Base64.strict_encode64(calculate_digest)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def random_bytes
|
24
|
+
OpenSSL::Random.random_bytes(64)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SmartId::Utils
|
2
|
+
class CertificateValidator
|
3
|
+
def self.validate!(hash_data, signature, certificate)
|
4
|
+
obj = new(hash_data, signature, certificate)
|
5
|
+
obj.validate_certificate!
|
6
|
+
obj.validate_signature!
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(hash_data, signature, certificate)
|
10
|
+
@hash_data = hash_data
|
11
|
+
@signature = signature
|
12
|
+
begin
|
13
|
+
@certificate = certificate.cert
|
14
|
+
rescue Exception
|
15
|
+
debugger
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def certificate_valid?
|
20
|
+
### TODO: Currently not working, because of error "unable to get local issuer certificate" - same error in bash with openssl
|
21
|
+
# cert_store = OpenSSL::X509::Store.new
|
22
|
+
# cert_chain.each {|c| cert_store.add_cert(c) }
|
23
|
+
# cert_store.add_dir(File.dirname(__FILE__)+"/../../../trusted_certs/")
|
24
|
+
# cert_store.purpose = OpenSSL::X509::PURPOSE_ANY
|
25
|
+
# OpenSSL::X509::Store.new.verify(@certificate) &&
|
26
|
+
@certificate.not_before.to_date < Date.today &&
|
27
|
+
@certificate.not_after.to_date > Date.today
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_certificate!
|
31
|
+
unless certificate_valid?
|
32
|
+
raise SmartId::InvalidResponseCertificate
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def cert_chain
|
37
|
+
[
|
38
|
+
OpenSSL::X509::Certificate.new(
|
39
|
+
File.read(File.dirname(__FILE__)+"/../../../trusted_certs/EID-SK_2016.pem.crt")
|
40
|
+
),
|
41
|
+
OpenSSL::X509::Certificate.new(
|
42
|
+
File.read(File.dirname(__FILE__)+"/../../../trusted_certs/NQ-SK_2016.pem.crt")
|
43
|
+
)
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_signature!
|
48
|
+
public_key = @certificate.public_key
|
49
|
+
|
50
|
+
unless public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(@signature), @hash_data)
|
51
|
+
raise SmartId::InvalidResponseSignature
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SmartId
|
2
|
+
module Utils
|
3
|
+
class VerificationCodeCalculator
|
4
|
+
##
|
5
|
+
# The Verification Code (VC) is computed as:
|
6
|
+
#
|
7
|
+
# integer(SHA256(hash)[−2:−1]) mod 10000
|
8
|
+
#
|
9
|
+
# where we take SHA256 result, extract 2 rightmost bytes from it,
|
10
|
+
# interpret them as a big-endian unsigned short and take the last 4 digits in decimal for display.
|
11
|
+
#
|
12
|
+
# SHA256 is always used here, no matter what was the algorithm used to calculate hash.
|
13
|
+
|
14
|
+
def self.calculate(digest)
|
15
|
+
rightmost_bytes = digest[-2..-1]
|
16
|
+
int = rightmost_bytes.unpack('n*')[0]
|
17
|
+
paddable_string = (int % 10000).to_s.chars.last(4).join
|
18
|
+
pad = 4 - paddable_string.length
|
19
|
+
|
20
|
+
"0" * pad + paddable_string
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/smart_id.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "smart_id/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "smart_id"
|
7
|
+
spec.version = SmartId::VERSION
|
8
|
+
spec.authors = ["Kristaps Kulikovskis"]
|
9
|
+
spec.email = ["kristaps.kulikovskis@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Smart ID wrapper libary for using Smart ID in Ruby applications}
|
12
|
+
spec.homepage = "https://github.com/zippyvision/smart-id-ruby"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
+
spec.metadata["changelog_uri"] = spec.homepage + "/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_dependency "rest-client", "~> 2.0"
|
31
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
32
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
+
spec.add_development_dependency "byebug"
|
35
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
|
3
|
+
MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
|
4
|
+
czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
|
5
|
+
CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
|
6
|
+
MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
|
7
|
+
ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
|
8
|
+
b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
|
9
|
+
AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
|
10
|
+
euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
|
11
|
+
bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
|
12
|
+
WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
|
13
|
+
MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
|
14
|
+
1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
|
15
|
+
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
|
16
|
+
zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
|
17
|
+
BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
|
18
|
+
BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
|
19
|
+
v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
|
20
|
+
E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
|
21
|
+
uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
|
22
|
+
iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
|
23
|
+
GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
|
24
|
+
-----END CERTIFICATE-----
|
25
|
+
-----BEGIN CERTIFICATE-----
|
26
|
+
MIIG4jCCBcqgAwIBAgIQO4A6a2nBKoxXxVAFMRvE2jANBgkqhkiG9w0BAQwFADB1
|
27
|
+
MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
|
28
|
+
czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
|
29
|
+
CSqGSIb3DQEJARYJcGtpQHNrLmVlMCAXDTE2MDgzMDA5MjEwOVoYDzIwMzAxMjE3
|
30
|
+
MjM1OTU5WjBgMQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVy
|
31
|
+
aW1pc2tlc2t1czEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxFDASBgNVBAMMC0VJ
|
32
|
+
RC1TSyAyMDE2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7XWFN0j
|
33
|
+
1CFoGIuVe9xRezEnA0Tk3vmvIpvURX+y7Z5DJsfub2mtpSLtbhXjAeynq9QV78zj
|
34
|
+
gQ73pNVGh+GQ6oPG7HF8KIlZuIYsf1+gBxPxNiLa0+sCWxa6p4HQbgdgYRVGod4I
|
35
|
+
Qbib9KbOki3wjCG5WiWh1SP9qcuTZVY+9zawkSMf65Px/Y4ChjtNFtY66MEvsPCh
|
36
|
+
lHHfsBNiUbtZ68jJNYCECjtkm0vxz2iiSXB2WRIv3/hTrRgMJ2CNMyFjRQoGQlpH
|
37
|
+
010+fcisObKeyPwA8kI22Oto9MzLw7KsY524OD3B1L5MExYxHD916XIEHT/9gBP2
|
38
|
+
Zn8qZu/BllKdSIapOIJW9ZEw+3w5UOU6LT3tTSbAzeQAnD3eCABPifYwHYC0lmKs
|
39
|
+
PpQJqtx0Q3Jbm3BGReYiZ9KuK36nF/G78YjhM+yioERr2B/cKf31j0W/GuGvyHak
|
40
|
+
bokwy7nsbL30sTuRLR70Oqi5UBMy4e8J2CduR3R3NJw5UqpScJIchngsLAx+WsyC
|
41
|
+
0w38AmMewMBcnlp/QbakKo52HrsYRR1m+NhCVDBy45Lzl8I0/OGd9Ikdg1h7T7SI
|
42
|
+
guZVpyzys8E0yfrcS5YMEd9hMqVPr7rszXCzbxyw0tVIk8QLMw/lI+XE1Oi7Skgz
|
43
|
+
A2i5Vpa6i2K0ard6GPHzRqGPTkjc5Z4DzZMCAwEAAaOCAn8wggJ7MB8GA1UdIwQY
|
44
|
+
MBaAFBLyWj7qVhy/zQas8fElyalL1BSZMB0GA1UdDgQWBBScCagHhww9rC6H/KCu
|
45
|
+
0vtlSYgo+zAOBgNVHQ8BAf8EBAMCAQYwgcQGA1UdIASBvDCBuTA8BgcEAIvsQAEC
|
46
|
+
MDEwLwYIKwYBBQUHAgEWI2h0dHBzOi8vd3d3LnNrLmVlL3JlcG9zaXRvb3JpdW0v
|
47
|
+
Q1BTMDwGBwQAi+xAAQAwMTAvBggrBgEFBQcCARYjaHR0cHM6Ly93d3cuc2suZWUv
|
48
|
+
cmVwb3NpdG9vcml1bS9DUFMwOwYGBACPegECMDEwLwYIKwYBBQUHAgEWI2h0dHBz
|
49
|
+
Oi8vd3d3LnNrLmVlL3JlcG9zaXRvb3JpdW0vQ1BTMBIGA1UdEwEB/wQIMAYBAf8C
|
50
|
+
AQAwJwYDVR0lBCAwHgYIKwYBBQUHAwkGCCsGAQUFBwMCBggrBgEFBQcDBDB8Bggr
|
51
|
+
BgEFBQcBAQRwMG4wIAYIKwYBBQUHMAGGFGh0dHA6Ly9vY3NwLnNrLmVlL0NBMEoG
|
52
|
+
CCsGAQUFBzAChj5odHRwOi8vd3d3LnNrLmVlL2NlcnRzL0VFX0NlcnRpZmljYXRp
|
53
|
+
b25fQ2VudHJlX1Jvb3RfQ0EuZGVyLmNydDBBBgNVHR4EOjA4oTYwBIICIiIwCocI
|
54
|
+
AAAAAAAAAAAwIocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJQYI
|
55
|
+
KwYBBQUHAQMEGTAXMBUGCCsGAQUFBwsCMAkGBwQAi+xJAQEwPQYDVR0fBDYwNDAy
|
56
|
+
oDCgLoYsaHR0cDovL3d3dy5zay5lZS9yZXBvc2l0b3J5L2NybHMvZWVjY3JjYS5j
|
57
|
+
cmwwDQYJKoZIhvcNAQEMBQADggEBAKSIoud5DSfhDU6yp+VrXYL40wi5zFTf19ha
|
58
|
+
/kO/zzLxZ1hf45VJmSyukMWaWXEqhaLWBZuw5kP78mQ0HyaRUennN0hom/pEiBz6
|
59
|
+
cuz9oc+xlmPAZM25ZoaLqa4upP2/+NCWoRTzYkIdc9MEECs5RMBUmyT1G4s8J6n8
|
60
|
+
L2M2yYadBMvPGJS3yXxYdc/b3a2foiw3kKa/q1tXAHXZCsuxFVYxXdZt3AwInYHe
|
61
|
+
mCVKjZg8BaRpvIEXd3AgJwt+9bpV/x0/MouRPNRv0jjWIx1sAlL94hO74WZDMFbZ
|
62
|
+
VaV6gpG77X2P3dPHKFIRWzjtSQJX4C5n1uvQBxO4ABoMswq0lq0=
|
63
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,61 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
|
3
|
+
MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
|
4
|
+
czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
|
5
|
+
CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
|
6
|
+
MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
|
7
|
+
ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
|
8
|
+
b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
|
9
|
+
AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
|
10
|
+
euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
|
11
|
+
bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
|
12
|
+
WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
|
13
|
+
MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
|
14
|
+
1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
|
15
|
+
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
|
16
|
+
zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
|
17
|
+
BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
|
18
|
+
BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
|
19
|
+
v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
|
20
|
+
E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
|
21
|
+
uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
|
22
|
+
iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
|
23
|
+
GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
|
24
|
+
-----END CERTIFICATE-----
|
25
|
+
-----BEGIN CERTIFICATE-----
|
26
|
+
MIIGYjCCBUqgAwIBAgIQV6nz7KIvDihXxU71YTbgWjANBgkqhkiG9w0BAQwFADB1
|
27
|
+
MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
|
28
|
+
czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
|
29
|
+
CSqGSIb3DQEJARYJcGtpQHNrLmVlMCAXDTE2MDgzMDA5MTYzN1oYDzIwMzAxMjE3
|
30
|
+
MjM1OTU5WjBfMQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVy
|
31
|
+
aW1pc2tlc2t1czEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxEzARBgNVBAMMCk5R
|
32
|
+
LVNLIDIwMTYwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdkRRNDxfO
|
33
|
+
6oKU9GDrGLNQc41PA+pqDKCEcDhSw1bnkC/nDumg4PawQk8xklyDHr2ShrsFrTo5
|
34
|
+
wps5UcgxxTMqb98bmMxQYghqxu5NqqpaZopbbSj+qDYUzrZkXIlVe+HFpUt5ce9W
|
35
|
+
NpEmeenVAlt4ZaN1/srDfv3NSMmcF2r9XiUIIhDavxQ+QgPy3CrgT0Ja3yw/PLpF
|
36
|
+
/ajCNQWaGWJHYkgNVzrnrKhKYDhgorc3lSqGfTfhW2Xf5klvBZokPfbhD26csnPe
|
37
|
+
JjQQQJ2Loot3Z9/QPzfY/Qnqp5hjkvfqjKksX2wAt/UB+Hk4sRG+6Nqa3b+gxqMc
|
38
|
+
ih1eI/I93Ii6OC7LijhN2k0R9L5+ArgQXhlAQYZGeCAC/unHmpCkiUQrEJq27kst
|
39
|
+
mzoENnwQnF3mhq81KQGZul/Guw1fsQOolALESEWG6dTP1szaLeba4LYN707b9puR
|
40
|
+
OVXk1WLoau131KZnIdc/+Ktu2ni4SVL3+qKbJ7+oqIfiFAqlSuCPTKssdFC49m7V
|
41
|
+
G4bXnrYeA5svUQjCvpANmzXqRs6DmdctKPuXUj+W/gnQNoLOvIEkK30TD/RKd4eh
|
42
|
+
uzzYj9qirhqBDFg+Ipqh9OByK7aY6f9KZ6qKmKttcPb4R7arBtuQoBlqadcXoGig
|
43
|
+
o/kr/iXVRabWfGVM73iQo36RZrklrSu5awIDAQABo4ICADCCAfwwHwYDVR0jBBgw
|
44
|
+
FoAUEvJaPupWHL/NBqzx8SXJqUvUFJkwHQYDVR0OBBYEFHq3hV+h88xBt67p6gZR
|
45
|
+
CuD5AsisMA4GA1UdDwEB/wQEAwIBBjBGBgNVHSAEPzA9MDsGBgQAj3oBATAxMC8G
|
46
|
+
CCsGAQUFBwIBFiNodHRwczovL3d3dy5zay5lZS9yZXBvc2l0b29yaXVtL0NQUzAS
|
47
|
+
BgNVHRMBAf8ECDAGAQH/AgEAMCcGA1UdJQQgMB4GCCsGAQUFBwMJBggrBgEFBQcD
|
48
|
+
AgYIKwYBBQUHAwQwfAYIKwYBBQUHAQEEcDBuMCAGCCsGAQUFBzABhhRodHRwOi8v
|
49
|
+
b2NzcC5zay5lZS9DQTBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5zay5lZS9jZXJ0
|
50
|
+
cy9FRV9DZXJ0aWZpY2F0aW9uX0NlbnRyZV9Sb290X0NBLmRlci5jcnQwQQYDVR0e
|
51
|
+
BDowOKE2MASCAiIiMAqHCAAAAAAAAAAAMCKHIAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
52
|
+
AAAAAAAAAAAAAAAAMCUGCCsGAQUFBwEDBBkwFzAVBggrBgEFBQcLAjAJBgcEAIvs
|
53
|
+
SQEBMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly93d3cuc2suZWUvcmVwb3NpdG9y
|
54
|
+
eS9jcmxzL2VlY2NyY2EuY3JsMA0GCSqGSIb3DQEBDAUAA4IBAQCu4HLsEBBpKmXw
|
55
|
+
agXpFkmEGqTOC/eYWrtwVZNnz/MB+z8c6TyxwW2cDmNwMwMfojXT447rQ/xlai/5
|
56
|
+
gjGkRwRE8P5W90h/JkO3rUWG4asrvPAwmkIiUHsHIHDVHCsSmhLNEgPdM4zP88/L
|
57
|
+
EmV89ZIvUWGjzZYcgBljYeAlK4dCy4/7U14JW9FCwvFjFOyfDcpoYwxbV7Jkbhsw
|
58
|
+
9J8uzzxjspGCvoq5izeTGuRV+WtV+yy6W/UOnpmYOJ6jxzUoYq6fnQGU+J9CLVxj
|
59
|
+
jE8Jj25fuWSU3BvPs8cms7RzvvuZvPgQWm8IZt6L5P6EHOzeER3m3nftERhG2OXE
|
60
|
+
+MHJ+onc
|
61
|
+
-----END CERTIFICATE-----
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smart_id
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kristaps Kulikovskis
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-04-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rest-client
|
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: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
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.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: byebug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- kristaps.kulikovskis@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- Gemfile
|
94
|
+
- Gemfile.lock
|
95
|
+
- LICENSE.txt
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- bin/console
|
99
|
+
- bin/setup
|
100
|
+
- lib/smart_id.rb
|
101
|
+
- lib/smart_id/api/authentication/base.rb
|
102
|
+
- lib/smart_id/api/authentication/confirmation_poller.rb
|
103
|
+
- lib/smart_id/api/authentication/document.rb
|
104
|
+
- lib/smart_id/api/authentication/identity_number.rb
|
105
|
+
- lib/smart_id/api/confirmation_response.rb
|
106
|
+
- lib/smart_id/api/request.rb
|
107
|
+
- lib/smart_id/api/response.rb
|
108
|
+
- lib/smart_id/authentication_certificate/certificate.rb
|
109
|
+
- lib/smart_id/authentication_certificate/content.rb
|
110
|
+
- lib/smart_id/exceptions.rb
|
111
|
+
- lib/smart_id/utils/authentication_hash.rb
|
112
|
+
- lib/smart_id/utils/certificate_validator.rb
|
113
|
+
- lib/smart_id/utils/verification_code_calculator.rb
|
114
|
+
- lib/smart_id/version.rb
|
115
|
+
- smart_id.gemspec
|
116
|
+
- trusted_certs/EID-SK_2016.pem.crt
|
117
|
+
- trusted_certs/NQ-SK_2016.pem.crt
|
118
|
+
homepage: https://github.com/zippyvision/smart-id-ruby
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata:
|
122
|
+
homepage_uri: https://github.com/zippyvision/smart-id-ruby
|
123
|
+
source_code_uri: https://github.com/zippyvision/smart-id-ruby
|
124
|
+
changelog_uri: https://github.com/zippyvision/smart-id-ruby/CHANGELOG.md
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubygems_version: 3.0.6
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: Smart ID wrapper libary for using Smart ID in Ruby applications
|
144
|
+
test_files: []
|