Merge pull request #1567 from chef/chris-rock/x509

x509_certificate and key_rsa resource
This commit is contained in:
Adam Leff 2017-03-22 07:30:19 -04:00 committed by GitHub
commit 730de65e36
15 changed files with 636 additions and 0 deletions

70
docs/resources/key_rsa.md Normal file
View file

@ -0,0 +1,70 @@
---
title: The key_rsa Resource
---
# key_rsa
Use the `key_rsa` InSpec audit resource to test RSA public/private keypairs.
This resource is mainly useful when used in conjunction with the x509_certificate resource but it can also be used for checking SSH keys.
## Syntax
An `key_rsa` resource block declares a `key file` to be tested.
describe key_rsa('mycertificate.key') do
it { should be_private }
it { should be_public }
its('public_key') { should match "-----BEGIN PUBLIC KEY-----\n3597459df9f3982" }
its('key_length') { should eq 2048 }
end
You can use an optional passphrase with `key_rsa`
describe key_rsa('mycertificate.key', 'passphrase') do
it { should be_private }
end
## Supported Properties
### public?
To verify if a key is public use the following:
describe key_rsa('/etc/pki/www.mywebsite.com.key') do
it { should be_public }
end
### public_key (String)
The `public_key` property returns the public part of the RSA key pair
describe key_rsa('/etc/pki/www.mywebsite.com.key') do
its('public_key') { should match "-----BEGIN PUBLIC KEY-----\n3597459df9f3982......" }
end
### private?
This property verifies that the key includes a private key:
describe key_rsa('/etc/pki/www.mywebsite.com.key') do
it { should be_private }
end
### private_key (String)
The `private_key` property returns the private key or the RSA key pair.
describe key_rsa('/etc/pki/www.mywebsite.com.key') do
its('private_key') { should match "-----BEGIN RSA PRIVATE KEY-----\nMIIJJwIBAAK......" }
end
### key_length
The `key_length` property allows testing the number of bits in the key pair.
describe key_rsa('/etc/pki/www.mywebsite.com.key') do
its('key_length') { should eq 2048 }
end

View file

