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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/README.md +144 -0
- data/Rakefile +2 -0
- data/lib/scep/endpoint.rb +169 -0
- data/lib/scep/keypair.rb +69 -0
- data/lib/scep/pkcs7_cert_only.rb +86 -0
- data/lib/scep/pki_operation/base.rb +121 -0
- data/lib/scep/pki_operation/proxy.rb +117 -0
- data/lib/scep/pki_operation/request.rb +70 -0
- data/lib/scep/pki_operation/response.rb +86 -0
- data/lib/scep/pki_operation.rb +10 -0
- data/lib/scep/version.rb +3 -0
- data/lib/scep.rb +41 -0
- data/scep.gemspec +32 -0
- data/spec/console +7 -0
- data/spec/fixtures/self-signed.crt +18 -0
- data/spec/fixtures/self-signed.csr +14 -0
- data/spec/fixtures/self-signed.key +15 -0
- data/spec/lib/scep/endpoint_spec.rb +131 -0
- data/spec/lib/scep/keypair_spec.rb +30 -0
- data/spec/lib/scep/pkcs7_cert_only_spec.rb +42 -0
- data/spec/lib/scep/pki_operation/base_spec.rb +56 -0
- data/spec/lib/scep/pki_operation/proxy_spec.rb +45 -0
- data/spec/lib/scep/pki_operation/request_spec.rb +55 -0
- data/spec/lib/scep/pki_operation/response_spec.rb +36 -0
- data/spec/spec_helper.rb +61 -0
- metadata +171 -0
@@ -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
|
data/lib/scep/version.rb
ADDED
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,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
|