lib/signed_xml/signature.rb in signed_xml-0.0.1 vs lib/signed_xml/signature.rb in signed_xml-1.0.0
- old
+ new
@@ -1,31 +1,65 @@
require 'base64'
module SignedXml
class Signature
include DigestMethodResolution
+ include Fingerprinting
+ include Logging
attr_accessor :here
+ attr_accessor :public_key
+ attr_accessor :certificate_store
def initialize(here)
@here = here
end
def is_verified?
is_signed_info_verified? && are_reference_digests_verified?
end
+ def sign(private_key, certificate = nil)
+ compute_digests_for_references
+ sign_signed_info(private_key)
+ set_certificate_data(certificate)
+ end
+
private
def is_signed_info_verified?
- public_key.verify(digester_for_id(signed_info.signature_method), decoded_value, signed_info.apply_transforms)
+ return false if public_key.nil?
+
+ result = public_key.verify(new_digester_for_id(signed_info.signature_method), decoded_value, signed_info.apply_transforms)
+ logger.info "verification of signature value [#{value}] failed" unless result
+ result
end
def are_reference_digests_verified?
references.all?(&:is_verified?)
end
+ def sign_signed_info(private_key)
+ data = signed_info.apply_transforms
+ logger.debug "data to sign: [#{data}]"
+ digester = new_digester_for_id(signed_info.signature_method)
+ logger.debug "digester: #{digester.inspect}"
+ sig_value = private_key.sign(digester, data)
+ logger.debug "signature value (before Base64 encoding): [#{sig_value}]"
+ value_node.content = Base64Transform.new.apply(sig_value)
+ logger.debug "SignatureValue set to [#{value}]"
+ end
+
+ def compute_digests_for_references
+ references.each(&:compute_and_set_digest_value)
+ end
+
+ def set_certificate_data(certificate)
+ x509_cert_data_node.content = certificate.to_pem.split("\n")[1..-2].join("\n") if certificate
+ logger.debug "set certificate data to [#{x509_cert_data}]"
+ end
+
def references
@references ||= init_references
end
def init_references
@@ -41,26 +75,55 @@
def decoded_value
@decoded_value ||= Base64.decode64 value
end
def value
- @value ||= here.at_xpath('//ds:SignatureValue', ds: XMLDSIG_NS).text.strip
+ @value ||= value_node.text.strip
end
+ def value_node
+ @value_node ||= here.at_xpath('//ds:SignatureValue', ds: XMLDSIG_NS)
+ end
+
def signed_info
@signed_info ||= SignedInfo.new(here.at_xpath("//ds:SignedInfo", ds: XMLDSIG_NS))
end
+ def certificate_store
+ @certificate_store ||= {}
+ end
+
def public_key
- @public_key ||= x509_certificate.public_key
+ # If the user provided a certificate store, we MUST only use a
+ # key which matches the one in the signature's KeyInfo. Otherwise,
+ # use the key in the signature.
+ @public_key ||= if certificate_store.any?
+ logger.debug "using cert store #{certificate_store}"
+ if certificate_store.has_key? x509_cert_fingerprint
+ certificate_store[x509_cert_fingerprint].public_key
+ else
+ logger.warn "Store has no certificate with fingerprint #{x509_cert_fingerprint}. Signature validation will fail."
+ nil
+ end
+ else
+ x509_certificate.public_key
+ end
end
def x509_certificate
- @x509_certificate ||= OpenSSL::X509::Certificate.new(certificate(x509_cert_data))
+ OpenSSL::X509::Certificate.new(certificate(x509_cert_data))
end
+ def x509_cert_fingerprint
+ @x509_cert_fingerprint ||= fingerprint(x509_certificate.to_der)
+ end
+
def x509_cert_data
- @x509_cert_data ||= here.at_xpath("//ds:X509Certificate", ds: XMLDSIG_NS).text
+ x509_cert_data_node.text
+ end
+
+ def x509_cert_data_node
+ @x509_cert_data_node ||= here.at_xpath("//ds:X509Certificate", ds: XMLDSIG_NS)
end
def certificate(data)
"-----BEGIN CERTIFICATE-----\n#{wrap_text(data, 64)}-----END CERTIFICATE-----\n"
end