@ -0,0 +1,146 @@
---
title: The x509_certificate Resource
---
# x509_certificate
Use the `x509_certificate` InSpec audit resource to test the fields and validity of an x.509 certificate.
X.509 certificates use public/private key pairs to sign and encrypt documents
or communications over a network. They may also be used for authentication.
Examples include SSL certificates, S/MIME certificates and VPN authentication
certificates.
## Syntax
An `x509_certificate` resource block declares a certificate `key file` to be tested.
describe x509_certificate('mycertificate.pem') do
its('validity_in_days') { should be > 30 }
end
## Supported Properties
### subject.XX
`subject` property makes it easier to access individual subject elements.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('subject.CN') { should eq "www.mywebsite.com" }
end
### subject_dn (String)
The `subject_dn` string returns the distinguished name of the subject field. It contains several fields separated by forward slashes. The field identifiers are the same ones used by OpenSSL to generate CSR's and certs. Use `subject.XX` instead to access the parsed version.
e.g. `/C=US/L=Seattle/O=Chef Software Inc/OU=Chefs/CN=Richard Nixon`
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('subject_dn') { should match "CN=www.mywebsite.com" }
end
### issuer.XX
`issuer` makes it easier to access individual issuer elements.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('issuer.CN') { should eq "Acme Trust CA" }
end
### issuer_dn (String)
The `issuer_dn` is the distinguished name from a CA (certificate authority) during the
certificate signing process. It describes which authority is guaranteeing the
identity of our certificate.
e.g. `/C=US/L=Seattle/CN=Acme Trust CA/emailAddress=support@acmetrust.org`
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('issuer_cn') { should match "CN=Acme Trust CA" }
end
### public_key (String)
The `public_key` property returns a base64 encoded public key in PEM format.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('public_key') { should match "-----BEGIN PUBLIC KEY-----\nblah blah blah..." }
end
### key_length (Integer)
The `key_length` property calculates the number of bits in the public key.
More bits increase security, but at the cost of speed and in extreme cases, compatibility.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('key_length') { should be 2048 }
end
### signature_algorithm (String)
The `signature_algorithm` property describes which hash function was used by the CA to
sign the certificate.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('signature_algorithm') { should be 'sha256WithRSAEncryption' }
end
### validity_in_days (Float)
The `validity_in_days` property can be used to check that certificates are not in
danger of expiring soon.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('validity_in_days') { should be > 30 }
end
### not_before and not_after (Time)
The `not_before` and `not_after` properties expose the start and end dates of certificate
validity. They are exposed as ruby Time class so that date arithmetic can be easily performed.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('not_before') { should be <= Time.utc.now }
its('not_after') { should be >= Time.utc.now }
end
### serial (Integer)
The `serial` property exposes the serial number of the certificate. The serial number is set by the CA during the signing process and should be unique within that CA.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('serial') { should eq 9623283588743302433 }
end
### version (Integer)
The `version` property exposes the certificate version.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('version') { should eq 2 }
end
### extensions (Hash)
The `extensions` hash property is mainly used to determine what the certificate can be used for.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
# Check what extension categories we have
its('extensions') { should include 'keyUsage' }
its('extensions') { should include 'extendedKeyUsage' }
its('extensions') { should include 'subjectAltName' }
# Check examples of basic 'keyUsage'
its('extensions.keyUsage') { should include 'Digital Signature' }
its('extensions.keyUsage') { should include 'Non Repudiation' }
its('extensions.keyUsage') { should include 'Data Encipherment' }
# Check examples of newer 'extendedKeyUsage'
its('extensions.extendedKeyUsage') { should include 'TLS Web Server Authentication' }
its('extensions.extendedKeyUsage') { should include 'Code Signing' }
# Check examples of 'subjectAltName'
its('extensions.subjectAltName') { should include 'email:support@chef.io' }
end

View file

@ -95,6 +95,7 @@ require 'resources/iptables'
require 'resources/json'
require 'resources/kernel_module'
require 'resources/kernel_parameter'
require 'resources/key_rsa'
require 'resources/limits_conf'
require 'resources/login_def'
require 'resources/mount'
@ -131,6 +132,7 @@ require 'resources/windows_feature'
require 'resources/windows_task'
require 'resources/xinetd'
require 'resources/wmi'
require 'resources/x509_certificate'
require 'resources/yum'
require 'resources/zfs_dataset'
require 'resources/zfs_pool'

67
lib/resources/key_rsa.rb Normal file
View file

@ -0,0 +1,67 @@
# encoding: utf-8
# author: Richard Nixon
# author: Christoph Hartmann
require 'openssl'
require 'hashie/mash'
module Inspec::Resources
class RsaKey < Inspec.resource(1)
name 'key_rsa'
desc 'public/private RSA key pair test'
example "
describe rsa_key('/etc/pki/www.mywebsite.com.key') do
its('public_key') { should match /BEGIN RSA PUBLIC KEY/ }
end
describe rsa_key('/etc/pki/www.mywebsite.com.key', 'passphrase') do
it { should be_private }
it { should be_public }
end
"
def initialize(keypath, passphrase = nil)
@key_path = keypath
@key_file = inspec.file(@key_path)
@key = nil
@passphrase = passphrase
return skip_resource "Unable to find key file #{@key_path}" unless @key_file.exist?
begin
@key = OpenSSL::PKey.read(@key_file.content, @passphrase)
rescue OpenSSL::PKey::RSAError => _
return skip_resource "Unable to load key file #{@key_path}"
end
end
def public?
return if @key.nil?
@key.public?
end
def public_key
return if @key.nil?
@key.public_key.to_s
end
def private?
return if @key.nil?
@key.private?
end
def private_key
return if @key.nil?
@key.to_s
end
def key_length
return if @key.nil?
@key.public_key.n.num_bytes * 8
end
def to_s
"rsa_key #{@key_path}"
end
end
end

