certificate_authority 0.1.6 → 1.0.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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +2 -8
  6. data/Gemfile.lock +71 -31
  7. data/README.rdoc +91 -2
  8. data/Rakefile +6 -41
  9. data/certificate_authority.gemspec +22 -66
  10. data/lib/certificate_authority.rb +6 -5
  11. data/lib/certificate_authority/certificate.rb +91 -36
  12. data/lib/certificate_authority/certificate_revocation_list.rb +34 -14
  13. data/lib/certificate_authority/core_extensions.rb +46 -0
  14. data/lib/certificate_authority/distinguished_name.rb +64 -16
  15. data/lib/certificate_authority/extensions.rb +417 -45
  16. data/lib/certificate_authority/key_material.rb +30 -9
  17. data/lib/certificate_authority/ocsp_handler.rb +75 -5
  18. data/lib/certificate_authority/pkcs11_key_material.rb +0 -2
  19. data/lib/certificate_authority/revocable.rb +14 -0
  20. data/lib/certificate_authority/serial_number.rb +15 -2
  21. data/lib/certificate_authority/signing_request.rb +91 -0
  22. data/lib/certificate_authority/validations.rb +31 -0
  23. data/lib/certificate_authority/version.rb +3 -0
  24. metadata +76 -48
  25. data/VERSION.yml +0 -5
  26. data/spec/spec_helper.rb +0 -4
  27. data/spec/units/certificate_authority_spec.rb +0 -4
  28. data/spec/units/certificate_revocation_list_spec.rb +0 -68
  29. data/spec/units/certificate_spec.rb +0 -428
  30. data/spec/units/distinguished_name_spec.rb +0 -59
  31. data/spec/units/extensions_spec.rb +0 -115
  32. data/spec/units/key_material_spec.rb +0 -100
  33. data/spec/units/ocsp_handler_spec.rb +0 -104
  34. data/spec/units/pkcs11_key_material_spec.rb +0 -41
  35. data/spec/units/serial_number_spec.rb +0 -20
  36. data/spec/units/signing_entity_spec.rb +0 -4
  37. data/spec/units/units_helper.rb +0 -1
@@ -15,11 +15,30 @@ module CertificateAuthority
15
15
  def is_in_memory?
16
16
  raise "Required implementation"
17
17
  end
18
+
19
+ def self.from_x509_key_pair(pair,password=nil)
20
+ if password.nil?
21
+ key = OpenSSL::PKey::RSA.new(pair)
22
+ else
23
+ key = OpenSSL::PKey::RSA.new(pair,password)
24
+ end
25
+ mem_key = MemoryKeyMaterial.new
26
+ mem_key.public_key = key.public_key
27
+ mem_key.private_key = key
28
+ mem_key
29
+ end
30
+
31
+ def self.from_x509_public_key(public_key_pem)
32
+ key = OpenSSL::PKey::RSA.new(public_key_pem)
33
+ signing_request_key = SigningRequestKeyMaterial.new
34
+ signing_request_key.public_key = key.public_key
35
+ signing_request_key
36
+ end
18
37
  end
19
38
 
20
39
  class MemoryKeyMaterial
21
40
  include KeyMaterial
22
- include ActiveModel::Validations
41
+ include Validations
23
42
 
24
43
  attr_accessor :keypair
25
44
  attr_accessor :private_key
@@ -28,11 +47,13 @@ module CertificateAuthority
28
47
  def initialize
29
48
  end
30
49
 
31
- validates_each :private_key do |record, attr, value|
32
- record.errors.add :private_key, "cannot be blank" if record.private_key.nil?
33
- end
34
- validates_each :public_key do |record, attr, value|
35
- record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
50
+ def validate
51
+ if private_key.nil?
52
+ errors.add :private_key, "cannot be blank"
53
+ end
54
+ if public_key.nil?
55
+ errors.add :public_key, "cannot be blank"
56
+ end
36
57
  end
37
58
 
38
59
  def is_in_hardware?
