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.
- checksums.yaml +7 -0
 - data/.gitignore +6 -0
 - data/.rspec +3 -0
 - data/.travis.yml +11 -0
 - data/Gemfile +2 -8
 - data/Gemfile.lock +71 -31
 - data/README.rdoc +91 -2
 - data/Rakefile +6 -41
 - data/certificate_authority.gemspec +22 -66
 - data/lib/certificate_authority.rb +6 -5
 - data/lib/certificate_authority/certificate.rb +91 -36
 - data/lib/certificate_authority/certificate_revocation_list.rb +34 -14
 - data/lib/certificate_authority/core_extensions.rb +46 -0
 - data/lib/certificate_authority/distinguished_name.rb +64 -16
 - data/lib/certificate_authority/extensions.rb +417 -45
 - data/lib/certificate_authority/key_material.rb +30 -9
 - data/lib/certificate_authority/ocsp_handler.rb +75 -5
 - data/lib/certificate_authority/pkcs11_key_material.rb +0 -2
 - data/lib/certificate_authority/revocable.rb +14 -0
 - data/lib/certificate_authority/serial_number.rb +15 -2
 - data/lib/certificate_authority/signing_request.rb +91 -0
 - data/lib/certificate_authority/validations.rb +31 -0
 - data/lib/certificate_authority/version.rb +3 -0
 - metadata +76 -48
 - data/VERSION.yml +0 -5
 - data/spec/spec_helper.rb +0 -4
 - data/spec/units/certificate_authority_spec.rb +0 -4
 - data/spec/units/certificate_revocation_list_spec.rb +0 -68
 - data/spec/units/certificate_spec.rb +0 -428
 - data/spec/units/distinguished_name_spec.rb +0 -59
 - data/spec/units/extensions_spec.rb +0 -115
 - data/spec/units/key_material_spec.rb +0 -100
 - data/spec/units/ocsp_handler_spec.rb +0 -104
 - data/spec/units/pkcs11_key_material_spec.rb +0 -41
 - data/spec/units/serial_number_spec.rb +0 -20
 - data/spec/units/signing_entity_spec.rb +0 -4
 - data/spec/units/units_helper.rb +0 -1
 
| 
         @@ -1,14 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module CertificateAuthority
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Certificate
         
     | 
| 
       3 
     | 
    
         
            -
                 
     | 
| 
       4 
     | 
    
         
            -
                include  
     | 
| 
      
 3 
     | 
    
         
            +
                include Validations
         
     | 
| 
      
 4 
     | 
    
         
            +
                include Revocable
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
                attr_accessor :distinguished_name
         
     | 
| 
       7 
7 
     | 
    
         
             
                attr_accessor :serial_number
         
     | 
| 
       8 
8 
     | 
    
         
             
                attr_accessor :key_material
         
     | 
| 
       9 
9 
     | 
    
         
             
                attr_accessor :not_before
         
     | 
| 
       10 
10 
     | 
    
         
             
                attr_accessor :not_after
         
     | 
| 
       11 
     | 
    
         
            -
                attr_accessor :revoked_at
         
     | 
| 
       12 
11 
     | 
    
         
             
                attr_accessor :extensions
         
     | 
| 
       13 
12 
     | 
    
         
             
                attr_accessor :openssl_body
         
     | 
| 
       14 
13 
     | 
    
         | 
| 
         @@ -16,7 +15,7 @@ module CertificateAuthority 
     | 
|
| 
       16 
15 
     | 
    
         | 
| 
       17 
16 
     | 
    
         
             
                attr_accessor :parent
         
     | 
| 
       18 
17 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                validate 
     | 
| 
      
 18 
     | 
    
         
            +
                def validate
         
     | 
| 
       20 
19 
     | 
    
         
             
                  errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid?
         
     | 
| 
       21 
20 
     | 
    
         
             
                  errors.add :base, "Key material must be valid" unless key_material.valid?
         
     | 
| 
       22 
21 
     | 
    
         
             
                  errors.add :base, "Serial number must be valid" unless serial_number.valid?
         
     | 
| 
         @@ -33,8 +32,8 @@ module CertificateAuthority 
     | 
|
| 
       33 
32 
     | 
    
         
             
                  self.distinguished_name = DistinguishedName.new
         
     | 
| 
       34 
33 
     | 
    
         
             
                  self.serial_number = SerialNumber.new
         
     | 
| 
       35 
34 
     | 
    
         
             
                  self.key_material = MemoryKeyMaterial.new
         
     | 
| 
       36 
     | 
    
         
            -
                  self.not_before =  
     | 
| 
       37 
     | 
    
         
            -
                  self.not_after =  
     | 
