Class: WSDL::Security::Verifier

Inherits:
Object
  • Object
show all
Defined in:
lib/wsdl/security/verifier.rb,
lib/wsdl/security/verifier/base.rb,
lib/wsdl/security/verifier/reference_validator.rb,
lib/wsdl/security/verifier/signature_validator.rb,
lib/wsdl/security/verifier/structure_validator.rb,
lib/wsdl/security/verifier/timestamp_validator.rb,
lib/wsdl/security/verifier/certificate_resolver.rb,
lib/wsdl/security/verifier/certificate_validator.rb,
lib/wsdl/security/verifier/element_position_validator.rb

Overview

Verifies XML Digital Signatures and timestamps in SOAP responses.

This class coordinates multiple validation steps to provide comprehensive security verification including:

  • Structural Validation — Detects XML Signature Wrapping (XSW) attacks
  • Certificate Resolution — Extracts or validates signing certificates
  • Certificate Validation — Checks validity period and trust chain
  • Reference Verification — Validates digests of signed elements
  • Signature Verification — Cryptographic validation of SignatureValue
  • Timestamp Validation — Freshness checks to prevent replay attacks

The verification process follows W3C XML Signature Best Practices, running structural checks before expensive cryptographic operations.

Examples:

Basic verification

verifier = Verifier.new(response_xml)
if verifier.valid?
  puts "Signature is valid!"
  puts "Signed elements: #{verifier.signed_elements}"
else
  puts "Signature invalid: #{verifier.errors}"
end

With a provided certificate

verifier = Verifier.new(response_xml, certificate: server_cert)
verifier.valid?

With certificate chain validation

verifier = Verifier.new(response_xml, trust_store: :system)
verifier.valid?

With custom CA certificates

verifier = Verifier.new(response_xml, trust_store: [ca_cert])
verifier.valid?

With timestamp validation (enabled by default)

verifier = Verifier.new(response_xml, clock_skew: 600)
verifier.valid?  # checks signature AND timestamp

Disable timestamp validation

verifier = Verifier.new(response_xml, validate_timestamp: false)
verifier.valid?  # checks signature only

See Also:

Defined Under Namespace

Classes: Base, CertificateResolver, CertificateValidator, ElementPositionValidator, ReferenceValidator, SignatureValidator, StructureValidator, TimestampValidator

Constant Summary collapse

SecurityNS =

Local aliases for namespace constants

Constants::NS::Security
SignatureNS =

Alias for XML Signature namespace constants.

Returns:

  • (Module)
Constants::NS::Signature
VALID_ID_PATTERN =

Pattern for valid XML element IDs (NCName production). Inherited from Base which defines the canonical pattern.

Returns:

  • (Regexp)

See Also:

Base::VALID_ID_PATTERN

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(xml, certificate: nil, trust_store: nil, check_validity: true, validate_timestamp: true, clock_skew: 300) ⇒ Verifier

Creates a new Verifier instance.

rubocop:disable Metrics/ParameterLists

Parameters:

  • xml (String)

    the SOAP response XML

  • certificate (OpenSSL::X509::Certificate, String, nil) (defaults to: nil)

    optional certificate to use for verification instead of extracting from the message

  • trust_store (OpenSSL::X509::Store, Symbol, String, Array, nil) (defaults to: nil)

    trust store for certificate chain validation:

    • :system — Use system default CA certificates
    • String — Path to CA bundle file or directory
    • Array<OpenSSL::X509::Certificate> — Array of trusted CA certificates
    • OpenSSL::X509::Store — Pre-configured certificate store
    • nil — Skip chain validation (default)
  • check_validity (Boolean) (defaults to: true)

    whether to check the certificate's validity period (not_before and not_after). Default: true

  • validate_timestamp (Boolean) (defaults to: true)

    whether to validate timestamp freshness. Default: true. Per WS-Security spec, timestamps are optional, so this only validates when a timestamp is present.

  • clock_skew (Integer) (defaults to: 300)

    acceptable clock skew in seconds for timestamp validation. Default: 300 (5 minutes). Per WS-I BSP guidance.



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/wsdl/security/verifier.rb', line 104

def initialize(xml, certificate: nil, trust_store: nil, check_validity: true,
               validate_timestamp: true, clock_skew: 300)
  # rubocop:enable Metrics/ParameterLists
  @document = parse_document(xml)
  @provided_certificate = certificate
  @trust_store = trust_store
  @check_validity = check_validity
  @validate_timestamp = validate_timestamp
  @clock_skew = clock_skew
  @errors = []
  @certificate = normalize_certificate(certificate) if certificate
