Class: SCEP::PKIOperation::Request

Inherits:
Base
  • Object
show all
Defined in:
lib/scep/pki_operation/request.rb

Overview

Handles decoding or creation of a scep CSR request.

EJBCA Support

This requires tampering of the SCEP request. Please see #tamper_scep_message_type

Examples:

Get Certificates Ready

ra_cert = SCEP::DEFAULT_RA_CERTIFICATE
ra_key  = SCEP::DEFAULT_RA_PRIVATE_KEY

Decrypt SCEP Request

# Get the encrypted and signed scep request (der format) somehow
encrypted_scep_request = foo

# Make request & decrypt
request = SCEP::PKIOperation::Request.new(ra_cert, ra_key)
request.x509_store.add_certificate some_cert  # Add cert to verify
csr = decrypt(encrypted_scep_request)  # OpenSSL::X509::Request

Encrypt SCEP Request

# Get a CSR object, usually from an earlier step
some_csr = foo

# This is the target OpenSSL::X509::Certificate that we should encrypt this for.
# This will usually be the RA certificate of another SCEP server
target_encryption_cert  = bar

request = SCEP::PKIOperation::request.new(ra_cert, ra_key)
request.csr = some_csr

# Finally, encrypt it in der format
request.encrypt(target_encryption_cert)

Constant Summary

Constants inherited from Base

Base::DEFAULT_CIPHER_ALGORITHM

Instance Attribute Summary collapse

Attributes inherited from Base

#p7enc, #p7sign, #ra_keypair, #x509_store

Instance Method Summary collapse

Methods inherited from Base

#add_verification_certificate, #check_if_recipient_matches_ra_certificate_name, create_default_cipher, #sign_and_encrypt_raw, #unsign_and_unencrypt_raw, #wrap_array

Methods included from Loggable

#logger

Constructor Details

#initialize(ra_keypair) ⇒ Request

Returns a new instance of Request.



70
71
72
73
# File 'lib/scep/pki_operation/request.rb', line 70

def initialize(ra_keypair)
  super
  @tamper_scep_message_type = false
end

Instance Attribute Details

#csrOpenSSL::X509::Request

The certificate request

Returns:

  • (OpenSSL::X509::Request)


41
42
43
# File 'lib/scep/pki_operation/request.rb', line 41

def csr
  @csr
end

#tamper_scep_message_typeBoolean Also known as: tamper_scep_message_type?

TODO:

Need to figure out how to re-calculate the SCEP extended attributes signature, which will make this obsolete!

Whether we should tamper with the SCEP message type. This is required to work with some SCEP implementations, but this may cause verification to fail. Only affects encryption.

Examples:

Without Tampering

request = SCEP::PKIOperation::Request.new(keypair)
encrypted = request.encrypt(another_keypair)

# Here, `encrypted` will not be accepted by EJBCA, but ruby will parse it just fine
p7sign = OpenSSL::PKCS7.new(encrypted)
verified = p7sign.verify([keypair.certificate], nil, nil)
p verified # => true

With Tampering

request = SCEP::PKIOperation::Request.new(keypair)
request.tamper_scep_message_type = true
encrypted = request.encrypt(another_keypair)

# Here, `encrypted` will be accepted by EJBCA, but rejected by ruby
p7sign = OpenSSL::PKCS7.new(encrypted)
verified = p7sign.verify([keypair.certificate], nil, nil)
p verified # => false

Returns:

  • (Boolean)

    whether to tamper with the SCEP message type



67
68
69
# File 'lib/scep/pki_operation/request.rb', line 67

def tamper_scep_message_type
  @tamper_scep_message_type
end

Instance Method Details

#add_scep_message_type(pkcs7) ⇒ OpenSSL::PKCS7 (protected)

Note:

Don't tamper with the signer info once you've used this method!

Adds a required message type to the PKCS7 request. I can't believe I'm doing this...

Take a look at https://tools.ietf.org/html/draft-nourse-scep-11. Here, we're adding the signerInfo/messageType of "PKCSReq" inside of the "Signed PKCSReq."

Parameters:

  • pkcs7 (OpenSSL::PKCS7)

    a pkcs7 message

Returns:

  • (OpenSSL::PKCS7)

    a new pkcs7 message with the proper scep message type



150
151
152
153
154
155
156
157
158
159
# File 'lib/scep/pki_operation/request.rb', line 150