| 
      
 35 
     | 
    
         
            +
                  self.not_before = Date.today.utc
         
     | 
| 
      
 36 
     | 
    
         
            +
                  self.not_after = Date.today.advance(:years => 1).utc
         
     | 
| 
       38 
37 
     | 
    
         
             
                  self.parent = self
         
     | 
| 
       39 
38 
     | 
    
         
             
                  self.extensions = load_extensions()
         
     | 
| 
       40 
39 
     | 
    
         | 
| 
         @@ -42,12 +41,31 @@ module CertificateAuthority 
     | 
|
| 
       42 
41 
     | 
    
         | 
| 
       43 
42 
     | 
    
         
             
                end
         
     | 
| 
       44 
43 
     | 
    
         | 
| 
      
 44 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 45 
     | 
    
         
            +
                def self.from_openssl openssl_cert
         
     | 
| 
      
 46 
     | 
    
         
            +
                  unless openssl_cert.is_a? OpenSSL::X509::Certificate
         
     | 
| 
      
 47 
     | 
    
         
            +
                    raise "Can only construct from an OpenSSL::X509::Certificate"
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  certificate = Certificate.new
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # Only subject, key_material, and body are used for signing
         
     | 
| 
      
 52 
     | 
    
         
            +
                  certificate.distinguished_name = DistinguishedName.from_openssl openssl_cert.subject
         
     | 
| 
      
 53 
     | 
    
         
            +
                  certificate.key_material.public_key = openssl_cert.public_key
         
     | 
| 
      
 54 
     | 
    
         
            +
                  certificate.openssl_body = openssl_cert
         
     | 
| 
      
 55 
     | 
    
         
            +
                  certificate.serial_number.number = openssl_cert.serial.to_i
         
     | 
| 
      
 56 
     | 
    
         
            +
                  certificate.not_before = openssl_cert.not_before
         
     | 
| 
      
 57 
     | 
    
         
            +
                  certificate.not_after = openssl_cert.not_after
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # TODO extensions
         
     | 
| 
      
 59 
     | 
    
         
            +
                  certificate
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
            =end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
       45 
63 
     | 
    
         
             
                def sign!(signing_profile={})
         
     | 
| 
       46 
64 
     | 
    
         
             
                  raise "Invalid certificate #{self.errors.full_messages}" unless valid?
         
     | 
| 
       47 
65 
     | 
    
         
             
                  merge_profile_with_extensions(signing_profile)
         
     | 
| 
       48 
66 
     | 
    
         | 
| 
       49 
67 
     | 
    
         
             
                  openssl_cert = OpenSSL::X509::Certificate.new
         
     | 
| 
       50 
     | 
    
         
            -
                  openssl_cert.version 
     | 
| 
      
 68 
     | 
    
         
            +
                  openssl_cert.version = 2
         
     | 
| 
       51 
69 
     | 
    
         
             
                  openssl_cert.not_before = self.not_before
         
     | 
| 
       52 
70 
     | 
    
         
             
                  openssl_cert.not_after = self.not_after
         
     | 
| 
       53 
71 
     | 
    
         
             
                  openssl_cert.public_key = self.key_material.public_key
         
     | 
| 
         @@ -59,7 +77,6 @@ module CertificateAuthority 
     | 
|
| 
       59 
77 
     | 
    
         | 
| 
       60 
78 
     | 
    
         
             
                  require 'tempfile'
         
     | 
| 
       61 
79 
     | 
    
         
             
                  t = Tempfile.new("bullshit_conf")
         
     | 
| 
       62 
     | 
    
         
            -
                  # t = File.new("/tmp/openssl.cnf")
         
     | 
| 
       63 
80 
     | 
    
         
             
                  ## The config requires a file even though we won't use it
         
     | 
| 
       64 
81 
     | 
    
         
             
                  openssl_config = OpenSSL::Config.new(t.path)
         
     | 
| 
       65 
82 
     | 
    
         | 
| 
         @@ -86,18 +103,19 @@ module CertificateAuthority 
     | 
|
| 
       86 
103 
     | 
    
         
             
                  self.extensions.keys.sort{|a,b| b<=>a}.each do |k|
         
     | 
| 
       87 
104 
     | 
    
         
             
                    e = extensions[k]
         
     | 
| 
       88 
105 
     | 
    
         
             
                    next if e.to_s.nil? or e.to_s == "" ## If the extension returns an empty string we won't include it
         
     | 
| 
       89 
     | 
    
         
            -
                    ext = factory.create_ext(e.openssl_identifier, e.to_s)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    ext = factory.create_ext(e.openssl_identifier, e.to_s, e.critical)
         
     | 
