smart_id 0.1.0
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 +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: []
|