View file

@ -0,0 +1,143 @@
# encoding: utf-8
# author: Richard Nixon
# author: Christoph Hartmann
require 'openssl'
require 'hashie/mash'
module Inspec::Resources
class X509CertificateResource < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
name 'x509_certificate'
desc 'Used to test x.509 certificates'
example "
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
its('subject') { should match /CN=My Website/ }
its('validity_in_days') { should be > 30 }
end
describe x509_certificate('trials/x509/cert.pem') do
it { should be_certificate }
it { should be_valid }
its('fingerprint') { should eq '62b137bdf427e7273dc2e487877b3033e4c8ce17' }
its('signature_algorithm') { should eq 'sha1WithRSAEncryption' }
its('validity_in_days') { should_not be < 100 }
its('validity_in_days') { should be >= 100 }
its('subject_dn') { should eq '/C=DE/ST=Berlin/L=Berlin/O=InSpec/OU=Chef Software, Inc/CN=inspec.io/emailAddress=support@chef.io' }
its('subject.C') { should eq 'DE' }
its('subject.emailAddress') { should_not be_empty }
its('subject.emailAddress') { should eq 'support@chef.io' }
its('issuer_dn') { should eq '/C=DE/ST=Berlin/L=Berlin/O=InSpec/OU=Chef Software, Inc/CN=inspec.io/emailAddress=support@chef.io' }
its('key_length') { should be >= 2048 }
its('extensions.subjectKeyIdentifier') { should cmp 'A5:16:0B:12:F4:48:0F:06:6C:32:29:67:98:12:DF:3D:0D:75:9D:5C' }
end
"
# @see https://tools.ietf.org/html/rfc5280#page-23
def initialize(filename)
@certpath = filename
@issuer = nil
@parsed_subject = nil
@parsed_issuer = nil
@extensions = nil
file = inspec.file(@certpath)
return skip_resource "Unable to find certificate file #{@certpath}" unless file.exist?
begin
@cert = OpenSSL::X509::Certificate.new file.content
rescue OpenSSL::X509::CertificateError
@cert = nil
return skip_resource "Unable to load certificate #{@certpath}"
end
end
# Forward these methods directly to OpenSSL::X509::Certificate instance
%w{version not_before not_after signature_algorithm public_key }.each do |m|
define_method m.to_sym do |*args|
@cert.method(m.to_sym).call(*args)
end
end
def certificate?
!@cert.nil?
end
def fingerprint
return if @cert.nil?
OpenSSL::Digest::SHA1.new(@cert.to_der).to_s
end
def serial
return if @cert.nil?
@cert.serial.to_i
end
def subject_dn
return if @cert.nil?
@cert.subject.to_s
end
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
def issuer_dn
return if @cert.nil?
@cert.issuer.to_s
end
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
def key_length
return if @cert.nil?
@cert.public_key.n.num_bytes * 8
end
def validity_in_days
(not_after - Time.now.utc) / 86400
end
def valid?
now = Time.now
certificate? && (now >= not_before && now <= not_after)
end
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
def to_s
"x509_certificate #{@certpath}"
end
end
end

View file

@ -12,3 +12,4 @@ depends 'postgresql'
depends 'httpd', '~> 0.2'
depends 'windows'
depends 'ssh-hardening'
depends 'openssl'

View file

@ -18,6 +18,7 @@ include_recipe('os_prepare::package')
include_recipe('os_prepare::registry_key')
include_recipe('os_prepare::iis_site')
include_recipe('os_prepare::iptables') unless node['osprepare']['docker']
include_recipe('os_prepare::x509')
# config file parsing
include_recipe('os_prepare::json_yaml_csv_ini')