| 
       90 
107 
     | 
    
         
             
                    openssl_cert.add_extension(ext)
         
     | 
| 
       91 
108 
     | 
    
         
             
                  end
         
     | 
| 
       92 
109 
     | 
    
         | 
| 
       93 
110 
     | 
    
         
             
                  if signing_profile["digest"].nil?
         
     | 
| 
       94 
     | 
    
         
            -
                    digest = OpenSSL::Digest 
     | 
| 
      
 111 
     | 
    
         
            +
                    digest = OpenSSL::Digest.new("SHA512")
         
     | 
| 
       95 
112 
     | 
    
         
             
                  else
         
     | 
| 
       96 
     | 
    
         
            -
                    digest = OpenSSL::Digest 
     | 
| 
      
 113 
     | 
    
         
            +
                    digest = OpenSSL::Digest.new(signing_profile["digest"])
         
     | 
| 
       97 
114 
     | 
    
         
             
                  end
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
                   
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  self.openssl_body = openssl_cert.sign(parent.key_material.private_key, digest)
         
     | 
| 
      
 117 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 118 
     | 
    
         
            +
                  t.close! if t # We can get rid of the ridiculous temp file
         
     | 
| 
       101 
119 
     | 
    
         
             
                end
         
     | 
| 
       102 
120 
     | 
    
         | 
| 
       103 
121 
     | 
    
         
             
                def is_signing_entity?
         
     | 
| 
         @@ -117,6 +135,34 @@ module CertificateAuthority 
     | 
|
| 
       117 
135 
     | 
    
         
             
                  self.openssl_body.to_pem
         
     | 
| 
       118 
136 
     | 
    
         
             
                end
         
     | 
| 
       119 
137 
     | 
    
         | 
| 
      
 138 
     | 
    
         
            +
                def to_csr
         
     | 
| 
      
 139 
     | 
    
         
            +
                  csr = SigningRequest.new
         
     | 
| 
      
 140 
     | 
    
         
            +
                  csr.distinguished_name = self.distinguished_name
         
     | 
| 
      
 141 
     | 
    
         
            +
                  csr.key_material = self.key_material
         
     | 
| 
      
 142 
     | 
    
         
            +
                  factory = OpenSSL::X509::ExtensionFactory.new
         
     | 
| 
      
 143 
     | 
    
         
            +
                  exts = []
         
     | 
| 
      
 144 
     | 
    
         
            +
                  self.extensions.keys.each do |k|
         
     | 
| 
      
 145 
     | 
    
         
            +
                    ## Don't copy over key identifiers for CSRs
         
     | 
| 
      
 146 
     | 
    
         
            +
                    next if k == "subjectKeyIdentifier" || k == "authorityKeyIdentifier"
         
     | 
| 
      
 147 
     | 
    
         
            +
                    e = extensions[k]
         
     | 
| 
      
 148 
     | 
    
         
            +
                    ## If the extension returns an empty string we won't include it
         
     | 
| 
      
 149 
     | 
    
         
            +
                    next if e.to_s.nil? or e.to_s == ""
         
     | 
| 
      
 150 
     | 
    
         
            +
                    exts << factory.create_ext(e.openssl_identifier, e.to_s, e.critical)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
                  attrval = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)])
         
     | 
| 
      
 153 
     | 
    
         
            +
                  attrs = [
         
     | 
| 
      
 154 
     | 
    
         
            +
                    OpenSSL::X509::Attribute.new("extReq", attrval),
         
     | 
| 
      
 155 
     | 
    
         
            +
                    OpenSSL::X509::Attribute.new("msExtReq", attrval)
         
     | 
| 
      
 156 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 157 
     | 
    
         
            +
                  csr.attributes = attrs
         
     | 
| 
      
 158 
     | 
    
         
            +
                  csr
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                def self.from_x509_cert(raw_cert)
         
     | 
| 
      
 162 
     | 
    
         
            +
                  openssl_cert = OpenSSL::X509::Certificate.new(raw_cert)
         
     | 
| 
      
 163 
     | 
    
         
            +
                  Certificate.from_openssl(openssl_cert)
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
       120 
166 
     | 
    
         
             
                def is_root_entity?
         
     | 
| 
       121 
167 
     | 
    
         
             
                  self.parent == self && is_signing_entity?
         
     | 
| 
       122 
168 
     | 
    
         
             
                end
         
     | 
| 
         @@ -135,6 +181,16 @@ module CertificateAuthority 
     | 
|
| 
       135 
181 
     | 
    
         
             
                    items = signing_config[k]
         
     | 
| 
       136 
182 
     | 
    
         
             
                    items.keys.each do |profile_item_key|
         
     | 
| 
       137 
183 
     | 
    
         
             
                      if extension.respond_to?("#{profile_item_key}=".to_sym)
         
     | 
| 
      
 184 
     | 
    
         
            +
                        if k == 'subjectAltName' && profile_item_key == 'emails'
         
     | 
| 
      
 185 
     | 
    
         
            +
                          items[profile_item_key].map do |email|
         
     | 
| 
      
 186 
     | 
    
         
            +
                            if email == 'email:copy'
         
     | 
| 
      
 187 
     | 
    
         
            +
                              fail "no email address provided for subject: #{subject.to_x509_name}" unless subject.email_address
         
     | 
| 
      
 188 
     | 
    
         
            +
                              "email:#{subject.email_address}"
         
     | 
| 
      
 189 
     | 
    
         
            +
                            else
         
     | 
| 
      
 190 
     | 
    
         
            +
                              email
         
     | 
| 
      
 191 
     | 
    
         
            +
                            end
         
     | 
| 
      
 192 
     | 
    
         
            +
                          end
         
     | 
| 
      
 193 
     | 
    
         
            +
                        end
         
     | 
| 
       138 
194 
     | 
    
         
             
                        extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] )
         
     | 
