ruby_scep 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
+ SHA1:
3
+ metadata.gz: 21d583fad0aab1365951300aaa682ada282a3e89
4
+ data.tar.gz: b67e773f8d760d5edeef761007f6eac24ceb2bec
5
+ SHA512:
6
+ metadata.gz: 40ab20bc9faf28782f58e726fb3cd9836afd431714aa1de335b95849506f168cb76e235ca1bd5391f95b8278fc7382d2cb57c869186bd516444a55330ff44728
7
+ data.tar.gz: 768b1540be23d55cc95bd2c7dbd44e896a44e1642254b9068343b3e4b2944ec26bc55a57e3ae2a264c2573b45310260b8759bc85a52c5d6610c52ab0c1a601cf
@@ -0,0 +1,30 @@
1
+ version: 2.0
2
+ jobs:
3
+ "ruby-2.3":
4
+ docker:
5
+ - image: circleci/ruby:2.3-node
6
+ working_directory: ~/ruby_scep
7
+ steps:
8
+ - checkout
9
+ - run: bundle install --path vendor/bundle
10
+ - run:
11
+ name: Run tests
12
+ command: bundle exec rspec spec --format progress
13
+
14
+ "ruby-2.4":
15
+ docker:
16
+ - image: circleci/ruby:2.4-node
17
+ working_directory: ~/ruby_scep
18
+ steps:
19
+ - checkout
20
+ - run: bundle install --path vendor/bundle
21
+ - run:
22
+ name: Run tests
23
+ command: bundle exec rspec spec --format progress
24
+
25
+ workflows:
26
+ version: 2
27
+ build:
28
+ jobs:
29
+ - "ruby-2.3"
30
+ - "ruby-2.4"
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ coderay (1.1.1)
5
+ diff-lcs (1.3)
6
+ method_source (0.8.2)
7
+ pry (0.10.4)
8
+ coderay (~> 1.1.0)
9
+ method_source (~> 0.8.1)
10
+ slop (~> 3.4)
11
+ rspec (3.6.0)
12
+ rspec-core (~> 3.6.0)
13
+ rspec-expectations (~> 3.6.0)
14
+ rspec-mocks (~> 3.6.0)
15
+ rspec-core (3.6.0)
16
+ rspec-support (~> 3.6.0)
17
+ rspec-expectations (3.6.0)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.6.0)
20
+ rspec-its (1.2.0)
21
+ rspec-core (>= 3.0.0)
22
+ rspec-expectations (>= 3.0.0)
23
+ rspec-mocks (3.6.0)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.6.0)
26
+ rspec-support (3.6.0)
27
+ slop (3.6.0)
28
+ timecop (0.9.1)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ pry
35
+ rspec
36
+ rspec-its
37
+ timecop
38
+
39
+ BUNDLED WITH
40
+ 1.15.3
data/License ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Appaloosa.io
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ Ruby SCEP [![CircleCI](https://circleci.com/gh/appaloosa-store/ruby_scep.svg?style=svg)](https://circleci.com/gh/appaloosa-store/ruby_scep)
2
+ ---
3
+ A Ruby gem to handle SCEP.
4
+
5
+ Installation
6
+ ---
7
+ To install *Ruby Scep*:
8
+
9
+ ```
10
+ $ gem install ruby_scep
11
+ ```
12
+
13
+ Or you can include this in your project's `Gemfile`:
14
+
15
+ ```
16
+ gem 'ruby_scep'
17
+ ```
18
+
19
+ Then execute:
20
+
21
+ ```
22
+ $ bundle
23
+ ```
24
+ Usage
25
+ ---
26
+
27
+ You must use a webserver (Webrick or related will do the trick) and declare two endpoints:
28
+ - `GET /scep`
29
+ - `POST /scep`
30
+ An example server is [included](https://github.com/appaloosa-store/ruby_scep/tree/master/example_server) in this gem.
31
+
32
+ Acknowledgements
33
+ ---
34
+ This gem would not exist without the following repos:
35
+ - Nolan Browns's IosCertEnrollment. Non-working SCEP but the profiles generation part worked alright https://github.com/nolanbrown/ios-cert-enrollment
36
+ - MicroMDM's SCEP Go server. It worked perfectly, so we could use it as a reference https://github.com/micromdm/scep/
37
+ - OneLogin SCEP gem. The first instance of OpenSSL::ASN1 that led me to the AppBlade repo https://github.com/onelogin/scep-gem/blob/master/lib/scep/asn1.rb
38
+ - AppBlade's SCEP controller. The final pieces of the puzzle: the PKIMessage building using a ASN1 structure. I could'nt have done it without them. https://github.com/AppBlade/TestHub/blob/master/app/controllers/scep_controller.rb
39
+
40
+ We decided to open-source our solution to give back to the community that helped us greatly. Do the same with your projects!
41
+
42
+ Documentation
43
+ ---
44
+ - CISCO's description of the SCEP protocol. Information about PKIMessage structure are also available here. https://www.cisco.com/c/en/us/support/docs/security-vpn/public-key-infrastructure-pki/116167-technote-scep-00.html
45
+ - SCEP RFC https://tools.ietf.org/html/draft-nourse-scep-23
46
+ - OID correspondance. A lifesaver to understand the ASN1 OIDs http://oid-info.com/
47
+
48
+ Contributing
49
+ ---
50
+ 1. Fork it ( https://github.com/appaloosa-store/ruby_scep )
51
+ 2. Create your feature branch (git checkout -b my-new-feature)
52
+ 3. Commit your changes (git commit -am 'Add some feature')
53
+ 4. Push to the branch (git push origin my-new-feature)
54
+ 5. Create a new Pull Request.
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+ ruby '2.4.1'
3
+ gem 'sinatra'
4
+ gem 'ruby_scep', path: '../'
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ ruby_scep (0.1.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ mustermann (1.0.1)
10
+ rack (2.0.3)
11
+ rack-protection (2.0.0)
12
+ rack
13
+ sinatra (2.0.0)
14
+ mustermann (~> 1.0)
15
+ rack (~> 2.0)
16
+ rack-protection (= 2.0.0)
17
+ tilt (~> 2.0)
18
+ tilt (2.0.8)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ ruby_scep!
25
+ sinatra
26
+
27
+ RUBY VERSION
28
+ ruby 2.4.1p111
29
+
30
+ BUNDLED WITH
31
+ 1.15.3
@@ -0,0 +1 @@
1
+ web: bundle exec rackup config.ru -p $PORT
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ require 'sinatra'
3
+ require 'yaml'
4
+ require 'sinatra/base'
5
+ require 'webrick'
6
+ require 'webrick/https'
7
+ require 'openssl'
8
+ require 'ruby_scep'
9
+
10
+ RubyScep.configure do |config|
11
+ config.ca_cert_path = 'certs/ca.pem'
12
+ config.ca_key_path = 'certs/passwordless.key'
13
+ end
14
+
15
+ get '/scep' do
16
+ p 'get scep'
17
+ case params['operation']
18
+ when 'GetCACert'
19
+ p 'operation: GetCACert'
20
+ # todo, verify signer
21
+ content_type 'application/x-x509-ca-cert'
22
+ RubyScep.configuration.ca.to_der
23
+ when 'GetCACaps'
24
+ p 'operation: GetCACaps'
25
+ content_type 'text/plain'
26
+ "SHA-1\nSHA-256\nAES\nDES3\nSCEPStandard\nPOSTPKIOperation"
27
+ # see complete list of capabilities https://tools.ietf.org/html/draft-nourse-scep-23#appendix-C.2
28
+ else
29
+ 'Invalid Action'
30
+ end
31
+ end
32
+
33
+ post '/scep' do
34
+ p 'post scep'
35
+ if params['operation'] == 'PKIOperation'
36
+ content_type 'application/x-pki-message'
37
+ RubyScep::PkiOperation.build_response(request.body.read)
38
+ else
39
+ 'Invalid Action'
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFHzCCAwegAwIBAgIBATANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJVUzEQ
3
+ MA4GA1UEChMHc2NlcC1jYTEQMA4GA1UECxMHU0NFUCBDQTAeFw0xNzA3MjUxMzIx
4
+ MzJaFw0yNzA3MjUxMzIxMzJaMDExCzAJBgNVBAYTAlVTMRAwDgYDVQQKEwdzY2Vw
5
+ LWNhMRAwDgYDVQQLEwdTQ0VQIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
6
+ CgKCAgEApMk9/OV2JFxvGHeG4KjqrJ2hua/rUHeXfsEvV2Kf7g7qtcY0+w24FSq1
7
+ wbTcnW7XG39eL5XNEtu2RF/0RdZp8jZ7VGuCUTX7X0tpKEzQqiy0+hSsarHoXEKA
8
+ k4lQMnJcM7tbiopz/vgtIjaREjAhmFcNv3OGkBAvNd7lk+4mVbiMpnh8IdZZBhf+
9
+ o0bVxGcTxxF39UIA6PqKp+SPvsVO1qZmEQJ1gpsu0nt0YTcl9PImIW+GYHD/r7UP
10
+ 8AV+jfRJLZB5rzk46vAKMjV91jMhjASYJc+NUHcl4LDrha3O0pw5I9qLDmzcHel+
11
+ SFzpaTf9vBDo5ufQfKYlau6feNyq6O+L40V645jPmphU2lPOqr2UDqAaXELbNosP
12
+ xz6eFPY+EiU4XKMLumJrYgOK0ToE9KNoPwvRWUWjnEOnnJZrfKIMBClc0h6TG96T
13
+ oPavNbu+5nGS97dxs9pBYaRP1EuzCzXnmip5d/o0bTr2+l3nfZ1bNEAL/70YNZBx
14
+ 2OIP1zGptBRqSV+2bLHJ1unRQSz8K/1xMnywJBAe9AS1HgaU0YTN59b8ptkQzDYE
15
+ UUuN85odKWiqp5QSaLDuZKA9GCnAFmg3FAGF7wMpkwXMdPXxo1SaQPzH0IC+rWIl
16
+ HD68kf3dmwMSrQKq63DxtuXx3kyOnA28Z6vZf4f/i4OZ66U3a0sCAwEAAaNCMEAw
17
+ DgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFO2b2cg9
18
+ xw7Cx8MFKadgIsMJMl10MA0GCSqGSIb3DQEBCwUAA4ICAQAi8hKgoJYyl8mzBoJ1
19
+ G4UKWXdrsbWBkrmzy2OpA6BeLGk+EboJ6gsfFmxJ1T7MsqscsNx0VgXrxh/fz73+
20
+ uWI9FNZ29IQEjN8e24FO2uaYRVCCtC2T+DHtwOilCE0RbdZtbjH7pdceAJ+lnHBB
21
+ 3zNnNm86lrjMG6dXF6NG68m7h8hV0sJnw5tGvccdg3b8nhxewXZSDfXgAew/nOQN
22
+ tdyZbIXOb6CKy+t4H3oKISC2DrSgreWqACLoIyAvFH6mC25QA/EIJBfoqbuHTinb
23
+ Mj8F13SpqgjeRsz4l0bhKzHDhmTgmcO0vXMmBbTFyzKtfa82eaL52rZ/YQrHOyef
24
+ ez1TmeHDtrfUZkbXxTEMEs01i6suiKfeb7rR2MzV6eIzX0yw/rSYgmbN+Ay7M6Vu
25
+ sVI3kTwEAFhlj2Gk2X1QjIXfB+rnmNe02TARLxNTAPZtuBrvhuLK7egLHofVRQcE
26
+ v6qWLZ2HmHn8rFs0wTsG73LFvvCo+zKboDDya+rHwbVGBFbk7cRw2cNTCoh58tSI
27
+ dzOqKwhKosE/uPNkFFu4wHoK7USjMSJL922B6DZKtZpzB02X5Q7M0pR507oFsBo/
28
+ 6jp2/pUKk1XCiFe7aE/Xz71B3a4HwIDPvY/9re9LmTaHM2+V1jC3RzSbAHpElv7N
29
+ fEgf4g2JSNreyvurE0jM672I7Q==
30
+ -----END CERTIFICATE-----
@@ -0,0 +1,51 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ 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
51
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,2 @@
1
+ require './application'
2
+ run Sinatra::Application
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module Server
3
+ class Configuration
4
+ attr_accessor :ca_cert_path, :ca_key_path,
5
+ :ca, :ca_key,
6
+ :certificates_store
7
+
8
+ def ca
9
+ @ca ||= OpenSSL::X509::Certificate.new(File.read(@ca_cert_path))
10
+ end
11
+
12
+ def ca_key
13
+ @ca_key ||= OpenSSL::PKey::RSA.new(File.read(@ca_key_path))
14
+ end
15
+
16
+ def certificates_store
17
+ return @certificates_store if defined?(@certificates_store)
18
+ @certificates_store = OpenSSL::X509::Store.new
19
+ @certificates_store.add_cert(ca)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module RubyScep
3
+ class CertificateBuilder
4
+ class << self
5
+ ONE_YEAR_IN_NUMBER_OF_SECONDS = 31536000
6
+ def build(csr)
7
+ certificate = OpenSSL::X509::Certificate.new
8
+ certificate.serial = Random.rand(730750818665451459101842416358141509827966271488) # will need to improve that
9
+ certificate.version = 1
10
+ certificate.public_key = csr.public_key
11
+ certificate.issuer = RubyScep.configuration.ca.subject
12
+ certificate.subject = csr.subject
13
+ certificate.not_before = Time.now
14
+ certificate.not_after = Time.now + ONE_YEAR_IN_NUMBER_OF_SECONDS
15
+ extension_factory = OpenSSL::X509::ExtensionFactory.new
16
+ extension_factory.subject_certificate = certificate
17
+ extension_factory.subject_request = csr
18
+ extension_factory.issuer_certificate = RubyScep.configuration.ca
19
+ certificate.add_extension(
20
+ extension_factory.create_extension(
21
+ 'keyUsage', 'digitalSignature,keyEncipherment'
22
+ )
23
+ )
24
+ certificate
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module RubyScep
3
+ class Configuration
4
+ attr_accessor :ca_cert_path, :ca_key_path,
5
+ :ca, :ca_key,
6
+ :certificates_store
7
+
8
+ def ca
9
+ @ca ||= OpenSSL::X509::Certificate.new(File.read(@ca_cert_path))
10
+ end
11
+
12
+ def ca_key
13
+ @ca_key ||= OpenSSL::PKey::RSA.new(File.read(@ca_key_path))
14
+ end
15
+
16
+ def certificates_store
17
+ return @certificates_store if defined?(@certificates_store)
18
+ @certificates_store = OpenSSL::X509::Store.new
19
+ @certificates_store.add_cert(ca)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ module RubyScep
3
+ class PkiMessage
4
+ class Degenerate
5
+
6
+ def initialize(certificate)
7
+ @certificate = certificate
8
+ end
9
+
10
+ def to_der
11
+ OpenSSL::ASN1::Sequence.new(
12
+ [
13
+ OpenSSL::ASN1::ObjectId.new(OID_SIGNED_DATA),
14
+ OpenSSL::ASN1::ASN1Data.new(
15
+ [
16
+ OpenSSL::ASN1::Sequence.new(
17
+ [
18
+ OpenSSL::ASN1::Integer.new(1),
19
+ OpenSSL::ASN1::Set.new([]),
20
+ OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::ObjectId.new(OID_DATA)]),
21
+ OpenSSL::ASN1::ASN1Data.new([OpenSSL::ASN1::decode(@certificate.to_der)], 0, :CONTEXT_SPECIFIC),
22
+ OpenSSL::ASN1::ASN1Data.new([], 1, :CONTEXT_SPECIFIC),
23
+ OpenSSL::ASN1::Set.new([])
24
+ ]
25
+ )
26
+ ],
27
+ 0,
28
+ :CONTEXT_SPECIFIC)
29
+ ]
30
+ ).to_der
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+ module RubyScep
3
+ class PkiMessage
4
+ class EnvelopedData
5
+
6
+ def initialize(p7, encryption_key, encryption_iv, encrypted_payload)
7
+ @p7 = p7
8
+ @encryption_key = encryption_key
9
+ @encryption_iv = encryption_iv
10
+ @encrypted_payload = encrypted_payload
11
+ end
12
+
13
+ def to_der
14
+ OpenSSL::ASN1::Sequence.new(
15
+ [
16
+ OpenSSL::ASN1::ObjectId.new(OID_ENVELOPED_DATA),
17
+ OpenSSL::ASN1::ASN1Data.new(
18
+ [
19
+ OpenSSL::ASN1::Sequence.new(
20
+ [
21
+ OpenSSL::ASN1::Integer.new(0),
22
+ OpenSSL::ASN1::Set.new(
23
+ [
24
+ OpenSSL::ASN1::Sequence.new(
25
+ [
26
+ OpenSSL::ASN1::Integer.new(0),
27
+ OpenSSL::ASN1::Sequence.new(
28
+ [
29
+ OpenSSL::ASN1::decode(@p7.certificates.first.subject.to_der),
30
+ OpenSSL::ASN1::Integer.new(@p7.certificates.first.serial.to_i)
31
+ ]
32
+ ),
33
+ OpenSSL::ASN1::Sequence.new(
34
+ [
35
+ OpenSSL::ASN1::ObjectId.new(OID_RSA_ENCRYPTION),
36
+ OpenSSL::ASN1::Null.new(nil)
37
+ ]
38
+ ),
39
+ OpenSSL::ASN1::OctetString.new(@p7.certificates.first.public_key.public_encrypt(@encryption_key))
40
+ ]
41
+ )
42
+ ]
43
+ ),
44
+ OpenSSL::ASN1::Sequence.new(
45
+ [
46
+ OpenSSL::ASN1::ObjectId.new(OID_DATA),
47
+ OpenSSL::ASN1::Sequence.new(
48
+ [
49
+ OpenSSL::ASN1::ObjectId.new(OID_DES_ALGO),
50
+ OpenSSL::ASN1::OctetString.new(@encryption_iv)
51
+ ]
52
+ ),
53
+ OpenSSL::ASN1::ASN1Data.new(@encrypted_payload, 0, :CONTEXT_SPECIFIC)
54
+ ]
55
+ )
56
+ ]
57
+ )
58
+ ],
59
+ 0,
60
+ :CONTEXT_SPECIFIC
61
+ )
62
+ ]
63
+ ).to_der
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+ module RubyScep
3
+ class PkiMessage
4
+ class SignedData
5
+
6
+ def initialize(envelop_sequence, ca, ca_key, sender_nonce, transaction_id)
7
+ @envelop_sequence = envelop_sequence
8
+ @ca = ca
9
+ @ca_key = ca_key
10
+ @sender_nonce = sender_nonce
11
+ @transaction_id = transaction_id
12
+ @sha1 = OpenSSL::Digest::SHA1.new
13
+ end
14
+
15
+ def to_der
16
+ signed_attributes = signed_attributes_sequence
17
+ signed_attributes_digest = @ca_key.private_encrypt(
18
+ algo_identifier_sequence(signed_attributes, @sha1).to_der
19
+ )
20
+ OpenSSL::ASN1::Sequence.new(
21
+ [
22
+ OpenSSL::ASN1::ObjectId.new(OID_SIGNED_DATA),
23
+ OpenSSL::ASN1::ASN1Data.new(
24
+ [
25
+ OpenSSL::ASN1::Sequence.new(
26
+ [
27
+ OpenSSL::ASN1::Integer.new(1),
28
+ OpenSSL::ASN1::Set.new(
29
+ [
30
+ OpenSSL::ASN1::Sequence.new(
31
+ [
32
+ OpenSSL::ASN1::ObjectId.new(OID_HASH_ALGO_IDENTIFIER),
33
+ OpenSSL::ASN1::Null.new(nil)
34
+ ]
35
+ )
36
+ ]
37
+ ),
38
+ OpenSSL::ASN1::Sequence.new(
39
+ [
40
+ OpenSSL::ASN1::ObjectId.new(OID_DATA),
41
+ OpenSSL::ASN1::ASN1Data.new([OpenSSL::ASN1::OctetString.new(@envelop_sequence)], 0, :CONTEXT_SPECIFIC)]
42
+ ),
43
+ OpenSSL::ASN1::Set.new(
44
+ [
45
+ OpenSSL::ASN1::Sequence.new(
46
+ [
47
+ OpenSSL::ASN1::Integer.new(1),
48
+ OpenSSL::ASN1::Sequence.new(
49
+ [
50
+ OpenSSL::ASN1::decode(@ca.subject.to_der),
51
+ OpenSSL::ASN1::Integer.new(@ca.serial)
52
+ ]
53
+ ),
54
+ OpenSSL::ASN1::Sequence.new(
55
+ [
56
+ OpenSSL::ASN1::ObjectId.new(OID_HASH_ALGO_IDENTIFIER),
57
+ OpenSSL::ASN1::Null.new(nil)
58
+ ]
59
+ ),
60
+ signed_attributes,
61
+ OpenSSL::ASN1::Sequence.new(
62
+ [
63
+ OpenSSL::ASN1::ObjectId.new(OID_RSA_ENCRYPTION),
64
+ OpenSSL::ASN1::Null.new(nil)
65
+ ]
66
+ ),
67
+ OpenSSL::ASN1::OctetString.new(signed_attributes_digest)
68
+ ]
69
+ ),
70
+ ]
71
+ )
72
+ ]
73
+ )],
74
+ 0,
75
+ :CONTEXT_SPECIFIC
76
+ )
77
+ ]
78
+ ).to_der
79
+ end
80
+
81
+ def signed_attributes_sequence
82
+ OpenSSL::ASN1::ASN1Data.new(
83
+ [
84
+ OpenSSL::ASN1::Sequence.new(
85
+ [
86
+ OpenSSL::ASN1::ObjectId.new(OID_CONTENT_TYPE),
87
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::ObjectId.new(OID_DATA)])
88
+ ]
89
+ ),
90
+ OpenSSL::ASN1::Sequence.new(
91
+ [
92
+ OpenSSL::ASN1::ObjectId.new(OID_SIGNING_TIME),
93
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::UTCTime.new(Time.now)])
94
+ ]
95
+ ),
96
+ OpenSSL::ASN1::Sequence.new(
97
+ [
98
+ OpenSSL::ASN1::ObjectId.new(OID_MESSAGE_DIGEST),
99
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::OctetString.new(@sha1.digest(@envelop_sequence))])
100
+ ]
101
+ ),
102
+ OpenSSL::ASN1::Sequence.new(
103
+ [
104
+ OpenSSL::ASN1::ObjectId.new(OID_MESSAGE_TYPE),
105
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::PrintableString.new(SCEP_MESSAGE_TYPES['CertRep'].to_s)])
106
+ ]
107
+ ),
108
+ OpenSSL::ASN1::Sequence.new(
109
+ [
110
+ OpenSSL::ASN1::ObjectId.new(OID_PKI_STATUS),
111
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::PrintableString.new(SCEP_PKI_STATUSES['SUCCESS'].to_s)])
112
+ ]
113
+ ),
114
+ OpenSSL::ASN1::Sequence.new(
115
+ [
116
+ OpenSSL::ASN1::ObjectId.new(OID_RECIPIENT_NOUNCE),
117
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::OctetString.new([@sender_nonce].pack('H*'))])
118
+ ]
119
+ ),
120
+ OpenSSL::ASN1::Sequence.new(
121
+ [
122
+ OpenSSL::ASN1::ObjectId.new(OID_SENDER_NONCE),
123
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::OctetString.new([@sender_nonce].pack('H*'))])
124
+ ]
125
+ ),
126
+ OpenSSL::ASN1::Sequence.new(
127
+ [
128
+ OpenSSL::ASN1::ObjectId.new(OID_TRANSACTION_ID),
129
+ OpenSSL::ASN1::Set.new([OpenSSL::ASN1::PrintableString.new(@transaction_id)])
130
+ ]
131
+ )
132
+ ],
133
+ 0,
134
+ :CONTEXT_SPECIFIC
135
+ )
136
+ end
137
+
138
+ def algo_identifier_sequence(signed_attributes, sha1)
139
+ OpenSSL::ASN1::Sequence.new(
140
+ [
141
+ OpenSSL::ASN1::Sequence.new(
142
+ [
143
+ OpenSSL::ASN1::ObjectId.new(OID_HASH_ALGO_IDENTIFIER),
144
+ OpenSSL::ASN1::Null.new(nil)
145
+ ]
146
+ ),
147
+ OpenSSL::ASN1::OctetString.new(sha1.digest(OpenSSL::ASN1::Set.new(signed_attributes.value[0..-1]).to_der))
148
+ ]
149
+ )
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+ require 'openssl'
3
+
4
+ module RubyScep
5
+ class PkiMessage
6
+ include OpenSSL::ASN1
7
+
8
+ # get OID corresponding name http://oid-info.com/get/<the oid>
9
+ # get possible balues for a given OID in the CMS RFC https://www.ietf.org/rfc/rfc3369.txt
10
+ OID_MESSAGE_TYPE = '2.16.840.1.113733.1.9.2'
11
+ OID_PKI_STATUS = '2.16.840.1.113733.1.9.3'
12
+ OID_FAIL_INFO = '2.16.840.1.113733.1.9.4'
13
+ OID_SENDER_NONCE = '2.16.840.1.113733.1.9.5'
14
+ OID_RECIPIENT_NOUNCE = '2.16.840.1.113733.1.9.6'
15
+ OID_TRANSACTION_ID = '2.16.840.1.113733.1.9.7'
16
+ OID_EXTENSION_REQUEST = '2.16.840.1.113733.1.9.8'
17
+ OID_SIGNED_DATA = '1.2.840.113549.1.7.2'
18
+ OID_DATA = '1.2.840.113549.1.7.1'
19
+ OID_ENVELOPED_DATA = '1.2.840.113549.1.7.3'
20
+ OID_RSA_ENCRYPTION = '1.2.840.113549.1.1.1'
21
+ OID_DES_ALGO = '1.2.840.113549.3.7'
22
+ OID_CONTENT_TYPE = '1.2.840.113549.1.9.3'
23
+ OID_SIGNING_TIME = '1.2.840.113549.1.9.5'
24
+ OID_MESSAGE_DIGEST = '1.2.840.113549.1.9.4'
25
+ OID_HASH_ALGO_IDENTIFIER = '1.3.14.3.2.26'
26
+
27
+ # complete list of possible SCEP values can be found in CISCO's documentation
28
+ # https://www.cisco.com/c/en/us/support/docs/security-vpn/public-key-infrastructure-pki/116167-technote-scep-00.html
29
+ SCEP_MESSAGE_TYPES = { 'PKCSReq' => 19, 'CertRep' => 3, 'GetCertInitial' => 20, 'GetCert' => 21, 'GetCRL' => 22 }
30
+ SCEP_PKI_STATUSES = { 'SUCCESS' => 0, 'FAILURE' => 2, 'PENDING' => 3 }
31
+ SCEP_FAIL_INFOS = { 'badAlg' => 0, 'badMessageCheck' => 1, 'badRequest' => 2, 'badTime' => 3, 'badCertId' => 4 }
32
+
33
+ attr_accessor :p7
34
+
35
+ def initialize(asn1, p7)
36
+ signed_attributes = retrieve_signed_attributes(asn1)
37
+ @message_type = SCEP_MESSAGE_TYPES.key(signed_attributes[OID_MESSAGE_TYPE].to_i)
38
+ @transaction_id = signed_attributes[OID_TRANSACTION_ID]
39
+ @sender_nonce = signed_attributes[OID_SENDER_NONCE]
40
+ @p7 = p7
41
+ end
42
+
43
+ # We are building a SCEP Secure Message Object with a valid PKCS7 structure, as referenced
44
+ # in https://tools.ietf.org/html/draft-nourse-scep-23#section-3
45
+ # To see a graphical representation of the final PKCS7 structure, go to
46
+ # https://www.cisco.com/c/dam/en/us/support/docs/security-vpn/public-key-infrastructure-pki/116167-technote-scep-00-01.jpeg
47
+ # Structure:
48
+ # 1. degenerate
49
+ # a. version
50
+ # b. x509
51
+ # 2. enveloped data
52
+ # a. version
53
+ # b. list of recepients
54
+ # c. encrypted data (aka 1. degenerate)
55
+ # 3. signed data
56
+ # a. version
57
+ # b. hashing algo
58
+ # c. signed (unencrypted) data (aka 2. enveloped data)
59
+ # d. ca certificate
60
+ # e. digital signature
61
+ def build_enrollment_response(csr)
62
+ degenerate_sequence = build_degenerate_sequence(csr)
63
+ enveloped_data_sequence = build_enveloped_data_sequence(degenerate_sequence)
64
+ build_signed_data_sequence(enveloped_data_sequence)
65
+ end
66
+
67
+ private
68
+
69
+ def retrieve_signed_attributes(asn1)
70
+ # cheers AppBlade! https://github.com/AppBlade/TestHub/blob/master/app/controllers/scep_controller.rb#L92-L112
71
+ raw_signed_attributes = asn1.value[1].value.first.value[4].first.value[3].value
72
+ raw_signed_attributes.inject({}) do |hash, raw_signed_attribute|
73
+ hash.merge(raw_signed_attribute.value.first.value => raw_signed_attribute.value.last.value.first.value)
74
+ end
75
+ end
76
+
77
+ def build_degenerate_sequence(csr)
78
+ certificate = CertificateBuilder.build(csr)
79
+ certificate.sign(RubyScep.configuration.ca_key, OpenSSL::Digest::SHA1.new)
80
+ PkiMessage::Degenerate.new(certificate).to_der
81
+ end
82
+
83
+ def build_enveloped_data_sequence(degenerate_sequence)
84
+ encrypted_payload, encryption_key, encryption_iv = encrypt_payload(degenerate_sequence)
85
+ PkiMessage::EnvelopedData.new(@p7, encryption_key, encryption_iv, encrypted_payload).to_der
86
+ end
87
+
88
+ def encrypt_payload(der)
89
+ des = OpenSSL::Cipher::Cipher.new('des-ede3-cbc')
90
+ des.encrypt
91
+ encryption_key = des.random_key
92
+ encryption_iv = des.random_iv
93
+ des.key = encryption_key
94
+ des.iv = encryption_iv
95
+ [des.update(der) + des.final, encryption_key, encryption_iv]
96
+ end
97
+
98
+ def build_signed_data_sequence(enveloped_data_sequence)
99
+ PkiMessage::SignedData.new(
100
+ enveloped_data_sequence,
101
+ RubyScep.configuration.ca,
102
+ RubyScep.configuration.ca_key,
103
+ @sender_nonce,
104
+ @transaction_id
105
+ ).to_der
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module RubyScep
3
+ class PkiOperation
4
+ class << self
5
+ # @param raw_csr [String] The binary encoded CSR
6
+ # @return DER-encoded [String], PkiMessage represented in an OpenSSL::ASN1 structure containing the
7
+ # device's MDM certificate to be installed
8
+ def build_response(raw_csr)
9
+ pki_message = parse_pki_message(raw_csr)
10
+ csr = decrypt_pki_envelope(pki_message)
11
+ pki_message.build_enrollment_response(csr)
12
+ end
13
+
14
+ private
15
+
16
+ # @param raw_csr [String] The binary encoded CSR
17
+ # @return [RubyScep::PkiMessage], containing the CSR info
18
+ def parse_pki_message(raw_csr)
19
+ p7 = OpenSSL::PKCS7.new(raw_csr)
20
+ flags = OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::NOVERIFY
21
+ # OpenSSL::PKCS7::NOVERIFY is necessary otherwise the verify step fails
22
+ p7.verify(nil, RubyScep.configuration.certificates_store, nil, flags) # necessary to populate the p7 data field
23
+ asn1 = OpenSSL::ASN1.decode(p7.to_der)
24
+ PkiMessage.new(asn1, p7)
25
+ end
26
+
27
+ # @param pki_message [RubyScep::PkiMessage] The PkiMessage containing the CSR info sent by the iOS device
28
+ # @return [OpenSSL::X509::Request], the decrypted CSR
29
+ def decrypt_pki_envelope(pki_message)
30
+ encrypted_p7 = OpenSSL::PKCS7.new(pki_message.p7.data)
31
+ raw_csr = encrypted_p7.decrypt(RubyScep.configuration.ca_key, RubyScep.configuration.ca, OpenSSL::PKCS7::BINARY)
32
+ # this is the moment when we could extract the device info from the CSR (device id and challenge password)
33
+ # see https://github.com/AppBlade/TestHub/blob/master/app/controllers/scep_controller.rb#L57
34
+ OpenSSL::X509::Request.new(raw_csr)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RubyScep
5
+ module Version
6
+ STRING = '0.1.0'
7
+
8
+ module_function
9
+
10
+ def version(*_args)
11
+ STRING
12
+ end
13
+ end
14
+ end
data/lib/ruby_scep.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ require 'ruby_scep/version'
3
+ require 'ruby_scep/configuration'
4
+ require 'ruby_scep/certificate_builder'
5
+ require 'ruby_scep/pki_message'
6
+ require 'ruby_scep/pki_message/degenerate'
7
+ require 'ruby_scep/pki_message/enveloped_data'
8
+ require 'ruby_scep/pki_message/signed_data'
9
+ require 'ruby_scep/pki_operation'
10
+
11
+ module RubyScep
12
+ attr_accessor :configuration
13
+
14
+ def self.configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ def self.configure
19
+ yield(configuration)
20
+ end
21
+ end
data/ruby_scep.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
4
+ require 'ruby_scep/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'ruby_scep'
8
+ s.version = RubyScep::Version::STRING
9
+ s.platform = Gem::Platform::RUBY
10
+ s.required_ruby_version = '>= 2.3.0'
11
+ s.authors = ['Christophe Valentin']
12
+ s.description = <<-EOF
13
+ Ruby implementation of SCEP
14
+ EOF
15
+ s.email = 'dev@appaloosa-store.com'
16
+ s.files = `git ls-files`.split($RS).reject do |file|
17
+ file =~ %r{^(?:
18
+ spec/.*
19
+ |Gemfile
20
+ |\.rspec
21
+ |\.gitignore
22
+ )$}x
23
+ end
24
+ s.extra_rdoc_files = %w(README.md)
25
+ s.homepage = 'https://github.com/appaloosa-store/ruby_scep'
26
+ s.licenses = ['MIT']
27
+ s.require_paths = ['lib']
28
+
29
+ s.summary = 'Ruby implementation of SCEP'
30
+
31
+ s.add_development_dependency('rspec', '~> 3.6')
32
+ s.add_development_dependency('rspec-its', '~> 1.2.0')
33
+ s.add_development_dependency('timecop', '~> 0.9.1')
34
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_scep
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Christophe Valentin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-its
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.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: 1.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: timecop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.1
55
+ description: " Ruby implementation of SCEP\n"
56
+ email: dev@appaloosa-store.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files:
60
+ - README.md
61
+ files:
62
+ - ".circleci/config.yml"
63
+ - Gemfile.lock
64
+ - License
65
+ - README.md
66
+ - example_server/Gemfile
67
+ - example_server/Gemfile.lock
68
+ - example_server/Procfile
69
+ - example_server/application.rb
70
+ - example_server/certs/ca.pem
71
+ - example_server/certs/passwordless.key
72
+ - example_server/config.ru
73
+ - example_server/configuration.rb
74
+ - lib/ruby_scep.rb
75
+ - lib/ruby_scep/certificate_builder.rb
76
+ - lib/ruby_scep/configuration.rb
77
+ - lib/ruby_scep/pki_message.rb
78
+ - lib/ruby_scep/pki_message/degenerate.rb
79
+ - lib/ruby_scep/pki_message/enveloped_data.rb
80
+ - lib/ruby_scep/pki_message/signed_data.rb
81
+ - lib/ruby_scep/pki_operation.rb
82
+ - lib/ruby_scep/version.rb
83
+ - ruby_scep.gemspec
84
+ homepage: https://github.com/appaloosa-store/ruby_scep
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 2.3.0
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.6.13
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Ruby implementation of SCEP
108
+ test_files: []