@@ -61,10 +82,10 @@ module CertificateAuthority
61
82
 
62
83
  class SigningRequestKeyMaterial
63
84
  include KeyMaterial
64
- include ActiveModel::Validations
85
+ include Validations
65
86
 
66
- validates_each :public_key do |record, attr, value|
67
- record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
87
+ def validate
88
+ errors.add :public_key, "cannot be blank" if public_key.nil?
68
89
  end
69
90
 
70
91
  attr_accessor :public_key
@@ -1,6 +1,74 @@
1
1
  module CertificateAuthority
2
+ class OCSPResponseBuilder
3
+ attr_accessor :ocsp_response
4
+ attr_accessor :verification_mechanism
5
+ attr_accessor :ocsp_request_reader
6
+ attr_accessor :parent
7
+ attr_accessor :next_update
8
+
9
+ GOOD = OpenSSL::OCSP::V_CERTSTATUS_GOOD
10
+ REVOKED = OpenSSL::OCSP::V_CERTSTATUS_REVOKED
11
+
12
+ NO_REASON=0
13
+ KEY_COMPROMISED=OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE
14
+ UNSPECIFIED=OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED
15
+
16
+ def build_response()
17
+ raise "Requires a parent for signing" if @parent.nil?
18
+ if @verification_mechanism.nil?
19
+ ## If no verification callback is provided we're marking it GOOD
20
+ @verification_mechanism = lambda {|cert_id| [GOOD,NO_REASON] }
21
+ end
22
+
23
+ @ocsp_request_reader.ocsp_request.certid.each do |cert_id|
24
+ result,reason = verification_mechanism.call(cert_id.serial)
25
+
26
+ ## cert_id, status, reason, rev_time, this update, next update, ext
27
+ ## - unit of time is seconds
28
+ ## - rev_time is currently set to "now"
29
+ @ocsp_response.add_status(cert_id,
30
+ result, reason,
31
+ 0, 0, @next_update, nil)
32
+ end
33
+
34
+ @ocsp_response.sign(OpenSSL::X509::Certificate.new(@parent.to_pem), @parent.key_material.private_key, nil, nil)
35
+ OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, @ocsp_response)
36
+ end
37
+
38
+ def self.from_request_reader(request_reader,verification_mechanism=nil)
39
+ response_builder = OCSPResponseBuilder.new
40
+ response_builder.ocsp_request_reader = request_reader
41
+
42
+ ocsp_response = OpenSSL::OCSP::BasicResponse.new
43
+ ocsp_response.copy_nonce(request_reader.ocsp_request)
44
+ response_builder.ocsp_response = ocsp_response
45
+ response_builder.next_update = 60*15 #Default of 15 minutes
46
+ response_builder
47
+ end
48
+ end
49
+
50
+ class OCSPRequestReader
51
+ attr_accessor :raw_ocsp_request
52
+ attr_accessor :ocsp_request
53
+
54
+ def serial_numbers
55
+ @ocsp_request.certid.collect do |cert_id|
56
+ cert_id.serial
57
+ end
58
+ end
59
+
60
+ def self.from_der(request_body)
61
+ reader = OCSPRequestReader.new
62
+ reader.raw_ocsp_request = request_body
63
+ reader.ocsp_request = OpenSSL::OCSP::Request.new(request_body)
64
+
65
+ reader
66
+ end
67
+ end
68
+
69
+ ## DEPRECATED
2
70
  class OCSPHandler
3
- include ActiveModel::Validations
71
+ include Validations
4
72
 
5
73
  attr_accessor :ocsp_request
6
74
  attr_accessor :certificate_ids
@@ -10,10 +78,10 @@ module CertificateAuthority
10
78
 
11
79
  attr_accessor :ocsp_response_body
12
80
 
13
- validate do |crl|
81
+ def validate
14
82
  errors.add :parent, "A parent entity must be set" if parent.nil?
83
+ all_certificates_available
15
84
  end
16
- validate :all_certificates_available
17
85
 
18
86
  def initialize