View file

@ -0,0 +1,15 @@
if node['platform_family'] != 'windows'
openssl_x509 '/tmp/mycert.pem' do
common_name 'www.f00bar.com'
org 'Foo Bar'
org_unit 'Lab'
country 'US'
expire 360
end
openssl_rsa_key '/tmp/server.key' do
key_length 2048
end
end

View file

@ -134,6 +134,10 @@ class MockLoader
'/etc/xinetd.d/echo' => mockfile.call('xinetd.d_echo'),
'/etc/sysctl.conf' => mockfile.call('sysctl.conf'),
'/etc/postgresql/9.4/main/postgresql.conf' => mockfile.call('postgresql.conf'),
# Test certificate/key for x509_certificate using RSA keys in PEM format
'test_certificate.rsa.crt.pem' => mockfile.call('test_certificate.rsa.crt.pem'),
'test_certificate.rsa.key.pem' => mockfile.call('test_certificate.rsa.key.pem'),
'test_ca_public.key.pem' => mockfile.call('test_ca_public.key.pem'),
}
# create all mock commands

View file

@ -0,0 +1,24 @@
# encoding: utf-8
if os.windows?
STDERR.puts "\033[1;33mTODO: Not running #{__FILE__} because we are not on Linux.\033[0m"
return
end
describe x509_certificate('/tmp/mycert.pem') do
it { should be_certificate }
it { should be_valid }
its('signature_algorithm') { should eq 'sha256WithRSAEncryption' }
its('validity_in_days') { should_not be < 100 }
its('validity_in_days') { should be >= 100 }
its('subject_dn') { should eq '/C=US/O=Foo Bar/OU=Lab/CN=www.f00bar.com' }
its('subject.C') { should eq 'US' }
its('issuer_dn') { should eq '/C=US/O=Foo Bar/OU=Lab/CN=www.f00bar.com' }
its('key_length') { should be >= 2048 }
end
describe key_rsa('/tmp/server.key') do
it { should be_private }
it { should be_public }
its('key_length') { should eq 2048 }
end

View file

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2Ceiaf2E+xnv2f0IHl8
SAd9j0CK+zuA1BbMStBaslkAD+XHTZ5vvC0NKyiuEFu/WpPegOavydpMaBD+l01Z
c3pHZxj9Ayl6uDCv27SKYTYmoiRybb1wFGMBuzb8Q2hseJH+XCx8rd1Kjn1VfAeC
5n36QoMJ8qghdnTSFSqVMP8IAz8hI0xKe89FRf5YbqLhYUmk+PMili0XxkwG723D
NXF62spg4runWmk//nsneGB5gqKLEHKxlgFDTYOrG2SgAcVtz5urAI+o24ikzhiN
nlZ5fmHKbpxn0wEHxVqQWieAx1gj5BLk8TUDZUdkWla8q9mJ3ZhxXm3H7OJ9vI65
gwIDAQAB
-----END PUBLIC KEY-----