| 
       139 
195 
     | 
    
         
             
                      else
         
     | 
| 
       140 
196 
     | 
    
         
             
                        p "Tried applying '#{profile_item_key}' to #{extension.class} but it doesn't respond!"
         
     | 
| 
         @@ -143,30 +199,25 @@ module CertificateAuthority 
     | 
|
| 
       143 
199 
     | 
    
         
             
                  end
         
     | 
| 
       144 
200 
     | 
    
         
             
                end
         
     | 
| 
       145 
201 
     | 
    
         | 
| 
      
 202 
     | 
    
         
            +
                # Enumeration of the extensions. Not the worst option since
         
     | 
| 
      
 203 
     | 
    
         
            +
                # the likelihood of these needing to be updated is low at best.
         
     | 
| 
      
 204 
     | 
    
         
            +
                EXTENSIONS = [
         
     | 
| 
      
 205 
     | 
    
         
            +
                    CertificateAuthority::Extensions::BasicConstraints,
         
     | 
| 
      
 206 
     | 
    
         
            +
                    CertificateAuthority::Extensions::CrlDistributionPoints,
         
     | 
| 
      
 207 
     | 
    
         
            +
                    CertificateAuthority::Extensions::SubjectKeyIdentifier,
         
     | 
| 
      
 208 
     | 
    
         
            +
                    CertificateAuthority::Extensions::AuthorityKeyIdentifier,
         
     | 
| 
      
 209 
     | 
    
         
            +
                    CertificateAuthority::Extensions::AuthorityInfoAccess,
         
     | 
| 
      
 210 
     | 
    
         
            +
                    CertificateAuthority::Extensions::KeyUsage,
         
     | 
| 
      
 211 
     | 
    
         
            +
                    CertificateAuthority::Extensions::ExtendedKeyUsage,
         
     | 
| 
      
 212 
     | 
    
         
            +
                    CertificateAuthority::Extensions::SubjectAlternativeName,
         
     | 
| 
      
 213 
     | 
    
         
            +
                    CertificateAuthority::Extensions::CertificatePolicies
         
     | 
| 
      
 214 
     | 
    
         
            +
                ]
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
       146 
216 
     | 
    
         
             
                def load_extensions
         
     | 
| 
       147 
217 
     | 
    
         
             
                  extension_hash = {}
         
     | 
| 
       148 
218 
     | 
    
         | 
| 
       149 
     | 
    
         
            -
                   
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
                  temp_extensions << basic_constraints
         
     | 
| 
       152 
     | 
    
         
            -
                  crl_distribution_points = CertificateAuthority::Extensions::CrlDistributionPoints.new
         
     | 
| 
       153 
     | 
    
         
            -
                  temp_extensions << crl_distribution_points
         
     | 
| 
       154 
     | 
    
         
            -
                  subject_key_identifier = CertificateAuthority::Extensions::SubjectKeyIdentifier.new
         
     | 
| 
       155 
     | 
    
         
            -
                  temp_extensions << subject_key_identifier
         
     | 
| 
       156 
     | 
    
         
            -
                  authority_key_identifier = CertificateAuthority::Extensions::AuthorityKeyIdentifier.new
         
     | 
| 
       157 
     | 
    
         
            -
                  temp_extensions << authority_key_identifier
         
     | 
| 
       158 
     | 
    
         
            -
                  authority_info_access = CertificateAuthority::Extensions::AuthorityInfoAccess.new
         
     | 
