certificate_authority 0.1.2 → 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 -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