Module: XMLSecurity

Extended by:
FFI::Library
Defined in:
lib/xml_sec.rb

Defined Under Namespace

Modules: SignedDocument Classes: SignatureFailure, XmlSecDSigCtx, XmlSecError, XmlSecKeyInfoCtx, XmlSecKeyReq, XmlSecPtrList, XmlSecTransformCtx

Constant Summary collapse

XMLSEC_ERRORS_R_INVALID_DATA =
12
ErrorCallback =
FFI::Function.new(:void,
    [ :string, :int, :string, :string,     :string,      :int,   :string ]
) do |file,    line, func,    errorObject, errorSubject, reason, msg     |
  XMLSecurity.handle_xmlsec_error_callback(file, line, func, errorObject, errorSubject, reason, msg)
end

Class Method Summary collapse

Class Method Details

.handle_xmlsec_error_callback(*args) ⇒ Object



254
255
256
257
# File 'lib/xml_sec.rb', line 254

def self.handle_xmlsec_error_callback(*args)
  raise_exception_if_necessary(*args)
  xmlSecErrorsDefaultCallback(*args)
end

.mute(&block) ⇒ Object



266
267
268
269
270
# File 'lib/xml_sec.rb', line 266

def self.mute(&block)
  xmlSecErrorsDefaultCallbackEnableOutput(false)
  block.call
  xmlSecErrorsDefaultCallbackEnableOutput(true)
end

.raise_exception_if_necessary(file, line, func, errorObject, errorSubject, reason, msg) ⇒ Object



259
260
261
262
263
# File 'lib/xml_sec.rb', line 259

def self.raise_exception_if_necessary(file, line, func, errorObject, errorSubject, reason, msg)
  if reason == XMLSEC_ERRORS_R_INVALID_DATA
    raise XmlSecError.new(msg)
  end
end

.register_xml_id_attribute(doc, root) ⇒ Object

Register 'ID' as an XML id attribute so we can properly sign/validate signatures with references of the form:

<dsig:Reference URI="#IdOfElementImSigning" />

Which refer to another element in the same document like:

For more information see:

http://www.aleksey.com/xmlsec/faq.html#section_3_4


519
520
521
522
523
524
# File 'lib/xml_sec.rb', line 519

def self.register_xml_id_attribute(doc, root)
  idary = FFI::MemoryPointer.new(:pointer, 2)
  idary[0].put_pointer(0, FFI::MemoryPointer.from_string("ID"))
  idary[1].put_pointer(0, nil)
  XMLSecurity.xmlSecAddIDs(doc, root, idary)
end

.sign(reference_id, xml_string, private_key, certificate) ⇒ Object



449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/xml_sec.rb', line 449

def self.sign(reference_id, xml_string, private_key, certificate)
  doc = self.xmlParseMemory(xml_string, xml_string.size)
  raise SignatureFailure.new("could not parse XML document") if doc.null?

  canonicalization_method_id = self.xmlSecTransformExclC14NGetKlass
  sign_method_id = self.xmlSecOpenSSLTransformRsaSha1GetKlass

  sign_node = self.xmlSecTmplSignatureCreate(doc, canonicalization_method_id, sign_method_id, nil)

  raise SignatureFailure.new("failed to create signature template") if sign_node.null?
  root = self.xmlDocGetRootElement(doc)
  self.xmlAddChild(root, sign_node)

  XMLSecurity.register_xml_id_attribute(doc, root)

  ref_node = self.xmlSecTmplSignatureAddReference(sign_node, self.xmlSecOpenSSLTransformSha1GetKlass, nil, reference_id && "##{reference_id}", nil)
  raise SignatureFailure.new("failed to add a reference") if ref_node.null?

  envelope_result = self.xmlSecTmplReferenceAddTransform(ref_node, self.xmlSecTransformEnvelopedGetKlass)
  raise SignatureFailure.new("failed to add envelope transform to reference") if envelope_result.null?

  key_info_node = self.xmlSecTmplSignatureEnsureKeyInfo(sign_node, nil)
  raise SignatureFailure.new("failed to add key info") if key_info_node.null?

  digital_signature_context = self.xmlSecDSigCtxCreate(nil)
  raise SignatureFailure.new("failed to create signature context") if digital_signature_context.null?

  digital_signature_context[:signKey] = self.xmlSecOpenSSLAppKeyLoad(private_key, :xmlSecKeyDataFormatPem, nil, nil, nil)
  raise SignatureFailure.new("failed to load private pem key from #{private_key}") if digital_signature_context[:signKey].null?

  if self.xmlSecOpenSSLAppKeyCertLoad(digital_signature_context[:signKey], certificate, :xmlSecKeyDataFormatPem) < 0
    raise SignatureFailure.new("failed to load public cert from #{certificate}")
  end

  x509_data_node = self.xmlSecTmplKeyInfoAddX509Data(key_info_node)
  raise SignatureFailure.new("failed to add <dsig:X509Data/> node") if x509_data_node.null?

  if self.xmlSecDSigCtxSign(digital_signature_context, sign_node) < 0
    raise SignatureFailure.new("signature failed!")
  end

  ptr = FFI::MemoryPointer.new(:pointer, 1)
  sizeptr = FFI::MemoryPointer.new(:pointer, 1)
  self.xmlDocDumpFormatMemory(doc, ptr, sizeptr, 0)
  strptr = ptr.read_pointer

  return strptr.null? ? nil : strptr.read_string
ensure
  ptr.free if defined?(ptr) && ptr
  sizeptr.free if defined?(sizeptr) && sizeptr
  self.xmlFreeDoc(doc) if defined?(doc) && doc && !doc.null?
  self.xmlSecDSigCtxDestroy(digital_signature_context) if defined?(digital_signature_context) && digital_signature_context && !digital_signature_context.null?
  self.xmlFree(strptr) if defined?(strptr) && strptr && !strptr.null?
end

.xmlFree(ptr) ⇒ Object



244
245
246
# File 'lib/xml_sec.rb', line 244

def self.xmlFree(ptr)
  __xmlFree.call(ptr)
end

.xmlMalloc(size) ⇒ Object



240
241
242
# File 'lib/xml_sec.rb', line 240

def self.xmlMalloc(size)
  __xmlMalloc.call(size)
end