Module: XMLSecurity::SignedDocument
- Defined in:
- lib/xml_sec.rb
Instance Attribute Summary collapse
-
#validation_error ⇒ Object
readonly
Returns the value of attribute validation_error.
Class Method Summary collapse
Instance Method Summary collapse
-
#decrypt!(settings) ⇒ Object
replaces EncryptedData nodes with decrypted copies.
- #decrypt_node(settings, xmlstr) ⇒ Object
- #has_signature? ⇒ Boolean
- #signatures ⇒ Object
- #signed_roots ⇒ Object
- #validate(idp_cert_fingerprint, logger = nil) ⇒ Object
- #validate_doc(xml, pem) ⇒ Object
- #xmlsec_decrypt(xmlstr, private_key) ⇒ Object
Instance Attribute Details
#validation_error ⇒ Object (readonly)
Returns the value of attribute validation_error.
273 274 275 |
# File 'lib/xml_sec.rb', line 273 def validation_error @validation_error end |
Class Method Details
.format_cert(cert) ⇒ Object
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/xml_sec.rb', line 275 def self.format_cert(cert) # re-encode the certificate in the proper format # this snippet is from http://bugs.ruby-lang.org/issues/4421 rsa = cert.public_key modulus = rsa.n exponent = rsa.e oid = OpenSSL::ASN1::ObjectId.new("rsaEncryption") alg_id = OpenSSL::ASN1::Sequence.new([oid, OpenSSL::ASN1::Null.new(nil)]) ary = [OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)] pub_key = OpenSSL::ASN1::Sequence.new(ary) enc_pk = OpenSSL::ASN1::BitString.new(pub_key.to_der) subject_pk_info = OpenSSL::ASN1::Sequence.new([alg_id, enc_pk]) base64 = Base64.encode64(subject_pk_info.to_der) # This is the equivalent to the X.509 encoding used in >= 1.9.3 "-----BEGIN PUBLIC KEY-----\n#{base64}-----END PUBLIC KEY-----" end |
Instance Method Details
#decrypt!(settings) ⇒ Object
replaces EncryptedData nodes with decrypted copies
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/xml_sec.rb', line 385 def decrypt!(settings) if settings.encryption_configured? find("//xenc:EncryptedData", Onelogin::NAMESPACES).each do |node| decrypted_xml = decrypt_node(settings, node.to_s) if decrypted_xml decrypted_doc = LibXML::XML::Document.string(decrypted_xml) decrypted_node = decrypted_doc.root decrypted_node = self.import(decrypted_node) node.parent.next = decrypted_node node.parent.remove! end end end true end |
#decrypt_node(settings, xmlstr) ⇒ Object
401 402 403 404 405 406 407 |
# File 'lib/xml_sec.rb', line 401 def decrypt_node(settings, xmlstr) settings.all_private_keys.each do |key| result = xmlsec_decrypt(xmlstr, key) return result if result end nil end |
#has_signature? ⇒ Boolean
293 294 295 |
# File 'lib/xml_sec.rb', line 293 def has_signature? signatures.any? end |
#signatures ⇒ Object
312 313 314 |
# File 'lib/xml_sec.rb', line 312 def signatures @signatures ||= self.find("//ds:Signature", Onelogin::NAMESPACES) end |
#signed_roots ⇒ Object
297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/xml_sec.rb', line 297 def signed_roots signatures.map do |sig| ref = sig.find('.//ds:Reference', Onelogin::NAMESPACES).first signed_element_id = ref['URI'].sub(/^#/, '') if signed_element_id.empty? self.root else xpath_id_query = %Q(ancestor::*[@ID = "#{signed_element_id}"]) ref.find(xpath_id_query, Onelogin::NAMESPACES).first end end.compact end |
#validate(idp_cert_fingerprint, logger = nil) ⇒ Object
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/xml_sec.rb', line 316 def validate(idp_cert_fingerprint, logger = nil) # get cert from response base64_cert = self.find_first("//ds:X509Certificate", Onelogin::NAMESPACES).content cert_text = Base64.decode64(base64_cert) cert = OpenSSL::X509::Certificate.new(cert_text) # check cert matches registered idp cert, unless we explicitly skip this check unless idp_cert_fingerprint == '*' fingerprint = Digest::SHA1.hexdigest(cert.to_der) expected_fingerprint = idp_cert_fingerprint.gsub(":", "").downcase if fingerprint != expected_fingerprint @validation_error = "Invalid fingerprint (expected #{expected_fingerprint}, got #{fingerprint})" return false end end # create a copy of the document with the certificate removed doc = LibXML::XML::Document.new doc.root = doc.import(self.root) sigcert = doc.find_first("//ds:Signature/ds:KeyInfo", Onelogin::NAMESPACES) sigcert.remove! # validate it! validate_doc(doc.to_s(:indent => false), SignedDocument.format_cert(cert)) end |
#validate_doc(xml, pem) ⇒ Object
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
# File 'lib/xml_sec.rb', line 342 def validate_doc(xml, pem) kmgr = nil ctx = nil result = false begin # set up the keymgr kmgr = XMLSecurity.xmlSecKeysMngrCreate raise "failed initializing key mgr" if XMLSecurity.xmlSecOpenSSLAppDefaultKeysMngrInit(kmgr) < 0 key = XMLSecurity.xmlSecOpenSSLAppKeyLoadMemory(pem, pem.length, :xmlSecKeyDataFormatPem, nil, nil, nil) raise "failed loading key" if key.null? raise "failed adding key to mgr" if XMLSecurity.xmlSecOpenSSLAppDefaultKeysMngrAdoptKey(kmgr, key) < 0 # parse the xml doc = XMLSecurity.xmlSecParseMemory(xml, xml.length, 0) root = XMLSecurity.xmlDocGetRootElement(doc) XMLSecurity.register_xml_id_attribute(doc, root) # get the root node, and then find the signature node = XMLSecurity.xmlSecFindNode(root, "Signature", "http://www.w3.org/2000/09/xmldsig#") raise "Signature node not found" if node.null? # create the sig context ctx = XMLSecurity.xmlSecDSigCtxCreate(kmgr) raise "failed creating digital signature context" if ctx.null? # verify! raise "failed verifying dsig" if XMLSecurity.xmlSecDSigCtxVerify(ctx, node) < 0 result = ctx[:status] == :xmlSecDSigStatusSucceeded @validation_error = ctx[:status].to_s unless result rescue Exception => e @validation_error = e. ensure XMLSecurity.xmlSecDSigCtxDestroy(ctx) if ctx XMLSecurity.xmlFreeDoc(doc) if doc XMLSecurity.xmlSecKeysMngrDestroy(kmgr) if kmgr end result end |
#xmlsec_decrypt(xmlstr, private_key) ⇒ Object
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/xml_sec.rb', line 409 def xmlsec_decrypt(xmlstr, private_key) kmgr = nil ctx = nil doc = nil result = nil begin kmgr = XMLSecurity.xmlSecKeysMngrCreate raise "Failed initializing key mgr" if XMLSecurity.xmlSecOpenSSLAppDefaultKeysMngrInit(kmgr) < 0 key = XMLSecurity.xmlSecOpenSSLAppKeyLoad(private_key, :xmlSecKeyDataFormatPem, nil, nil, nil) raise "Failed loading key" if key.null? raise "Failed adding key to mgr" if XMLSecurity.xmlSecOpenSSLAppDefaultKeysMngrAdoptKey(kmgr, key) < 0 doc = XMLSecurity.xmlSecParseMemory(xmlstr, xmlstr.length, 0) raise "Failed to parse node" if doc.null? ctx = XMLSecurity.xmlSecEncCtxCreate(kmgr) raise "failed creating enc ctx" if ctx.null? node = XMLSecurity.xmlDocGetRootElement(doc) raise "failed decrypting" if XMLSecurity.xmlSecEncCtxDecrypt(ctx, node) < 0 ptr = FFI::MemoryPointer.new(:pointer, 1) sizeptr = FFI::MemoryPointer.new(:pointer, 1) XMLSecurity.xmlDocDumpFormatMemory(doc, ptr, sizeptr, 0) strptr = ptr.read_pointer result = strptr.null? ? nil : strptr.read_string rescue Exception => e @logger.warn "Could not decrypt: #{e.message}" if @logger ensure XMLSecurity.xmlSecEncCtxDestroy(ctx) if ctx XMLSecurity.xmlFreeDoc(doc) if doc XMLSecurity.xmlSecKeysMngrDestroy(kmgr) if kmgr end result end |