View file

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEGjCCAwKgAwIBAgIBJTANBgkqhkiG9w0BAQsFADCBljEXMBUGA1UEAwwOSW5z
cGVjIFRlc3QgQ0ExGjAYBgNVBAoMEUNoZWYgU29mdHdhcmUgSW5jMRMwEQYDVQQL
DApDZXJ0aWZpZXJzMQswCQYDVQQIDAJXQTELMAkGA1UEBhMCVVMxEDAOBgNVBAcM
B1NlYXR0bGUxHjAcBgkqhkiG9w0BCQEWD3N1cHBvcnRAY2hlZi5pbzAeFw0xNzAz
MDEwMTI4NTdaFw0xODAzMDEwMTI4NTdaMIGjMSAwHgYDVQQDDBdJbnNwZWMgVGVz
dCBDZXJ0aWZpY2F0ZTEaMBgGA1UECgwRQ2hlZiBTb2Z0d2FyZSBJbmMxFzAVBgNV
BAsMDkluc3BlYyBUZXN0ZXJzMQswCQYDVQQIDAJXQTELMAkGA1UEBhMCVVMxEDAO
BgNVBAcMB1NlYXR0bGUxHjAcBgkqhkiG9w0BCQEWD3N1cHBvcnRAY2hlZi5pbzCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMYtU6eHT0PhlPkYqbILlFk+
dGun7AQqXvkE9GHVv+xhupMp0c0fn80NKtSw8aYKgB/ngyFhABWKEz3PIWgI2bwG
J+yZV1EKXqCu33NgT4GANJa99xHtB/Z6nmZZ8rUPvxFVKqg8etXIHltwEQCtSDg8
Ibs4rk4u1zbvh08d33h+KIavxaaQduj2HyaNYwpre2MR8ZegBq2KxF+c7m0EdALT
wCc4NP2qgx+RiRyRE45aCMYfyi5xpWxtwycpRVPrz8b57NUyjNHXs4PFHn13q0bc
QwMEose7DXLV8GtMU6HOjvnv0rTT0dHi7yC0vIkfPX7jC+e2eABhVjDXak2KI6sC
AwEAAaNkMGIwDgYDVR0PAQH/BAQDAgTQMDQGA1UdJQEB/wQqMCgGCCsGAQUFBwME
BggrBgEFBQcDAgYIKwYBBQUHAwEGCCsGAQUFBwMDMBoGA1UdEQQTMBGBD3N1cHBv
cnRAY2hlZi5pbzANBgkqhkiG9w0BAQsFAAOCAQEAOYrs1VK5AA3kL39wh9PVHbqG
d54YnurpDVPzHBDAt6BS5naMQ5hFPlT9Mb9ksyh86B7m/MtVUCkRmUiKk12Pv3t8
bs05NRYy6efAAAe4lvmQaAxmPoRdHRWQkoX7BM7o6GdM7sJN9Wyz8iKlKwcpg9KL
OsBJ37TTkkMElr/yGFgVmm+uXWLsj5JqOYL+hNXkZBY42bMgcDodiOe7kCoPO3Vm
h0Ygd9TBqMoMSxQNVeD6hgsoej47XIs1K16LU31bExsFV4YW5bLezJNN9U0FW+il
LoDQcg6wxt8BvVuH7qxtzdu4uKDvLebvWQDeATjjdAu9m4t0AMtrkVk7dzDLUQ==
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAxi1Tp4dPQ+GU+RipsguUWT50a6fsBCpe+QT0YdW/7GG6kynR
zR+fzQ0q1LDxpgqAH+eDIWEAFYoTPc8haAjZvAYn7JlXUQpeoK7fc2BPgYA0lr33
Ee0H9nqeZlnytQ+/EVUqqDx61cgeW3ARAK1IODwhuziuTi7XNu+HTx3feH4ohq/F
ppB26PYfJo1jCmt7YxHxl6AGrYrEX5zubQR0AtPAJzg0/aqDH5GJHJETjloIxh/K
LnGlbG3DJylFU+vPxvns1TKM0dezg8UefXerRtxDAwSix7sNctXwa0xToc6O+e/S
tNPR0eLvILS8iR89fuML57Z4AGFWMNdqTYojqwIDAQABAoIBAEhPd6u0Mpb5M6tk
dV7S3NFneYFipzqp3zeLuEQOg1YUHsjdxIDNHjjqqgsreTD1ueRqTC2cwDQbyoOO
FYlpWVFDCcRJ+1NFrluBE2V86eW9yvKJ5CH1VCd6fFuqTGYGldgUNFlooAVrXLBO
htWxZJ2oS0KOHwPGEZ8o7T1QEB9dfGpqROJJ444gnAWZ5iUTDqXwy3+pQlPUdAP3
A2VpBG/QyT6UKtUpCcAdkzENJpy8Z1BEIigLp+24fYtLRaIme8yEm/JrLneWqgYr
fNcKqh7z2MlNElOflyaT5381Kj3GgYM7qrPz6SQ92VEmUemg4xhMAsO9lMLEoUly
aT5InUkCgYEA75OXkqY7c12txpZxkRR49dTWc6V97RuSEfkJuTuJQe/bQ7vhKWFt
uRhpBjc6czfr3KcwLNz9axQpnzQmhVfJ7GyYH8VLwaAHgwEAiTcdOf3Xp0fdGe1N
wLcaeSxH+goMff410YWbtJGlyf5AetKl6XKsjeWa/+3vx1JcmnufYY0CgYEA08Mz
nNycP+74X9yfHWRMFi/DNhX3jnOc897yc1WC7TUj3+l9KcOdEz21uVOUZod+ThJJ
vUhmMXzBs7yeQiiv80Wm4ONrPjo0dgttsob3VFo419c5x9uzqJWhg9LqryQRFJKL
GskKIhwN8ErZil8Tm5yg6ihX8xh6L6SesGyR4BcCgYEA2pUci85jG4TzEecdQrMd
EZ3Y87agR/8JrKA9QOWS+7fto8T9UBX2WBRvbh5hk9IHvlBD4grWpCXHO9wG8U4B
i1YhDYui7MwnTl1Rsd+5KLnzUkp87jTW5eepnbjLCtS0RRf03m86eusQClWRWv5q
Ja5cxTIh0zOxu3fnyYLVDdkCgYAMzvXEOyPISjADvFhzcqmXffQUxWdf2mZX6dhI
WZe9uUUeOgU0DXzmuQjQ2NlVCkT9e+Wx6TslKyKcOIBqCAP8du4NFDRcYzDhIvfT
oI49L+fYRlBcYlGPlN1cF9nSFiBiWirHx/kw7vl4204lLHMHKoYhI6eOMKDTWOWw
TiDUqQKBgG+xIdvtIxpjtqddJwLgMlEhpVCdG5Xrpj9TjVfoEL2u7Lmr+beEf6DU
Xx7U8zTSI4RgaK/ckkPbI8s+WzBLvDCY3pWc58m8f+Od8hDKQsIijATE0se2lkPa
pspN5y2nI9dphprCzSrr5VOtffxIt88OX+ecCEwyR64uvoQtcLPj
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,17 @@
# encoding: utf-8
# author: Richard Nixon
require 'helper'
require 'inspec/resource'
describe 'Inspec::Resources::RsaKey' do
let (:resource_key) { load_resource('key_rsa', 'test_certificate.rsa.key.pem')}
it 'parses the public key' do
_(resource_key.send('public_key')).must_match "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxi1Tp4dPQ+GU+RipsguU\nWT50a6fsBCpe+QT0YdW/7GG6kynRzR+fzQ0q1LDxpgqAH+eDIWEAFYoTPc8haAjZ\nvAYn7JlXUQpeoK7fc2BPgYA0lr33Ee0H9nqeZlnytQ+/EVUqqDx61cgeW3ARAK1I\nODwhuziuTi7XNu+HTx3feH4ohq/FppB26PYfJo1jCmt7YxHxl6AGrYrEX5zubQR0\nAtPAJzg0/aqDH5GJHJETjloIxh/KLnGlbG3DJylFU+vPxvns1TKM0dezg8UefXer\nRtxDAwSix7sNctXwa0xToc6O+e/StNPR0eLvILS8iR89fuML57Z4AGFWMNdqTYoj\nqwIDAQAB\n-----END PUBLIC KEY-----\n"
end
it 'decodes the key length' do
_(resource_key.send('key_length')).must_equal 2048
end
end

