certificate_authority 0.1.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []