Coder Social home page Coder Social logo

xml-kit's Introduction

Xml::Kit

Build Status Gem Version

Xml::Kit is a toolkit for working with XML. It supports adding XML Digital Signatures and XML Encryption.

Installation

Add this line to your application's Gemfile:

gem 'xml-kit'

And then execute:

$ bundle

Or install it yourself as:

$ gem install xml-kit

Usage

# ./templates/item.builder

xml.instruct!
xml.Item ID: id do
  signature_for reference_id: id, xml: xml
  xml.Encrypted do
    encrypt_data_for xml: xml do |encrypted_xml|
      encrypted_xml.EncryptMe do
        encrypted_xml.Secret "secret"
      end
    end
  end
end
require 'xml/kit'

class Item
  include ::Xml::Kit::Templatable

  attr_reader :id

  def initialize(signing_key_pair, encryption_certificate)
    @id = ::Xml::Kit::Id.generate
    sign_with(signing_key_pair)
    encrypt_with(encryption_certificate)
  end

  def template_path
    current_path = File.expand_path(File.dirname(__FILE__))
    File.join(current_path, "./templates/item.builder")
  end
end

signing_key_pair = ::Xml::Kit::KeyPair.generate(use: :signing)
encryption_certificate = ::Xml::Kit::KeyPair.generate(use: :encryption).certificate
puts Item.new(signing_key_pair, encryption_certificate).to_xml

This will produce something like the following:

<?xml version="1.0" encoding="UTF-8"?>
<Item ID="_de3f6209-f842-400f-b1f7-85159aa90299">
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
      <Reference URI="#_de3f6209-f842-400f-b1f7-85159aa90299">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <DigestValue>3cH13yM8oR0sgWbhAx0mo536KMJDkVBynbPo7ehwJPE=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>ZCSx4dad704jz0Z6rCMsnOs/oyVH3YBeEF9wtk2UFmWBW+VfhoBKw7N50GnzmAGCHyI6zajRPdff5i6UMDz3fOzh7rlROnqW0TXoG77xPiIfqJswCKE/4LzzBLrEHVbdUz90U8n0M1Ahbesrt+pbf/NkJghpvDhJW+w6oho7dyU6k57C5D//kTaSb7DvKte3a7/o8xWvPRztQhYekK+RyWjK9k/lU4WEXk5rGbx+QrD9rgIXBQOdcSjOtUosZJADz7uFod6AWRak246U62Xahz8JxE/1N22LhZY9whvB7s+c76f1Uv44NtF87D0P8UXs0TVx2jsnhEwLsT7DPQ6jDg==</SignatureValue>
    <KeyInfo>
      <X509Data>
        <X509Certificate>MIIDQTCCAimgAwIBAgIBADANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJDQTEQMA4GA1UECAwHQWxiZXJ0YTEQMA4GA1UEBwwHQ2FsZ2FyeTEPMA0GA1UECgwGWG1sS2l0MQ8wDQYDVQQLDAZYbWxLaXQxDzANBgNVBAMMBlhtbEtpdDAeFw0xNzEyMzAxOTM1MjZaFw0xODAxMjkwNzAwMDBaMGQxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdBbGJlcnRhMRAwDgYDVQQHDAdDYWxnYXJ5MQ8wDQYDVQQKDAZYbWxLaXQxDzANBgNVBAsMBlhtbEtpdDEPMA0GA1UEAwwGWG1sS2l0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz8yvaY1zvqiSTpDc0vFgS00N0R05ytanViNy0YrcAvLH2njvLOYi8e5lWAjCUzoWTe6FMJQySIHuzr9NvZztlQBp5tydmxDsOFQ3DrBhiqtyafdCd5s8OQz1CekavgToTOm5VdZEWLD7HSCFvHXeuiS/zwEh4yYpJBAERtsSaYxT7L1wNggxc6F6UEfF1vwrGxMNH/OUi4okeS773esXeRlP5fHyMUvVC70KHauSYt/kjNR8/WuZBOY8/kFv3XiErf0PNSAYhyGHozabv8hJ2Bho0+HR12P6Xv+qKXFlDnMeAOHy23eShuUpCEBaEPAG4o8w4g/lrn0nJ+e9XrYaNQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCWybi6buMD75KBCcyd5aRtSKavYoDaZlzuohKh4z1HEzHS/fbpbxVQOrfXtuawZjNxcn62LFIe/w68EImzYkAss8LKojRcaKnIeF1/3Pzo6qfnmFpaecfYvX3ZTtw9JPOd4chy2X2WFAUMRscjSvjNvTBzFOXg60F0UMDnWOWMbc5Di/aZD8r2s/RDE3QxcUou8QhBMc2nYw77mQsXBnWmBeUA2aGP8OG/fOgtBKkZnNF8gx7wuodbYSmKAfFGx8+CGtnkwNr4/hXgd1qg5KmsAx+9VYozCjGKSkVUIqC5khy6N+1Pb5jMKrMQ+QU9zGhylWoJ2jiK65hzUUVUESIB</X509Certificate>
      </X509Data>
    </KeyInfo>
  </Signature>
  <Encrypted>
    <EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
          <CipherData>
            <CipherValue>rBJwm+gmL6eUHBZDXs2swIL3DiZ+MfmBPpM52eF0RWFtZv/gutY02KlsFLlmjc+DO7X5p9l1Br67FjGJrTdfSSqHf35cS1cioyaKLtgniSrD7Hf9d8qIuWt56dLWjmCi21cePMJHhNiFe5yRjFHNp5LZ9dX5hvNXjbn0+p90fj8zlO2TWZv9atooON3BaYGCezZlmG0bWyEmloqKHiGjqaKtkdeSKJDzoo/AvubDEgz56rinCpw26rEOg8BBd/KNfSXyDUifOOzXmn6myq+8+W/FFQ+6y+5SgtsbONRCqe2cKkNi3fYhilwLxWCaXFjONimEOkeG03yR5QnWhzEOpw==</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>45rM0phzM/S/vpiq8Ev+uQZ6WL5qZ8av0UDVzWAlHn6Qr7zWYjHea+NF94lKpvmTPWQDEnfv2UW8l0VdCLc+51zHjluRE/xJh31Gk3rVuRJtLioSge/N9UM45g901rE9</CipherValue>
      </CipherData>
    </EncryptedData>
  </Encrypted>
</Item>

Development

After checking out the repo, run bin/setup to install dependencies. Then, run bin/test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/xlgmokha/xml-kit.

License

The gem is available as open source under the terms of the MIT License.

xml-kit's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar mokhan avatar rngtng avatar xlgmokha avatar

Stargazers

 avatar  avatar  avatar

Forkers

rngtng j27lee

xml-kit's Issues

Please document a way to set digest_method and signature_method for signing

I'm updating some code that uses rsa-sha1 for signing to use xml-kit instead of doing things more manually. One of my first tests, I noticed that it switches to rsa-sha256 by default, which breaks compatibility with some external parties we've integrated with. I investigated the source and discovered that the class including ::Xml::Kit::Templatable can override digest_method and signature_method to control the signing algorithms, but that interface is totally undocumented.

I would like to not depend on undocumented internals, and would really appreciate some documented way to control the signing algorithms.

Diffrent Template for Encryption builder

Happy new year! Me again with (hopefully) a new challenge ๐Ÿ˜„

I'm now facing an issue were the output of encryption XML builder is not sufficient for my case - the EncryptedKey node is not accepted within the body/EncryptedData node, but required to be referenced in the header. Basically it should follow EXAMPLE 11 & 12 here: https://www.w3.org/TR/xmlenc-core1/#sec-Usage (or see below for full output).

So far I did dirty workarounds with ignoring the template and build her markup myself, but IMHO cooperating that into your gem would make things way nicer. Any suggestions how this could be achieved - easily? Is it just allowing a custom template_name in Template class?

Thx!

I need to build sth like:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:setupDebitCard="http://setupdebit.service.x2p.sia.net" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <env:Header>
      <wsse:Security env:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
         <xenc:EncryptedKey Id="EK-E2C32E59F27A1320A215468956686717" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
               <wsse:SecurityTokenReference>
                  <ds:X509Data>
                     <ds:X509IssuerSerial>
                        <ds:X509IssuerName>...</ds:X509IssuerName>
                        <ds:X509SerialNumber>...</ds:X509SerialNumber>
                     </ds:X509IssuerSerial>
                  </ds:X509Data>
               </wsse:SecurityTokenReference>
            </ds:KeyInfo>
            <xenc:CipherData>
               <xenc:CipherValue>...</xenc:CipherValue>
            </xenc:CipherData>
            <xenc:ReferenceList>
               <xenc:DataReference URI="#ED-E2C32E59F27A1320A215468956686868"/>
            </xenc:ReferenceList>
         </xenc:EncryptedKey>
         <wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="X509-E2C32E59F27A1320A215468956684692">...</wsse:BinarySecurityToken>   
         ... 
         ...
         ...     
      </wsse:Security>
   </env:Header>
   <env:Body wsu:Id="id-E2C32E59F27A1320A215468956684895" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
      <xenc:EncryptedData Id="ED-E2C32E59F27A1320A215468956686868" Type="http://www.w3.org/2001/04/xmlenc#Content" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
         <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
         <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
               <wsse:Reference URI="#EK-E2C32E59F27A1320A215468956686717"/>
            </wsse:SecurityTokenReference>
         </ds:KeyInfo>
         <xenc:CipherData>
            <xenc:CipherValue>...</xenc:CipherValue>
         </xenc:CipherData>
      </xenc:EncryptedData>
   </env:Body>
</env:Envelope>

XML signing fails when using "Id" instead of "ID" for xml.Object builder elements

Describe the bug

The xml-kit gem fails when an XML enveloping signature uses the correct casing for an Object's ID field. When a builder file contains xml.Object ID: "reference" ..., the signing works as expected. However, if the builder file contains xml.Object Id: "reference, the following error is reported:

irb(main):117:0> EnvelopingSignature.new('<data>123</data>', signing_key_pair).to_xml
Traceback (most recent call last):
       16: from /Users/.../ruby/2.7.4/lib/ruby/2.7.0/bundler/cli/exec.rb:63:in `load'
       15: from /Users/.../ruby/2.7.4/bin/irb:23:in `<top (required)>'
       14: from /Users/.../ruby/2.7.4/bin/irb:23:in `load'
       13: from /Users/.../ruby/2.7.4/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
       12: from (irb):117
       11: from /Users/.../test/local_gems/ruby/2.7.0/gems/xml-kit-0.4.0/lib/xml/kit/templatable.rb:30:in `to_xml'
       10: from /Users/.../test/local_gems/ruby/2.7.0/gems/xml-kit-0.4.0/lib/xml/kit/signatures.rb:38:in `complete'
        9: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/signed_document.rb:21:in `sign'
        8: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/signed_document.rb:21:in `each'
        7: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/signed_document.rb:22:in `block in sign'
        6: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/signature.rb:22:in `sign'
        5: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/signature.rb:22:in `each'
        4: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/signature.rb:22:in `block in sign'
        3: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/reference.rb:20:in `sign'
        2: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/reference.rb:62:in `calculate_digest_value'
        1: from /Users/.../test/local_gems/ruby/2.7.0/gems/xmldsig-0.7.0/lib/xmldsig/reference.rb:42:in `referenced_node'
