certificate_authority 0.1.2 → 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 -27
  7. data/README.rdoc +184 -89
  8. data/Rakefile +6 -41
  9. data/certificate_authority.gemspec +22 -81
  10. data/lib/certificate_authority.rb +7 -6
  11. data/lib/certificate_authority/certificate.rb +151 -71
  12. data/lib/certificate_authority/certificate_revocation_list.rb +46 -26
  13. data/lib/certificate_authority/core_extensions.rb +46 -0
  14. data/lib/certificate_authority/distinguished_name.rb +84 -17
  15. data/lib/certificate_authority/extensions.rb +483 -96
  16. data/lib/certificate_authority/key_material.rb +75 -21
  17. data/lib/certificate_authority/ocsp_handler.rb +99 -29
  18. data/lib/certificate_authority/pkcs11_key_material.rb +13 -15
  19. data/lib/certificate_authority/revocable.rb +14 -0
  20. data/lib/certificate_authority/serial_number.rb +18 -5
  21. data/lib/certificate_authority/signing_entity.rb +5 -7
  22. data/lib/certificate_authority/signing_request.rb +91 -0
  23. data/lib/certificate_authority/validations.rb +31 -0
  24. data/lib/certificate_authority/version.rb +3 -0
  25. metadata +96 -94
  26. data/VERSION.yml +0 -5
  27. data/spec/spec_helper.rb +0 -4
  28. data/spec/units/certificate_authority_spec.rb +0 -4
  29. data/spec/units/certificate_revocation_list_spec.rb +0 -68
  30. data/spec/units/certificate_spec.rb +0 -351
  31. data/spec/units/distinguished_name_spec.rb +0 -38
  32. data/spec/units/extensions_spec.rb +0 -53
  33. data/spec/units/key_material_spec.rb +0 -96
  34. data/spec/units/ocsp_handler_spec.rb +0 -104
  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
@@ -3,60 +3,114 @@ module CertificateAuthority
3
3
  def public_key
4
4
  raise "Required implementation"
5
5
  end
6
-
6
+
7
7
  def private_key
8
8
  raise "Required implementation"
9
9
  end
10
-
10
+
11
11
  def is_in_hardware?
12
12
  raise "Required implementation"
13
13
  end
14
-
14
+
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
23
-
41
+ include Validations
42
+
24
43
  attr_accessor :keypair
25
44
  attr_accessor :private_key
26
45
  attr_accessor :public_key
27
-
46
+
28
47
  def initialize
29
48
  end
30
-
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?
49
+
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?
39
60
  false
40
61
  end
41
-
62
+
42
63
  def is_in_memory?
43
64
  true
44
65
  end
45
-
46
- def generate_key(modulus_bits=1024)
66
+
67
+ def generate_key(modulus_bits=2048)
47
68
  self.keypair = OpenSSL::PKey::RSA.new(modulus_bits)
48
69
  self.private_key = keypair
49
70
  self.public_key = keypair.public_key
50
71
  self.keypair
51
72
  end
52
-
73
+
53
74
  def private_key
54
75
  @private_key
55
76
  end
56
-
77
+
78
+ def public_key
79
+ @public_key
80
+ end
81
+ end
82
+
83
+ class SigningRequestKeyMaterial
84
+ include KeyMaterial
85
+ include Validations
86
+
87
+ def validate
88
+ errors.add :public_key, "cannot be blank" if public_key.nil?
89
+ end
90
+
91
+ attr_accessor :public_key
92
+
93
+ def initialize(request=nil)
94
+ if request.is_a? OpenSSL::X509::Request
95
+ raise "Invalid certificate signing request" unless request.verify request.public_key
96
+ self.public_key = request.public_key
97
+ end
98
+ end
99
+
100
+ def is_in_hardware?
101
+ false
102
+ end
103
+
104
+ def is_in_memory?
105
+ true
106
+ end
107
+
108
+ def private_key
109
+ nil
110
+ end
111
+
57
112
  def public_key
58
113
  @public_key
59
114
  end
60
-
61
115
  end
62
- end
116
+ end
@@ -1,77 +1,147 @@
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
4
-
71
+ include Validations
72
+
5
73
  attr_accessor :ocsp_request
6
74
  attr_accessor :certificate_ids
7
-
75
+
8
76
  attr_accessor :certificates
9
77
  attr_accessor :parent
10
-
78
+
11
79
  attr_accessor :ocsp_response_body
12
-
13
- validate do |crl|
80
+
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 = {}
20
88
  end
