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, XmlSecTransformUriType

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

.disable_remote_references!(dsig_context) ⇒ Object



301
302
303
304
# File 'lib/xml_sec.rb', line 301

def self.disable_remote_references!(dsig_context)
  dsig_context[:transformCtx][:enabledUris] = XmlSecTransformUriType.conservative
  dsig_context[:enabledReferenceUris] = XmlSecTransformUriType.conservative
end

.disable_xslt_transforms!(dsig_context) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
# File 'lib/xml_sec.rb', line 289

def self.disable_xslt_transforms!(dsig_context)
  all_transforms = XMLSecurity.xmlSecTransformIdsGet

  (0...XMLSecurity.xmlSecPtrListGetSize(all_transforms)).each do |pos|
    transform = XMLSecurity.xmlSecPtrListGetItem(all_transforms, pos)
    unless transform == XMLSecurity.xmlSecTransformXsltGetKlass
      XMLSecurity.xmlSecPtrListAdd(dsig_context[:transformCtx][:enabledTransforms], transform)
      XMLSecurity.xmlSecDSigCtxEnableReferenceTransform(dsig_context, transform)
    end
  end
end

.handle_xmlsec_error_callback(*args) ⇒ Object



271
272
273
274
# File 'lib/xml_sec.rb', line 271

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

.mute(&block) ⇒ Object



283
284
285
286
287
# File 'lib/xml_sec.rb', line 283

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

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



276
277
278
279
280
# File 'lib/xml_sec.rb', line 276

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:

<elem ID="IdOfElementImSigning" />

For more information see:

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


556
557
558
559
560
561
# File 'lib/xml_sec.rb', line 556

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



486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/xml_sec.rb', line 486

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



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

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

.xmlMalloc(size) ⇒ Object



257
258
259
# File 'lib/xml_sec.rb', line 257

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