19
87
  self.certificates = {}
@@ -24,9 +92,11 @@ module CertificateAuthority
24
92
  end
25
93
 
26
94
  def extract_certificate_serials
27
- raise "No valid OCSP request was supplied" if self.ocsp_request.nil?
28
- openssl_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
95
+ openssl_request = OpenSSL::OCSP::Request.new(@ocsp_request)
29
96
 
97
+ if openssl_request.certid.nil?
98
+ raise "Invalid openssl request"
99
+ end
30
100
  self.certificate_ids = openssl_request.certid.collect do |cert_id|
31
101
  cert_id.serial
32
102
  end
@@ -1,8 +1,6 @@
1
1
  module CertificateAuthority
2
2
  class Pkcs11KeyMaterial
3
3
  include KeyMaterial
4
- include ActiveModel::Validations
5
- include ActiveModel::Serialization
6
4
 
7
5
  attr_accessor :engine
8
6
  attr_accessor :token_id
@@ -0,0 +1,14 @@
1
+ module CertificateAuthority
2
+ module Revocable
3
+ attr_accessor :revoked_at
4
+
5
+ def revoke!(time=Time.now)
6
+ @revoked_at = time
7
+ end
8
+
9
+ def revoked?
10
+ # If we have a time, then we're revoked
11
+ !@revoked_at.nil?
12
+ end
13
+ end
14
+ end
@@ -1,9 +1,22 @@
1
+ require 'securerandom'
2
+
1
3
  module CertificateAuthority
2
4
  class SerialNumber
3
- include ActiveModel::Validations
5
+ include Validations
6
+ include Revocable
4
7
 
5
8
  attr_accessor :number
6
9
 
7
- validates :number, :presence => true, :numericality => {:greater_than => 0}
10
+ def validate
11
+ if self.number.nil?
12
+ errors.add :number, "must not be empty"
13
+ elsif self.number.to_i <= 0
14
+ errors.add :number, "must be greater than zero"
15
+ end
16
+ end
17
+
18
+ def initialize
19
+ self.number = SecureRandom.random_number(2**128-1)
20
+ end
8
21
  end
9
22
  end