def add_scep_message_type(pkcs7)
  asn1 = OpenSSL::ASN1.decode(pkcs7.to_der)
  pkcs_cert_resp_signed = asn1.value[1].value[0]
  signer_info = pkcs_cert_resp_signed.value[4].value[0]
  authenticated_attributes = signer_info.value[3]
  authenticated_attributes.value << SCEP::ASN1.message_type(SCEP::ASN1::MESSAGE_TYPE_PKCS_REQ)
  # todo: broken?? --
  # recalculate_authenticated_attributes_digest(signer_info)
  return OpenSSL::PKCS7.new(asn1.to_der)
end

#challenge_passwordString?

Get the challenge password from the request, if any

Returns:

  • (String, nil)

    a STRING representation of the challenge password, NIL if there is no challenge password



83
84
85
86
# File 'lib/scep/pki_operation/request.rb', line 83

def challenge_password
  return nil unless challenge_password?
  csr_challenge_password.value.value.first.value
end

#challenge_password?Boolean

Returns TRUE if the request has a challenge password, FALSE otherwise.

Returns:

  • (Boolean)

    TRUE if the request has a challenge password, FALSE otherwise



76
77
78
# File 'lib/scep/pki_operation/request.rb', line 76

def challenge_password?
  csr && csr.challenge_password?
end

#csr_challenge_passwordObject (protected)

Gets the challenge password from the CSR



138
139
140
# File 'lib/scep/pki_operation/request.rb', line 138

def csr_challenge_password
  csr.send(:read_attributes_by_oid, 'challengePassword')
end

#decrypt(signed_and_encrypted_csr, verify = true) ⇒ OpenSSL::X509::Request

Decrypts a signed and encrypted csr. Sets #csr to the decrypted value

Parameters:

  • signed_and_encrypted_csr (String)

    the raw and encrypted

  • verify (Boolean) (defaults to: true)

    if TRUE, verifies against Base#x509_store. If FALSE, skips verification

Returns:

  • (OpenSSL::X509::Request)

    the raw CSR

Raises:



107
108
109
110
# File 'lib/scep/pki_operation/request.rb', line 107

def decrypt(signed_and_encrypted_csr, verify = true)
  raw_csr = unsign_and_unencrypt_raw(signed_and_encrypted_csr, verify)
  @csr = OpenSSL::X509::Request.new(raw_csr)
end

#encrypt(target_encryption_certs) ⇒ OpenSSL::PKCS7

Encrypt and sign the CSR

Parameters:

  • target_encryption_certs (OpenSSL::X509::Certificate)

    the certificat(s) we should encrypt this for

Returns:

  • (OpenSSL::PKCS7)

Raises:

  • (ArgumentError)


115
116
117
118
119
120
121
122
123
124
# File 'lib/scep/pki_operation/request.rb', line 115

def encrypt(target_encryption_certs)
  raise ArgumentError, '#csr must be an OpenSSL::X509::Request' unless
    csr.is_a?(OpenSSL::X509::Request)
  p7enc = sign_and_encrypt_raw(csr.to_der, target_encryption_certs)
  if tamper_scep_message_type?
    logger.info 'Tampering SCEP message type - request may be rejected by RA'
    p7enc = add_scep_message_type(p7enc)
  end
  p7enc
end

#proxy(signed_and_encrypted_csr, target_encryption_certs, verify = true) ⇒ OpenSSL::PKCS7

Decrypts a signed and encrypted payload and then re-encrypts it. #csr will contain the CSR object

Parameters:

  • signed_and_encrypted_csr (String)
  • target_encryption_certs (OpenSSL::X509::Certificate)

Returns:

  • (OpenSSL::PKCS7)


130
131
132
133
# File 'lib/scep/pki_operation/request.rb', line 130

def proxy(signed_and_encrypted_csr, target_encryption_certs, verify = true)
  decrypt(signed_and_encrypted_csr, verify)
  encrypt(target_encryption_certs)
end

#recalculate_authenticated_attributes_digest(signer_info) ⇒ Object (protected)

todo: this currently does not work! Kept here for future purposes



162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/scep/pki_operation/request.rb', line 162

def recalculate_authenticated_attributes_digest(signer_info)
  digest_algorithm = signer_info.value[2].value[0].sn # => "SHA256"

  # This is where this is not working - we need to hash the "authenticatedAttributes",
  # but this does not appear to be hashing the correct thing!
  authenticated_attributes = signer_info.value[3]

  new_digest = SCEP::ASN1.calculate_and_generate_pkcs7_signature_hash(
    authenticated_attributes.to_der,
    digest_algorithm)

  encrypted_digest = ra_keypair.private_key.private_encrypt(new_digest.to_der)
  signer_info.value.last.value = encrypted_digest
end