| 
       159 
     | 
    
         
            -
                  temp_extensions << authority_info_access
         
     | 
| 
       160 
     | 
    
         
            -
                  key_usage = CertificateAuthority::Extensions::KeyUsage.new
         
     | 
| 
       161 
     | 
    
         
            -
                  temp_extensions << key_usage
         
     | 
| 
       162 
     | 
    
         
            -
                  extended_key_usage = CertificateAuthority::Extensions::ExtendedKeyUsage.new
         
     | 
| 
       163 
     | 
    
         
            -
                  temp_extensions << extended_key_usage
         
     | 
| 
       164 
     | 
    
         
            -
                  subject_alternative_name = CertificateAuthority::Extensions::SubjectAlternativeName.new
         
     | 
| 
       165 
     | 
    
         
            -
                  temp_extensions << subject_alternative_name
         
     | 
| 
       166 
     | 
    
         
            -
                  certificate_policies = CertificateAuthority::Extensions::CertificatePolicies.new
         
     | 
| 
       167 
     | 
    
         
            -
                  temp_extensions << certificate_policies
         
     | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
                  temp_extensions.each do |extension|
         
     | 
| 
      
 219 
     | 
    
         
            +
                  EXTENSIONS.each do |klass|
         
     | 
| 
      
 220 
     | 
    
         
            +
                    extension = klass.new
         
     | 
| 
       170 
221 
     | 
    
         
             
                    extension_hash[extension.openssl_identifier] = extension
         
     | 
| 
       171 
222 
     | 
    
         
             
                  end
         
     | 
| 
       172 
223 
     | 
    
         | 
| 
         @@ -193,7 +244,11 @@ module CertificateAuthority 
     | 
|
| 
       193 
244 
     | 
    
         
             
                  certificate.serial_number.number = openssl_cert.serial.to_i
         
     | 
| 
       194 
245 
     | 
    
         
             
                  certificate.not_before = openssl_cert.not_before
         
     | 
| 
       195 
246 
     | 
    
         
             
                  certificate.not_after = openssl_cert.not_after
         
     | 
| 
       196 
     | 
    
         
            -
                   
     | 
| 
      
 247 
     | 
    
         
            +
                  EXTENSIONS.each do |klass|
         
     | 
| 
      
 248 
     | 
    
         
            +
                    _,v,c = (openssl_cert.extensions.detect { |e| e.to_a.first == klass::OPENSSL_IDENTIFIER } || []).to_a
         
     | 
| 
      
 249 
     | 
    
         
            +
                    certificate.extensions[klass::OPENSSL_IDENTIFIER] = klass.parse(v, c) if v
         
     | 
| 
      
 250 
     | 
    
         
            +
                  end
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
       197 
252 
     | 
    
         
             
                  certificate
         
     | 
| 
       198 
253 
     | 
    
         
             
                end
         
     | 
| 
       199 
254 
     | 
    
         | 
| 
         @@ -1,36 +1,52 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module CertificateAuthority
         
     | 
| 
       2 
2 
     | 
    
         
             
              class CertificateRevocationList
         
     | 
| 
       3 
     | 
    
         
            -
                include  
     | 
| 
      
 3 
     | 
    
         
            +
                include Validations
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
                attr_accessor :certificates
         
     | 
| 
       6 
6 
     | 
    
         
             
                attr_accessor :parent
         
     | 
| 
       7 
7 
     | 
    
         
             
                attr_accessor :crl_body
         
     | 
| 
       8 
8 
     | 
    
         
             
                attr_accessor :next_update
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_accessor :last_update_skew_seconds
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                validate 
     | 
| 
       11 
     | 
    
         
            -
                  errors.add :next_update, "Next update must be a positive value" if  
     | 
| 
       12 
     | 
    
         
            -
                  errors.add :parent, "A parent entity must be set" if  
     | 
| 
      
 11 
     | 
    
         
            +
                def validate
         
     | 
| 
      
 12 
     | 
    
         
            +
                  errors.add :next_update, "Next update must be a positive value" if self.next_update < 0
         
     | 
| 
      
 13 
     | 
    
         
            +
                  errors.add :parent, "A parent entity must be set" if self.parent.nil?
         
     | 
| 
       13 
14 
     | 
    
         
             
                end
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
                def initialize
         
     | 
| 
       16 
17 
     | 
    
         
             
                  self.certificates = []
         
     | 
| 
       17 
18 
     | 
    
         
             
                  self.next_update = 60 * 60 * 4 # 4 hour default
         
     | 