21
-
89
+
22
90
  def <<(cert)
23
91
  self.certificates[cert.serial_number.number.to_s] = cert
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)
29
-
95
+ openssl_request = OpenSSL::OCSP::Request.new(@ocsp_request)
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
33
-
103
+
34
104
  self.certificate_ids
35
105
  end
36
-
37
-
106
+
107
+
38
108
  def response
39
109
  raise "Invalid response" unless valid?
40
-
110
+
41
111
  openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new
42
112
  openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
43
113
  openssl_ocsp_response.copy_nonce(openssl_ocsp_request)
44
-
114
+
45
115
  openssl_ocsp_request.certid.each do |cert_id|
46
116
  certificate = self.certificates[cert_id.serial.to_s]
47
-
48
- openssl_ocsp_response.add_status(cert_id,
49
- OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
117
+
118
+ openssl_ocsp_response.add_status(cert_id,
119
+ OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
50
120
  0, 0, 30, nil)
51
121
  end
52
-
53
-
122
+
123
+
54
124
  openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil)
55
125
  final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response)
56
126
  self.ocsp_response_body = final_response
57
127
  self.ocsp_response_body
58
128
  end
59
-
129
+
60
130
  def to_der
61
131
  raise "No signed OCSP response body available" if self.ocsp_response_body.nil?
62
132
  self.ocsp_response_body.to_der
63
133
  end
64
-
134
+
65
135
  private
66
-
136
+
67
137
  def all_certificates_available
68
138
  openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
69
-
139
+
70
140
  openssl_ocsp_request.certid.each do |cert_id|
71
141
  certificate = self.certificates[cert_id.serial.to_s]
72
142
  errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil?
73
143
  end
74
144
  end
75
-
145
+
76
146
  end
77
- end
147
+ end
@@ -1,45 +1,43 @@
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
9
7
  attr_accessor :pkcs11_lib
10
8
  attr_accessor :openssl_pkcs11_engine_lib
11
9
  attr_accessor :pin
12
-
10
+
13
11
  def initialize(attributes = {})
14
12
  @attributes = attributes
15
13
  initialize_engine
16
14
  end
17
-
15
+
18
16
  def is_in_hardware?
19
17
  true
20
18
  end
21
-
19
+
22
20
  def is_in_memory?
23
21
  false
24
22
  end
25
-
23
+
26
24
  def generate_key(modulus_bits=1024)
27
25
  puts "Key generation is not currently supported in hardware"
28
26
  nil
29
27
  end
30
-
28
+
31
29
  def private_key
32
30
  initialize_engine
33
31
  self.engine.load_private_key(self.token_id)
34
32
  end
35
-
33
+
36
34
  def public_key
37
35
  initialize_engine
38
36
  self.engine.load_public_key(self.token_id)
39
37
  end
40
-
38
+
41
39
  private
42
-
40
+
43
41
  def initialize_engine
44
42
  ## We're going to return early and try again later if params weren't passed in
45
43
  ## at initialization. Any attempt at getting a public/private key will try
@@ -47,7 +45,7 @@ module CertificateAuthority
47
45
  return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil?
48
46
  return self.engine unless self.engine.nil?
49
47
  OpenSSL::Engine.load
50
-
48
+
51
49
  pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e|
52
50
  e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib)
53
51
  e.ctrl_cmd("ID","pkcs11")
@@ -56,10 +54,10 @@ module CertificateAuthority
56
54
  e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == ""
57
55
  e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib)
58
56
  end
59
-
57
+
60
58
  self.engine = pkcs11
61
59
  pkcs11
62
60
  end
63
-
61
+
64
62
  end
65
- end
63
+ end
@@ -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
4
-
5
+ include Validations
6
+ include Revocable
7
+
5
8
  attr_accessor :number
6
-
7
- validates :number, :presence => true, :numericality => {:greater_than => 0}
9
+
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
- end
22
+ end
@@ -1,18 +1,16 @@
1
1
  module CertificateAuthority
2
2
  module SigningEntity
3
-
3
+
4
4
  def self.included(mod)
5
5
  mod.class_eval do
6
- attr_accessor :signing_entity
6
+ attr_accessor :signing_entity
7
7
  end
8
8
  end
9
-
9
+
10
10
  def signing_entity=(val)
11
11
  raise "invalid param" unless [true,false].include?(val)
12
12
  @signing_entity = val
13
13
  end
14
-
15
-
16
-
14
+
17
15
  end
18
- end
16
+ end