@@ -0,0 +1,91 @@
1
+ module CertificateAuthority
2
+ class SigningRequest
3
+ attr_accessor :distinguished_name
4
+ attr_accessor :key_material
5
+ attr_accessor :raw_body
6
+ attr_accessor :openssl_csr
7
+ attr_accessor :digest
8
+ attr_accessor :attributes
9
+
10
+ def initialize()
11
+ @attributes = []
12
+ end
13
+
14
+ # Fake attribute for convenience because adding
15
+ # alternative names on a CSR is remarkably non-trivial.
16
+ def subject_alternative_names=(alt_names)
17
+ raise "alt_names must be an Array" unless alt_names.is_a?(Array)
18
+
19
+ factory = OpenSSL::X509::ExtensionFactory.new
20
+ name_list = alt_names.map{|m| "DNS:#{m}"}.join(",")
21
+ ext = factory.create_ext("subjectAltName",name_list,false)
22
+ ext_set = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence([ext])])
23
+ attr = OpenSSL::X509::Attribute.new("extReq", ext_set)
24
+ @attributes << attr
25
+ end
26
+
27
+ def read_attributes_by_oid(*oids)
28
+ attributes.detect { |a| oids.include?(a.oid) }
29
+ end
30
+ protected :read_attributes_by_oid
31
+
32
+ def to_cert
33
+ cert = Certificate.new
34
+ if !@distinguished_name.nil?
35
+ cert.distinguished_name = @distinguished_name
36
+ end
37
+ cert.key_material = @key_material
38
+ if attribute = read_attributes_by_oid('extReq', 'msExtReq')
39
+ set = OpenSSL::ASN1.decode(attribute.value)
40
+ seq = set.value.first
41
+ seq.value.collect { |asn1ext| OpenSSL::X509::Extension.new(asn1ext).to_a }.each do |o, v, c|
42
+ Certificate::EXTENSIONS.each do |klass|
43
+ cert.extensions[klass::OPENSSL_IDENTIFIER] = klass.parse(v, c) if v && klass::OPENSSL_IDENTIFIER == o
44
+ end
45
+ end
46
+ end
47
+ cert
48
+ end
49
+
50
+ def to_pem
51
+ to_x509_csr.to_pem
52
+ end
53
+
54
+ def to_x509_csr
55
+ raise "Must specify a DN/subject on csr" if @distinguished_name.nil?
56
+ raise "Invalid DN in request" unless @distinguished_name.valid?
57
+ raise "CSR must have key material" if @key_material.nil?
58
+ raise "CSR must include a public key on key material" if @key_material.public_key.nil?
59
+ raise "Need a private key on key material for CSR generation" if @key_material.private_key.nil?
60
+
61
+ opensslcsr = OpenSSL::X509::Request.new
62
+ opensslcsr.subject = @distinguished_name.to_x509_name
63
+ opensslcsr.public_key = @key_material.public_key
64
+ opensslcsr.attributes = @attributes unless @attributes.nil?
65
+ opensslcsr.sign @key_material.private_key, OpenSSL::Digest.new(@digest || "SHA512")
66
+ opensslcsr
67
+ end
68
+
69
+ def self.from_x509_csr(raw_csr)
70
+ csr = SigningRequest.new
71
+ openssl_csr = OpenSSL::X509::Request.new(raw_csr)
72
+ csr.distinguished_name = DistinguishedName.from_openssl openssl_csr.subject
73
+ csr.raw_body = raw_csr
74
+ csr.openssl_csr = openssl_csr
75
+ csr.attributes = openssl_csr.attributes
76
+ key_material = SigningRequestKeyMaterial.new
77
+ key_material.public_key = openssl_csr.public_key
78
+ csr.key_material = key_material
79
+ csr
80
+ end
81
+
82
+ def self.from_netscape_spkac(raw_spkac)
83
+ openssl_spkac = OpenSSL::Netscape::SPKI.new raw_spkac
84
+ csr = SigningRequest.new
85
+ csr.raw_body = raw_spkac
86
+ key_material = SigningRequestKeyMaterial.new
87
+ key_material.public_key = openssl_spkac.public_key
88
+ csr
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # This is a super simple replacement for ActiveSupport::Validations
3
+ #
4
+
5
+ module CertificateAuthority
6
+ class Errors < Array
7
+ def add(symbol, msg)
8
+ self.push([symbol, msg])
9
+ end
10
+ def full_messages
11
+ self.map {|i| i[0].to_s + ": " + i[1]}.join("\n")
12
+ end
13
+ end
14
+
15
+ module Validations
16
+ def valid?
17
+ @errors = Errors.new
18
+ validate
19
+ errors.empty?
20
+ end
21
+
22
+ # must be overridden
23
+ def validate
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def errors
28
+ @errors ||= Errors.new
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module CertificateAuthority
2
+ VERSION = '1.0.0'.freeze
3
+ end
metadata CHANGED
@@ -1,111 +1,139 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: certificate_authority
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
5
- prerelease:
4
+ version: 1.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Chris Chandler
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-08-12 00:00:00.000000000 Z
11
+ date: 2020-05-28 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: activemodel
16
- requirement: &70182567433260 !ruby/object:Gem::Requirement
17
- none: false
14
+ name: coveralls
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
- version: 3.0.6
22
- type: :runtime
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
23
49
  prerelease: false
24
- version_requirements: *70182567433260
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
25
55
  - !ruby/object:Gem::Dependency
26
56
  name: rspec
27
- requirement: &70182567432240 !ruby/object:Gem::Requirement
28
- none: false
57
+ requirement: !ruby/object:Gem::Requirement
29
58
  requirements:
30
- - - ! '>='
59
+ - - ">="
31
60
  - !ruby/object:Gem::Version
32
61
  version: '0'
33
62
  type: :development
34
63
  prerelease: false