end

Instance Attribute Details

#certificateOpenSSL::X509::Certificate? (readonly)

Returns certificate used for verification.

Returns:

  • (OpenSSL::X509::Certificate, nil)

    certificate used for verification



82
83
84
# File 'lib/wsdl/security/verifier.rb', line 82

def certificate
  @certificate
end

#errorsArray<String> (readonly)

Returns errors encountered during verification.

Returns:

  • (Array<String>)

    errors encountered during verification



79
80
81
# File 'lib/wsdl/security/verifier.rb', line 79

def errors
  @errors
end

Instance Method Details

#digest_algorithmString?

Returns the digest algorithm URI from the first reference.

Returns:

  • (String, nil)

    the algorithm URI (e.g., 'http://www.w3.org/2001/04/xmlenc#sha256')



173
174
175
# File 'lib/wsdl/security/verifier.rb', line 173

def digest_algorithm
  signed_info_node&.at_xpath('ds:Reference/ds:DigestMethod/@Algorithm', ns)&.value
end

#signature_algorithmString?

Returns the signature algorithm URI.

Returns:

  • (String, nil)

    the algorithm URI (e.g., 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256')



166
167
168
# File 'lib/wsdl/security/verifier.rb', line 166

def signature_algorithm
  signature_validator&.signature_algorithm
end

#signature_present?Boolean

Returns whether a signature is present in the document.

Returns:

  • (Boolean)

    true if a ds:Signature element exists



143
144
145
# File 'lib/wsdl/security/verifier.rb', line 143

def signature_present?
  structure_validator.signature_present?
end

#signed_element_idsArray<String>

Returns the IDs of all signed elements.

Returns:

  • (Array<String>)

    element IDs (without # prefix)



150
151
152
153
154
# File 'lib/wsdl/security/verifier.rb', line 150

def signed_element_ids
  return [] unless signature_present?

  reference_validator.referenced_ids
end

#signed_elementsArray<String>

Returns the names of all signed elements.

Returns:

  • (Array<String>)

    element names (e.g., ['Body', 'Timestamp'])



159
160
161
# File 'lib/wsdl/security/verifier.rb', line 159

def signed_elements
  signed_element_ids.filter_map { |id| find_element_by_id(id)&.name }
end

#timestampHash?

Returns the parsed timestamp information.

Returns:

  • (Hash, nil)

    hash with :created_at and :expires_at keys, or nil if no timestamp present



199
200
201
# File 'lib/wsdl/security/verifier.rb', line 199

def timestamp
  timestamp_validator.timestamp
end

#timestamp_errorsArray<String>

Returns timestamp validation errors.

Each call re-evaluates timestamp freshness against the current time, ensuring errors reflect the latest state.

Returns:

  • (Array<String>)

    timestamp validation errors



209
210
211
212
213
# File 'lib/wsdl/security/verifier.rb', line 209

def timestamp_errors
  validator = timestamp_validator
  validator.valid?
  validator.errors.dup
end

#timestamp_present?Boolean

Returns whether a timestamp is present in the document.

Returns:

  • (Boolean)

    true if wsu:Timestamp exists in the Security header



180
181
182
# File 'lib/wsdl/security/verifier.rb', line 180

def timestamp_present?
  timestamp_validator.timestamp_present?
end

#timestamp_valid?Boolean

Returns whether the timestamp is valid (fresh).

Returns true if:

  • No timestamp is present (timestamps are optional per spec)
  • Timestamp is present and within acceptable time bounds

Returns:

  • (Boolean)

    true if timestamp is valid or not present



191
192
193
# File 'lib/wsdl/security/verifier.rb', line 191

def timestamp_valid?
  timestamp_validator.valid?
end

#valid?Boolean

Returns whether the signature (and timestamp, if enabled) is valid.

Phases 1-5 (structural, certificate, reference, and signature verification) are deterministic for a given document and are cached after the first evaluation.

Phase 6 (timestamp freshness) is time-dependent and is re-evaluated on every call so that a Verifier held across a time boundary correctly detects expiration.

Returns:

  • (Boolean)

    true if signature is present and valid



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/wsdl/security/verifier.rb', line 128

def valid?
  verify_crypto unless defined?(@crypto_valid)

  # Reset errors to the crypto baseline for this evaluation
  @errors = @crypto_errors.dup

  return false unless @crypto_valid

  # Phase 6: Timestamp freshness — always re-evaluate
  run_timestamp_validation
end