View file

@ -0,0 +1,86 @@
# encoding: utf-8
# author: Richard Nixon
require 'helper'
require 'inspec/resource'
describe 'Inspec::Resources::X509Certificate' do
let (:resource_cert) {
load_resource(
'x509_certificate',
'test_certificate.rsa.crt.pem'
)
}
it 'verify subject distingushed name' do
_(resource_cert.send('subject_dn')).must_match 'Inspec Test Certificate'
end
it 'parses the certificate subject' do
_(resource_cert.send('subject').CN).must_equal 'Inspec Test Certificate'
_(resource_cert.send('subject').emailAddress).must_equal 'support@chef.io'
end
it 'verify issue distingushed name' do
_(resource_cert.send('issuer_dn')).must_match 'Inspec Test CA'
end
it 'parses the issuer' do
_(resource_cert.send('issuer').CN).must_equal 'Inspec Test CA'
end
it 'parses the public key' do
_(resource_cert.send('public_key').to_s).must_match "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxi1Tp4dPQ+GU+RipsguU\nWT50a6fsBCpe+QT0YdW/7GG6kynRzR+fzQ0q1LDxpgqAH+eDIWEAFYoTPc8haAjZ\nvAYn7JlXUQpeoK7fc2BPgYA0lr33Ee0H9nqeZlnytQ+/EVUqqDx61cgeW3ARAK1I\nODwhuziuTi7XNu+HTx3feH4ohq/FppB26PYfJo1jCmt7YxHxl6AGrYrEX5zubQR0\nAtPAJzg0/aqDH5GJHJETjloIxh/KLnGlbG3DJylFU+vPxvns1TKM0dezg8UefXer\nRtxDAwSix7sNctXwa0xToc6O+e/StNPR0eLvILS8iR89fuML57Z4AGFWMNdqTYoj\nqwIDAQAB\n-----END PUBLIC KEY-----\n"
end
it 'can determine fingerprint' do
_(resource_cert.send('fingerprint')).must_equal '62bb500b0190ae47fd593c29a0b92ddbeb6c1eb6'
end
it 'can determine the key length' do
_(resource_cert.send('key_length')).must_equal 2048
end
it 'parses the serial number' do
_(resource_cert.send('serial')).must_equal 37
end
it 'parses the signature algorithm' do
_(resource_cert.send('signature_algorithm')).must_equal 'sha256WithRSAEncryption'
end
it 'parses the x.509 certificate version' do
_(resource_cert.send('version')).must_equal 2
end
it 'includes the standard extensions even if they are not in the certificate' do
_(resource_cert.send('extensions').length).must_equal 16
_(resource_cert.send('extensions')).must_include 'keyUsage'
_(resource_cert.send('extensions')).must_include 'extendedKeyUsage'
_(resource_cert.send('extensions')).must_include 'subjectAltName'
end
it 'parses the x.509 certificate extensions' do
_(resource_cert.send('extensions')['keyUsage']).must_include "Digital Signature"
_(resource_cert.send('extensions')['keyUsage']).must_include "Non Repudiation"
_(resource_cert.send('extensions')['keyUsage']).must_include "Data Encipherment"
_(resource_cert.send('extensions')['extendedKeyUsage']).must_include "TLS Web Server Authentication"
_(resource_cert.send('extensions')['extendedKeyUsage']).must_include "Code Signing"
_(resource_cert.send('extensions')['subjectAltName']).must_include "email:support@chef.io"
end
it 'parses missing x.509 certificate extensions' do
_(resource_cert.send('extensions')['nameConstraints']).wont_include "Fried Chicken"
end
it 'calculates the remaining days of validity' do
# Still valid
Time.stub :now, Time.new(2018, 2, 1, 1, 28, 57, '+00:00') do
_(resource_cert.send('validity_in_days')).must_equal 28
end
# Expired
Time.stub :now, Time.new(2018, 4, 1, 1, 28, 57, '+00:00') do
_(resource_cert.send('validity_in_days')).must_equal (-31)
end
end
end