35
- version_requirements: *70182567432240
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
36
69
  - !ruby/object:Gem::Dependency
37
- name: jeweler
38
- requirement: &70182567430960 !ruby/object:Gem::Requirement
39
- none: false
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
40
72
  requirements:
41
- - - ! '>='
73
+ - - ">="
42
74
  - !ruby/object:Gem::Version
43
- version: 1.5.2
75
+ version: '0'
44
76
  type: :development
45
77
  prerelease: false
46
- version_requirements: *70182567430960
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
47
83
  description:
48
- email: chris@flatterline.com
84
+ email:
85
+ - squanderingtime@gmail.com
49
86
  executables: []
50
87
  extensions: []
51
- extra_rdoc_files:
52
- - README.rdoc
88
+ extra_rdoc_files: []
53
89
  files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
54
93
  - Gemfile
55
94
  - Gemfile.lock
56
95
  - README.rdoc
57
96
  - Rakefile
58
- - VERSION.yml
59
97
  - certificate_authority.gemspec
60
98
  - lib/certificate_authority.rb
61
99
  - lib/certificate_authority/certificate.rb
62
100
  - lib/certificate_authority/certificate_revocation_list.rb
101
+ - lib/certificate_authority/core_extensions.rb
63
102
  - lib/certificate_authority/distinguished_name.rb
64
103
  - lib/certificate_authority/extensions.rb
65
104
  - lib/certificate_authority/key_material.rb
66
105
  - lib/certificate_authority/ocsp_handler.rb
67
106
  - lib/certificate_authority/pkcs11_key_material.rb
107
+ - lib/certificate_authority/revocable.rb
68
108
  - lib/certificate_authority/serial_number.rb
69
109
  - lib/certificate_authority/signing_entity.rb
110
+ - lib/certificate_authority/signing_request.rb
111
+ - lib/certificate_authority/validations.rb
112
+ - lib/certificate_authority/version.rb
70
113
  - lib/tasks/certificate_authority.rake
71
- - spec/spec_helper.rb
72
- - spec/units/certificate_authority_spec.rb
73
- - spec/units/certificate_revocation_list_spec.rb
74
- - spec/units/certificate_spec.rb
75
- - spec/units/distinguished_name_spec.rb
76
- - spec/units/extensions_spec.rb
77
- - spec/units/key_material_spec.rb
78
- - spec/units/ocsp_handler_spec.rb
79
- - spec/units/pkcs11_key_material_spec.rb
80
- - spec/units/serial_number_spec.rb
81
- - spec/units/signing_entity_spec.rb
82
- - spec/units/units_helper.rb
83
- homepage: http://github.com/cchandler/certificate_authority
114
+ homepage: https://github.com/cchandler/certificate_authority
84
115
  licenses:
85
116
  - MIT
117
+ metadata:
118
+ homepage_uri: https://github.com/cchandler/certificate_authority
119
+ source_code_uri: https://github.com/cchandler/certificate_authority
86
120
  post_install_message:
87
121
  rdoc_options: []
88
122
  require_paths:
89
123
  - lib
90
124
  required_ruby_version: !ruby/object:Gem::Requirement
91
- none: false
92
125
  requirements:
93
- - - ! '>='
126
+ - - ">="
94
127
  - !ruby/object:Gem::Version
95
- version: '0'
96
- segments:
97
- - 0
98
- hash: -2366790866817530073
128
+ version: '2.4'
99
129
  required_rubygems_version: !ruby/object:Gem::Requirement
100
- none: false
101
130
  requirements:
102
- - - ! '>='
131
+ - - ">="
103
132
  - !ruby/object:Gem::Version
104
133
  version: '0'
105
134
  requirements: []
106
- rubyforge_project:
107
- rubygems_version: 1.8.15
135
+ rubygems_version: 3.1.2
108
136
  signing_key:
109
- specification_version: 3
137
+ specification_version: 4
110
138
  summary: Ruby gem for managing the core functions outlined in RFC-3280 for PKI
111
139
  test_files: []