Xmldsig::Reference::ReferencedNodeNotFound (Could not find the referenced node obj-EemEkjoVLi')

According to the XML Digital Signature specification, section 4.6 The Object Element (https://www.w3.org/TR/xmldsig-core/#sec-Object) the casing for an Object'd ID field should be Id instead of ID.

To Reproduce
Steps to reproduce the behavior:

  1. Execute the following lines in irb with the following Envelope template class and builder below:
> cert = nil
> key = OpenSSL::PKey::RSA.new(2048)
> passphrase = nil
> signing_key_pair = ::Xml::Kit::KeyPair.new(cert, key, passphrase, :signing)
# NOTE: THERE IS AN ERROR HERE (TypeError) which is another issue, but the one found here. The signing_key_pair will have been successfully created here

> Envelope.new("<data>123</data>", signing_key_pair).to_xml

Envelope class

class Envelope
  include ::Xml::Kit::Templatable

  def initialize(raw_xml, signing_key_pair)
    @reference_id = "obj-#{SecureRandom.alphanumeric(10)}"
    @raw_xml = raw_xml
    sign_with(signing_key_pair)
  end

  def template_path
    "<path_to_builder_file>"
  end
end

Example builder

xml.Signature 'xmlns' => ::Xml::Kit::Namespaces::XMLDSIG do
  xml.SignedInfo do
    xml.CanonicalizationMethod Algorithm: 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
    xml.SignatureMethod Algorithm: ::Xml::Kit::Namespaces::RSA_SHA256 # signature_method
    xml.Reference URI: "##{reference_id}" do
      xml.Transforms do
        xml.Transform Algorithm: 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' # TODO: add constant in xml-kit
      end
      xml.DigestMethod Algorithm: ::Xml::Kit::Namespaces::SHA256 # digest_method
      xml.DigestValue ''
    end
  end
  xml.SignatureValue ''
  xml.Object Id: reference_id do |object_xml|
    object_xml << raw_xml
  end
end
  1. Error produced: Xmldsig::Reference::ReferencedNodeNotFound

Expected behavior

The reference node should be able to be found using the correct casing for an Object's ID field (Id)

Screenshots

Desktop (please complete the following information):

Smartphone (please complete the following information):

Additional context

(possible) break in xml-enc semantics with inherited namespaces.

First off, I'm not 100% sure about this one. I've read the xml-enc spec, but all the places I could see this issue directly addressed were in sections explicitly called out as non-normative. Still, my best inference is that the non-normative portions were the algorithms being described - the semantics reflected should be valid.

That said, I think encrypted blocks are not handling inherited namespaces properly. I believe that if an EncryptedData element decrypts to xml content using namespaces that aren't defined within the decrypted content, the spec says to use namespaces in the current parsing context at the point the EncryptedData element was found.

This came up when generating documents that declared all their namespaces at the top level, then contained an encrypted element which contained a signature. The different data structure used to construct the encrypted element does not contain the namespace context for where it is being inserted in the parent document. This broke the signature, because the normalization algorithm treated the namespace identifiers as unknown, rather than as references to the namespaces declared in the root.

I worked around this by re-specifying the namespaces in the encrypted element. But my reading of the spec suggests that isn't strictly necessary, and the way I wanted to do it should work.

Is my reading right? If so, is this a thing that could be added without a ridiculous amount of work?

Suggestions for some minor tweaks

After a couple of experiments I got the setup in a use state. It now looks like:

data_id =  ::Xml::Kit::Id.generate
key_info = ::Xml::Kit::KeyInfo.new(x509: @encryption_certificate.x509) do |x|
  x.encrypted_key = ::Xml::Kit::EncryptedKey.new(
    asymmetric_cipher: ::Xml::Kit::Crypto.cipher_for(
      ::Xml::Kit::Crypto::OaepCipher::ALGORITHM, x.x509_data.public_key
    ),
    symmetric_cipher: ::Xml::Kit::Crypto::SymmetricCipher.new
  )
end

encrypted_data = Xml::Kit::EncryptedData.new(raw_xml,
  symmetric_cipher:  key_info.encrypted_key.symmetric_cipher,
  asymmetric_cipher: key_info.encrypted_key.asymmetric_cipher,
  key_info: key_info
)

After that I can render the request just based on encrypted_data object. That's nice, although I see some minor tweaks!?

  • Set symmetric_cipher to default value: ::Xml::Kit::Crypto::SymmetricCipher.new for EncryptedKey (and/or EncryptedData).
  • Add id to EncryptedData in similar manner as EncryptedKey
  • Extend EncryptedData constructor: either allow x509_data to be set, or set cyphers based on given key_info?

with that setup could be shrunk down to e.g.

key_info = ::Xml::Kit::KeyInfo.new(x509: @encryption_certificate.x509) do |x|
  x.encrypted_key = ::Xml::Kit::EncryptedKey.new(
    asymmetric_cipher: ::Xml::Kit::Crypto.cipher_for(
      ::Xml::Kit::Crypto::OaepCipher::ALGORITHM, x.x509_data.public_key
    )
  )
end

encrypted_data = Xml::Kit::EncryptedData.new(raw_xml, key_info: key_info)

Lastly a question on how are EncryptedKey and KeyInfo hierarchal associated -
EncryptedKey -> KeyInfo or KeyInfo <-EncryptedKey? I saw key_info on EncryptedKey but IMHO it's never set..?

Let me know what you think and I'm happy to provide a PR! Thanks again

How to use encryption with OaepCipher

Hey hey

I wonder how to use encryption with OaepCipher. I expected sth like:

cert = OpenSSL::X509::Certificate.new(File.read("/gem/credentials/cert"))
enc = Xml::Kit::Encryption.new(body, cert.public_key, {
  asymmetric_algorithm: ::Xml::Kit::Crypto::OaepCipher::ALGORITHM
})

But ::Xml::Kit::Crypto::OaepCipher has only ALGORITHMS and yet I can't actually read from code swapping the asymmetric_algorithm value would actually matter - could you please showcase, please?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.