scep 0.0.1

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.
@@ -0,0 +1,117 @@
1
+ module SCEP
2
+
3
+ module PKIOperation
4
+ # Enables proxying a PKI SCEP request from the DSL to another SCEP server
5
+ # @example
6
+ # def pkioperation
7
+ # server = SCEP::Endpoint.new
8
+ # proxy = SCEP::Proxy.new(server, @ra_cert, @ra_pk)
9
+ # proxy.add_verification_certificate @some_cert # For decrypting the request - this way not "anyone" can decrypt
10
+ # response = proxy.forward_pki_request(request.raw_post)
11
+ # send_data response.p7enc_response.to_der
12
+ # end
13
+ class Proxy
14
+ attr_accessor :server
15
+
16
+ attr_accessor :ra_keypair
17
+
18
+ # Whether we should verify certificates when decrypting
19
+ # @return [Boolean]
20
+ attr_accessor :verify_request
21
+
22
+ attr_accessor :verify_response
23
+
24
+ # X509 certificates to verify against the request
25
+ # @return [Array<OpenSSL::X509::Certificate>] a list of certs
26
+ attr_accessor :request_verification_certificates
27
+
28
+ attr_accessor :response_verification_certificates
29
+
30
+ # @param [SCEP::Endpoint] server
31
+ # @param [Keypair] ra_keypair
32
+ def initialize(server, ra_keypair)
33
+ @server = server
34
+ @ra_keypair = ra_keypair
35
+ @verify_request = true
36
+ @verify_response = true
37
+ @request_verification_certificates = []
38
+ @response_verification_certificates = []
39
+ end
40
+
41
+ # Add certificates to verify when decrypting a request
42
+ # @param [OpenSSL::X509::Certificate]
43
+ def add_response_verification_certificate(cert)
44
+ @response_verification_certificates << cert
45
+ end
46
+
47
+ def add_request_verification_certificate(cert)
48
+ @request_verification_certificates << cert
49
+ end
50
+
51
+ # Don't verify certificates (possibly dangerous)
52
+ def no_verify!
53
+ no_verify_response!
54
+ no_verify_request!
55
+ end
56
+
57
+ def no_verify_response!
58
+ @verify_response = false
59
+ end
60
+
61
+ def no_verify_request!
62
+ @verify_request = false
63
+ end
64
+
65
+ # Proxies the raw post request to another SCEP server. Extracts CSR andpublic keys along the way
66
+ # @param [String] raw_post the raw post data. Should be a PKCS#7 der encoded message
67
+ # @return [SCEP::Proxy::Result] the results of
68
+ def forward_pki_request(raw_post)
69
+ # Decrypt the request and re-encrypt for the target SCEP server
70
+ request = SCEP::PKIOperation::Request.new(ra_keypair)
71
+ request_verification_certificates.each do |cert|
72
+ request.verify_against(cert)
73
+ end
74
+ reencrypted = request.proxy(raw_post, server.ra_certificate, verify_request).to_der
75
+
76
+ # Forward to SCEP server
77
+ http_response_body = server.pki_operation(reencrypted, true)
78
+
79
+ # Decrypt response and re-encrypt for the device
80
+ response = SCEP::PKIOperation::Response.new(ra_keypair)
81
+ response_verification_certificates.each do |cert|
82
+ response.verify_against(cert)
83
+ end
84
+ response_reencrypted = response.proxy(http_response_body, request.p7sign.certificates, verify_response)
85
+
86
+ # Package relevant information
87
+ return Result.new(request.csr, response.signed_certificates, response_reencrypted)
88
+ end
89
+
90
+ # Contains useful data from the results of proxying a SCEP request. Includes unencrypted
91
+ # CSRs, Signed certificates and encrypted response
92
+ class Result
93
+
94
+ # The CSR sent to us
95
+ # @return [OpenSSL::X509::Request]
96
+ attr_accessor :csr
97
+
98
+ # The signed certificates from the scep server
99
+ # @return [Array<OpenSSL::X509::Certificate>]
100
+ attr_accessor :signed_certificates
101
+
102
+ # The resulting encrypted result. Should be sent back to client as DER
103
+ # @return [OpenSSL::PKCS7]
104
+ attr_accessor :p7enc_response
105
+
106
+ # @param [OpenSSL::X509::Request] csr
107
+ # @param [Array<OpenSSL::X509::Certificate>] signed_certificates
108
+ # @param [OpenSSL::PKCS7] p7enc_response
109
+ def initialize(csr, signed_certificates, p7enc_response)
110
+ @csr = csr
111
+ @signed_certificates= signed_certificates
112
+ @p7enc_response = p7enc_response
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,70 @@
1
+ module SCEP
2
+
3
+ module PKIOperation
4
+
5
+ # Handles decoding or creation of a scep CSR request.
6
+ #
7
+ # @example Get Certificates Ready
8
+ # ra_cert = SCEP::DEFAULT_RA_CERTIFICATE
9
+ # ra_key = SCEP::DEFAULT_RA_PRIVATE_KEY
10
+ #
11
+ # @example Decrypt SCEP Request
12
+ # # Get the encrypted and signed scep request (der format) somehow
13
+ # encrypted_scep_request = foo
14
+ #
15
+ # # Make request & decrypt
16
+ # request = SCEP::PKIOperation::Request.new(ra_cert, ra_key)
17
+ # request.x509_store.add_certificate some_cert # Add cert to verify
18
+ # csr = decrypt(encrypted_scep_request) # OpenSSL::X509::Request
19
+ #
20
+ # @example Encrypt SCEP Request
21
+ # # Get a CSR object, usually from an earlier step
22
+ # some_csr = foo
23
+ #
24
+ # # This is the target OpenSSL::X509::Certificate that we should encrypt this for.
25
+ # # This will usually be the RA certificate of another SCEP server
26
+ # target_encryption_cert = bar
27
+ #
28
+ # request = SCEP::PKIOperation::request.new(ra_cert, ra_key)
29
+ # request.csr = some_csr
30
+ #
31
+ # # Finally, encrypt it in der format
32
+ # request.encrypt(target_encryption_cert)
33
+ #
34
+ class Request < Base
35
+
36
+ # The certificate request
37
+ # @return [OpenSSL::X509::Request]
38
+ attr_accessor :csr
39
+
40
+ # Decrypts a signed and encrypted csr. Sets {#csr} to the decrypted value
41
+ # @param [String] signed_and_encrypted_csr the raw and encrypted
42
+ # @param [Boolean] verify if TRUE, verifies against {#x509_store}. If FALSE, skips verification
43
+ # @raise [SCEP::PKIOperation::VerificationFailed] if `verify` is TRUE and the signed payload
44
+ # was *not* verified against the {#x509_store}.
45
+ # @return [OpenSSL::X509::Request] the raw CSR
46
+ def decrypt(signed_and_encrypted_csr, verify = true)
47
+ raw_csr = unsign_and_unencrypt_raw(signed_and_encrypted_csr, verify)
48
+ @csr = OpenSSL::X509::Request.new(raw_csr)
49
+ end
50
+
51
+ # Encrypt and sign the CSR
52
+ # @param [OpenSSL::X509::Certificate] target_encryption_certs the certificat(s) we should encrypt this for
53
+ # @return [OpenSSL::PKCS7]
54
+ def encrypt(target_encryption_certs)
55
+ raise ArgumentError, '#csr must be an OpenSSL::X509::Request' unless
56
+ csr.is_a?(OpenSSL::X509::Request)
57
+ sign_and_encrypt_raw(csr.to_der, target_encryption_certs)
58
+ end
59
+
60
+ # Decrypts a signed and encrypted payload and then re-encrypts it. {#csr} will contain the CSR object
61
+ # @param [String] signed_and_encrypted_csr
62
+ # @param [OpenSSL::X509::Certificate] target_encryption_certs
63
+ # @return [OpenSSL::PKCS7]
64
+ def proxy(signed_and_encrypted_csr, target_encryption_certs, verify = true)
65
+ decrypt(signed_and_encrypted_csr, verify)
66
+ encrypt(target_encryption_certs)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,86 @@
1
+ module SCEP
2
+ module PKIOperation
3
+
4
+ # Represents a SCEP response from the PKIOperation, which can do two of the following:
5
+ #
6
+ # * Parse a response form another SCEP server (useful for proxying)
7
+ # * Create our own SCEP response
8
+ #
9
+ # @example Get Certificates Ready
10
+ # ra_cert = SCEP::DEFAULT_RA_CERTIFICATE
11
+ # ra_key = SCEP::DEFAULT_RA_PRIVATE_KEY
12
+ #
13
+ # @example Decrypt a SCEP Response
14
+ # # get encrypted and signed scep response somehow
15
+ # encrypted_scep_response = foo
16
+ #
17
+ # # Make response & decrypt
18
+ # response = SCEP::PKIOperation::Response.new(ra_cert, ra_key)
19
+ # certs = response.decrypt(encrypted_scep_response) # Array of OpenSSL::X509::Certificate
20
+ #
21
+ # @example Create an Encrypted and Signed Response
22
+ # # This should be an OpenSSL::X509::Certificate signed by a CA.
23
+ # # This will be from an earlier part of the scep flow
24
+ # recently_signed_x509_cert = foo
25
+ #
26
+ # # This is the target OpenSSL::X509::Certificate that we should encrypt this for.
27
+ # # This will usually be the certificate of whomever signed the initial scep request
28
+ # target_encryption_cert = bar
29
+ #
30
+ # # Make the response objects and attach certs
31
+ # response = SCEP::PKIOperation::Response.new(ra_cert, ra_key)
32
+ # response.signed_certificates = recently_signed_x509_cert
33
+ #
34
+ # # Finally, encrypt it in a der format
35
+ # encrypted_binary_string = response.encrypt(target_encryption_cert)
36
+ #
37
+ class Response < Base
38
+
39
+
40
+ # Adds a single, or many certificates to encrypt and sign further
41
+ # @param [Array<OpenSSL::X509::Certificate>] certs
42
+ def signed_certificates=(certs)
43
+ @signed_certificates = wrap_array(certs)
44
+ end
45
+
46
+ # Gets any signed certificates that will be encrypted and signed in a SCEP format
47
+ # @return [Array<OpenSSL::X509::Certificate>]
48
+ def signed_certificates
49
+ @signed_certificates ||= []
50
+ end
51
+
52
+ # Decrypts a raw response and assigns {#signed_certificates}
53
+ # @param [String] raw_string the raw response
54
+ # @return [Array<OpenSSL::X509::Certificates>] the certificates that were contained
55
+ # in `raw_string`.
56
+ def decrypt(raw_string, verify = true)
57
+ p7raw = unsign_and_unencrypt_raw(raw_string, verify)
58
+ p7certs = OpenSSL::PKCS7.new(p7raw)
59
+ @signed_certificates = p7certs.certificates
60
+ end
61
+
62
+ # Takes the {#signed_certificates} attached to this object and return them in a format
63
+ # defined by SCEP.
64
+ # @param [Array<OpenSSL::X509::Certificate>] target_encryption_certs only those who possess a
65
+ # private key of one of the `target_encryption_certs` will be able to decrypt the resulting
66
+ # payload.
67
+ # @return [String] the signed and encrypted payload in binary (DER) format
68
+ def encrypt(target_encryption_certs)
69
+ raise ArgumentError, 'Must contain at least one of #signed_certificates' unless
70
+ signed_certificates.any?
71
+ p7certs = PKCS7CertOnly.new(signed_certificates)
72
+ sign_and_encrypt_raw(p7certs.to_der, target_encryption_certs)
73
+ end
74
+
75
+ # Decrypts a signed and encrypted response, gets the certificates ({#signed_certificates}) and then
76
+ # re-encrypts and signs it.
77
+ # @param [String] signed_and_encrypted_certs
78
+ # @param [OpenSSL::X509::Certificate] target_encryption_certs
79
+ # @return [OpenSSL::PKCS7]
80
+ def proxy(signed_and_encrypted_certs, target_encryption_certs, verify = true)
81
+ decrypt(signed_and_encrypted_certs, verify)
82
+ encrypt(target_encryption_certs)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,10 @@
1
+ module SCEP
2
+ module PKIOperation
3
+ class VerificationFailed < StandardError; end
4
+
5
+ autoload :Base, 'scep/pki_operation/base'
6
+ autoload :Proxy, 'scep/pki_operation/proxy'
7
+ autoload :Request, 'scep/pki_operation/request'
8
+ autoload :Response, 'scep/pki_operation/response'
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module SCEP
2
+ VERSION = '0.0.1'
3
+ end
data/lib/scep.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'logger'
2
+ require 'openssl'
3
+
4
+ require 'scep/version'
5
+
6
+ module SCEP
7
+ autoload :Endpoint, 'scep/endpoint'
8
+ autoload :PKIOperation, 'scep/pki_operation'
9
+ autoload :PKCS7CertOnly, 'scep/pkcs7_cert_only'
10
+ autoload :Keypair, 'scep/keypair'
11
+
12
+ # Allows backwards-compatibility between ruby 1.8.7 and newer versions
13
+ # @return [OpenSSL::PKCS7]
14
+ PKCS7 = defined?(OpenSSL::PKCS7::PKCS7) ? OpenSSL::PKCS7::PKCS7 : OpenSSL::PKCS7
15
+
16
+ class << self
17
+
18
+ # Allows you to set the SCEP logger
19
+ # @example
20
+ # SCEP.logger = Rails.logger
21
+ attr_writer :logger
22
+
23
+ # Gets the logger that the SCEP library will use
24
+ # @return [Logger]
25
+ def logger
26
+ @logger ||= default_logger
27
+ end
28
+
29
+ protected
30
+
31
+ def default_logger
32
+ defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
33
+ end
34
+ end
35
+
36
+ module Loggable
37
+ def logger
38
+ SCEP.logger
39
+ end
40
+ end
41
+ end
data/scep.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'scep/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'scep'
8
+ spec.version = SCEP::VERSION
9
+ spec.authors = ['Christopher Thornton']
10
+ spec.email = ['christopher.thornton@onelogin.com']
11
+ spec.summary = %q{SCEP libraries}
12
+ spec.description = %q{Makes development of SCEP services easier}
13
+ spec.homepage = 'https://github.com/onelogin/scep-gem'
14
+ spec.license = 'Proprietary'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ if RUBY_VERSION < '1.9'
22
+ spec.add_dependency 'httparty', '<= 0.11'
23
+ else
24
+ spec.add_dependency 'httparty'
25
+ end
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.7'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'pry', '~> 0.9.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.add_development_dependency 'webmock'
32
+ end
data/spec/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ require 'bundler'
3
+ Bundler.require :default
4
+ require 'scep'
5
+ require 'pry'
6
+
7
+ pry
@@ -0,0 +1,18 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIC7zCCAlgCCQDmERYwZBL3OzANBgkqhkiG9w0BAQUFADCBuzELMAkGA1UEBhMC
3
+ VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
4
+ HDAaBgNVBAoTE09uZUxvZ2luIFRlc3QgU3VpdGUxEzARBgNVBAsTClRlc3QgU3Vp
5
+ dGUxGjAYBgNVBAMTEW9uZWxvZ2luLXRlc3QuY29tMTAwLgYJKoZIhvcNAQkBFiFj
6
+ aHJpc3RvcGhlci50aG9ybnRvbkBvbmVsb2dpbi5jb20wHhcNMTUwMzA2MjEzOTEy
7
+ WhcNMTYwMzA1MjEzOTEyWjCBuzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
8
+ b3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xHDAaBgNVBAoTE09uZUxvZ2lu
9
+ IFRlc3QgU3VpdGUxEzARBgNVBAsTClRlc3QgU3VpdGUxGjAYBgNVBAMTEW9uZWxv
10
+ Z2luLXRlc3QuY29tMTAwLgYJKoZIhvcNAQkBFiFjaHJpc3RvcGhlci50aG9ybnRv
11
+ bkBvbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK5t7qT9
12
+ CJJ6KA2+BRCxAY7kyufZlCAUSs0c2pJ/WlT+JKA+B8/toUU7ymqri2CffgOdAazJ
13
+ Wk3o9isZGV2SFIag5cFBkoVH6xUS9VtoaGofJFa6GfLmhr7DOoUZm6VAwKJ9944M
14
+ hxvL901KjV8/UGI2jqk5RFoiRdiF1ItjlDGTAgMBAAEwDQYJKoZIhvcNAQEFBQAD
15
+ gYEAMQMOtDWkQqLfm41gSSXRNqjhT21jk0EpGdPFmHcyrNXPIkO+98jJxWYG9Wrf
16
+ +RO2Ov05RhgXyIk0OXvDf3QDikZIH4oLVKj4ospWAeLQN79KjWFVFnalbmnG6LDf
17
+ ApMWNzv5J9iMy9bRTQnV/E5vTW8iEKYqp7+EwVSJvnzZXLs=
18
+ -----END CERTIFICATE-----
@@ -0,0 +1,14 @@
1
+ -----BEGIN CERTIFICATE REQUEST-----
2
+ MIICMDCCAZkCAQAwgbsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
3
+ MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRwwGgYDVQQKExNPbmVMb2dpbiBUZXN0
4
+ IFN1aXRlMRMwEQYDVQQLEwpUZXN0IFN1aXRlMRowGAYDVQQDExFvbmVsb2dpbi10
5
+ ZXN0LmNvbTEwMC4GCSqGSIb3DQEJARYhY2hyaXN0b3BoZXIudGhvcm50b25Ab25l
6
+ bG9naW4uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCube6k/QiSeigN
7
+ vgUQsQGO5Mrn2ZQgFErNHNqSf1pU/iSgPgfP7aFFO8pqq4tgn34DnQGsyVpN6PYr
8
+ GRldkhSGoOXBQZKFR+sVEvVbaGhqHyRWuhny5oa+wzqFGZulQMCiffeODIcby/dN
9
+ So1fP1BiNo6pOURaIkXYhdSLY5QxkwIDAQABoDQwFQYJKoZIhvcNAQkHMQgTBmZv
10
+ b2JhcjAbBgkqhkiG9w0BCQIxDhMMT25lTG9naW4gSU5DMA0GCSqGSIb3DQEBBQUA
11
+ A4GBAFo8Jpl/kjSY7vmI3e40wuwbj1ttMydGV7e5iVuUA5vtOSoxPwZ2T4xpeq6U
12
+ fe3iaJ43G0pBdmAZhNh2mb/Rk6rCeZHPKJxt987yzPD2fvOv6HPWUqj0gqy2nl8b
13
+ /moA7meHhbWTBh2hhiYCJwu1jS97zWHY0pWA3moVT38HiLcb
14
+ -----END CERTIFICATE REQUEST-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXAIBAAKBgQCube6k/QiSeigNvgUQsQGO5Mrn2ZQgFErNHNqSf1pU/iSgPgfP
3
+ 7aFFO8pqq4tgn34DnQGsyVpN6PYrGRldkhSGoOXBQZKFR+sVEvVbaGhqHyRWuhny
4
+ 5oa+wzqFGZulQMCiffeODIcby/dNSo1fP1BiNo6pOURaIkXYhdSLY5QxkwIDAQAB
5
+ AoGABzcm8w5Ah4akF4VvwjQAQ3/1YMfgV79fJML3y62W+AMXUsWeHxzOWgXSJr13
6
+ 44RVeyLIifQ+VxC29itwoC1FygZerxsN6JnnbzYpqcF4XbgjYaVFiHI05PMWPGnv
7
+ Tv4cSmi4/BdTSXzC06FWFqxm2vejNd5S4dTTWrKUt3pLJGkCQQDkb6h7ZcQh2QUX
8
+ OV+7+xyM8WTC8eTGwOQ3MPrskBfIvmfAisUcmO6AesWS0eC9uoH2sxj/2Wcm+ve+
9
+ DZDONVKFAkEAw3oElz3g/UX88k2aHVqWhJdwcKvP0yhsiaiznVlGgx/tBdDN0Xz5
10
+ EgsVKJpZKPXIN7hLDQ8mAF/KyMbg/fDLNwJAEaFUfYGTK5GWRP6WlumAgJg40Jre
11
+ r0A/3MqY8x0D2OhpFYEgSV68OYpAKV6tW7dDRIj6CvT6cxW3fSGK5X9UCQJAW2YL
12
+ DXh2YZY+7kCQpdb8d+SjQ1tiYhYNodQKn7DlglwEJGr8QU5Q2znpW8HHnJpHUp5O
13
+ IT4LA7PGilhLTREwwwJBANvaRo0WdMyokz6LBYJxJqu/ZQO+Tyr7I9Yx3xMoluCO
14
+ nhzDimCUTwe5FcIZUohlhxMuQchJrnXIf9nsxnFC5e4=
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCEP::Endpoint do
4
+
5
+ # The CA and RA of our SCEP server
6
+ let(:ca_keypair) { generate_keypair }
7
+ let(:ra_keypair) { generate_keypair(ca_keypair)}
8
+
9
+ # Not signed by CA!
10
+ let(:invalid_ra_keypair) { generate_keypair }
11
+
12
+ let(:base_url) { 'http://scep.server' }
13
+ let(:p7certs_ca_ra) { SCEP::PKCS7CertOnly.new([ca_keypair.cert, ra_keypair.cert])}
14
+
15
+ let(:capabilities) { ['POSTPKIOperation', 'DES3' ]}
16
+
17
+ subject { SCEP::Endpoint.new(base_url) }
18
+
19
+ # General-purpose stubs
20
+ before do
21
+ stub_request(:get, "http://scep.server/?operation=GetCACert").
22
+ to_return(:status => 200, :body => p7certs_ca_ra.to_der, :headers => { 'Content-Type' => 'application/x-x509-ca-ra-cert'})
23
+
24
+ stub_request(:get, "http://scep.server/?operation=GetCACaps").
25
+ to_return(:status => 200, :body => capabilities.join("\n"), :headers => {})
26
+ end
27
+
28
+ describe '#ca_certificate' do
29
+ it 'calls #download_certificate when not previously called' do
30
+ expect(subject).to receive(:download_certificates)
31
+ subject.ca_certificate
32
+ end
33
+ end
34
+
35
+ describe '#ra_certificate' do
36
+ it 'calls #ca_certificate when not previously called' do
37
+ expect(subject).to receive(:ca_certificate)
38
+ subject.ra_certificate
39
+ end
40
+ end
41
+
42
+ describe '#supports_ra_certificate?' do
43
+ context 'when it does support an RA certificate' do
44
+ it 'returns true' do
45
+ expect(subject.supports_ra_certificate?).to eql(true)
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#capabilities' do
51
+ it 'gets a list of capabilities from the server' do
52
+ caps = subject.capabilities
53
+ expect(caps.to_a.sort).to eql(capabilities.sort)
54
+ end
55
+ end
56
+
57
+ describe '#post_pki_operation?' do
58
+ context 'when it supports the POSTPKIOperation' do
59
+ it 'returns true' do
60
+ expect(subject.post_pki_operation?).to eql(true)
61
+ end
62
+ end
63
+
64
+ context 'when it does not support the POSTPKIOperation' do
65
+ let(:capabilities) { ['Foo'] }
66
+ it 'returns false' do
67
+ expect(subject.post_pki_operation?).to eql(false)
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#download_certificates' do
73
+ context 'with a valid response' do
74
+
75
+ context 'with both an CA and RA certificate' do
76
+ it 'successfully assigns #ca_certificate and #ra_certificate' do
77
+ subject.download_certificates
78
+ expect(subject.ca_certificate.to_s).to eql(ca_keypair.cert.to_s)
79
+ expect(subject.ra_certificate.to_s).to eql(ra_keypair.cert.to_s)
80
+ end
81
+ end
82
+
83
+ context 'with only a CA certificate' do
84
+ before do
85
+ stub_request(:get, "http://scep.server/?operation=GetCACert").
86
+ to_return(:status => 200, :body => ca_keypair.cert.to_der, :headers => { 'Content-Type' => 'application/x-x509-ca-cert'})
87
+ end
88
+
89
+ it 'successfully assigns #ca_certificate' do
90
+ subject.download_certificates
91
+ expect(subject.ca_certificate.to_s).to eql(ca_keypair.cert.to_s)
92
+ end
93
+
94
+ it 'assigns #ca_certificate and #ra_certificate to be the same' do
95
+ subject.download_certificates
96
+ expect(subject.ca_certificate).to eql(subject.ra_certificate)
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ context 'with an invalid response' do
103
+ context 'when RA cert is not signed by the CA cert' do
104
+ let(:p7certs_ca_ra) { SCEP::PKCS7CertOnly.new([ca_keypair.cert, invalid_ra_keypair.cert])}
105
+
106
+ it 'raises a SCEP::ProtocolError' do
107
+ expect {
108
+ subject.download_certificates
109
+ }.to raise_error(SCEP::Endpoint::ProtocolError, 'RA certificate must be signed by CA certificate when using RA/CA cert combination')
110
+ end
111
+ end
112
+
113
+ context 'when the content type is not correct' do
114
+ before do
115
+ stub_request(:get, "http://scep.server/?operation=GetCACert").
116
+ to_return(:status => 200, :body => ca_keypair.cert.to_der, :headers => { 'Content-Type' => 'application/octet-stream'})
117
+ end
118
+
119
+ it 'raises a ProtocolError' do
120
+ expect {
121
+ subject.download_certificates
122
+ }.to raise_error(SCEP::Endpoint::ProtocolError, 'SCEP server returned invalid content type of application/octet-stream')
123
+ end
124
+ end
125
+
126
+
127
+ end
128
+
129
+ end
130
+
131
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCEP::Keypair do
4
+ let(:certificate) { OpenSSL::X509::Certificate.new read_fixture('self-signed.crt') }
5
+ let(:private_key) { OpenSSL::PKey::RSA.new read_fixture('self-signed.key') }
6
+
7
+ describe '#initialize' do
8
+ it 'creates a keypair with valid parameters' do
9
+ SCEP::Keypair.new(certificate, private_key)
10
+ end
11
+
12
+ it 'fails with invalid arguments' do
13
+ expect {
14
+ SCEP::Keypair.new(private_key, private_key)
15
+ }.to raise_error(ArgumentError)
16
+ end
17
+ end
18
+
19
+ describe '.read' do
20
+ it 'reads from the correct files' do
21
+ keypair = SCEP::Keypair.read(
22
+ fixture_path('self-signed.crt'),
23
+ fixture_path('self-signed.key')
24
+ )
25
+
26
+ expect(keypair.certificate.to_pem).to eql(certificate.to_pem)
27
+ expect(keypair.private_key.to_pem).to eql(private_key.to_pem)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCEP::PKCS7CertOnly do
4
+ let(:certificate1) { generate_keypair.certificate }
5
+ let(:certificate2) { generate_keypair.certificate }
6
+ let(:certs) { [certificate1, certificate2] }
7
+ let(:p7certs) { SCEP::PKCS7CertOnly.new(certs) }
8
+
9
+ describe '#initialize' do
10
+ it 'creates an object with certificates' do
11
+ p7certs = SCEP::PKCS7CertOnly.new(certs)
12
+ expect(p7certs.certificates).to eql(certs)
13
+ end
14
+ end
15
+
16
+ # Sufficiently tests #to_asn1
17
+ describe '#to_der' do
18
+ it 'encodes correctly such that it can be read by OpenSSL' do
19
+ expect {
20
+ der = p7certs.to_der
21
+ OpenSSL::PKCS7.new(der)
22
+ }.to_not raise_error
23
+ end
24
+
25
+ it 'encodes certificates in the correct order' do
26
+ der = p7certs.to_der
27
+ decoded_certs = OpenSSL::PKCS7.new(der).certificates
28
+ expect(decoded_certs[0].serial).to eql(certificate1.serial)
29
+ expect(decoded_certs[1].serial).to eql(certificate2.serial)
30
+ end
31
+ end
32
+
33
+ describe '#decode' do
34
+ it 'takes a PKCS7 cert-only der and creates a new PKCS7CertOnly' do
35
+ der = p7certs.to_der
36
+ decoded_p7certs = SCEP::PKCS7CertOnly.decode(der)
37
+ decoded_certs = decoded_p7certs.certificates
38
+ expect(decoded_certs[0].serial).to eql(certificate1.serial)
39
+ expect(decoded_certs[1].serial).to eql(certificate2.serial)
40
+ end
41
+ end
42
+ end