Class: Inspec::Resources::X509CertificateResource

Inherits:
Object
  • Object
show all
Includes:
FileReader
Defined in:
lib/inspec/resources/x509_certificate.rb

Instance Method Summary collapse

Methods included from FileReader

#read_file_content

Constructor Details

#initialize(opts) ⇒ X509CertificateResource

Returns a new instance of X509CertificateResource.



37
38
39
40
41
42
43
44
45
46
# File 'lib/inspec/resources/x509_certificate.rb', line 37

def initialize(opts)
  @opts = options(opts)
  @issuer = nil
  @parsed_subject = nil
  @parsed_issuer = nil
  @extensions = nil
  @content = @opts[:content]
  @content ||= read_file_content(@opts[:filepath])
  @cert = OpenSSL::X509::Certificate.new @content
end

Instance Method Details

#certificate?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/inspec/resources/x509_certificate.rb', line 55

def certificate?
  !@cert.nil?
end

#emailObject

This property is equivalent to subject.emailAddress



87
88
89
# File 'lib/inspec/resources/x509_certificate.rb', line 87

def email
  subject.emailAddress
end

#extensionsObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/inspec/resources/x509_certificate.rb', line 123

def extensions
  # Return cached Mash if we already parsed the certificate extensions
  return @extensions if @extensions
  # Return the exception class if we failed to instantiate a Cert from file
  return @cert unless @cert.respond_to? :extensions

  # Use a Mash to make it easier to access hash elements in "its('entensions') {should ...}"
  @extensions = Hashie::Mash.new({})
  # Make sure standard extensions exist so we don't get nil for nil:NilClass
  # when the user tests for extensions which aren't present
  %w{
    keyUsage extendedKeyUsage basicConstraints subjectKeyIdentifier
    authorityKeyIdentifier subjectAltName issuerAltName authorityInfoAccess
    crlDistributionPoints issuingDistributionPoint certificatePolicies
    policyConstraints nameConstraints noCheck tlsfeature nsComment
  }.each { |extension| @extensions[extension] ||= [] }
  # Now parse the extensions into the Mash
  extension_array = @cert.extensions.map(&:to_s)
  extension_array.each do |extension|
    kv = extension.split(/ *= */, 2)
    @extensions[kv.first] = kv.last.split(/ *, */)
  end
  @extensions
end

#fetch_purpose(cert_file_or_path) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/inspec/resources/x509_certificate.rb', line 171

def fetch_purpose(cert_file_or_path)
  openssl_utility = check_openssl_or_error

  # The below command is used to view the Certificate purposes
  # The -in argument expects a certificate file or path to certificate file.
  cert_purpose_cmd = "#{openssl_utility} x509 -noout -purpose -in #{cert_file_or_path}"
  cert_purpose = inspec.command(cert_purpose_cmd)

  raise Inspec::Exceptions::ResourceFailed, "Executing #{cert_purpose_cmd} failed: #{cert_purpose.stderr}" if cert_purpose.exit_status.to_i != 0

  cert_purpose.stdout
end

#fingerprintObject



59
60
61
62
63
# File 'lib/inspec/resources/x509_certificate.rb', line 59

def fingerprint
  return if @cert.nil?

  OpenSSL::Digest.new("SHA1", @cert.to_der).to_s
end

#has_purpose?(purpose) ⇒ Boolean

check purpose of the certificate

Returns:

  • (Boolean)


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/inspec/resources/x509_certificate.rb', line 149

def has_purpose?(purpose)
  # If we have the filepath in our options we use the filepath to fetch the purposes.
  # Else, we create a temporary file and write the content to that file.
  # Then, use the temporary file to fetch the purposes.
  # Todo: Check if this can be optimized or improved.

  if @opts[:filepath]
    cert_purpose = fetch_purpose(@opts[:filepath])
  else
    begin
      f = File.open("temporary_certificate.pem", "w")
      f.write(@cert.to_pem)
      f.rewind
      cert_purpose = fetch_purpose("temporary_certificate.pem")
    ensure
      f.close unless f.nil? || f.closed?
      File.delete("temporary_certificate.pem") if File.exist? "temporary_certificate.pem"
    end
  end
  cert_purpose =~ /purpose/ ? true : false
end

#issuerObject



97
98
99
100
101
102
103
104
# File 'lib/inspec/resources/x509_certificate.rb', line 97

def issuer
  return if @cert.nil?
  # Return cached subject if we have already parsed it
  return @parsed_issuer if @parsed_issuer

  # Use a Mash to make it easier to access hash elements in "its('issuer') {should ...}"
  @parsed_issuer = Hashie::Mash.new(Hash[@cert.issuer.to_a.map { |k, v, _| [k, v] }])
end

#issuer_dnObject



91
92
93
94
95
# File 'lib/inspec/resources/x509_certificate.rb', line 91

def issuer_dn
  return if @cert.nil?

  @cert.issuer.to_s
end

#key_lengthObject Also known as: keylength



106
107
108
109
110
# File 'lib/inspec/resources/x509_certificate.rb', line 106

def key_length
  return if @cert.nil?

  @cert.public_key.n.num_bytes * 8
end

#resource_idObject



188
189
190
# File 'lib/inspec/resources/x509_certificate.rb', line 188

def resource_id
  @opts[:filepath] || subject.CN || "x509 Certificate"
end

#serialObject



65
66
67
68
69
# File 'lib/inspec/resources/x509_certificate.rb', line 65

def serial
  return if @cert.nil?

  @cert.serial.to_i
end

#subjectObject



77
78
79
80
81
82
83
84
# File 'lib/inspec/resources/x509_certificate.rb', line 77

def subject
  return if @cert.nil?
  # Return cached subject if we have already parsed it
  return @parsed_subject if @parsed_subject

  # Use a Mash to make it easier to access hash elements in "its('subject') {should ...}"
  @parsed_subject = Hashie::Mash.new(Hash[@cert.subject.to_a.map { |k, v, _| [k, v] }])
end

#subject_alt_namesObject



184
185
186
# File 'lib/inspec/resources/x509_certificate.rb', line 184

def subject_alt_names
  extensions["subjectAltName"]
end

#subject_dnObject



71
72
73
74
75
# File 'lib/inspec/resources/x509_certificate.rb', line 71

def subject_dn
  return if @cert.nil?

  @cert.subject.to_s
end

#to_sObject



192
193
194
195
196
# File 'lib/inspec/resources/x509_certificate.rb', line 192

def to_s
  cert = @opts[:filepath]
  cert ||= subject.CN
  "x509_certificate #{cert}"
end

#valid?Boolean

Returns:

  • (Boolean)


118
119
120
121
# File 'lib/inspec/resources/x509_certificate.rb', line 118

def valid?
  now = Time.now
  certificate? && (now >= not_before && now <= not_after)
end

#validity_in_daysObject



114
115
116
# File 'lib/inspec/resources/x509_certificate.rb', line 114

def validity_in_days
  (not_after - Time.now.utc) / 86400
end