Class: SCEP::Endpoint

Inherits:
Object
  • Object
show all
Includes:
HTTParty, Loggable
Defined in:
lib/scep/endpoint.rb

Overview

TODO:

GetCACaps

Handles making requests to a SCEP server and storing the RA and CA certs. Currently uses the URL defined in the config/endpoints.yml file.

Examples:

scep_endpoint = SCEP::Endpoint.new('https://scep-server-url.com')
# Downloads RA, CA certs
puts scep_endpoint.ca_certificate # => OpenSSL::X509::Certificate
puts scep_endpoint.ra_certificate

Defined Under Namespace

Classes: ProtocolError

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Loggable

#logger

Constructor Details

#initialize(base_uri, default_options = {}) ⇒ Endpoint

Returns a new instance of Endpoint.



30
31
32
# File 'lib/scep/endpoint.rb', line 30

def initialize(base_uri, default_options = {})
  @default_options = default_options.merge(:base_uri => base_uri)
end

Instance Attribute Details

#ca_certificateOpenSSL::X509::Certificate

Gets the CA certificate. Will automatically download the CA certificate from the server if it has not yet been downloaded.

Returns:

  • (OpenSSL::X509::Certificate)

Raises:

  • (ProtocolError)

    if the SCEP server does not return valid certs



38
39
40
41
# File 'lib/scep/endpoint.rb', line 38

def ca_certificate
  download_certificates if @ca_certificate.nil?
  return @ca_certificate
end

#default_optionsObject

Returns the value of attribute default_options.



28
29
30
# File 'lib/scep/endpoint.rb', line 28

def default_options
  @default_options
end

#ra_certificateOpenSSL::X509::Certificate

Note:

This will return the CA certificate if the SCEP server does not support RA certs.

Gets the RA certificate.

Returns:

  • (OpenSSL::X509::Certificate)

Raises:

  • (ProtocolError)

    if the SCEP server does not return valid certs



48
49
50
51
# File 'lib/scep/endpoint.rb', line 48

def ra_certificate
  # Force download of CA, possibly RA certificate
  @ra_certificate || ca_certificate
end

Instance Method Details

#capabilitiesSet<String>

Gets server capabilities. Memoized version of #fetch_capabilities

Returns:

  • (Set<String>)

    a set of capabilities



80
81
82
# File 'lib/scep/endpoint.rb', line 80

def capabilities
  @capabilities || fetch_capabilities
end

#download_certificatesHTTParty::Response Also known as: get_ca_cert

Downloads RA and CA certificates from the SCEP server using the GetCACert operation. Will give #ra_certificate and #ca_certificate values.

Returns:

  • (HTTParty::Response)

    the response from the SCEP server.

Raises:



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/scep/endpoint.rb', line 63

def download_certificates
  logger.debug 'Downloading CA, possibly RA certificate from SCEP server'
  response = scep_request 'GetCACert'
  if response.content_type == 'application/x-x509-ca-cert' # Only a CA cert
    handle_ca_only_cert(response.body)
  elsif response.content_type == 'application/x-x509-ca-ra-cert'
    handle_ca_ra_cert(response.body)
  else
    fail ProtocolError, "SCEP server returned invalid content type of #{response.content_type}"
  end
  return response
end

#fetch_capabilitiesSet<String>

Gets server capabilities. Always triggers a download of capabilities

Returns:

  • (Set<String>)

    a set of capabilities



86
87
88
89
90
91
92
93
# File 'lib/scep/endpoint.rb', line 86

def fetch_capabilities
  logger.debug 'Getting SCEP endpoint capabilities'
  response = scep_request 'GetCACaps'
  caps = response.body.strip.split("\n")
  @capabilities = Set.new(caps)
  logger.debug "SCEP endpoint supports capabilities: #{@capabilities.inspect}"
  return @capabilities
end

#handle_ca_only_cert(response_body) ⇒ Object (protected)



141
142
143
144
145
146
# File 'lib/scep/endpoint.rb', line 141

def handle_ca_only_cert(response_body)
  logger.debug 'SCEP server does not support RA certificate - only using CA cert'
  @ca_certificate = OpenSSL::X509::Certificate.new(response_body)
rescue StandardError
  fail ProtocolError, 'SCEP server did not return parseable X509::Certificate'
end

#handle_ca_ra_cert(response_body) ⇒ Object (protected)



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

def handle_ca_ra_cert(response_body)
  logger.debug 'SCEP server has both RA and CA certificate'

  begin
    pcerts = PKCS7CertOnly.decode(response_body)
  rescue StandardError
    fail ProtocolError, 'SCEP server did not return a parseable PKCS#7'
  end

  fail ProtocolError,
       'SCEP server did not return two certificates in PKCS#7 cert chain' unless
    pcerts.certificates.length == 2


  unless pcerts.certificates[1].verify(pcerts.certificates[0].public_key)
    fail ProtocolError,
      'RA certificate must be signed by CA certificate when using RA/CA cert combination'
  end

  @ca_certificate = pcerts.certificates[0]
  @ra_certificate = pcerts.certificates[1]
end

#pki_operation(payload) ⇒ String

@todo: handle GET PKIOperations @todo: verify actually signed by CA?

Parameters:

  • payload (String)

    the raw payload to send

Returns:

  • (String)

    the response body



130
131
132
133
134
135
136
137
# File 'lib/scep/endpoint.rb', line 130

def pki_operation(payload)
  response = scep_request('PKIOperation', payload, true)
  if response.content_type != 'application/x-pki-message'
    raise ProtocolError,
      "SCEP PKIOperation didn't return content-type of application/x-pki-message (returned #{response.content_type})"
  end
  return response.body
end

#post_pki_operation?Boolean

Whether the SCEP endpoint supports the POSTPKIOperation

Returns:

  • (Boolean)

    TRUE if it is supported, FALSE otherwise



97
98
99
# File 'lib/scep/endpoint.rb', line 97

def post_pki_operation?
  capabilities.include?('POSTPKIOperation')
end

#scep_request(operation, message = nil, is_post = false) ⇒ HTTParty::Response

Executes a SCEP request.

Parameters:

  • operation (String)

    the SCEP operation to perform

  • message (String) (defaults to: nil)

    an optional message to send

Returns:

  • (HTTParty::Response)

    the httparty response



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/scep/endpoint.rb', line 105

def scep_request(operation, message = nil, is_post = false)
  query = { :operation => operation }
  query[:message] = message unless message.nil?
  if is_post
    logger.debug "Executing POST ?operation=#{operation}"
    response = self.class.post '/', { :query => { :operation => operation}, :body => message }.merge(default_options)
  else
    logger.debug "Executing GET ?operation=#{operation}&message=#{message}"
    response = self.class.get '/', { :query => query }.merge(default_options)
  end

  if response.code != 200
    logger.debug "Response body:"
    logger.debug response.body
    raise ProtocolError, "SCEP request returned non-200 code of #{response.code}"
  end

  return response
end

#supports_ra_certificate?Boolean

Checks to see if the SCEP server supports the RA certificate

Returns:

  • (Boolean)


55
56
57
# File 'lib/scep/endpoint.rb', line 55

def supports_ra_certificate?
  ca_certificate != ra_certificate
end