duo_universal_ruby 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1d8b04367166707ba3036e2f31abe79fe59e1ae37e9e804d6494d5399d92a98f
4
+ data.tar.gz: 7d71a751b484f66f29d62226c9807217aa8287408ca2a4645a8aca58e9611b84
5
+ SHA512:
6
+ metadata.gz: 015ed26f061ce08c4d2265bf192bfc79e196d8804bd6a3893845ca87c483605da0738117cfb2bf44d09c0eaa425615e2dadc3e5c6cebff6a64cb8f11ba3c6497
7
+ data.tar.gz: 5c24588c3626c91923dbbec002fdbb72786bcac43ba2674856ec29a486a1b9458f69a18a2f8f65c1055e98bda3061cb3c5f1056bc22f3e5d3e0d96681cc40a22
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-07-28
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Todd Parsnick
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,106 @@
1
+ # DuoUniversalRuby
2
+
3
+ ## A Ruby implementation of the Duo WebSDKv4 with Universal Prompt
4
+ - https://duo.com/docs/duoweb
5
+ - https://duo.com/docs/oauthapi
6
+
7
+ https://github.com/duosecurity/duo_universal_python was used as a guide.
8
+
9
+ ## Installation
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ $ bundle add duo_universal_ruby
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ $ gem install duo_universal_ruby
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ client = DuoUniversalRails::Client.new(
23
+ client_id: DUO_CLIENT_ID,
24
+ client_secret: DUO_CLIENT_SECRET,
25
+ api_host: DUO_HOST,
26
+ redirect_uri: DUO_REDIRECT_URI
27
+ )
28
+ # Initializes instance of Client class
29
+
30
+ # Arguments:
31
+ # client_id -- Client ID for the application in Duo
32
+ # client_secret -- Client secret for the application in Duo
33
+ # host -- Duo api host
34
+ # redirect_uri -- Uri to redirect to after a successful auth
35
+
36
+ client.health_check
37
+ # Checks whether Duo is available.
38
+ # POST /oauth/v1/health_check
39
+
40
+ # Returns:
41
+ # {'response': {'timestamp': <int:unix timestamp>}, 'stat': 'OK'}
42
+
43
+ # Raises:
44
+ # DuoException on error for invalid credentials
45
+ # or problem connecting to Duo
46
+
47
+ state = client.generate_state
48
+ # Random value that is checked after interactions to protect against CSRF attacks
49
+
50
+ client.create_auth_url(username, state)
51
+ # Generate uri to Duo's prompt
52
+ # GET /oauth/v1/authorize
53
+
54
+ # Arguments:
55
+ # username -- username trying to authenticate with Duo
56
+ # state -- Randomly generated character string of at least 16
57
+ # and at most 1024 characters returned to the integration by Duo after 2FA
58
+
59
+ # Returns:
60
+ # Authorization uri to redirect to for the Duo prompt
61
+
62
+ # After a successful Duo login, Duo redirect the user to the redirect_uri, e.g. /duo_callback with a duoCode and state
63
+
64
+ decoded_token = client.exchange_authorization_code_for_2fa_result(duoCode, username)
65
+ # Exchange the duo_code for a token with Duo to determine
66
+ # if the auth was successful.
67
+
68
+ # POST /oauth/v1/token
69
+
70
+ # Arguments:
71
+ # duoCode -- Authentication session transaction id
72
+ # returned by Duo
73
+ # username -- Name of the user authenticating with Duo
74
+
75
+ # Return:
76
+ # A token with meta-data about the auth
77
+
78
+ # Raises:
79
+ # DuoException on error for invalid duo_codes, invalid credentials,
80
+ # or problems connecting to Duo
81
+
82
+ ```
83
+
84
+ ## Development
85
+
86
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
87
+
88
+ 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
89
+
90
+ ## Tests
91
+
92
+ Run `bundle exec rspec` or `rspec spec` to run the tests.
93
+
94
+ These RSpec tests do not requre a real working Duo host. The suite uses WebMock to stub HTTP calls.
95
+
96
+ ## Demos
97
+ - https://github.com/tparsnick/duo_universal_rails_demo
98
+ - https://github.com/tparsnick/duo_universal_sinatra_demo
99
+
100
+ ## Contributing
101
+
102
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tparsnick/duo_universal_ruby.
103
+
104
+ ## License
105
+
106
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/duo_universal_ruby/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "duo_universal_ruby"
7
+ spec.version = DuoUniversalRuby::VERSION
8
+ spec.authors = ["Todd Parsnick"]
9
+ spec.email = ["tparsnick@gmail.com"]
10
+
11
+ spec.summary = "Easily add two-factor authentication to any Ruby web authentication flow using a Web SDKv4 app with the universal prompt in Duo."
12
+ spec.description = "Easily add two-factor authentication to any Ruby web authentication flow using a Web SDKv4 app with the universal prompt in Duo."
13
+ spec.homepage = 'https://github.com/tparsnick/duo_universal_ruby'
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.2"
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = 'https://github.com/tparsnick/duo_universal_ruby'
21
+ spec.metadata["changelog_uri"] = 'https://github.com/tparsnick/duo_universal_ruby/CHANGELOG.md'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (File.expand_path(f) == __FILE__) ||
28
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
29
+ end
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ # Runtime dependencies
36
+ spec.add_runtime_dependency 'jwt'
37
+
38
+ # For more information and examples about making a new gem, check out our
39
+ # guide at: https://bundler.io/guides/creating_gem.html
40
+ end
@@ -0,0 +1,490 @@
1
+ # Source URL: https://www.amazontrust.com/repository/AmazonRootCA1.cer
2
+ # Certificate #1 Details:
3
+ # Original Format: DER
4
+ # Subject: CN=Amazon Root CA 1,O=Amazon,C=US
5
+ # Issuer: CN=Amazon Root CA 1,O=Amazon,C=US
6
+ # Expiration Date: 2038-01-17 00:00:00
7
+ # Serial Number: 66C9FCF99BF8C0A39E2F0788A43E696365BCA
8
+ # SHA256 Fingerprint: 8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e
9
+
10
+ -----BEGIN CERTIFICATE-----
11
+ MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
12
+ ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
13
+ b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
14
+ MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
15
+ b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
16
+ ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
17
+ 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
18
+ IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
19
+ VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
20
+ 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
21
+ jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
22
+ AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
23
+ A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
24
+ U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
25
+ N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
26
+ o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
27
+ 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
28
+ rqXRfboQnoZsG4q5WTP468SQvvG5
29
+ -----END CERTIFICATE-----
30
+
31
+
32
+ # Source URL: https://www.amazontrust.com/repository/AmazonRootCA2.cer
33
+ # Certificate #1 Details:
34
+ # Original Format: DER
35
+ # Subject: CN=Amazon Root CA 2,O=Amazon,C=US
36
+ # Issuer: CN=Amazon Root CA 2,O=Amazon,C=US
37
+ # Expiration Date: 2040-05-26 00:00:00
38
+ # Serial Number: 66C9FD29635869F0A0FE58678F85B26BB8A37
39
+ # SHA256 Fingerprint: 1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4
40
+
41
+ -----BEGIN CERTIFICATE-----
42
+ MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF
43
+ ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
44
+ b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL
45
+ MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
46
+ b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK
47
+ gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ
48
+ W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg
49
+ 1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K
50
+ 8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r
51
+ 2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me
52
+ z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR
53
+ 8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj
54
+ mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz
55
+ 7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6
56
+ +XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI
57
+ 0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
58
+ Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm
59
+ UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2
60
+ LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
61
+ +gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS
62
+ k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl
63
+ 7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm
64
+ btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl
65
+ urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+
66
+ fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63
67
+ n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE
68
+ 76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H
69
+ 9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT
70
+ 4PsJYGw=
71
+ -----END CERTIFICATE-----
72
+
73
+
74
+ # Source URL: https://www.amazontrust.com/repository/AmazonRootCA3.cer
75
+ # Certificate #1 Details:
76
+ # Original Format: DER
77
+ # Subject: CN=Amazon Root CA 3,O=Amazon,C=US
78
+ # Issuer: CN=Amazon Root CA 3,O=Amazon,C=US
79
+ # Expiration Date: 2040-05-26 00:00:00
80
+ # Serial Number: 66C9FD5749736663F3B0B9AD9E89E7603F24A
81
+ # SHA256 Fingerprint: 18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4
82
+
83
+ -----BEGIN CERTIFICATE-----
84
+ MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
85
+ MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
86
+ Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
87
+ A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
88
+ Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
89
+ ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
90
+ QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
91
+ ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
92
+ BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
93
+ YyRIHN8wfdVoOw==
94
+ -----END CERTIFICATE-----
95
+
96
+
97
+ # Source URL: https://www.amazontrust.com/repository/AmazonRootCA4.cer
98
+ # Certificate #1 Details:
99
+ # Original Format: DER
100
+ # Subject: CN=Amazon Root CA 4,O=Amazon,C=US
101
+ # Issuer: CN=Amazon Root CA 4,O=Amazon,C=US
102
+ # Expiration Date: 2040-05-26 00:00:00
103
+ # Serial Number: 66C9FD7C1BB104C2943E5717B7B2CC81AC10E
104
+ # SHA256 Fingerprint: e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092
105
+
106
+ -----BEGIN CERTIFICATE-----
107
+ MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
108
+ MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
109
+ Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
110
+ A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
111
+ Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
112
+ 9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
113
+ M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
114
+ /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
115
+ MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
116
+ CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
117
+ 1KyLa2tJElMzrdfkviT8tQp21KW8EA==
118
+ -----END CERTIFICATE-----
119
+
120
+
121
+ # Source URL: https://www.amazontrust.com/repository/SFSRootCAG2.cer
122
+ # Certificate #1 Details:
123
+ # Original Format: DER
124
+ # Subject: CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US
125
+ # Issuer: CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US
126
+ # Expiration Date: 2037-12-31 23:59:59
127
+ # Serial Number: 0
128
+ # SHA256 Fingerprint: 568d6905a2c88708a4b3025190edcfedb1974a606a13c6e5290fcb2ae63edab5
129
+
130
+ -----BEGIN CERTIFICATE-----
131
+ MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
132
+ EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
133
+ HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
134
+ ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
135
+ MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
136
+ VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
137
+ ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
138
+ dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
139
+ hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
140
+ OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
141
+ 8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
142
+ Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
143
+ hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
144
+ 6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
145
+ DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
146
+ AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
147
+ bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
148
+ ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
149
+ qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
150
+ iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
151
+ 0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
152
+ sSi6
153
+ -----END CERTIFICATE-----
154
+
155
+
156
+ # Source URL: https://cacerts.digicert.com/DigiCertHighAssuranceEVRootCA.crt
157
+ # Certificate #1 Details:
158
+ # Original Format: DER
159
+ # Subject: CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
160
+ # Issuer: CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
161
+ # Expiration Date: 2031-11-10 00:00:00
162
+ # Serial Number: 2AC5C266A0B409B8F0B79F2AE462577
163
+ # SHA256 Fingerprint: 7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf
164
+
165
+ -----BEGIN CERTIFICATE-----
166
+ MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
167
+ MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
168
+ d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
169
+ ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
170
+ MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
171
+ LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
172
+ RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
173
+ +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
174
+ PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
175
+ xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
176
+ Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
177
+ hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
178
+ EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
179
+ MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
180
+ FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
181
+ nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
182
+ eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
183
+ hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
184
+ Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
185
+ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
186
+ +OkuE6N36B9K
187
+ -----END CERTIFICATE-----
188
+
189
+
190
+ # Source URL: https://cacerts.digicert.com/DigiCertTLSECCP384RootG5.crt
191
+ # Certificate #1 Details:
192
+ # Original Format: DER
193
+ # Subject: CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US
194
+ # Issuer: CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US
195
+ # Expiration Date: 2046-01-14 23:59:59
196
+ # Serial Number: 9E09365ACF7D9C8B93E1C0B042A2EF3
197
+ # SHA256 Fingerprint: 018e13f0772532cf809bd1b17281867283fc48c6e13be9c69812854a490c1b05
198
+
199
+ -----BEGIN CERTIFICATE-----
200
+ MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
201
+ CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
202
+ Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
203
+ MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
204
+ bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
205
+ ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
206
+ 7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
207
+ 0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
208
+ B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
209
+ BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
210
+ LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
211
+ DXZDjC5Ty3zfDBeWUA==
212
+ -----END CERTIFICATE-----
213
+
214
+
215
+ # Source URL: https://cacerts.digicert.com/DigiCertTLSRSA4096RootG5.crt
216
+ # Certificate #1 Details:
217
+ # Original Format: DER
218
+ # Subject: CN=DigiCert TLS RSA4096 Root G5,O=DigiCert\, Inc.,C=US
219
+ # Issuer: CN=DigiCert TLS RSA4096 Root G5,O=DigiCert\, Inc.,C=US
220
+ # Expiration Date: 2046-01-14 23:59:59
221
+ # Serial Number: 8F9B478A8FA7EDA6A333789DE7CCF8A
222
+ # SHA256 Fingerprint: 371a00dc0533b3721a7eeb40e8419e70799d2b0a0f2c1d80693165f7cec4ad75
223
+
224
+ -----BEGIN CERTIFICATE-----
225
+ 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
254
+ -----END CERTIFICATE-----
255
+
256
+
257
+ # Source URL: https://secure.globalsign.com/cacert/rootr46.crt
258
+ # Certificate #1 Details:
259
+ # Original Format: DER
260
+ # Subject: CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE
261
+ # Issuer: CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE
262
+ # Expiration Date: 2046-03-20 00:00:00
263
+ # Serial Number: 11D2BBB9D723189E405F0A9D2DD0DF2567D1
264
+ # SHA256 Fingerprint: 4fa3126d8d3a11d1c4855a4f807cbad6cf919d3a5a88b03bea2c6372d93c40c9
265
+
266
+ -----BEGIN CERTIFICATE-----
267
+ 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
296
+ -----END CERTIFICATE-----
297
+
298
+
299
+ # Source URL: https://secure.globalsign.com/cacert/roote46.crt
300
+ # Certificate #1 Details:
301
+ # Original Format: DER
302
+ # Subject: CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE
303
+ # Issuer: CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE
304
+ # Expiration Date: 2046-03-20 00:00:00
305
+ # Serial Number: 11D2BBBA336ED4BCE62468C50D841D98E843
306
+ # SHA256 Fingerprint: cbb9c44d84b8043e1050ea31a69f514955d7bfd2e2c6b49301019ad61d9f5058
307
+
308
+ -----BEGIN CERTIFICATE-----
309
+ MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx
310
+ CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD
311
+ ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw
312
+ MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex
313
+ HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA
314
+ IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq
315
+ R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd
316
+ yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
317
+ DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ
318
+ 7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8
319
+ +RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A=
320
+ -----END CERTIFICATE-----
321
+
322
+
323
+ # Source URL: https://i.pki.goog/r2.crt
324
+ # Certificate #1 Details:
325
+ # Original Format: DER
326
+ # Subject: CN=GTS Root R2,O=Google Trust Services LLC,C=US
327
+ # Issuer: CN=GTS Root R2,O=Google Trust Services LLC,C=US
328
+ # Expiration Date: 2036-06-22 00:00:00
329
+ # Serial Number: 203E5AEC58D04251AAB1125AA
330
+ # SHA256 Fingerprint: 8d25cd97229dbf70356bda4eb3cc734031e24cf00fafcfd32dc76eb5841c7ea8
331
+
332
+ -----BEGIN CERTIFICATE-----
333
+ MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw
334
+ CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
335
+ MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
336
+ MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
337
+ Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA
338
+ A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt
339
+ nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY
340
+ 6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu
341
+ MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k
342
+ RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg
343
+ f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV
344
+ +3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo
345
+ dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW
346
+ Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa
347
+ G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq
348
+ gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID
349
+ AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
350
+ FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H
351
+ vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8
352
+ 0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC
353
+ B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u
354
+ NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg
355
+ yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev
356
+ HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6
357
+ xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR
358
+ TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg
359
+ JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV
360
+ 7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl
361
+ 6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL
362
+ -----END CERTIFICATE-----
363
+
364
+
365
+ # Source URL: https://i.pki.goog/r4.crt
366
+ # Certificate #1 Details:
367
+ # Original Format: DER
368
+ # Subject: CN=GTS Root R4,O=Google Trust Services LLC,C=US
369
+ # Issuer: CN=GTS Root R4,O=Google Trust Services LLC,C=US
370
+ # Expiration Date: 2036-06-22 00:00:00
371
+ # Serial Number: 203E5C068EF631A9C72905052
372
+ # SHA256 Fingerprint: 349dfa4058c5e263123b398ae795573c4e1313c83fe68f93556cd5e8031b3c7d
373
+
374
+ -----BEGIN CERTIFICATE-----
375
+ MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD
376
+ VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
377
+ A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
378
+ WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
379
+ IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
380
+ AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi
381
+ QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR
382
+ HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
383
+ BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D
384
+ 9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8
385
+ p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD
386
+ -----END CERTIFICATE-----
387
+
388
+
389
+ # Source URL: https://www.identrust.com/file-download/download/public/5718
390
+ # Certificate #1 Details:
391
+ # Original Format: PKCS7-DER
392
+ # Subject: CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US
393
+ # Issuer: CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US
394
+ # Expiration Date: 2034-01-16 18:12:23
395
+ # Serial Number: A0142800000014523C844B500000002
396
+ # SHA256 Fingerprint: 5d56499be4d2e08bcfcad08a3e38723d50503bde706948e42f55603019e528ae
397
+
398
+ -----BEGIN CERTIFICATE-----
399
+ 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
428
+ -----END CERTIFICATE-----
429
+
430
+
431
+ # Source URL: https://www.identrust.com/file-download/download/public/5842
432
+ # Certificate #1 Details:
433
+ # Original Format: PKCS7-PEM
434
+ # Subject: CN=IdenTrust Commercial Root TLS ECC CA 2,O=IdenTrust,C=US
435
+ # Issuer: CN=IdenTrust Commercial Root TLS ECC CA 2,O=IdenTrust,C=US
436
+ # Expiration Date: 2039-04-11 21:11:10
437
+ # Serial Number: 40018ECF000DE911D7447B73E4C1F82E
438
+ # SHA256 Fingerprint: 983d826ba9c87f653ff9e8384c5413e1d59acf19ddc9c98cecae5fdea2ac229c
439
+
440
+ -----BEGIN CERTIFICATE-----
441
+ MIICbDCCAc2gAwIBAgIQQAGOzwAN6RHXRHtz5MH4LjAKBggqhkjOPQQDBDBSMQsw
442
+ CQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MS8wLQYDVQQDEyZJZGVuVHJ1
443
+ c3QgQ29tbWVyY2lhbCBSb290IFRMUyBFQ0MgQ0EgMjAeFw0yNDA0MTEyMTExMTFa
444
+ Fw0zOTA0MTEyMTExMTBaMFIxCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlJZGVuVHJ1
445
+ c3QxLzAtBgNVBAMTJklkZW5UcnVzdCBDb21tZXJjaWFsIFJvb3QgVExTIEVDQyBD
446
+ QSAyMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBwomiZTgLg8KqEImMmnO5rNPb
447
+ Oo9sv5w4nJh45CXs9Gcu8YET9ulxsyVBCVSfSYeppdtXFEWYyBi0QRCAlp5YZHQB
448
+ H675v5rWVKRXvhzsuUNi9Xw0Zy1bAXaikmsrY/J0L52j2RulW4q4WvE7f23VFwZu
449
+ d82J8k0YG+M4MpmdOho1rsKjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
450
+ BAQDAgGGMB0GA1UdDgQWBBQhNGgGrnXhVx/FuQqjXpuH+IlbwzAKBggqhkjOPQQD
451
+ BAOBjAAwgYgCQgDc9F4WOxAgci2uQWfsX9cjeIvDXaaeVjDz31Ycc+ZdPrK1JKrB
452
+ f6CuTwWy8VojtGxdM3PJMkJC4LGPuhcvkHLo4gJCAV5h+PXe4bDJ3QxE8hkGFoUW
453
+ Ak6KtMCIpbLyt5pHrROi+YW9MpScoNGJkg96G1ETvJTWz6dv0uQYjKXt3jlOfQ7g
454
+ -----END CERTIFICATE-----
455
+
456
+
457
+ # Source URL: https://ssl-ccp.secureserver.net/repository/sfroot-g2.crt
458
+ # Certificate #1 Details:
459
+ # Original Format: PEM
460
+ # Subject: CN=Starfield Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US
461
+ # Issuer: CN=Starfield Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US
462
+ # Expiration Date: 2037-12-31 23:59:59
463
+ # Serial Number: 0
464
+ # SHA256 Fingerprint: 2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5
465
+
466
+ -----BEGIN CERTIFICATE-----
467
+ MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
468
+ EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
469
+ HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
470
+ ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
471
+ MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
472
+ b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
473
+ aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
474
+ Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
475
+ ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
476
+ nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
477
+ HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
478
+ Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
479
+ dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
480
+ HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
481
+ BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
482
+ CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
483
+ sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
484
+ 4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
485
+ 8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
486
+ pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
487
+ mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
488
+ -----END CERTIFICATE-----
489
+
490
+
@@ -0,0 +1,225 @@
1
+ require 'net/http' # for Net::HTTP, Net::HTTP::Post, URI HTTP requests
2
+ require 'uri' # for URI and URI.parse
3
+ require 'json' # for JSON.parse
4
+ require 'jwt' # for JWT.encode / JWT.decode (gem)
5
+ require 'securerandom' # for SecureRandom in generate_rand_alphanumeric
6
+ require 'openssl' # for OpenSSL::SSL constants and verify modes
7
+
8
+ module DuoUniversalRuby
9
+ class Client
10
+ def initialize(client_id:, client_secret:, api_host:, redirect_uri:, duo_certs: DEFAULT_CA_CERT_PATH,
11
+ use_duo_code_attribute: true, http_proxy: nil, exp_seconds: FIVE_MINUTES_IN_SECONDS)
12
+ # Initializes instance of Client class
13
+
14
+ # Arguments:
15
+
16
+ # client_id -- Client ID for the application in Duo
17
+ # client_secret -- Client secret for the application in Duo
18
+ # host -- Duo api host
19
+ # redirect_uri -- Uri to redirect to after a successful auth
20
+ # duo_certs -- (Optional: default is ca_certs.pem) Provide custom CA certs
21
+ # use_duo_code_attribute -- (Optional: default true) Flag to use `duo_code` instead of `code` for returned authorization parameter
22
+ # http_proxy -- (Optional) HTTP proxy to tunnel requests through
23
+ # exp_seconds -- (Optional) The number of seconds used for JWT expiry. Must be be at most 5 minutes.
24
+
25
+ validate_init_config(client_id, client_secret, api_host, redirect_uri, exp_seconds)
26
+
27
+ @client_id = client_id
28
+ @client_secret = client_secret
29
+ @api_host = api_host
30
+ @redirect_uri = redirect_uri
31
+ @use_duo_code_attribute = use_duo_code_attribute
32
+ @duo_certs = duo_certs == "DISABLE" ? false : (duo_certs || DEFAULT_CA_CERT_PATH)
33
+ @http_proxy = http_proxy
34
+ @exp_seconds = exp_seconds
35
+ end
36
+
37
+ def clamped_expiry_duration
38
+ [[FIVE_MINUTES_IN_SECONDS, @exp_seconds].min, 1].max
39
+ end
40
+
41
+ def generate_state
42
+ # Random value passed initially in the OAUTH_V1_AUTHORIZE_ENDPOINT and verfied throughout interactions.
43
+ # It is up to the client to verify that it is the same value as a security measure.
44
+ # This value specifically protects against CSRF attacks (see RFC 6749)
45
+ generate_rand_alphanumeric(STATE_LENGTH)
46
+ end
47
+
48
+ def health_check
49
+ # Checks whether Duo is available.
50
+
51
+ # Returns:
52
+ # {'response': {'timestamp': <int:unix timestamp>}, 'stat': 'OK'}
53
+
54
+ # Raises:
55
+ # DuoException on error for invalid credentials
56
+ # or problem connecting to Duo
57
+
58
+ endpoint = format(OAUTH_V1_HEALTH_CHECK_ENDPOINT, @api_host)
59
+ payload = create_jwt_payload(endpoint)
60
+ body = {
61
+ 'client_assertion' => JWT.encode(payload, @client_secret, 'HS512'),
62
+ 'client_id' => @client_id
63
+ }
64
+
65
+ response = post_form(endpoint, body)
66
+ result = JSON.parse(response.body)
67
+ raise Error, result unless result['stat'] == 'OK'
68
+ result
69
+ rescue => e
70
+ raise Error, e.message
71
+ end
72
+
73
+ def create_auth_url(username:, state:, nonce: nil)
74
+ # Generate uri to Duo's prompt
75
+
76
+ # Arguments:
77
+ # username -- username trying to authenticate with Duo
78
+ # state -- Randomly generated character string of at least 16
79
+ # and at most 1024 characters returned to the integration by Duo after 2FA
80
+ # nonce -- (Optional) Randomly generated character string of at least 16
81
+ # and at most 1024 characters used as the nonce for the underlying OIDC flow
82
+
83
+ # Returns:
84
+ # Authorization uri to redirect to for the Duo prompt
85
+
86
+ raise Error, ERR_STATE_LEN unless state && state.length.between?(MINIMUM_STATE_LENGTH, MAXIMUM_STATE_LENGTH)
87
+ raise Error, ERR_USERNAME unless username
88
+ raise Error, ERR_NONCE_LEN if nonce && !nonce.length.between?(MINIMUM_STATE_LENGTH, MAXIMUM_STATE_LENGTH)
89
+
90
+ endpoint = format(OAUTH_V1_AUTHORIZE_ENDPOINT, @api_host)
91
+ jwt_payload = {
92
+ 'scope' => 'openid',
93
+ 'redirect_uri' => @redirect_uri,
94
+ 'client_id' => @client_id,
95
+ 'iss' => @client_id,
96
+ 'aud' => format(API_HOST_URI_FORMAT, @api_host),
97
+ 'exp' => Time.now.to_i + clamped_expiry_duration,
98
+ 'state' => state,
99
+ 'response_type' => 'code',
100
+ 'duo_uname' => username,
101
+ 'use_duo_code_attribute' => @use_duo_code_attribute
102
+ }
103
+ request_jwt = JWT.encode(jwt_payload, @client_secret, 'HS512')
104
+
105
+ params = {
106
+ 'response_type' => 'code',
107
+ 'client_id' => @client_id,
108
+ 'request' => request_jwt
109
+ }
110
+ params['nonce'] = nonce if nonce
111
+
112
+ uri = URI(endpoint)
113
+ uri.query = URI.encode_www_form(params)
114
+ uri.to_s
115
+ end
116
+
117
+ def exchange_authorization_code_for_2fa_result(duo_code:, username:, nonce: nil)
118
+ # Exchange the duo_code for a token with Duo to determine
119
+ # if the auth was successful.
120
+
121
+ # Arguments:
122
+ # duoCode -- Authentication session transaction id
123
+ # returned by Duo
124
+ # username -- Name of the user authenticating with Duo
125
+ # nonce -- (Optional) Random 36B string used to associate
126
+ # a session with an ID token
127
+
128
+ # Return:
129
+ # A token with meta-data about the auth
130
+
131
+ # Raises:
132
+ # DuoException on error for invalid duo_codes, invalid credentials,
133
+ # or problems connecting to Duo
134
+
135
+ raise Error, ERR_CODE unless duo_code
136
+
137
+ endpoint = format(OAUTH_V1_TOKEN_ENDPOINT, @api_host)
138
+ payload = create_jwt_payload(endpoint)
139
+ request_data = {
140
+ 'grant_type' => 'authorization_code',
141
+ 'code' => duo_code,
142
+ 'redirect_uri' => @redirect_uri,
143
+ 'client_id' => @client_id,
144
+ 'client_assertion_type' => CLIENT_ASSERT_TYPE,
145
+ 'client_assertion' => JWT.encode(payload, @client_secret, 'HS512')
146
+ }
147
+
148
+ user_agent = "duo_universal_ruby/1.0 ruby/#{RUBY_VERSION} #{RUBY_PLATFORM}"
149
+ headers = { 'User-Agent' => user_agent }
150
+
151
+ response = post_form(endpoint, request_data, headers)
152
+ raise Error, JSON.parse(response.body) unless response.code.to_i == SUCCESS_STATUS_CODE
153
+
154
+ id_token = JSON.parse(response.body)['id_token']
155
+ decoded, = JWT.decode(
156
+ id_token,
157
+ @client_secret,
158
+ true,
159
+ {
160
+ aud: @client_id,
161
+ iss: format(OAUTH_V1_TOKEN_ENDPOINT, @api_host),
162
+ leeway: LEEWAY,
163
+ algorithm: 'HS512',
164
+ verify_iss: true,
165
+ verify_aud: true,
166
+ verify_iat: true,
167
+ require: ['exp', 'iat']
168
+ }
169
+ )
170
+
171
+ # ID Token validation
172
+ # https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
173
+ raise Error, ERR_USERNAME unless decoded['preferred_username'] == username
174
+ raise Error, ERR_NONCE if nonce && decoded['nonce'] != nonce
175
+
176
+ decoded
177
+ rescue => e
178
+ raise Error, e.message
179
+ end
180
+
181
+ private
182
+
183
+ def generate_rand_alphanumeric(length)
184
+ raise ArgumentError, ERR_GENERATE_LEN if length < [MINIMUM_STATE_LENGTH, JTI_LENGTH].min
185
+ charset = [('A'..'Z'), ('a'..'z'), ('0'..'9')].map(&:to_a).flatten
186
+ Array.new(length) { charset.sample(random: SecureRandom) }.join
187
+ end
188
+
189
+ def validate_init_config(client_id, client_secret, api_host, redirect_uri, exp_seconds)
190
+ raise Error, ERR_CLIENT_ID unless client_id && client_id.length == CLIENT_ID_LENGTH
191
+ raise Error, ERR_CLIENT_SECRET unless client_secret && client_secret.length == CLIENT_SECRET_LENGTH
192
+ raise Error, ERR_API_HOST unless api_host
193
+ raise Error, ERR_REDIRECT_URI unless redirect_uri
194
+ raise Error, ERR_EXP_SECONDS_TOO_LONG if exp_seconds > FIVE_MINUTES_IN_SECONDS
195
+ raise Error, ERR_EXP_SECONDS_TOO_SHORT if exp_seconds < 0
196
+ end
197
+
198
+ def create_jwt_payload(aud)
199
+ {
200
+ 'iss' => @client_id,
201
+ 'sub' => @client_id,
202
+ 'aud' => aud,
203
+ 'exp' => Time.now.to_i + clamped_expiry_duration,
204
+ 'jti' => generate_rand_alphanumeric(JTI_LENGTH)
205
+ }
206
+ end
207
+
208
+ def post_form(url, form_data, headers = {})
209
+ uri = URI.parse(url)
210
+ req = Net::HTTP::Post.new(uri)
211
+ req.set_form_data(form_data)
212
+ headers.each { |k, v| req[k] = v }
213
+
214
+ http = Net::HTTP.new(uri.host, uri.port)
215
+ http.use_ssl = true
216
+ if @duo_certs != false
217
+ http.ca_file = @duo_certs
218
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
219
+ else
220
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
221
+ end
222
+ http.request(req)
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuoUniversalRuby
4
+ CLIENT_ID_LENGTH = 20
5
+ CLIENT_SECRET_LENGTH = 40
6
+ JTI_LENGTH = 36
7
+ MINIMUM_STATE_LENGTH = 16
8
+ MAXIMUM_STATE_LENGTH = 1024
9
+ STATE_LENGTH = 36
10
+ SUCCESS_STATUS_CODE = 200
11
+ FIVE_MINUTES_IN_SECONDS = 300
12
+ LEEWAY = 60
13
+
14
+ ERR_USERNAME = 'The username is invalid.'
15
+ ERR_NONCE = 'The nonce is invalid.'
16
+ ERR_CLIENT_ID = 'The Duo client id is invalid.'
17
+ ERR_CLIENT_SECRET = 'The Duo client secret is invalid.'
18
+ ERR_API_HOST = 'The Duo api host is invalid'
19
+ ERR_REDIRECT_URI = 'No redirect uri'
20
+ ERR_CODE = 'Missing authorization code'
21
+ ERR_GENERATE_LEN = 'Length needs to be at least 16'
22
+ ERR_STATE_LEN = "State must be at least #{MINIMUM_STATE_LENGTH} characters long and no longer than #{MAXIMUM_STATE_LENGTH} characters"
23
+ ERR_NONCE_LEN = "Nonce must be at least #{MINIMUM_STATE_LENGTH} characters long and no longer than #{MAXIMUM_STATE_LENGTH} characters"
24
+ ERR_EXP_SECONDS_TOO_LONG = 'Client may not be configured for a JWT expiry longer than five minutes.'
25
+ ERR_EXP_SECONDS_TOO_SHORT = 'Invalid JWT expiry duration.'
26
+
27
+ API_HOST_URI_FORMAT = 'https://%s'
28
+ OAUTH_V1_HEALTH_CHECK_ENDPOINT = 'https://%s/oauth/v1/health_check'
29
+ OAUTH_V1_AUTHORIZE_ENDPOINT = 'https://%s/oauth/v1/authorize'
30
+ OAUTH_V1_TOKEN_ENDPOINT = 'https://%s/oauth/v1/token'
31
+ DEFAULT_CA_CERT_PATH = File.join(__dir__, 'ca_certs.pem')
32
+
33
+ CLIENT_ASSERT_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuoUniversalRuby
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuoUniversalRuby
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "duo_universal_ruby/version"
2
+ require_relative "duo_universal_ruby/constants"
3
+ require_relative "duo_universal_ruby/error"
4
+ require_relative "duo_universal_ruby/client"
5
+
6
+ module DuoUniversalRuby
7
+
8
+ end
@@ -0,0 +1,4 @@
1
+ module DuoUniversalRuby
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duo_universal_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Todd Parsnick
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: jwt
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: Easily add two-factor authentication to any Ruby web authentication flow
27
+ using a Web SDKv4 app with the universal prompt in Duo.
28
+ email:
29
+ - tparsnick@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".rspec"
35
+ - CHANGELOG.md
36
+ - LICENSE.txt
37
+ - README.md
38
+ - Rakefile
39
+ - duo_universal_ruby.gemspec
40
+ - lib/duo_universal_ruby.rb
41
+ - lib/duo_universal_ruby/ca_certs.pem
42
+ - lib/duo_universal_ruby/client.rb
43
+ - lib/duo_universal_ruby/constants.rb
44
+ - lib/duo_universal_ruby/error.rb
45
+ - lib/duo_universal_ruby/version.rb
46
+ - sig/duo_universal_ruby.rbs
47
+ homepage: https://github.com/tparsnick/duo_universal_ruby
48
+ licenses:
49
+ - MIT
50
+ metadata:
51
+ homepage_uri: https://github.com/tparsnick/duo_universal_ruby
52
+ source_code_uri: https://github.com/tparsnick/duo_universal_ruby
53
+ changelog_uri: https://github.com/tparsnick/duo_universal_ruby/CHANGELOG.md
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.2
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubygems_version: 3.7.1
69
+ specification_version: 4
70
+ summary: Easily add two-factor authentication to any Ruby web authentication flow
71
+ using a Web SDKv4 app with the universal prompt in Duo.
72
+ test_files: []