| 
      
 19 
     | 
    
         
            +
                  self.last_update_skew_seconds = 0
         
     | 
| 
       18 
20 
     | 
    
         
             
                end
         
     | 
| 
       19 
21 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                def <<( 
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
                   
     | 
| 
      
 22 
     | 
    
         
            +
                def <<(revocable)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  case revocable
         
     | 
| 
      
 24 
     | 
    
         
            +
                  when Revocable
         
     | 
| 
      
 25 
     | 
    
         
            +
                    raise "Only revoked entities can be added to a CRL" unless revocable.revoked?
         
     | 
| 
      
 26 
     | 
    
         
            +
                    self.certificates << revocable
         
     | 
| 
      
 27 
     | 
    
         
            +
                  when OpenSSL::X509::Certificate
         
     | 
| 
      
 28 
     | 
    
         
            +
                    raise "Not implemented yet"
         
     | 
| 
      
 29 
     | 
    
         
            +
                  else
         
     | 
| 
      
 30 
     | 
    
         
            +
                    raise "#{revocable.class} cannot be included in a CRL"
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
       23 
32 
     | 
    
         
             
                end
         
     | 
| 
       24 
33 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                def sign!
         
     | 
| 
      
 34 
     | 
    
         
            +
                def sign!(signing_profile={})
         
     | 
| 
       26 
35 
     | 
    
         
             
                  raise "No parent entity has been set!" if self.parent.nil?
         
     | 
| 
       27 
36 
     | 
    
         
             
                  raise "Invalid CRL" unless self.valid?
         
     | 
| 
       28 
37 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                  revocations = self.certificates.collect do | 
     | 
| 
      
 38 
     | 
    
         
            +
                  revocations = self.certificates.collect do |revocable|
         
     | 
| 
       30 
39 
     | 
    
         
             
                    revocation = OpenSSL::X509::Revoked.new
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                     
     | 
| 
       33 
     | 
    
         
            -
                     
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    ## We really just need a serial number, now we have to dig it out
         
     | 
| 
      
 42 
     | 
    
         
            +
                    case revocable
         
     | 
| 
      
 43 
     | 
    
         
            +
                    when Certificate
         
     | 
| 
      
 44 
     | 
    
         
            +
                      x509_cert = OpenSSL::X509::Certificate.new(revocable.to_pem)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      revocation.serial = x509_cert.serial
         
     | 
| 
      
 46 
     | 
    
         
            +
                    when SerialNumber
         
     | 
| 
      
 47 
     | 
    
         
            +
                      revocation.serial = revocable.number
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    revocation.time = revocable.revoked_at
         
     | 
| 
       34 
50 
     | 
    
         
             
                    revocation
         
     | 
| 
       35 
51 
     | 
    
         
             
                  end
         
     | 
| 
       36 
52 
     | 
    
         | 
| 
         @@ -40,11 +56,15 @@ module CertificateAuthority 
     | 
|
| 
       40 
56 
     | 
    
         
             
                  end
         
     | 
| 
       41 
57 
     | 
    
         | 
| 
       42 
58 
     | 
    
         
             
                  crl.version = 1
         
     | 
| 
       43 
     | 
    
         
            -
                  crl.last_update = Time.now
         
     | 
| 
      
 59 
     | 
    
         
            +
                  crl.last_update = Time.now - self.last_update_skew_seconds
         
     | 
| 
       44 
60 
     | 
    
         
             
                  crl.next_update = Time.now + self.next_update
         
     | 
| 
       45 
61 
     | 
    
         | 
| 
       46 
62 
     | 
    
         
             
                  signing_cert = OpenSSL::X509::Certificate.new(self.parent.to_pem)
         
     | 
| 
       47 
     | 
    
         
            -
                   
     | 
| 
      
 63 
     | 
    
         
            +
                  if signing_profile["digest"].nil?
         
     | 
| 
      
 64 
     | 
    
         
            +
                    digest = OpenSSL::Digest.new("SHA512")
         
     | 
| 
      
 65 
     | 
    
         
            +
                  else
         
     | 
| 
      
 66 
     | 
    
         
            +
                    digest = OpenSSL::Digest.new(signing_profile["digest"])
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
       48 
68 
     | 
    
         
             
                  crl.issuer = signing_cert.subject
         
     | 
| 
       49 
69 
     | 
    
         
             
                  self.crl_body = crl.sign(self.parent.key_material.private_key, digest)
         
     | 
| 
       50 
70 
     | 
    
         | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # ActiveSupport has these modifications. Now that we don't use ActiveSupport,
         
     | 
| 
      
 3 
     | 
    
         
            +
            # these are added here as a kindness.
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require 'date'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            unless nil.respond_to?(:blank?)
         
     | 
| 
      
 9 
     | 
    
         
            +
              class NilClass
         
     | 
| 
      
 10 
     | 
    
         
            +
                def blank?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  true
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            unless String.respond_to?(:blank?)
         
     | 
| 
      
 17 
     | 
    
         
            +
              class String
         
     | 
| 
      
 18 
     | 
    
         
            +
                def blank?
         
     | 
| 
      
 19 
     | 
    
         
            +
                  self.empty?
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            class Date
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def today
         
     | 
| 
      
 27 
     | 
    
         
            +
                t = Time.now.utc
         
     | 
| 
      
 28 
     | 
    
         
            +
                Date.new(t.year, t.month, t.day)
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def utc
         
     | 
| 
      
 32 
     | 
    
         
            +
                self.to_datetime.to_time.utc
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              unless Date.respond_to?(:advance)
         
     | 
| 
      
 36 
     | 
    
         
            +
                def advance(options)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  options = options.dup
         
     | 
| 
      
 38 
     | 
    
         
            +
                  d = self
         
     | 
| 
      
 39 
     | 
    
         
            +
                  d = d >> options.delete(:years) * 12 if options[:years]
         
     | 
| 
      
 40 
     | 
    
         
            +
                  d = d >> options.delete(:months)     if options[:months]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  d = d +  options.delete(:weeks) * 7  if options[:weeks]
         
     | 
| 
      
 42 
     | 
    
         
            +
                  d = d +  options.delete(:days)       if options[:days]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  d
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,58 +1,106 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module CertificateAuthority
         
     | 
| 
       2 
2 
     | 
    
         
             
              class DistinguishedName
         
     | 
| 
       3 
     | 
    
         
            -
                include  
     | 
| 
      
 3 
     | 
    
         
            +
                include Validations
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
                 
     | 
| 
      
 5 
     | 
    
         
            +
                def validate
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if self.common_name.nil? || self.common_name.empty?
         
     | 
| 
      
 7 
     | 
    
         
            +
                    errors.add :common_name, 'cannot be blank'
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
       6 
10 
     | 
    
         | 
| 
       7 
11 
     | 
    
         
             
                attr_accessor :common_name
         
     | 
| 
       8 
12 
     | 
    
         
             
                alias :cn :common_name
         
     | 
| 
      
 13 
     | 
    
         
            +
                alias :cn= :common_name=
         
     | 
| 
       9 
14 
     | 
    
         | 
| 
       10 
15 
     | 
    
         
             
                attr_accessor :locality
         
     | 
| 
       11 
16 
     | 
    
         
             
                alias :l :locality
         
     | 
| 
      
 17 
     | 
    
         
            +
                alias :l= :locality=
         
     | 
| 
       12 
18 
     | 
    
         | 
| 
       13 
19 
     | 
    
         
             
                attr_accessor :state
         
     | 
| 
       14 
20 
     | 
    
         
             
                alias :s :state
         
     | 
| 
      
 21 
     | 
    
         
            +
                alias :st= :state=
         
     | 
| 
       15 
22 
     | 
    
         | 
| 
       16 
23 
     | 
    
         
             
                attr_accessor :country
         
     | 
| 
       17 
24 
     | 
    
         
             
                alias :c :country
         
     | 
| 
      
 25 
     | 
    
         
            +
                alias :c= :country=
         
     | 
| 
       18 
26 
     | 
    
         | 
| 
       19 
27 
     | 
    
         
             
                attr_accessor :organization
         
     | 
| 
       20 
28 
     | 
    
         
             
                alias :o :organization
         
     | 
| 
      
 29 
     | 
    
         
            +
                alias :o= :organization=
         
     | 
| 
       21 
30 
     | 
    
         | 
| 
       22 
31 
     | 
    
         
             
                attr_accessor :organizational_unit
         
     | 
| 
       23 
32 
     | 
    
         
             
                alias :ou :organizational_unit
         
     | 
| 
      
 33 
     | 
    
         
            +
                alias :ou= :organizational_unit=
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                attr_accessor :email_address
         
     | 
| 
      
 36 
     | 
    
         
            +
                alias :emailAddress :email_address
         
     | 
| 
      
 37 
     | 
    
         
            +
                alias :emailAddress= :email_address=
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                attr_accessor :serial_number
         
     | 
| 
      
 40 
     | 
    
         
            +
                alias :serialNumber :serial_number
         
     | 
| 
      
 41 
     | 
    
         
            +
                alias :serialNumber= :serial_number=
         
     | 
| 
       24 
42 
     | 
    
         | 
| 
       25 
43 
     | 
    
         
             
                def to_x509_name
         
     | 
| 
       26 
44 
     | 
    
         
             
                  raise "Invalid Distinguished Name" unless valid?
         
     | 
| 
       27 
45 
     | 
    
         | 
| 
       28 
46 
     | 
    
         
             
                  # NB: the capitalization in the strings counts
         
     | 
| 
       29 
47 
     | 
    
         
             
                  name = OpenSSL::X509::Name.new
         
     | 
| 
       30 
     | 
    
         
            -
                  name.add_entry(" 
     | 
| 
       31 
     | 
    
         
            -
                  name.add_entry(" 
     | 
| 
       32 
     | 
    
         
            -
                  name.add_entry("OU", organizational_unit) unless organizational_unit.blank?
         
     | 
| 
      
 48 
     | 
    
         
            +
                  name.add_entry("serialNumber", serial_number) unless serial_number.blank?
         
     | 
| 
      
 49 
     | 
    
         
            +
                  name.add_entry("C", country) unless country.blank?
         
     | 
| 
       33 
50 
     | 
    
         
             
                  name.add_entry("ST", state) unless state.blank?
         
     | 
| 
       34 
51 
     | 
    
         
             
                  name.add_entry("L", locality) unless locality.blank?
         
     | 
| 
       35 
     | 
    
         
            -
                  name.add_entry(" 
     | 
| 
      
 52 
     | 
    
         
            +
                  name.add_entry("O", organization) unless organization.blank?
         
     | 
| 
      
 53 
     | 
    
         
            +
                  name.add_entry("OU", organizational_unit) unless organizational_unit.blank?
         
     | 
| 
      
 54 
     | 
    
         
            +
                  name.add_entry("CN", common_name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  name.add_entry("emailAddress", email_address) unless email_address.blank?
         
     | 
| 
       36 
56 
     | 
    
         
             
                  name
         
     | 
| 
       37 
57 
     | 
    
         
             
                end
         
     | 
| 
       38 
58 
     | 
    
         | 
| 
      
 59 
     | 
    
         
            +
                def ==(other)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # Use the established OpenSSL comparison
         
     | 
| 
      
 61 
     | 
    
         
            +
                  self.to_x509_name() == other.to_x509_name()
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
       39 
64 
     | 
    
         
             
                def self.from_openssl openssl_name
         
     | 
| 
       40 
65 
     | 
    
         
             
                  unless openssl_name.is_a? OpenSSL::X509::Name
         
     | 
| 
       41 
66 
     | 
    
         
             
                    raise "Argument must be a OpenSSL::X509::Name"
         
     | 
| 
       42 
67 
     | 
    
         
             
                  end
         
     | 
| 
       43 
68 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 69 
     | 
    
         
            +
                  WrappedDistinguishedName.new(openssl_name)
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              ## This is a significantly more complicated case. It's possible that
         
     | 
| 
      
 74 
     | 
    
         
            +
              ## generically handled certificates will include custom OIDs in the
         
     | 
| 
      
 75 
     | 
    
         
            +
              ## subject.
         
     | 
| 
      
 76 
     | 
    
         
            +
              class WrappedDistinguishedName < DistinguishedName
         
     | 
| 
      
 77 
     | 
    
         
            +
                attr_accessor :x509_name
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def initialize(x509_name)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @x509_name = x509_name
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  subject = @x509_name.to_a
         
     | 
| 
      
 83 
     | 
    
         
            +
                  subject.each do |element|
         
     | 
| 
      
 84 
     | 
    
         
            +
                    field = element[0].downcase
         
     | 
| 
      
 85 
     | 
    
         
            +
                    value = element[1]
         
     | 
| 
      
 86 
     | 
    
         
            +
                    #type = element[2] ## -not used
         
     | 
| 
      
 87 
     | 
    
         
            +
                    method_sym = "#{field}=".to_sym
         
     | 
| 
      
 88 
     | 
    
         
            +
                    if self.respond_to?(method_sym)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      self.send("#{field}=",value)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    else
         
     | 
| 
      
 91 
     | 
    
         
            +
                      ## Custom OID
         
     | 
| 
      
 92 
     | 
    
         
            +
                      @custom_oids = true
         
     | 
| 
       53 
93 
     | 
    
         
             
                    end
         
     | 
| 
       54 
94 
     | 
    
         
             
                  end
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def to_x509_name
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @x509_name
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                def custom_oids?
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @custom_oids
         
     | 
| 
       56 
104 
     | 
    
         
             
                end
         
     | 
| 
       57 
105 
     | 
    
         
             
              end
         
     | 
| 
       58 
106 
     | 
    
         
             
            end
         
     |